diff --git a/libs/openal-soft/alc/backends/oboe.cpp b/libs/openal-soft/alc/backends/oboe.cpp index 38f048cb..f3b3d58d 100644 --- a/libs/openal-soft/alc/backends/oboe.cpp +++ b/libs/openal-soft/alc/backends/oboe.cpp @@ -10,6 +10,7 @@ #include "alnumeric.h" #include "core/device.h" #include "core/logging.h" +#include "ringbuffer.h" #include "oboe/Oboe.h" @@ -188,13 +189,15 @@ void OboePlayback::stop() } -struct OboeCapture final : public BackendBase { +struct OboeCapture final : public BackendBase, public oboe::AudioStreamCallback { OboeCapture(DeviceBase *device) : BackendBase{device} { } oboe::ManagedStream mStream; - std::vector mSamples; - uint mLastAvail{0u}; + RingBufferPtr mRing{nullptr}; + + oboe::DataCallbackResult onAudioReady(oboe::AudioStream *oboeStream, void *audioData, + int32_t numFrames) override; void open(const char *name) override; void start() override; @@ -203,6 +206,14 @@ struct OboeCapture final : public BackendBase { uint availableSamples() override; }; +oboe::DataCallbackResult OboeCapture::onAudioReady(oboe::AudioStream*, void *audioData, + int32_t numFrames) +{ + mRing->write(audioData, static_cast(numFrames)); + return oboe::DataCallbackResult::Continue; +} + + void OboeCapture::open(const char *name) { if(!name) @@ -217,8 +228,8 @@ void OboeCapture::open(const char *name) ->setSampleRateConversionQuality(oboe::SampleRateConversionQuality::High) ->setChannelConversionAllowed(true) ->setFormatConversionAllowed(true) - ->setBufferCapacityInFrames(static_cast(mDevice->BufferSize)) - ->setSampleRate(static_cast(mDevice->Frequency)); + ->setSampleRate(static_cast(mDevice->Frequency)) + ->setCallback(this); /* Only use mono or stereo at user request. There's no telling what * other counts may be inferred as. */ @@ -240,7 +251,7 @@ void OboeCapture::open(const char *name) } /* FIXME: This really should support UByte, but Oboe doesn't. We'll need to - * use a temp buffer and convert. + * convert. */ switch(mDevice->FmtType) { @@ -263,22 +274,13 @@ void OboeCapture::open(const char *name) if(result != oboe::Result::OK) throw al::backend_exception{al::backend_error::DeviceError, "Failed to create stream: %s", oboe::convertToText(result)}; - if(static_cast(mDevice->BufferSize) > mStream->getBufferCapacityInFrames()) - throw al::backend_exception{al::backend_error::DeviceError, - "Buffer size too large (%u > %d)", mDevice->BufferSize, - mStream->getBufferCapacityInFrames()}; - auto buffer_result = mStream->setBufferSizeInFrames(static_cast(mDevice->BufferSize)); - if(!buffer_result) - throw al::backend_exception{al::backend_error::DeviceError, - "Failed to set buffer size: %s", oboe::convertToText(buffer_result.error())}; - else if(buffer_result.value() < static_cast(mDevice->BufferSize)) - throw al::backend_exception{al::backend_error::DeviceError, - "Failed to set large enough buffer size (%u > %d)", mDevice->BufferSize, - buffer_result.value()}; - mDevice->BufferSize = static_cast(buffer_result.value()); TRACE("Got stream with properties:\n%s", oboe::convertToText(mStream.get())); + /* Ensure a minimum ringbuffer size of 100ms. */ + mRing = RingBuffer::Create(maxu(mDevice->BufferSize, mDevice->Frequency/10), + static_cast(mStream->getBytesPerFrame()), false); + mDevice->DeviceName = name; } @@ -292,23 +294,6 @@ void OboeCapture::start() void OboeCapture::stop() { - /* Capture any unread samples before stopping. Oboe drops whatever's left - * in the stream. - */ - if(auto availres = mStream->getAvailableFrames()) - { - const auto avail = std::max(static_cast(availres.value()), mLastAvail); - const size_t frame_size{static_cast(mStream->getBytesPerFrame())}; - const size_t pos{mSamples.size()}; - mSamples.resize(pos + avail*frame_size); - - auto result = mStream->read(&mSamples[pos], availres.value(), 0); - uint got{bool{result} ? static_cast(result.value()) : 0u}; - if(got < avail) - std::fill_n(&mSamples[pos + got*frame_size], (avail-got)*frame_size, al::byte{}); - mLastAvail = 0; - } - const oboe::Result result{mStream->stop()}; if(result != oboe::Result::OK) throw al::backend_exception{al::backend_error::DeviceError, "Failed to stop stream: %s", @@ -316,38 +301,10 @@ void OboeCapture::stop() } uint OboeCapture::availableSamples() -{ - /* Keep track of the max available frame count, to ensure it doesn't go - * backwards. - */ - if(auto result = mStream->getAvailableFrames()) - mLastAvail = std::max(static_cast(result.value()), mLastAvail); - - const auto frame_size = static_cast(mStream->getBytesPerFrame()); - return static_cast(mSamples.size()/frame_size) + mLastAvail; -} +{ return static_cast(mRing->readSpace()); } void OboeCapture::captureSamples(al::byte *buffer, uint samples) -{ - const auto frame_size = static_cast(mStream->getBytesPerFrame()); - if(const size_t storelen{mSamples.size()}) - { - const auto instore = static_cast(storelen / frame_size); - const uint tocopy{std::min(samples, instore) * frame_size}; - std::copy_n(mSamples.begin(), tocopy, buffer); - mSamples.erase(mSamples.begin(), mSamples.begin() + tocopy); - - buffer += tocopy; - samples -= tocopy/frame_size; - if(!samples) return; - } - - auto result = mStream->read(buffer, static_cast(samples), 0); - uint got{bool{result} ? static_cast(result.value()) : 0u}; - if(got < samples) - std::fill_n(buffer + got*frame_size, (samples-got)*frame_size, al::byte{}); - mLastAvail = std::max(mLastAvail, samples) - samples; -} +{ mRing->read(buffer, samples); } } // namespace diff --git a/libs/openal-soft/alc/backends/opensl.cpp b/libs/openal-soft/alc/backends/opensl.cpp index 85a5f483..696bf85a 100644 --- a/libs/openal-soft/alc/backends/opensl.cpp +++ b/libs/openal-soft/alc/backends/opensl.cpp @@ -878,7 +878,6 @@ void OpenSLCapture::captureSamples(al::byte *buffer, uint samples) { const uint update_size{mDevice->UpdateSize}; const uint chunk_size{update_size * mFrameSize}; - const auto silence = (mDevice->FmtType == DevFmtUByte) ? al::byte{0x80} : al::byte{0}; /* Read the desired samples from the ring buffer then advance its read * pointer. @@ -907,39 +906,52 @@ void OpenSLCapture::captureSamples(al::byte *buffer, uint samples) i += rem; } - mRing->readAdvance(adv_count); SLAndroidSimpleBufferQueueItf bufferQueue{}; - if LIKELY(mDevice->Connected.load(std::memory_order_acquire)) + if(likely(mDevice->Connected.load(std::memory_order_acquire))) { const SLresult result{VCALL(mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bufferQueue)}; PRINTERR(result, "recordObj->GetInterface"); - if UNLIKELY(SL_RESULT_SUCCESS != result) + if(unlikely(SL_RESULT_SUCCESS != result)) { mDevice->handleDisconnect("Failed to get capture buffer queue: 0x%08x", result); bufferQueue = nullptr; } } + if(unlikely(!bufferQueue) || adv_count == 0) + return; - if LIKELY(bufferQueue) + /* For each buffer chunk that was fully read, queue another writable buffer + * chunk to keep the OpenSL queue full. This is rather convulated, as a + * result of the ring buffer holding more elements than are writable at a + * given time. The end of the write vector increments when the read pointer + * advances, which will "expose" a previously unwritable element. So for + * every element that we've finished reading, we queue that many elements + * from the end of the write vector. + */ + mRing->readAdvance(adv_count); + + SLresult result{SL_RESULT_SUCCESS}; + auto wdata = mRing->getWriteVector(); + if(likely(adv_count > wdata.second.len)) { - SLresult result{SL_RESULT_SUCCESS}; - auto wdata = mRing->getWriteVector(); - std::fill_n(wdata.first.buf, wdata.first.len*chunk_size, silence); - for(size_t i{0u};i < wdata.first.len && SL_RESULT_SUCCESS == result;i++) + auto len1 = std::min(wdata.first.len, adv_count-wdata.second.len); + auto buf1 = wdata.first.buf + chunk_size*(wdata.first.len-len1); + for(size_t i{0u};i < len1 && SL_RESULT_SUCCESS == result;i++) { - result = VCALL(bufferQueue,Enqueue)(wdata.first.buf + chunk_size*i, chunk_size); + result = VCALL(bufferQueue,Enqueue)(buf1 + chunk_size*i, chunk_size); PRINTERR(result, "bufferQueue->Enqueue"); } - if(wdata.second.len > 0) + } + if(wdata.second.len > 0) + { + auto len2 = std::min(wdata.second.len, adv_count); + auto buf2 = wdata.second.buf + chunk_size*(wdata.second.len-len2); + for(size_t i{0u};i < len2 && SL_RESULT_SUCCESS == result;i++) { - std::fill_n(wdata.second.buf, wdata.second.len*chunk_size, silence); - for(size_t i{0u};i < wdata.second.len && SL_RESULT_SUCCESS == result;i++) - { - result = VCALL(bufferQueue,Enqueue)(wdata.second.buf + chunk_size*i, chunk_size); - PRINTERR(result, "bufferQueue->Enqueue"); - } + result = VCALL(bufferQueue,Enqueue)(buf2 + chunk_size*i, chunk_size); + PRINTERR(result, "bufferQueue->Enqueue"); } } }