mirror of
https://github.com/AxioDL/boo.git
synced 2025-12-09 13:37:48 +00:00
Allow audio AQS buffering to occur during frame idle
This commit is contained in:
@@ -47,7 +47,8 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine
|
||||
std::mutex m_engineMutex;
|
||||
std::condition_variable m_engineEnterCv;
|
||||
std::condition_variable m_engineLeaveCv;
|
||||
bool m_cbWaiting = false;
|
||||
bool m_inRetrace = false;
|
||||
bool m_inCb = false;
|
||||
bool m_cbRunning = true;
|
||||
|
||||
static void Callback(AQSAudioVoiceEngine* engine, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
|
||||
@@ -56,25 +57,30 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine
|
||||
return;
|
||||
|
||||
std::unique_lock<std::mutex> lk(engine->m_engineMutex);
|
||||
engine->m_cbWaiting = true;
|
||||
if (engine->m_engineEnterCv.wait_for(lk,
|
||||
std::chrono::nanoseconds(engine->m_mixInfo.m_periodFrames * 750000000 /
|
||||
size_t(engine->m_mixInfo.m_sampleRate))) == std::cv_status::timeout)
|
||||
engine->m_inCb = true;
|
||||
if (!engine->m_inRetrace)
|
||||
{
|
||||
inBuffer->mAudioDataByteSize = engine->m_frameBytes;
|
||||
memset(inBuffer->mAudioData, 0, engine->m_frameBytes);
|
||||
AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, nullptr);
|
||||
engine->m_cbWaiting = false;
|
||||
engine->m_engineLeaveCv.notify_one();
|
||||
return;
|
||||
if (engine->m_engineEnterCv.wait_for(lk,
|
||||
std::chrono::nanoseconds(engine->m_mixInfo.m_periodFrames * 1000000000 /
|
||||
size_t(engine->m_mixInfo.m_sampleRate))) == std::cv_status::timeout ||
|
||||
!engine->m_inRetrace)
|
||||
{
|
||||
inBuffer->mAudioDataByteSize = engine->m_frameBytes;
|
||||
memset(inBuffer->mAudioData, 0, engine->m_frameBytes);
|
||||
AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, nullptr);
|
||||
engine->m_engineLeaveCv.notify_one();
|
||||
engine->m_inCb = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
engine->m_cbWaiting = false;
|
||||
|
||||
engine->_pumpAndMixVoices(engine->m_mixInfo.m_periodFrames, reinterpret_cast<float*>(inBuffer->mAudioData));
|
||||
engine->_pumpAndMixVoices(engine->m_mixInfo.m_periodFrames,
|
||||
reinterpret_cast<float*>(inBuffer->mAudioData));
|
||||
inBuffer->mAudioDataByteSize = engine->m_frameBytes;
|
||||
AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, nullptr);
|
||||
|
||||
engine->m_engineLeaveCv.notify_one();
|
||||
engine->m_inCb = false;
|
||||
}
|
||||
|
||||
static void DummyCallback(AQSAudioVoiceEngine* engine, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) {}
|
||||
@@ -698,7 +704,7 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine
|
||||
while (chMapOut.m_channelCount < chCount)
|
||||
chMapOut.m_channels[chMapOut.m_channelCount++] = AudioChannel::Unknown;
|
||||
|
||||
m_mixInfo.m_periodFrames = 2400 * size_t(actualSampleRate) / 48000;
|
||||
m_mixInfo.m_periodFrames = 1200 * size_t(actualSampleRate) / 48000;
|
||||
for (int i=0 ; i<3 ; ++i)
|
||||
if (AudioQueueAllocateBuffer(m_queue, m_mixInfo.m_periodFrames * chCount * 4, &m_buffers[i]))
|
||||
{
|
||||
@@ -726,22 +732,50 @@ struct AQSAudioVoiceEngine : BaseAudioVoiceEngine
|
||||
~AQSAudioVoiceEngine()
|
||||
{
|
||||
m_cbRunning = false;
|
||||
if (m_cbWaiting)
|
||||
if (m_inCb)
|
||||
m_engineEnterCv.notify_one();
|
||||
AudioQueueDispose(m_queue, true);
|
||||
if (m_midiClient)
|
||||
MIDIClientDispose(m_midiClient);
|
||||
}
|
||||
|
||||
/* This is temperamental for AudioQueueServices
|
||||
* (which has unpredictable buffering windows).
|
||||
* _pumpAndMixVoicesRetrace() is highly recommended. */
|
||||
void pumpAndMixVoices()
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_engineMutex);
|
||||
if (m_cbWaiting)
|
||||
if (m_inCb)
|
||||
{
|
||||
/* Wake up callback */
|
||||
m_engineEnterCv.notify_one();
|
||||
/* Wait for callback completion */
|
||||
m_engineLeaveCv.wait(lk);
|
||||
}
|
||||
}
|
||||
|
||||
void _pumpAndMixVoicesRetrace()
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_engineMutex);
|
||||
m_inRetrace = true;
|
||||
while (m_inRetrace)
|
||||
{
|
||||
if (m_inCb) /* Wake up callback */
|
||||
m_engineEnterCv.notify_one();
|
||||
/* Wait for callback completion */
|
||||
m_engineLeaveCv.wait(lk);
|
||||
}
|
||||
}
|
||||
|
||||
void _retraceBreak()
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_engineMutex);
|
||||
m_inRetrace = false;
|
||||
if (m_inCb) /* Break out of callback */
|
||||
m_engineEnterCv.notify_one();
|
||||
else /* Break out of client */
|
||||
m_engineLeaveCv.notify_one();
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<IAudioVoiceEngine> NewAudioVoiceEngine()
|
||||
|
||||
@@ -30,14 +30,14 @@ void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, int16_t* dataOut)
|
||||
if (remFrames < m_5msFrames)
|
||||
{
|
||||
thisFrames = remFrames;
|
||||
if (m_5msCallback)
|
||||
m_5msCallback(thisFrames / double(m_5msFrames) * 5.0 / 1000.0);
|
||||
if (m_engineCallback)
|
||||
m_engineCallback->on5MsInterval(*this, thisFrames / double(m_5msFrames) * 5.0 / 1000.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
thisFrames = m_5msFrames;
|
||||
if (m_5msCallback)
|
||||
m_5msCallback(5.0 / 1000.0);
|
||||
if (m_engineCallback)
|
||||
m_engineCallback->on5MsInterval(*this, 5.0 / 1000.0);
|
||||
}
|
||||
|
||||
for (auto it = m_linearizedSubmixes.rbegin() ; it != m_linearizedSubmixes.rend() ; ++it)
|
||||
@@ -57,6 +57,9 @@ void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, int16_t* dataOut)
|
||||
remFrames -= thisFrames;
|
||||
dataOut += sampleCount;
|
||||
}
|
||||
|
||||
if (m_engineCallback)
|
||||
m_engineCallback->onPumpCycleComplete(*this);
|
||||
}
|
||||
|
||||
void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, int32_t* dataOut)
|
||||
@@ -77,14 +80,14 @@ void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, int32_t* dataOut)
|
||||
if (remFrames < m_5msFrames)
|
||||
{
|
||||
thisFrames = remFrames;
|
||||
if (m_5msCallback)
|
||||
m_5msCallback(thisFrames / double(m_5msFrames) * 5.0 / 1000.0);
|
||||
if (m_engineCallback)
|
||||
m_engineCallback->on5MsInterval(*this, thisFrames / double(m_5msFrames) * 5.0 / 1000.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
thisFrames = m_5msFrames;
|
||||
if (m_5msCallback)
|
||||
m_5msCallback(5.0 / 1000.0);
|
||||
if (m_engineCallback)
|
||||
m_engineCallback->on5MsInterval(*this, 5.0 / 1000.0);
|
||||
}
|
||||
|
||||
for (auto it = m_linearizedSubmixes.rbegin() ; it != m_linearizedSubmixes.rend() ; ++it)
|
||||
@@ -104,6 +107,9 @@ void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, int32_t* dataOut)
|
||||
remFrames -= thisFrames;
|
||||
dataOut += sampleCount;
|
||||
}
|
||||
|
||||
if (m_engineCallback)
|
||||
m_engineCallback->onPumpCycleComplete(*this);
|
||||
}
|
||||
|
||||
void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, float* dataOut)
|
||||
@@ -124,14 +130,14 @@ void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, float* dataOut)
|
||||
if (remFrames < m_5msFrames)
|
||||
{
|
||||
thisFrames = remFrames;
|
||||
if (m_5msCallback)
|
||||
m_5msCallback(thisFrames / double(m_5msFrames) * 5.0 / 1000.0);
|
||||
if (m_engineCallback)
|
||||
m_engineCallback->on5MsInterval(*this, thisFrames / double(m_5msFrames) * 5.0 / 1000.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
thisFrames = m_5msFrames;
|
||||
if (m_5msCallback)
|
||||
m_5msCallback(5.0 / 1000.0);
|
||||
if (m_engineCallback)
|
||||
m_engineCallback->on5MsInterval(*this, 5.0 / 1000.0);
|
||||
}
|
||||
|
||||
for (auto it = m_linearizedSubmixes.rbegin() ; it != m_linearizedSubmixes.rend() ; ++it)
|
||||
@@ -151,6 +157,9 @@ void BaseAudioVoiceEngine::_pumpAndMixVoices(size_t frames, float* dataOut)
|
||||
remFrames -= thisFrames;
|
||||
dataOut += sampleCount;
|
||||
}
|
||||
|
||||
if (m_engineCallback)
|
||||
m_engineCallback->onPumpCycleComplete(*this);
|
||||
}
|
||||
|
||||
void BaseAudioVoiceEngine::_unbindFrom(std::list<AudioVoice*>::iterator it)
|
||||
@@ -197,14 +206,9 @@ BaseAudioVoiceEngine::allocateNewSubmix(bool mainOut, IAudioSubmixCallback* cb,
|
||||
return ret;
|
||||
}
|
||||
|
||||
void BaseAudioVoiceEngine::register5MsCallback(std::function<void(double dt)>&& callback)
|
||||
void BaseAudioVoiceEngine::setCallbackInterface(IAudioVoiceEngineCallback* cb)
|
||||
{
|
||||
m_5msCallback = std::move(callback);
|
||||
}
|
||||
|
||||
void BaseAudioVoiceEngine::unregister5MsCallback()
|
||||
{
|
||||
m_5msCallback = {};
|
||||
m_engineCallback = cb;
|
||||
}
|
||||
|
||||
void BaseAudioVoiceEngine::setVolume(float vol)
|
||||
|
||||
@@ -33,7 +33,7 @@ protected:
|
||||
std::list<AudioVoice*> m_activeVoices;
|
||||
std::list<AudioSubmix*> m_activeSubmixes;
|
||||
size_t m_5msFrames = 0;
|
||||
std::function<void(double dt)> m_5msCallback;
|
||||
IAudioVoiceEngineCallback* m_engineCallback = nullptr;
|
||||
|
||||
/* Shared scratch buffers for accumulating audio data for resampling */
|
||||
std::vector<int16_t> m_scratchIn;
|
||||
@@ -68,8 +68,7 @@ public:
|
||||
|
||||
std::unique_ptr<IAudioSubmix> allocateNewSubmix(bool mainOut, IAudioSubmixCallback* cb, int busId);
|
||||
|
||||
void register5MsCallback(std::function<void(double dt)>&& callback);
|
||||
void unregister5MsCallback();
|
||||
void setCallbackInterface(IAudioVoiceEngineCallback* cb);
|
||||
|
||||
void setVolume(float vol);
|
||||
const AudioVoiceEngineMixInfo& mixInfo() const;
|
||||
|
||||
Reference in New Issue
Block a user