From ca38805776c814034af28f68144de64868601da1 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Wed, 23 Mar 2016 16:50:36 -1000 Subject: [PATCH] AudioQueueServices VoiceEngine implemented --- lib/audiodev/AQS.cpp | 361 +++++++++++++++++++------------------------ 1 file changed, 163 insertions(+), 198 deletions(-) diff --git a/lib/audiodev/AQS.cpp b/lib/audiodev/AQS.cpp index 9bc0239..7a5f4e2 100644 --- a/lib/audiodev/AQS.cpp +++ b/lib/audiodev/AQS.cpp @@ -1,8 +1,11 @@ -#include "boo/audiodev/IAudioVoiceAllocator.hpp" +#include "AudioVoiceEngine.hpp" #include "logvisor/logvisor.hpp" #include +#include +#include + namespace boo { static logvisor::Module Log("boo::AQS"); @@ -31,208 +34,39 @@ static AudioChannel AQSChannelToBooChannel(AudioChannelLabel ch) return AudioChannel::Unknown; } -struct AQSAudioVoice : IAudioVoice +struct AQSAudioVoiceEngine : BaseAudioVoiceEngine { - ChannelMap m_map; - IAudioVoiceCallback* m_cb; AudioQueueRef m_queue = nullptr; AudioQueueBufferRef m_buffers[3]; - size_t m_bufferFrames = 1024; - size_t m_frameSize; + size_t m_frameBytes; - const ChannelMap& channelMap() const {return m_map;} + std::mutex m_engineMutex; + std::condition_variable m_engineCv; - AudioQueueBufferRef m_callbackBuf = nullptr; - unsigned m_primeBuf; - static void Callback(void* inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) + static void Callback(AQSAudioVoiceEngine* voice, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) { - AQSAudioVoice* voice = static_cast(inUserData); - voice->m_callbackBuf = inBuffer; - voice->m_cb->needsNextBuffer(*voice, voice->m_bufferFrames); - voice->m_callbackBuf = nullptr; + std::unique_lock lk(voice->m_engineMutex); + voice->m_engineCv.wait(lk); + + voice->_pumpAndMixVoices(voice->m_mixInfo.m_periodFrames, reinterpret_cast(inBuffer->mAudioData)); + inBuffer->mAudioDataByteSize = voice->m_frameBytes; + AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, nullptr); } - AQSAudioVoice(AudioChannelSet set, unsigned sampleRate, IAudioVoiceCallback* cb) - : m_cb(cb) - { - unsigned chCount = ChannelCount(set); + static void DummyCallback(AQSAudioVoiceEngine* voice, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) {} - AudioStreamBasicDescription desc = {}; - desc.mSampleRate = sampleRate; - desc.mFormatID = kAudioFormatLinearPCM; - desc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; - desc.mBytesPerPacket = chCount * 2; - desc.mFramesPerPacket = 1; - desc.mBytesPerFrame = chCount * 2; - desc.mChannelsPerFrame = chCount; - desc.mBitsPerChannel = 16; - - OSStatus err; - while ((err = AudioQueueNewOutput(&desc, AudioQueueOutputCallback(Callback), - this, nullptr, nullptr, 0, &m_queue))) - { - if (set == AudioChannelSet::Stereo) - break; - set = AudioChannelSet(int(set) - 1); - chCount = ChannelCount(set); - desc.mBytesPerPacket = chCount * 2; - desc.mBytesPerFrame = chCount * 2; - desc.mChannelsPerFrame = chCount; - } - if (err) - { - Log.report(logvisor::Fatal, "unable to create output audio queue"); - return; - } - - if (chCount > 2) - { - AudioChannelLayout layout; - UInt32 layoutSz = sizeof(layout); - if (AudioQueueGetProperty(m_queue, kAudioQueueProperty_ChannelLayout, &layout, &layoutSz)) - { - Log.report(logvisor::Fatal, "unable to get channel layout from audio queue"); - return; - } - - switch (layout.mChannelLayoutTag) - { - case kAudioChannelLayoutTag_UseChannelDescriptions: - m_map.m_channelCount = layout.mNumberChannelDescriptions; - for (int i=0 ; ineedsNextBuffer(*this, m_bufferFrames); - } - AudioQueuePrime(m_queue, 0, nullptr); - } - - ~AQSAudioVoice() - { - AudioQueueDispose(m_queue, false); - } - - void bufferSampleData(const int16_t* data, size_t frames) - { - if (m_callbackBuf) - { - m_callbackBuf->mAudioDataByteSize = - std::min(UInt32(frames * m_frameSize), m_callbackBuf->mAudioDataBytesCapacity); - memcpy(m_callbackBuf->mAudioData, data, m_callbackBuf->mAudioDataByteSize); - AudioQueueEnqueueBuffer(m_queue, m_callbackBuf, 0, nullptr); - } - else - { - AudioQueueBufferRef buf = m_buffers[m_primeBuf]; - buf->mAudioDataByteSize = - std::min(UInt32(frames * m_frameSize), buf->mAudioDataBytesCapacity); - memcpy(buf->mAudioData, data, buf->mAudioDataByteSize); - AudioQueueEnqueueBuffer(m_queue, buf, 0, nullptr); - } - } - - void start() - { - AudioQueueStart(m_queue, nullptr); - } - - void stop() - { - AudioQueueStop(m_queue, false); - } -}; - -struct AQSAudioVoiceAllocator : IAudioVoiceEngine -{ - static void DummyCallback(void* inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) {} - - AudioChannelSet getAvailableSet() + AudioChannelSet _getAvailableSet() { const unsigned chCount = 8; AudioStreamBasicDescription desc = {}; - desc.mSampleRate = 32000; + desc.mSampleRate = 96000; desc.mFormatID = kAudioFormatLinearPCM; desc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; - desc.mBytesPerPacket = chCount * 2; + desc.mBytesPerPacket = chCount * 4; desc.mFramesPerPacket = 1; - desc.mBytesPerFrame = chCount * 2; + desc.mBytesPerFrame = chCount * 4; desc.mChannelsPerFrame = chCount; - desc.mBitsPerChannel = 16; + desc.mBitsPerChannel = 32; AudioQueueRef queue; if (AudioQueueNewOutput(&desc, AudioQueueOutputCallback(DummyCallback), @@ -269,23 +103,154 @@ struct AQSAudioVoiceAllocator : IAudioVoiceEngine return AudioChannelSet::Unknown; } - std::unique_ptr allocateNewVoice(AudioChannelSet layoutOut, - unsigned sampleRate, - IAudioVoiceCallback* cb) + AQSAudioVoiceEngine() { - AQSAudioVoice* newVoice = new AQSAudioVoice(layoutOut, sampleRate, cb); - std::unique_ptr ret(newVoice); - if (!newVoice->m_queue) - return {}; - return ret; + m_mixInfo.m_channels = _getAvailableSet(); + unsigned chCount = ChannelCount(m_mixInfo.m_channels); + + AudioStreamBasicDescription desc = {}; + desc.mSampleRate = 96000; + desc.mFormatID = kAudioFormatLinearPCM; + desc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; + desc.mBytesPerPacket = chCount * 4; + desc.mFramesPerPacket = 1; + desc.mBytesPerFrame = chCount * 4; + desc.mChannelsPerFrame = chCount; + desc.mBitsPerChannel = 32; + + OSStatus err; + if ((err = AudioQueueNewOutput(&desc, AudioQueueOutputCallback(Callback), + this, nullptr, nullptr, 0, &m_queue))) + { + Log.report(logvisor::Fatal, "unable to create output audio queue"); + return; + } + + m_mixInfo.m_sampleRate = 96000.0; + m_mixInfo.m_sampleFormat = SOXR_INT32_I; + m_mixInfo.m_bitsPerSample = 32; + + ChannelMap& chMapOut = m_mixInfo.m_channelMap; + if (chCount > 2) + { + AudioChannelLayout layout; + UInt32 layoutSz = sizeof(layout); + if (AudioQueueGetProperty(m_queue, kAudioQueueProperty_ChannelLayout, &layout, &layoutSz)) + { + Log.report(logvisor::Fatal, "unable to get channel layout from audio queue"); + return; + } + + switch (layout.mChannelLayoutTag) + { + case kAudioChannelLayoutTag_UseChannelDescriptions: + chMapOut.m_channelCount = layout.mNumberChannelDescriptions; + for (int i=0 ; i(m_buffers[i]->mAudioData)); + m_buffers[i]->mAudioDataByteSize = m_frameBytes; + AudioQueueEnqueueBuffer(m_queue, m_buffers[i], 0, nullptr); + } + AudioQueuePrime(m_queue, 0, nullptr); + AudioQueueStart(m_queue, nullptr); } - void pumpVoices() {} + ~AQSAudioVoiceEngine() + { + AudioQueueDispose(m_queue, false); + } + + void pumpAndMixVoices() + { + std::unique_lock lk(m_engineMutex); + m_engineCv.notify_one(); + lk.unlock(); + lk.lock(); + } }; -std::unique_ptr NewAudioVoiceAllocator() +std::unique_ptr NewAudioVoiceEngine() { - return std::make_unique(); + std::unique_ptr ret = std::make_unique(); + if (!static_cast(*ret).m_queue) + return {}; + return ret; } }