all repos — mgba @ ba0596da078cd83d92007792fbad9d8f78ee4b11

mGBA Game Boy Advance Emulator

Hook up audio for DMA sound channels
Jeffrey Pfau jeffrey@endrift.com
Wed, 02 Oct 2013 02:40:16 -0700
commit

ba0596da078cd83d92007792fbad9d8f78ee4b11

parent

a834f8b1f69bc62fb8f56dc529860ed8c7f5bd54

5 files changed, 123 insertions(+), 7 deletions(-)

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

@@ -3,7 +3,7 @@

#include "gba.h" #include "gba-io.h" -const unsigned GBA_AUDIO_SAMPLES = 4096 * sizeof(int32_t); +const unsigned GBA_AUDIO_SAMPLES = 1024; const unsigned GBA_AUDIO_FIFO_SIZE = 8 * sizeof(int32_t); static void _sample(struct GBAAudio* audio);

@@ -15,8 +15,8 @@ audio->nextSample = 0;

audio->sampleRate = 0x8000; audio->sampleInterval = GBA_ARM7TDMI_FREQUENCY / audio->sampleRate; - CircleBufferInit(&audio->left, GBA_AUDIO_SAMPLES); - CircleBufferInit(&audio->right, GBA_AUDIO_SAMPLES); + 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); }

@@ -148,12 +148,28 @@ struct GBADMA* dma = &audio->p->memory.dma[channel->dmaSource];

dma->nextCount = 4; GBAMemoryServiceDMA(&audio->p->memory, channel->dmaSource, dma); } - CircleBufferRead32(&channel->fifo, &channel->sample); + CircleBufferRead8(&channel->fifo, &channel->sample); } static void _sample(struct GBAAudio* audio) { int32_t sampleLeft = 0; int32_t sampleRight = 0; + + if (audio->chALeft) { + sampleLeft += audio->chA.sample; + } + + if (audio->chARight) { + sampleRight += audio->chA.sample; + } + + if (audio->chBLeft) { + sampleLeft += audio->chB.sample; + } + + if (audio->chBRight) { + sampleRight += audio->chB.sample; + } CircleBufferWrite32(&audio->left, sampleLeft); CircleBufferWrite32(&audio->right, sampleRight);
M src/gba/gba-audio.hsrc/gba/gba-audio.h

@@ -7,6 +7,8 @@ #include <stdint.h>

struct GBADMA; +const unsigned GBA_AUDIO_SAMPLES; + union GBAAudioWave { struct { unsigned length : 6;

@@ -100,7 +102,7 @@

struct GBAAudioFIFO { struct CircleBuffer fifo; int dmaSource; - int32_t sample; + int8_t sample; }; struct GBAAudio {
M src/gl-main.csrc/gl-main.c

@@ -1,6 +1,7 @@

#include "debugger.h" #include "gba-thread.h" #include "gba.h" +#include "sdl-audio.h" #include "sdl-events.h" #include "renderers/video-software.h"

@@ -19,6 +20,8 @@ #include <unistd.h>

struct GLSoftwareRenderer { struct GBAVideoSoftwareRenderer d; + struct GBASDLAudio audio; + struct GBASDLEvents events; GLuint tex; };

@@ -69,6 +72,7 @@ context.fname = fname;

context.useDebugger = 1; context.renderer = &renderer.d.d; GBAThreadStart(&context); + renderer.audio.audio = &context.gba->audio; _GBASDLRunloop(&context, &renderer);

@@ -85,7 +89,8 @@ if (SDL_Init(SDL_INIT_VIDEO) < 0) {

return 0; } - GBASDLInitEvents(); + GBASDLInitEvents(&renderer->events); + GBASDLInitAudio(&renderer->audio); SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1); SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);

@@ -155,6 +160,7 @@

static void _GBASDLDeinit(struct GLSoftwareRenderer* renderer) { free(renderer->d.outputBuffer); - GBASDLDeinitEvents(); + GBASDLDeinitEvents(&renderer->events); + GBASDLDeinitAudio(&renderer->audio); SDL_Quit(); }
A src/sdl/sdl-audio.c

@@ -0,0 +1,70 @@

+#include "sdl-audio.h" + +#include "gba.h" + +static void _GBASDLAudioCallback(void* context, Uint8* data, int len); + +int GBASDLInitAudio(struct GBASDLAudio* context) { + if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { + GBALog(0, GBA_LOG_ERROR, "Could not initialize SDL sound system"); + return 0; + } + + context->desiredSpec.freq = 44100; + context->desiredSpec.format = AUDIO_S16SYS; + context->desiredSpec.channels = 2; + context->desiredSpec.samples = GBA_AUDIO_SAMPLES >> 1; + context->desiredSpec.callback = _GBASDLAudioCallback; + context->desiredSpec.userdata = context; + context->audio = 0; + context->drift = 0.f; + if (SDL_OpenAudio(&context->desiredSpec, &context->obtainedSpec) < 0) { + GBALog(0, GBA_LOG_ERROR, "Could not open SDL sound system"); + return 0; + } + SDL_PauseAudio(0); + return 1; +} + +void GBASDLDeinitAudio(struct GBASDLAudio* context) { + (void)(context); + 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 << 7; + } else { + context->currentSample.left = 0; + } + if (CircleBufferRead32(&context->audio->right, &value)) { + context->currentSample.right = value << 7; + } else { + context->currentSample.right = 0; + } +} + +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; + } + return; + } + struct StereoSample* ssamples = (struct StereoSample*) data; + len /= 2 * audioContext->obtainedSpec.channels; + if (audioContext->obtainedSpec.channels == 2) { + 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; + } + } +}
A src/sdl/sdl-audio.h

@@ -0,0 +1,22 @@

+#ifndef SDL_AUDIO_H +#define SDL_AUDIO_H + +#include <SDL.h> + +struct StereoSample { + Sint16 left; + Sint16 right; +}; + +struct GBASDLAudio { + SDL_AudioSpec desiredSpec; + SDL_AudioSpec obtainedSpec; + float drift; + struct GBAAudio* audio; + struct StereoSample currentSample; +}; + +int GBASDLInitAudio(struct GBASDLAudio* context); +void GBASDLDeinitAudio(struct GBASDLAudio* context); + +#endif