all repos — mgba @ 2b558a5a65558280e290b0d17fedbefd49178a70

mGBA Game Boy Advance Emulator

Rearchitect audio copying to make it cleaner and more atomic
Jeffrey Pfau jeffrey@endrift.com
Wed, 15 Jan 2014 03:43:56 -0800
commit

2b558a5a65558280e290b0d17fedbefd49178a70

parent

b8167f55b1e69819ee4c74174502b9f31fbb5538

M src/gba/gba-audio.csrc/gba/gba-audio.c

@@ -48,8 +48,6 @@ CircleBufferInit(&audio->left, GBA_AUDIO_SAMPLES * sizeof(int32_t));

CircleBufferInit(&audio->right, GBA_AUDIO_SAMPLES * sizeof(int32_t)); CircleBufferInit(&audio->chA.fifo, GBA_AUDIO_FIFO_SIZE); CircleBufferInit(&audio->chB.fifo, GBA_AUDIO_FIFO_SIZE); - - pthread_mutex_init(&audio->bufferMutex, 0); } void GBAAudioDeinit(struct GBAAudio* audio) {

@@ -57,9 +55,6 @@ CircleBufferDeinit(&audio->left);

CircleBufferDeinit(&audio->right); CircleBufferDeinit(&audio->chA.fifo); CircleBufferDeinit(&audio->chB.fifo); - - pthread_mutex_lock(&audio->bufferMutex); - pthread_mutex_destroy(&audio->bufferMutex); } int32_t GBAAudioProcessEvents(struct GBAAudio* audio, int32_t cycles) {

@@ -397,6 +392,27 @@ }

CircleBufferRead8(&channel->fifo, &channel->sample); } +unsigned GBAAudioCopy(struct GBAAudio* audio, void* left, void* right, unsigned nSamples) { + GBASyncLockAudio(audio->p->sync); + unsigned read = 0; + if (left) { + unsigned readL = CircleBufferRead(&audio->left, left, nSamples * sizeof(int32_t)) >> 2; + if (readL < nSamples) { + memset((int32_t*) left + readL, 0, nSamples - readL); + } + read = readL; + } + if (right) { + unsigned readR = CircleBufferRead(&audio->right, right, nSamples * sizeof(int32_t)) >> 2; + if (readR < nSamples) { + memset((int32_t*) right + readR, 0, nSamples - readR); + } + read = read >= readR ? read : readR; + } + GBASyncConsumeAudio(audio->p->sync); + return read; +} + static int32_t _updateSquareChannel(struct GBAAudioSquareControl* control, int duty) { control->hi = !control->hi; int period = 16 * (2048 - control->frequency);

@@ -579,14 +595,9 @@ if (audio->chBRight) {

sampleRight += (audio->chB.sample << 2) >> !audio->volumeChB; } - pthread_mutex_lock(&audio->bufferMutex); - while (CircleBufferSize(&audio->left) + (GBA_AUDIO_SAMPLES * 2 / 5) >= audio->left.capacity) { - if (!audio->p->sync->audioWait) { - break; - } - GBASyncProduceAudio(audio->p->sync, &audio->bufferMutex); - } - CircleBufferWrite32(&audio->left, sampleLeft); - CircleBufferWrite32(&audio->right, sampleRight); - pthread_mutex_unlock(&audio->bufferMutex); + GBASyncLockAudio(audio->p->sync); + CircleBufferWrite32(&audio->left, sampleLeft << 5); + CircleBufferWrite32(&audio->right, sampleRight << 5); + unsigned produced = CircleBufferSize(&audio->left); + GBASyncProduceAudio(audio->p->sync, produced >= GBA_AUDIO_SAMPLES * 3); }
M src/gba/gba-audio.hsrc/gba/gba-audio.h

@@ -3,7 +3,6 @@ #define GBA_AUDIO_H

#include "circle-buffer.h" -#include <pthread.h> #include <stdint.h> struct GBADMA;

@@ -203,8 +202,6 @@ int32_t nextCh4;

int32_t nextSample; int32_t sampleInterval; - - pthread_mutex_t bufferMutex; }; void GBAAudioInit(struct GBAAudio* audio);

@@ -230,5 +227,7 @@

void GBAAudioWriteWaveRAM(struct GBAAudio* audio, int address, uint32_t value); void GBAAudioWriteFIFO(struct GBAAudio* audio, int address, uint32_t value); void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId); + +unsigned GBAAudioCopy(struct GBAAudio* audio, void* left, void* right, unsigned nSamples); #endif
M src/gba/gba-thread.csrc/gba/gba-thread.c

@@ -135,6 +135,7 @@

pthread_mutex_init(&threadContext->sync.videoFrameMutex, 0); pthread_cond_init(&threadContext->sync.videoFrameAvailableCond, 0); pthread_cond_init(&threadContext->sync.videoFrameRequiredCond, 0); + pthread_mutex_init(&threadContext->sync.audioBufferMutex, 0); pthread_cond_init(&threadContext->sync.audioRequiredCond, 0); pthread_mutex_lock(&threadContext->stateMutex);

@@ -168,6 +169,7 @@ pthread_cond_destroy(&threadContext->sync.videoFrameRequiredCond);

pthread_cond_broadcast(&threadContext->sync.audioRequiredCond); pthread_cond_destroy(&threadContext->sync.audioRequiredCond); + pthread_mutex_destroy(&threadContext->sync.audioBufferMutex); } void GBAThreadPause(struct GBAThread* threadContext) {

@@ -277,12 +279,18 @@ int GBASyncDrawingFrame(struct GBASync* sync) {

return sync->videoFrameSkip <= 0; } -void GBASyncProduceAudio(struct GBASync* sync, pthread_mutex_t* mutex) { - if (&sync->audioWait) { - pthread_cond_wait(&sync->audioRequiredCond, mutex); +void GBASyncProduceAudio(struct GBASync* sync, int wait) { + if (sync->audioWait && wait) { + pthread_cond_wait(&sync->audioRequiredCond, &sync->audioBufferMutex); } + pthread_mutex_unlock(&sync->audioBufferMutex); +} + +void GBASyncLockAudio(struct GBASync* sync) { + pthread_mutex_lock(&sync->audioBufferMutex); } void GBASyncConsumeAudio(struct GBASync* sync) { pthread_cond_broadcast(&sync->audioRequiredCond); + pthread_mutex_unlock(&sync->audioBufferMutex); }
M src/gba/gba-thread.hsrc/gba/gba-thread.h

@@ -49,6 +49,7 @@ pthread_cond_t videoFrameRequiredCond;

int audioWait; pthread_cond_t audioRequiredCond; + pthread_mutex_t audioBufferMutex; } sync; };

@@ -65,7 +66,8 @@ int GBASyncWaitFrameStart(struct GBASync* sync, int frameskip);

void GBASyncWaitFrameEnd(struct GBASync* sync); int GBASyncDrawingFrame(struct GBASync* sync); -void GBASyncProduceAudio(struct GBASync* sync, pthread_mutex_t* mutex); +void GBASyncProduceAudio(struct GBASync* sync, int wait); +void GBASyncLockAudio(struct GBASync* sync); void GBASyncConsumeAudio(struct GBASync* sync); #endif
M src/platform/sdl/sdl-audio.csrc/platform/sdl/sdl-audio.c

@@ -3,6 +3,13 @@

#include "gba.h" #include "gba-thread.h" +#define BUFFER_SIZE (GBA_AUDIO_SAMPLES >> 2) + +struct StereoSample { + Sint16 left; + Sint16 right; +}; + static void _GBASDLAudioCallback(void* context, Uint8* data, int len); int GBASDLInitAudio(struct GBASDLAudio* context) {

@@ -34,42 +41,52 @@ SDL_CloseAudio();

SDL_QuitSubSystem(SDL_INIT_AUDIO); } -static void _pulldownResample(struct GBASDLAudio* context) { - int32_t value; - if (CircleBufferRead32(&context->audio->left, &value)) { - context->currentSample.left = value << 5; - } else { - context->currentSample.left = 0; - } - if (CircleBufferRead32(&context->audio->right, &value)) { - context->currentSample.right = value << 5; - } else { - context->currentSample.right = 0; +static void _pulldownResample(struct GBASDLAudio* context, struct StereoSample* output, ssize_t samples) { + int32_t left[BUFFER_SIZE]; + int32_t right[BUFFER_SIZE]; + + // toRead is in GBA samples + int toRead = samples / context->ratio; + while (samples > 0) { + int currentRead = BUFFER_SIZE >> 2; + if (currentRead > toRead) { + currentRead = toRead; + } + unsigned read = GBAAudioCopy(context->audio, left, right, currentRead); + toRead -= read; + unsigned i; + for (i = 0; i < read; ++i) { + context->drift += context->ratio; + while (context->drift >= 0) { + output->left = left[i]; + output->right = right[i]; + ++output; + --samples; +#ifndef NDEBUG + if (samples < 0) { + abort(); + } +#endif + context->drift -= 1.f; + } + } + if (read < BUFFER_SIZE >> 2) { + memset(output, 0, toRead); + return; + } } } static void _GBASDLAudioCallback(void* context, Uint8* data, int len) { struct GBASDLAudio* audioContext = context; - int i; if (!context || !audioContext->audio) { - for (i = 0; i < len; ++i) { - data[i] = 0; - } + memset(data, 0, len); return; } + audioContext->ratio = audioContext->obtainedSpec.freq / (float) audioContext->audio->sampleRate; struct StereoSample* ssamples = (struct StereoSample*) data; len /= 2 * audioContext->obtainedSpec.channels; if (audioContext->obtainedSpec.channels == 2) { - pthread_mutex_lock(&audioContext->audio->bufferMutex); - for (i = 0; i < len; ++i) { - audioContext->drift += audioContext->audio->sampleRate / (float) audioContext->obtainedSpec.freq; - while (audioContext->drift >= 0) { - _pulldownResample(audioContext); - audioContext->drift -= 1.f; - } - ssamples[i] = audioContext->currentSample; - } - GBASyncConsumeAudio(audioContext->audio->p->sync); - pthread_mutex_unlock(&audioContext->audio->bufferMutex); + _pulldownResample(audioContext, ssamples, len); } }
M src/platform/sdl/sdl-audio.hsrc/platform/sdl/sdl-audio.h

@@ -3,17 +3,12 @@ #define SDL_AUDIO_H

#include <SDL.h> -struct StereoSample { - Sint16 left; - Sint16 right; -}; - struct GBASDLAudio { SDL_AudioSpec desiredSpec; SDL_AudioSpec obtainedSpec; float drift; + float ratio; struct GBAAudio* audio; - struct StereoSample currentSample; }; int GBASDLInitAudio(struct GBASDLAudio* context);
M src/util/circle-buffer.csrc/util/circle-buffer.c

@@ -87,3 +87,29 @@ }

buffer->size -= sizeof(int32_t); return 1; } + +int CircleBufferRead(struct CircleBuffer* buffer, void* output, size_t length) { + int8_t* data = buffer->readPtr; + if (buffer->size == 0) { + return 0; + } + if (length > buffer->size) { + length = buffer->size; + } + size_t remaining = buffer->capacity - ((int8_t*) data - (int8_t*) buffer->data); + if (length <= remaining) { + memcpy(output, data, length); + if (length == remaining) { + buffer->readPtr = buffer->data; + } else { + buffer->readPtr = (int8_t*) data + length; + } + } else { + memcpy(output, data, remaining); + memcpy((int8_t*) output + remaining, buffer->data, length - remaining); + buffer->readPtr = (int8_t*) buffer->data + length - remaining; + } + + buffer->size -= length; + return length; +}
M src/util/circle-buffer.hsrc/util/circle-buffer.h

@@ -2,6 +2,7 @@ #ifndef CIRCLE_BUFFER_H

#define CIRCLE_BUFFER_H #include <stdint.h> +#include <string.h> struct CircleBuffer { void* data;

@@ -18,5 +19,6 @@ int CircleBufferWrite8(struct CircleBuffer* buffer, int8_t value);

int CircleBufferWrite32(struct CircleBuffer* buffer, int32_t value); int CircleBufferRead8(struct CircleBuffer* buffer, int8_t* value); int CircleBufferRead32(struct CircleBuffer* buffer, int32_t* value); +int CircleBufferRead(struct CircleBuffer* buffer, void* output, size_t length); #endif