145 lines
5.1 KiB
C++
145 lines
5.1 KiB
C++
//
|
|
// Created by wolverindev on 07.02.20.
|
|
//
|
|
|
|
#include "SoundIO.h"
|
|
#include <algorithm>
|
|
#include "../../logger.h"
|
|
|
|
using namespace tc::audio;
|
|
|
|
SoundIORecord::SoundIORecord(struct ::SoundIoDevice *device) : device_handle{device} {
|
|
soundio_device_ref(device);
|
|
}
|
|
|
|
SoundIORecord::~SoundIORecord() {
|
|
soundio_device_unref(this->device_handle);
|
|
}
|
|
|
|
bool SoundIORecord::impl_start(std::string &error) {
|
|
assert(this->device_handle);
|
|
|
|
this->buffer = soundio_ring_buffer_create(nullptr, kChunkSize * sizeof(float) * 2); //2 Channels
|
|
if(!buffer) {
|
|
error = "failed to allocate the buffer";
|
|
return false;
|
|
}
|
|
soundio_ring_buffer_clear(this->buffer);
|
|
|
|
this->stream = soundio_instream_create(this->device_handle);
|
|
if(!this->stream) {
|
|
error = "out of memory";
|
|
return false;
|
|
}
|
|
|
|
this->stream->userdata = this;
|
|
this->stream->format = SoundIoFormatFloat32LE;
|
|
this->stream->software_latency = 0.02;
|
|
|
|
this->stream->overflow_callback = [](auto str) {
|
|
auto handle = reinterpret_cast<SoundIORecord*>(str->userdata);
|
|
log_info(category::audio, tr("Having an overflow on {}"), handle->device_handle->id);
|
|
};
|
|
|
|
this->stream->error_callback = [](auto str, int err) {
|
|
auto handle = reinterpret_cast<SoundIORecord*>(str->userdata);
|
|
log_info(category::audio, tr("Having an error on {}: {}. Aborting recording."), handle->device_handle->id, soundio_strerror(err));
|
|
handle->stream_invalid = true;
|
|
};
|
|
|
|
this->stream->read_callback = [](struct SoundIoInStream *str, int frame_count_min, int frame_count_max) {
|
|
auto handle = reinterpret_cast<SoundIORecord*>(str->userdata);
|
|
handle->read_callback(frame_count_min, frame_count_max);
|
|
};
|
|
|
|
if(auto err = soundio_instream_open(this->stream); err) {
|
|
error = soundio_strerror(err) + std::string{" (open)"};
|
|
soundio_instream_destroy(this->stream);
|
|
this->stream = nullptr;
|
|
return false;
|
|
}
|
|
|
|
if(false && this->stream->layout_error) {
|
|
error = std::string{"failed to set audio layout: "} + soundio_strerror(this->stream->layout_error);
|
|
soundio_instream_destroy(this->stream);
|
|
this->stream = nullptr;
|
|
return false;
|
|
}
|
|
|
|
if(auto err = soundio_instream_start(this->stream); err) {
|
|
error = soundio_strerror(err) + std::string{" (start)"};
|
|
soundio_instream_destroy(this->stream);
|
|
this->stream = nullptr;
|
|
return false;
|
|
}
|
|
|
|
//TODO: Test for interleaved channel layout!
|
|
|
|
return true;
|
|
}
|
|
|
|
void SoundIORecord::impl_stop() {
|
|
if(!this->stream) return;
|
|
|
|
soundio_instream_destroy(this->stream);
|
|
this->stream = nullptr;
|
|
|
|
soundio_ring_buffer_destroy(this->buffer);
|
|
this->buffer = nullptr;
|
|
}
|
|
|
|
void SoundIORecord::read_callback(int frame_count_min, int frame_count_max) {
|
|
const struct SoundIoChannelLayout *layout = &this->stream->layout;
|
|
|
|
struct SoundIoChannelArea *areas;
|
|
|
|
int frames_left{frame_count_max};
|
|
int buffer_samples = soundio_ring_buffer_free_count(this->buffer) / (sizeof(float) * layout->channel_count);
|
|
|
|
while(frames_left > 0) {
|
|
int frame_count{frames_left};
|
|
if(frame_count > buffer_samples)
|
|
frame_count = buffer_samples;
|
|
if(auto err = soundio_instream_begin_read(this->stream, &areas, &frame_count); err) {
|
|
log_error(category::audio, tr("Failed to begin read from input stream buffer: {}"), soundio_strerror(err));
|
|
return;
|
|
}
|
|
|
|
/* test for interleaved */
|
|
{
|
|
char* begin = areas[0].ptr - sizeof(float);
|
|
for(size_t channel{0}; channel < layout->channel_count; channel++) {
|
|
if((begin += sizeof(float)) != areas[channel].ptr) {
|
|
log_error(category::audio, tr("Expected interleaved buffer, which it isn't"));
|
|
return;
|
|
}
|
|
|
|
if(areas[channel].step != sizeof(float) * layout->channel_count) {
|
|
log_error(category::audio, tr("Invalid step size for channel {}"), channel);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
const auto length = sizeof(float) * layout->channel_count * frame_count;
|
|
memcpy(soundio_ring_buffer_write_ptr(this->buffer), areas[0].ptr, length);
|
|
soundio_ring_buffer_advance_write_ptr(this->buffer, length);
|
|
buffer_samples -= frame_count;
|
|
frames_left -= frame_count;
|
|
|
|
if(buffer_samples == 0) {
|
|
std::lock_guard consumer{this->consumer_lock};
|
|
const auto byte_count = soundio_ring_buffer_fill_count(this->buffer);
|
|
const auto frame_count = byte_count / (sizeof(float) * layout->channel_count);
|
|
for(auto& consumer : this->_consumers)
|
|
consumer->consume(soundio_ring_buffer_read_ptr(this->buffer), frame_count, layout->channel_count);
|
|
soundio_ring_buffer_advance_read_ptr(this->buffer, byte_count);
|
|
buffer_samples = frame_count;
|
|
}
|
|
|
|
if(auto err = soundio_instream_end_read(this->stream); err) {
|
|
log_error(category::audio, tr("Failed to close input stream buffer: {}"), soundio_strerror(err));
|
|
return;
|
|
}
|
|
}
|
|
} |