// // Created by wolverindev on 07.02.20. // #include "SoundIO.h" #include #include "../../logger.h" using namespace tc::audio; SoundIOPlayback::SoundIOPlayback(struct ::SoundIoDevice *device) : device_handle{device} { soundio_device_ref(device); } SoundIOPlayback::~SoundIOPlayback() { soundio_device_unref(this->device_handle); } bool SoundIOPlayback::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_outstream_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->underflow_callback = [](auto str) { auto handle = reinterpret_cast(str->userdata); log_info(category::audio, tr("Having an underflow 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 playback."), handle->device_handle->id, soundio_strerror(err)); handle->stream_invalid = true; }; this->stream->write_callback = [](struct SoundIoOutStream *str, int frame_count_min, int frame_count_max) { auto handle = reinterpret_cast(str->userdata); handle->write_callback(frame_count_min, frame_count_max); }; if(auto err = soundio_outstream_open(this->stream); err) { error = soundio_strerror(err) + std::string{" (open)"}; soundio_outstream_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_outstream_destroy(this->stream); this->stream = nullptr; return false; } if(auto err = soundio_outstream_start(this->stream); err) { error = soundio_strerror(err) + std::string{" (start)"}; soundio_outstream_destroy(this->stream); this->stream = nullptr; return false; } //TODO: Test for interleaved channel layout! return true; } void SoundIOPlayback::impl_stop() { if(!this->stream) return; soundio_outstream_destroy(this->stream); this->stream = nullptr; soundio_ring_buffer_destroy(this->buffer); this->buffer = nullptr; } void SoundIOPlayback::write_callback(int frame_count_min, int frame_count_max) { const struct SoundIoChannelLayout *layout = &this->stream->layout; struct SoundIoChannelArea *areas; int frames_left{frame_count_min}, err; if(frames_left < 120) frames_left = 120; if(frames_left > frame_count_max) frames_left = frame_count_max; while(frames_left > 0) { int frame_count{frames_left}; auto buffered = soundio_ring_buffer_fill_count(this->buffer) / (sizeof(float) * layout->channel_count); if(frame_count > buffered) { if(buffered == 0) { const auto length = sizeof(float) * frame_count * layout->channel_count; this->fill_buffer(soundio_ring_buffer_write_ptr(this->buffer), frame_count, layout->channel_count); soundio_ring_buffer_advance_write_ptr(this->buffer, length); } else frame_count = buffered; } if((err = soundio_outstream_begin_write(this->stream, &areas, &frame_count))) { log_warn(category::audio, tr("Failed to begin a write to the soundio buffer: {}"), 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) * frame_count * layout->channel_count; memcpy(areas[0].ptr, soundio_ring_buffer_read_ptr(this->buffer), length); soundio_ring_buffer_advance_read_ptr(this->buffer, length); if((err = soundio_outstream_end_write(this->stream))) { log_warn(category::audio, tr("Failed to end a write to the soundio buffer: {}"), err); return; } frames_left -= frame_count; } }