Added CoreAudioOut gapless playback support.

This commit is contained in:
Casey Langen 2016-06-01 22:34:22 -07:00
parent a8f8425f91
commit 32965dd2af
2 changed files with 47 additions and 18 deletions

View File

@ -6,29 +6,51 @@
using namespace musik::core::audio; using namespace musik::core::audio;
void audioCallback(void *customData, AudioQueueRef queue, AudioQueueBufferRef buffer) void audioCallback(void *customData, AudioQueueRef queue, AudioQueueBufferRef buffer) {
{ CoreAudioOut* output = (CoreAudioOut *) customData;
CoreAudioOut::BufferContext* context = (CoreAudioOut::BufferContext *) buffer->mUserData;
OSStatus result = AudioQueueFreeBuffer(queue, buffer); OSStatus result = AudioQueueFreeBuffer(queue, buffer);
if (result != 0) { if (result != 0) {
std::cerr << "AudioQueueFreeBuffer failed: " << result << "\n"; std::cerr << "AudioQueueFreeBuffer failed: " << result << "\n";
} }
CoreAudioOut* output = (CoreAudioOut *) customData; output->NotifyBufferCompleted(context);
IBuffer* coreBuffer = (IBuffer *) buffer->mUserData;
output->NotifyBufferCompleted(coreBuffer);
} }
void CoreAudioOut::NotifyBufferCompleted(IBuffer *buffer) { size_t countBuffersWithProvider(
const std::list<CoreAudioOut::BufferContext*>& buffers,
const IBufferProvider* provider)
{
size_t count = 0;
auto it = buffers.begin();
while (it != buffers.end()) {
if ((*it)->provider == provider) {
++count;
}
++it;
}
return count;
}
void CoreAudioOut::NotifyBufferCompleted(BufferContext *context) {
boost::recursive_mutex::scoped_lock lock(this->mutex); boost::recursive_mutex::scoped_lock lock(this->mutex);
--bufferCount;
this->bufferProvider->OnBufferProcessed(buffer); auto it = this->buffers.begin();
while (it != this->buffers.end()) {
if (*it == context) {
this->buffers.erase(it);
}
++it;
}
context->provider->OnBufferProcessed(context->buffer);
delete context;
} }
CoreAudioOut::CoreAudioOut() { CoreAudioOut::CoreAudioOut() {
this->bufferProvider = NULL;
this->quit = false; this->quit = false;
this->bufferCount = 0;
this->volume = 1.0f; this->volume = 1.0f;
this->audioFormat = (AudioStreamBasicDescription) { 0 }; this->audioFormat = (AudioStreamBasicDescription) { 0 };
@ -51,7 +73,7 @@ CoreAudioOut::CoreAudioOut() {
bool CoreAudioOut::Play(IBuffer *buffer, IBufferProvider *provider) { bool CoreAudioOut::Play(IBuffer *buffer, IBufferProvider *provider) {
boost::recursive_mutex::scoped_lock lock(this->mutex); boost::recursive_mutex::scoped_lock lock(this->mutex);
if (this->bufferCount >= BUFFER_COUNT) { if (countBuffersWithProvider(this->buffers, provider) >= BUFFER_COUNT) {
/* enough buffers are already in the queue. bail, we'll notify the /* enough buffers are already in the queue. bail, we'll notify the
caller when there's more data available */ caller when there's more data available */
return false; return false;
@ -94,20 +116,22 @@ bool CoreAudioOut::Play(IBuffer *buffer, IBufferProvider *provider) {
} }
} }
this->bufferProvider = provider;
AudioQueueBufferRef audioQueueBuffer = NULL; AudioQueueBufferRef audioQueueBuffer = NULL;
size_t bytes = buffer->Bytes(); size_t bytes = buffer->Bytes();
result = AudioQueueAllocateBuffer(this->audioQueue, bytes, &audioQueueBuffer); result = AudioQueueAllocateBuffer(this->audioQueue, bytes, &audioQueueBuffer);
BufferContext* context = new BufferContext();
context->provider = provider;
context->buffer = buffer;
if (result != 0) { if (result != 0) {
std::cerr << "AudioQueueAllocateBuffer failed: " << result << "\n"; std::cerr << "AudioQueueAllocateBuffer failed: " << result << "\n";
return false; return false;
} }
audioQueueBuffer->mUserData = (void *) buffer; audioQueueBuffer->mUserData = (void *) context;
audioQueueBuffer->mAudioDataByteSize = bytes; audioQueueBuffer->mAudioDataByteSize = bytes;
memcpy( memcpy(
@ -120,10 +144,11 @@ bool CoreAudioOut::Play(IBuffer *buffer, IBufferProvider *provider) {
if (result != 0) { if (result != 0) {
std::cerr << "AudioQueueEnqueueBuffer failed: " << result << "\n"; std::cerr << "AudioQueueEnqueueBuffer failed: " << result << "\n";
delete context;
return false; return false;
} }
++bufferCount; this->buffers.push_back(context);
return true; return true;
} }

View File

@ -12,6 +12,11 @@
class CoreAudioOut : public musik::core::audio::IOutput { class CoreAudioOut : public musik::core::audio::IOutput {
public: public:
struct BufferContext {
musik::core::audio::IBuffer *buffer;
musik::core::audio::IBufferProvider *provider;
};
CoreAudioOut(); CoreAudioOut();
virtual ~CoreAudioOut(); virtual ~CoreAudioOut();
@ -25,14 +30,13 @@ class CoreAudioOut : public musik::core::audio::IOutput {
musik::core::audio::IBuffer *buffer, musik::core::audio::IBuffer *buffer,
musik::core::audio::IBufferProvider *provider); musik::core::audio::IBufferProvider *provider);
void NotifyBufferCompleted(musik::core::audio::IBuffer *buffer); void NotifyBufferCompleted(BufferContext *context);
private: private:
musik::core::audio::IBufferProvider *bufferProvider;
AudioStreamBasicDescription audioFormat; AudioStreamBasicDescription audioFormat;
AudioQueueRef audioQueue; AudioQueueRef audioQueue;
double volume; double volume;
size_t bufferCount; std::list<BufferContext*> buffers;
boost::thread thread; boost::thread thread;
boost::recursive_mutex mutex; boost::recursive_mutex mutex;
bool quit; bool quit;