// // Created by wolverindev on 07.02.20. // #include "SoundIO.h" #include #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(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(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(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; } } }