mirror of
https://github.com/love2d/megasource.git
synced 2026-08-20 04:30:45 +02:00
Apply OpenAL-soft patch to fix Android audio recording.
kcat/openal-soft@7f1d9725b2 kcat/openal-soft@d7367aeee0
This commit is contained in:
@@ -10,6 +10,7 @@
|
|||||||
#include "alnumeric.h"
|
#include "alnumeric.h"
|
||||||
#include "core/device.h"
|
#include "core/device.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
|
#include "ringbuffer.h"
|
||||||
|
|
||||||
#include "oboe/Oboe.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} { }
|
OboeCapture(DeviceBase *device) : BackendBase{device} { }
|
||||||
|
|
||||||
oboe::ManagedStream mStream;
|
oboe::ManagedStream mStream;
|
||||||
|
|
||||||
std::vector<al::byte> mSamples;
|
RingBufferPtr mRing{nullptr};
|
||||||
uint mLastAvail{0u};
|
|
||||||
|
oboe::DataCallbackResult onAudioReady(oboe::AudioStream *oboeStream, void *audioData,
|
||||||
|
int32_t numFrames) override;
|
||||||
|
|
||||||
void open(const char *name) override;
|
void open(const char *name) override;
|
||||||
void start() override;
|
void start() override;
|
||||||
@@ -203,6 +206,14 @@ struct OboeCapture final : public BackendBase {
|
|||||||
uint availableSamples() override;
|
uint availableSamples() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
oboe::DataCallbackResult OboeCapture::onAudioReady(oboe::AudioStream*, void *audioData,
|
||||||
|
int32_t numFrames)
|
||||||
|
{
|
||||||
|
mRing->write(audioData, static_cast<uint32_t>(numFrames));
|
||||||
|
return oboe::DataCallbackResult::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void OboeCapture::open(const char *name)
|
void OboeCapture::open(const char *name)
|
||||||
{
|
{
|
||||||
if(!name)
|
if(!name)
|
||||||
@@ -217,8 +228,8 @@ void OboeCapture::open(const char *name)
|
|||||||
->setSampleRateConversionQuality(oboe::SampleRateConversionQuality::High)
|
->setSampleRateConversionQuality(oboe::SampleRateConversionQuality::High)
|
||||||
->setChannelConversionAllowed(true)
|
->setChannelConversionAllowed(true)
|
||||||
->setFormatConversionAllowed(true)
|
->setFormatConversionAllowed(true)
|
||||||
->setBufferCapacityInFrames(static_cast<int32_t>(mDevice->BufferSize))
|
->setSampleRate(static_cast<int32_t>(mDevice->Frequency))
|
||||||
->setSampleRate(static_cast<int32_t>(mDevice->Frequency));
|
->setCallback(this);
|
||||||
/* Only use mono or stereo at user request. There's no telling what
|
/* Only use mono or stereo at user request. There's no telling what
|
||||||
* other counts may be inferred as.
|
* 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
|
/* FIXME: This really should support UByte, but Oboe doesn't. We'll need to
|
||||||
* use a temp buffer and convert.
|
* convert.
|
||||||
*/
|
*/
|
||||||
switch(mDevice->FmtType)
|
switch(mDevice->FmtType)
|
||||||
{
|
{
|
||||||
@@ -263,22 +274,13 @@ void OboeCapture::open(const char *name)
|
|||||||
if(result != oboe::Result::OK)
|
if(result != oboe::Result::OK)
|
||||||
throw al::backend_exception{al::backend_error::DeviceError, "Failed to create stream: %s",
|
throw al::backend_exception{al::backend_error::DeviceError, "Failed to create stream: %s",
|
||||||
oboe::convertToText(result)};
|
oboe::convertToText(result)};
|
||||||
if(static_cast<int32_t>(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<int32_t>(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<int32_t>(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<uint>(buffer_result.value());
|
|
||||||
|
|
||||||
TRACE("Got stream with properties:\n%s", oboe::convertToText(mStream.get()));
|
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<uint32_t>(mStream->getBytesPerFrame()), false);
|
||||||
|
|
||||||
mDevice->DeviceName = name;
|
mDevice->DeviceName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,23 +294,6 @@ void OboeCapture::start()
|
|||||||
|
|
||||||
void OboeCapture::stop()
|
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<uint>(availres.value()), mLastAvail);
|
|
||||||
const size_t frame_size{static_cast<uint32_t>(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<uint>(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()};
|
const oboe::Result result{mStream->stop()};
|
||||||
if(result != oboe::Result::OK)
|
if(result != oboe::Result::OK)
|
||||||
throw al::backend_exception{al::backend_error::DeviceError, "Failed to stop stream: %s",
|
throw al::backend_exception{al::backend_error::DeviceError, "Failed to stop stream: %s",
|
||||||
@@ -316,38 +301,10 @@ void OboeCapture::stop()
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint OboeCapture::availableSamples()
|
uint OboeCapture::availableSamples()
|
||||||
{
|
{ return static_cast<uint>(mRing->readSpace()); }
|
||||||
/* 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<uint>(result.value()), mLastAvail);
|
|
||||||
|
|
||||||
const auto frame_size = static_cast<uint32_t>(mStream->getBytesPerFrame());
|
|
||||||
return static_cast<uint>(mSamples.size()/frame_size) + mLastAvail;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OboeCapture::captureSamples(al::byte *buffer, uint samples)
|
void OboeCapture::captureSamples(al::byte *buffer, uint samples)
|
||||||
{
|
{ mRing->read(buffer, samples); }
|
||||||
const auto frame_size = static_cast<uint>(mStream->getBytesPerFrame());
|
|
||||||
if(const size_t storelen{mSamples.size()})
|
|
||||||
{
|
|
||||||
const auto instore = static_cast<uint>(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<int32_t>(samples), 0);
|
|
||||||
uint got{bool{result} ? static_cast<uint>(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|||||||
@@ -878,7 +878,6 @@ void OpenSLCapture::captureSamples(al::byte *buffer, uint samples)
|
|||||||
{
|
{
|
||||||
const uint update_size{mDevice->UpdateSize};
|
const uint update_size{mDevice->UpdateSize};
|
||||||
const uint chunk_size{update_size * mFrameSize};
|
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
|
/* Read the desired samples from the ring buffer then advance its read
|
||||||
* pointer.
|
* pointer.
|
||||||
@@ -907,39 +906,52 @@ void OpenSLCapture::captureSamples(al::byte *buffer, uint samples)
|
|||||||
|
|
||||||
i += rem;
|
i += rem;
|
||||||
}
|
}
|
||||||
mRing->readAdvance(adv_count);
|
|
||||||
|
|
||||||
SLAndroidSimpleBufferQueueItf bufferQueue{};
|
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,
|
const SLresult result{VCALL(mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
||||||
&bufferQueue)};
|
&bufferQueue)};
|
||||||
PRINTERR(result, "recordObj->GetInterface");
|
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);
|
mDevice->handleDisconnect("Failed to get capture buffer queue: 0x%08x", result);
|
||||||
bufferQueue = nullptr;
|
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 len1 = std::min(wdata.first.len, adv_count-wdata.second.len);
|
||||||
auto wdata = mRing->getWriteVector();
|
auto buf1 = wdata.first.buf + chunk_size*(wdata.first.len-len1);
|
||||||
std::fill_n(wdata.first.buf, wdata.first.len*chunk_size, silence);
|
for(size_t i{0u};i < len1 && SL_RESULT_SUCCESS == result;i++)
|
||||||
for(size_t i{0u};i < wdata.first.len && 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");
|
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);
|
result = VCALL(bufferQueue,Enqueue)(buf2 + chunk_size*i, chunk_size);
|
||||||
for(size_t i{0u};i < wdata.second.len && SL_RESULT_SUCCESS == result;i++)
|
PRINTERR(result, "bufferQueue->Enqueue");
|
||||||
{
|
|
||||||
result = VCALL(bufferQueue,Enqueue)(wdata.second.buf + chunk_size*i, chunk_size);
|
|
||||||
PRINTERR(result, "bufferQueue->Enqueue");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user