diff --git a/src/gba/gba-audio.c b/src/gba/gba-audio.c index 05427f7a0..641b95550 100644 --- a/src/gba/gba-audio.c +++ b/src/gba/gba-audio.c @@ -4,8 +4,9 @@ #include "gba-io.h" #include "gba-serialize.h" #include "gba-thread.h" +#include "gba-video.h" -const unsigned GBA_AUDIO_SAMPLES = 512; +const unsigned GBA_AUDIO_SAMPLES = 2048; const unsigned GBA_AUDIO_FIFO_SIZE = 8 * sizeof(int32_t); #define SWEEP_CYCLES (GBA_ARM7TDMI_FREQUENCY / 128) @@ -19,9 +20,9 @@ static int32_t _updateChannel4(struct GBAAudioChannel4* ch); static int _applyBias(struct GBAAudio* audio, int sample); static void _sample(struct GBAAudio* audio); -void GBAAudioInit(struct GBAAudio* audio) { - CircleBufferInit(&audio->left, GBA_AUDIO_SAMPLES * sizeof(int32_t)); - CircleBufferInit(&audio->right, GBA_AUDIO_SAMPLES * sizeof(int32_t)); +void GBAAudioInit(struct GBAAudio* audio, size_t samples) { + CircleBufferInit(&audio->left, samples * sizeof(int32_t)); + CircleBufferInit(&audio->right, samples * sizeof(int32_t)); CircleBufferInit(&audio->chA.fifo, GBA_AUDIO_FIFO_SIZE); CircleBufferInit(&audio->chB.fifo, GBA_AUDIO_FIFO_SIZE); } @@ -69,6 +70,40 @@ void GBAAudioDeinit(struct GBAAudio* audio) { CircleBufferDeinit(&audio->chB.fifo); } +void GBAAudioResizeBuffer(struct GBAAudio* audio, size_t samples) { + if (samples >= GBA_AUDIO_SAMPLES) { + return; + } + + GBASyncLockAudio(audio->p->sync); + int32_t buffer[GBA_AUDIO_SAMPLES]; + int32_t dummy; + size_t read; + size_t i; + + read = CircleBufferDump(&audio->left, buffer, sizeof(buffer)); + CircleBufferDeinit(&audio->left); + CircleBufferInit(&audio->left, samples * sizeof(int32_t)); + for (i = 0; i * sizeof(int32_t) < read; ++i) { + if (!CircleBufferWrite32(&audio->left, buffer[i])) { + CircleBufferRead32(&audio->left, &dummy); + CircleBufferWrite32(&audio->left, buffer[i]); + } + } + + read = CircleBufferDump(&audio->right, buffer, sizeof(buffer)); + CircleBufferDeinit(&audio->right); + CircleBufferInit(&audio->right, samples * sizeof(int32_t)); + for (i = 0; i * sizeof(int32_t) < read; ++i) { + if (!CircleBufferWrite32(&audio->right, buffer[i])) { + CircleBufferRead32(&audio->right, &dummy); + CircleBufferWrite32(&audio->right, buffer[i]); + } + } + + GBASyncUnlockAudio(audio->p->sync); +} + int32_t GBAAudioProcessEvents(struct GBAAudio* audio, int32_t cycles) { audio->nextEvent -= cycles; audio->eventDiff += cycles; @@ -669,7 +704,7 @@ static void _sample(struct GBAAudio* audio) { CircleBufferWrite32(&audio->left, sampleLeft); CircleBufferWrite32(&audio->right, sampleRight); unsigned produced = CircleBufferSize(&audio->left); - GBASyncProduceAudio(audio->p->sync, produced >= GBA_AUDIO_SAMPLES * 3); + GBASyncProduceAudio(audio->p->sync, produced >= CircleBufferCapacity(&audio->left) / sizeof(int32_t) * 3); } void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* state) { @@ -751,3 +786,7 @@ void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState audio->eventDiff = state->audio.eventDiff; audio->nextSample = state->audio.nextSample; } + +float GBAAudioCalculateRatio(struct GBAAudio* audio, float desiredFPS, float desiredSampleRate) { + return desiredSampleRate * GBA_ARM7TDMI_FREQUENCY / (VIDEO_TOTAL_LENGTH * desiredFPS * audio->sampleRate); +} diff --git a/src/gba/gba-audio.h b/src/gba/gba-audio.h index e6ce78fc0..8fbc3960f 100644 --- a/src/gba/gba-audio.h +++ b/src/gba/gba-audio.h @@ -218,10 +218,12 @@ struct GBAStereoSample { int16_t right; }; -void GBAAudioInit(struct GBAAudio* audio); +void GBAAudioInit(struct GBAAudio* audio, size_t samples); void GBAAudioReset(struct GBAAudio* audio); void GBAAudioDeinit(struct GBAAudio* audio); +void GBAAudioResizeBuffer(struct GBAAudio* audio, size_t samples); + int32_t GBAAudioProcessEvents(struct GBAAudio* audio, int32_t cycles); void GBAAudioScheduleFifoDma(struct GBAAudio* audio, int number, struct GBADMA* info); @@ -251,4 +253,6 @@ struct GBASerializedState; void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* state); void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState* state); +float GBAAudioCalculateRatio(struct GBAAudio* audio, float desiredFPS, float desiredSampleRatio); + #endif diff --git a/src/gba/gba-thread.c b/src/gba/gba-thread.c index 9029ba9b0..404cb26a6 100644 --- a/src/gba/gba-thread.c +++ b/src/gba/gba-thread.c @@ -11,6 +11,8 @@ #include +static const float _defaultFPSTarget = 60.f; + #ifdef USE_PTHREADS static pthread_key_t _contextKey; static pthread_once_t _contextOnce = PTHREAD_ONCE_INIT; @@ -84,6 +86,11 @@ static THREAD_ENTRY _GBAThreadRun(void* context) { #else TlsSetValue(_contextKey, threadContext); #endif + + if (threadContext->audioBuffers) { + GBAAudioResizeBuffer(&gba.audio, threadContext->audioBuffers); + } + if (threadContext->renderer) { GBAVideoAssociateRenderer(&gba.video, threadContext->renderer); } @@ -202,6 +209,10 @@ bool GBAThreadStart(struct GBAThread* threadContext) { threadContext->rewindBuffer = 0; } + if (!threadContext->fpsTarget) { + threadContext->fpsTarget = _defaultFPSTarget; + } + if (threadContext->rom && !GBAIsROM(threadContext->rom)) { threadContext->rom->close(threadContext->rom); threadContext->rom = 0; @@ -545,6 +556,10 @@ void GBASyncLockAudio(struct GBASync* sync) { MutexLock(&sync->audioBufferMutex); } +void GBASyncUnlockAudio(struct GBASync* sync) { + MutexUnlock(&sync->audioBufferMutex); +} + void GBASyncConsumeAudio(struct GBASync* sync) { ConditionWake(&sync->audioRequiredCond); MutexUnlock(&sync->audioBufferMutex); diff --git a/src/gba/gba-thread.h b/src/gba/gba-thread.h index a870b9610..9e8c96bd4 100644 --- a/src/gba/gba-thread.h +++ b/src/gba/gba-thread.h @@ -56,6 +56,8 @@ struct GBAThread { const char* fname; int activeKeys; int frameskip; + float fpsTarget; + size_t audioBuffers; // Threading state Thread thread; @@ -105,6 +107,7 @@ bool GBASyncDrawingFrame(struct GBASync* sync); void GBASyncProduceAudio(struct GBASync* sync, int wait); void GBASyncLockAudio(struct GBASync* sync); +void GBASyncUnlockAudio(struct GBASync* sync); void GBASyncConsumeAudio(struct GBASync* sync); #endif diff --git a/src/gba/gba.c b/src/gba/gba.c index 6338e9425..05f97a0d5 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -121,7 +121,7 @@ static void GBAInit(struct ARMCore* cpu, struct ARMComponent* component) { GBAVideoInit(&gba->video); gba->audio.p = gba; - GBAAudioInit(&gba->audio); + GBAAudioInit(&gba->audio, GBA_AUDIO_SAMPLES); GBAIOInit(gba); diff --git a/src/platform/sdl/gl-main.c b/src/platform/sdl/gl-main.c index acc5f4363..65e85c770 100644 --- a/src/platform/sdl/gl-main.c +++ b/src/platform/sdl/gl-main.c @@ -85,6 +85,7 @@ int main(int argc, char** argv) { struct GBAThread context = { .renderer = &renderer.d.d, + .audioBuffers = 512, .startCallback = _GBASDLStart, .cleanCallback = _GBASDLClean, .sync.videoFrameWait = 0, @@ -96,6 +97,9 @@ int main(int argc, char** argv) { GBAMapOptionsToContext(&opts, &context); + renderer.audio.samples = context.audioBuffers; + GBASDLInitAudio(&renderer.audio); + GBAThreadStart(&context); _GBASDLRunloop(&context, &renderer); @@ -115,7 +119,6 @@ static int _GBASDLInit(struct GLSoftwareRenderer* renderer) { } GBASDLInitEvents(&renderer->events); - GBASDLInitAudio(&renderer->audio); #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); @@ -230,6 +233,7 @@ static void _GBASDLDeinit(struct GLSoftwareRenderer* renderer) { static void _GBASDLStart(struct GBAThread* threadContext) { struct GLSoftwareRenderer* renderer = threadContext->userData; renderer->audio.audio = &threadContext->gba->audio; + renderer->audio.thread = threadContext; } static void _GBASDLClean(struct GBAThread* threadContext) { diff --git a/src/platform/sdl/sdl-audio.c b/src/platform/sdl/sdl-audio.c index 52f35fe0c..566ce4a2e 100644 --- a/src/platform/sdl/sdl-audio.c +++ b/src/platform/sdl/sdl-audio.c @@ -4,7 +4,6 @@ #include "gba-thread.h" #define BUFFER_SIZE (GBA_AUDIO_SAMPLES >> 2) -#define FPS_TARGET 60.f static void _GBASDLAudioCallback(void* context, Uint8* data, int len); @@ -17,10 +16,11 @@ bool GBASDLInitAudio(struct GBASDLAudio* context) { context->desiredSpec.freq = 44100; context->desiredSpec.format = AUDIO_S16SYS; context->desiredSpec.channels = 2; - context->desiredSpec.samples = GBA_AUDIO_SAMPLES; + context->desiredSpec.samples = context->samples; context->desiredSpec.callback = _GBASDLAudioCallback; context->desiredSpec.userdata = context; context->audio = 0; + context->thread = 0; context->drift = 0.f; if (SDL_OpenAudio(&context->desiredSpec, &context->obtainedSpec) < 0) { GBALog(0, GBA_LOG_ERROR, "Could not open SDL sound system"); @@ -43,8 +43,7 @@ static void _GBASDLAudioCallback(void* context, Uint8* data, int len) { memset(data, 0, len); return; } - float ratio = 280896.0f * FPS_TARGET / GBA_ARM7TDMI_FREQUENCY; - audioContext->ratio = audioContext->obtainedSpec.freq / ratio / (float) audioContext->audio->sampleRate; + audioContext->ratio = GBAAudioCalculateRatio(audioContext->audio, audioContext->thread->fpsTarget, audioContext->obtainedSpec.freq); struct GBAStereoSample* ssamples = (struct GBAStereoSample*) data; len /= 2 * audioContext->obtainedSpec.channels; if (audioContext->obtainedSpec.channels == 2) { diff --git a/src/platform/sdl/sdl-audio.h b/src/platform/sdl/sdl-audio.h index 78e51f134..f857d581e 100644 --- a/src/platform/sdl/sdl-audio.h +++ b/src/platform/sdl/sdl-audio.h @@ -6,11 +6,17 @@ #include struct GBASDLAudio { + // Input + size_t samples; + + // State SDL_AudioSpec desiredSpec; SDL_AudioSpec obtainedSpec; float drift; float ratio; + struct GBAAudio* audio; + struct GBAThread* thread; }; bool GBASDLInitAudio(struct GBASDLAudio* context); diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index 6ab5245fd..8eca7fe38 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -248,14 +248,7 @@ static void _GBASDLHandleWindowEvent(struct GBAThread* context, struct GBASDLEve void GBASDLHandleEvent(struct GBAThread* context, struct GBASDLEvents* sdlContext, const union SDL_Event* event) { switch (event->type) { case SDL_QUIT: - // FIXME: this isn't thread-safe - if (context->debugger) { - context->debugger->state = DEBUGGER_EXITING; - } - MutexLock(&context->stateMutex); - context->state = THREAD_EXITING; - ConditionWake(&context->stateCond); - MutexUnlock(&context->stateMutex); + GBAThreadEnd(context); break; #if SDL_VERSION_ATLEAST(2, 0, 0) case SDL_WINDOWEVENT: diff --git a/src/platform/sdl/sw-main.c b/src/platform/sdl/sw-main.c index c69907c7c..99eed9e79 100644 --- a/src/platform/sdl/sw-main.c +++ b/src/platform/sdl/sw-main.c @@ -79,6 +79,9 @@ int main(int argc, char** argv) { GBAMapOptionsToContext(&opts, &context); + renderer.audio.samples = context.audioBuffers; + GBASDLInitAudio(&renderer.audio); + #if SDL_VERSION_ATLEAST(2, 0, 0) renderer.events.fullscreen = graphicsOpts.fullscreen; renderer.window = SDL_CreateWindow(PROJECT_NAME, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, renderer.viewportWidth, renderer.viewportHeight, SDL_WINDOW_OPENGL | (SDL_WINDOW_FULLSCREEN_DESKTOP * renderer.events.fullscreen)); @@ -145,7 +148,6 @@ static int _GBASDLInit(struct SoftwareRenderer* renderer) { } GBASDLInitEvents(&renderer->events); - GBASDLInitAudio(&renderer->audio); #if !SDL_VERSION_ATLEAST(2, 0, 0) #ifdef COLOR_16_BIT diff --git a/src/util/circle-buffer.c b/src/util/circle-buffer.c index f98569be5..bb4a1ead2 100644 --- a/src/util/circle-buffer.c +++ b/src/util/circle-buffer.c @@ -2,13 +2,13 @@ #ifndef NDEBUG static int _checkIntegrity(struct CircleBuffer* buffer) { - if ((int8_t*) buffer->writePtr - (int8_t*) buffer->readPtr == buffer->size) { + if ((int8_t*) buffer->writePtr - (int8_t*) buffer->readPtr == (ssize_t) buffer->size) { return 1; } - if (buffer->capacity - buffer->size == ((int8_t*) buffer->writePtr - (int8_t*) buffer->readPtr)) { + if ((ssize_t) (buffer->capacity - buffer->size) == ((int8_t*) buffer->writePtr - (int8_t*) buffer->readPtr)) { return 1; } - if (buffer->capacity - buffer->size == ((int8_t*) buffer->readPtr - (int8_t*) buffer->writePtr)) { + if ((ssize_t) (buffer->capacity - buffer->size) == ((int8_t*) buffer->readPtr - (int8_t*) buffer->writePtr)) { return 1; } return 0; @@ -26,10 +26,14 @@ void CircleBufferDeinit(struct CircleBuffer* buffer) { buffer->data = 0; } -unsigned CircleBufferSize(const struct CircleBuffer* buffer) { +size_t CircleBufferSize(const struct CircleBuffer* buffer) { return buffer->size; } +size_t CircleBufferCapacity(const struct CircleBuffer* buffer) { + return buffer->capacity; +} + void CircleBufferClear(struct CircleBuffer* buffer) { buffer->size = 0; buffer->readPtr = buffer->data; @@ -140,7 +144,7 @@ int CircleBufferRead32(struct CircleBuffer* buffer, int32_t* value) { return 4; } -int CircleBufferRead(struct CircleBuffer* buffer, void* output, size_t length) { +size_t CircleBufferRead(struct CircleBuffer* buffer, void* output, size_t length) { int8_t* data = buffer->readPtr; if (buffer->size == 0) { return 0; @@ -171,7 +175,7 @@ int CircleBufferRead(struct CircleBuffer* buffer, void* output, size_t length) { return length; } -int CircleBufferDump(const struct CircleBuffer* buffer, void* output, size_t length) { +size_t CircleBufferDump(const struct CircleBuffer* buffer, void* output, size_t length) { int8_t* data = buffer->readPtr; if (buffer->size == 0) { return 0; diff --git a/src/util/circle-buffer.h b/src/util/circle-buffer.h index 2e6ec55d2..776f62253 100644 --- a/src/util/circle-buffer.h +++ b/src/util/circle-buffer.h @@ -5,21 +5,22 @@ struct CircleBuffer { void* data; - unsigned capacity; - unsigned size; + size_t capacity; + size_t size; void* readPtr; void* writePtr; }; void CircleBufferInit(struct CircleBuffer* buffer, unsigned capacity); void CircleBufferDeinit(struct CircleBuffer* buffer); -unsigned CircleBufferSize(const struct CircleBuffer* buffer); +size_t CircleBufferSize(const struct CircleBuffer* buffer); +size_t CircleBufferCapacity(const struct CircleBuffer* buffer); void CircleBufferClear(struct CircleBuffer* buffer); 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); -int CircleBufferDump(const struct CircleBuffer* buffer, void* output, size_t length); +size_t CircleBufferRead(struct CircleBuffer* buffer, void* output, size_t length); +size_t CircleBufferDump(const struct CircleBuffer* buffer, void* output, size_t length); #endif