From 09b4a4a29a6393d2c5e2671810d8b046d46fdf13 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 1 Oct 2013 23:56:10 -0700 Subject: [PATCH] Build up DMA channel audio infrastructure from GBA.js --- CMakeLists.txt | 4 +- src/gba/gba-audio.c | 100 ++++++++++++++++++++++++++++++++++++--- src/gba/gba-audio.h | 72 +++++++++++++++++++++++++++- src/gba/gba-memory.c | 2 +- src/gba/gba.c | 7 +++ src/gba/gba.h | 2 + src/util/circle-buffer.c | 59 +++++++++++++++++++++++ src/util/circle-buffer.h | 19 ++++++++ 8 files changed, 256 insertions(+), 9 deletions(-) create mode 100644 src/util/circle-buffer.c create mode 100644 src/util/circle-buffer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 77fce228f..92e954d03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,12 +5,14 @@ set(CMAKE_C_FLAGS_DEBUG "-g -Wall -Wextra -Wno-error=type-limits --std=gnu99") set(CMAKE_C_FLAGS_RELEASE "-O3 -Wall -Wextra --std=gnu99") file(GLOB ARM_SRC ${CMAKE_SOURCE_DIR}/src/arm/*.c) file(GLOB GBA_SRC ${CMAKE_SOURCE_DIR}/src/gba/*.c) +file(GLOB UTIL_SRC ${CMAKE_SOURCE_DIR}/src/util/*.c) file(GLOB RENDERER_SRC ${CMAKE_SOURCE_DIR}/src/gba/renderers/video-software.c) file(GLOB DEBUGGER_SRC ${CMAKE_SOURCE_DIR}/src/debugger/*.c) file(GLOB THIRD_PARTY ${CMAKE_SOURCE_DIR}/third-party/linenoise/linenoise.c) include_directories(${CMAKE_SOURCE_DIR}/src/arm) include_directories(${CMAKE_SOURCE_DIR}/src/gba) include_directories(${CMAKE_SOURCE_DIR}/src/debugger) +include_directories(${CMAKE_SOURCE_DIR}/src/util) include_directories(${CMAKE_SOURCE_DIR}/third-party/linenoise) find_package(SDL 1.2 REQUIRED) @@ -31,5 +33,5 @@ else() endif() include_directories(${SDL_INCLUDE_DIR} ${OPENGL_INCLUDE_DIR}) -add_executable(${BINARY_NAME} ${ARM_SRC} ${GBA_SRC} ${DEBUGGER_SRC} ${RENDERER_SRC} ${THIRD_PARTY} ${SDL_SRC} ${MAIN_SRC}) +add_executable(${BINARY_NAME} ${ARM_SRC} ${GBA_SRC} ${DEBUGGER_SRC} ${RENDERER_SRC} ${UTIL_SRC} ${THIRD_PARTY} ${SDL_SRC} ${MAIN_SRC}) target_link_libraries(${BINARY_NAME} m pthread ${SDL_LIBRARY} ${OPENGL_LIBRARY}) diff --git a/src/gba/gba-audio.c b/src/gba/gba-audio.c index e2a46164c..320c9149d 100644 --- a/src/gba/gba-audio.c +++ b/src/gba/gba-audio.c @@ -1,13 +1,61 @@ #include "gba-audio.h" #include "gba.h" +#include "gba-io.h" + +const unsigned GBA_AUDIO_SAMPLES = 4096 * sizeof(int32_t); +const unsigned GBA_AUDIO_FIFO_SIZE = 8 * sizeof(int32_t); + +static void _sample(struct GBAAudio* audio); void GBAAudioInit(struct GBAAudio* audio) { - (void)(audio); + audio->nextEvent = 0; + audio->eventDiff = 0; + 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->chA.fifo, GBA_AUDIO_FIFO_SIZE); + CircleBufferInit(&audio->chB.fifo, GBA_AUDIO_FIFO_SIZE); } void GBAAudioDeinit(struct GBAAudio* audio) { - (void)(audio); + CircleBufferDeinit(&audio->left); + CircleBufferDeinit(&audio->right); + CircleBufferDeinit(&audio->chA.fifo); + CircleBufferDeinit(&audio->chB.fifo); +} + +int32_t GBAAudioProcessEvents(struct GBAAudio* audio, int32_t cycles) { + audio->nextEvent -= cycles; + if (audio->nextEvent <= 0) { + audio->nextSample -= audio->eventDiff; + if (audio->nextSample <= 0) { + _sample(audio); + audio->nextSample += audio->sampleInterval; + } + + audio->nextEvent = audio->nextSample; + audio->eventDiff = audio->nextEvent; + } + return audio->nextEvent; +} + +void GBAAudioScheduleFifoDma(struct GBAAudio* audio, int number, struct GBADMA* info) { + switch (info->dest) { + case BASE_IO | REG_FIFO_A_LO: + audio->chA.dmaSource = number; + break; + case BASE_IO | REG_FIFO_B_LO: + audio->chB.dmaSource = number; + break; + default: + GBALog(audio->p, GBA_LOG_GAME_ERROR, "Invalid FIFO destination: 0x%08X", info->dest); + return; + } + info->dstControl = DMA_FIXED; } void GBAAudioWriteSOUND1CNT_LO(struct GBAAudio* audio, uint16_t value) { @@ -51,15 +99,15 @@ void GBAAudioWriteSOUND4CNT_HI(struct GBAAudio* audio, uint16_t value) { } void GBAAudioWriteSOUNDCNT_LO(struct GBAAudio* audio, uint16_t value) { - GBALog(audio->p, GBA_LOG_STUB, "Audio unimplemented"); + audio->soundcntLo = value; } void GBAAudioWriteSOUNDCNT_HI(struct GBAAudio* audio, uint16_t value) { - GBALog(audio->p, GBA_LOG_STUB, "Audio unimplemented"); + audio->soundcntHi = value; } void GBAAudioWriteSOUNDCNT_X(struct GBAAudio* audio, uint16_t value) { - GBALog(audio->p, GBA_LOG_STUB, "Audio unimplemented"); + audio->soundcntX = (value & 0xF0) | (audio->soundcntX & 0x0F); } void GBAAudioWriteWaveRAM(struct GBAAudio* audio, int address, uint32_t value) { @@ -67,6 +115,46 @@ void GBAAudioWriteWaveRAM(struct GBAAudio* audio, int address, uint32_t value) { } void GBAAudioWriteFIFO(struct GBAAudio* audio, int address, uint32_t value) { - GBALog(audio->p, GBA_LOG_STUB, "Audio unimplemented"); + struct CircleBuffer* fifo; + switch (address) { + case REG_FIFO_A_LO: + fifo = &audio->chA.fifo; + break; + case REG_FIFO_B_LO: + fifo = &audio->chB.fifo; + break; + default: + GBALog(audio->p, GBA_LOG_ERROR, "Bad FIFO write to address 0x%03x", address); + return; + } + while (!CircleBufferWrite32(fifo, value)) { + int32_t dummy; + CircleBufferRead32(fifo, &dummy); + } } +void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId) { + struct GBAAudioFIFO* channel; + if (fifoId == 0) { + channel = &audio->chA; + } else if (fifoId == 1) { + channel = &audio->chB; + } else { + GBALog(audio->p, GBA_LOG_ERROR, "Bad FIFO write to address 0x%03x", fifoId); + return; + } + if (CircleBufferSize(&channel->fifo) < 4 * sizeof(int32_t)) { + struct GBADMA* dma = &audio->p->memory.dma[channel->dmaSource]; + dma->nextCount = 4; + GBAMemoryServiceDMA(&audio->p->memory, channel->dmaSource, dma); + } + CircleBufferRead32(&channel->fifo, &channel->sample); +} + +static void _sample(struct GBAAudio* audio) { + int32_t sampleLeft = 0; + int32_t sampleRight = 0; + + CircleBufferWrite32(&audio->left, sampleLeft); + CircleBufferWrite32(&audio->right, sampleRight); +} diff --git a/src/gba/gba-audio.h b/src/gba/gba-audio.h index fccb21b26..210f57aed 100644 --- a/src/gba/gba-audio.h +++ b/src/gba/gba-audio.h @@ -1,8 +1,12 @@ #ifndef GBA_AUDIO_H #define GBA_AUDIO_H +#include "circle-buffer.h" + #include +struct GBADMA; + union GBAAudioWave { struct { unsigned length : 6; @@ -94,7 +98,9 @@ struct GBAAudioChannel4 { }; struct GBAAudioFIFO { - + struct CircleBuffer fifo; + int dmaSource; + int32_t sample; }; struct GBAAudio { @@ -107,11 +113,74 @@ struct GBAAudio { struct GBAAudioFIFO chA; struct GBAAudioFIFO chB; + + struct CircleBuffer left; + struct CircleBuffer right; + + union { + struct { + unsigned volumeRight : 3; + unsigned : 1; + unsigned volumeLeft : 3; + unsigned : 1; + unsigned ch1Right : 1; + unsigned ch2Right : 1; + unsigned ch3Right : 1; + unsigned ch4Right : 1; + unsigned ch1Left : 1; + unsigned ch2Left : 1; + unsigned ch3Left : 1; + unsigned ch4Left : 1; + }; + uint16_t soundcntLo; + }; + + union { + struct { + unsigned volume : 2; + unsigned volumeDmaA : 1; + unsigned volumeDmaB : 1; + unsigned : 4; + unsigned dmaARight : 1; + unsigned dmaALeft : 1; + unsigned dmaATimer : 1; + unsigned dmaAReset : 1; + unsigned dmaBRight : 1; + unsigned dmaBLeft : 1; + unsigned dmaBTimer : 1; + unsigned dmaBReset : 1; + }; + uint16_t soundcntHi; + }; + + union { + struct { + unsigned playingCh1 : 1; + unsigned playingCh2 : 1; + unsigned playingCh3 : 1; + unsigned playingCh4 : 1; + unsigned : 3; + unsigned enable : 1; + unsigned : 8; + }; + uint16_t soundcntX; + }; + + unsigned sampleRate; + + int32_t nextEvent; + int32_t eventDiff; + int32_t nextSample; + + int32_t sampleInterval; }; void GBAAudioInit(struct GBAAudio* audio); void GBAAudioDeinit(struct GBAAudio* audio); +int32_t GBAAudioProcessEvents(struct GBAAudio* audio, int32_t cycles); +void GBAAudioScheduleFifoDma(struct GBAAudio* audio, int number, struct GBADMA* info); + void GBAAudioWriteSOUND1CNT_LO(struct GBAAudio* audio, uint16_t value); void GBAAudioWriteSOUND1CNT_HI(struct GBAAudio* audio, uint16_t value); void GBAAudioWriteSOUND1CNT_X(struct GBAAudio* audio, uint16_t value); @@ -128,5 +197,6 @@ void GBAAudioWriteSOUNDCNT_X(struct GBAAudio* audio, uint16_t value); 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); #endif diff --git a/src/gba/gba-memory.c b/src/gba/gba-memory.c index e5fe195a7..7b4c6dac1 100644 --- a/src/gba/gba-memory.c +++ b/src/gba/gba-memory.c @@ -634,7 +634,7 @@ void GBAMemoryScheduleDMA(struct GBAMemory* memory, int number, struct GBADMA* i break; case 1: case 2: - //this.cpu.irq.audio.scheduleFIFODma(number, info); + GBAAudioScheduleFifoDma(&memory->p->audio, number, info); break; case 3: //this.cpu.irq.video.scheduleVCaptureDma(dma, info); diff --git a/src/gba/gba.c b/src/gba/gba.c index 792143228..79b9d5e4f 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -14,6 +14,8 @@ #include #include +const uint32_t GBA_ARM7TDMI_FREQUENCY = 0x1000000; + enum { SP_BASE_SYSTEM = 0x03FFFF00, SP_BASE_IRQ = 0x03FFFFA0, @@ -95,6 +97,11 @@ static void GBAProcessEvents(struct ARMBoard* board) { nextEvent = testEvent; } + testEvent = GBAAudioProcessEvents(&gbaBoard->p->audio, cycles); + if (testEvent < nextEvent) { + nextEvent = testEvent; + } + testEvent = GBAMemoryProcessEvents(&gbaBoard->p->memory, cycles); if (testEvent < nextEvent) { nextEvent = testEvent; diff --git a/src/gba/gba.h b/src/gba/gba.h index 50b6ff704..b5fc86af9 100644 --- a/src/gba/gba.h +++ b/src/gba/gba.h @@ -7,6 +7,8 @@ #include "gba-video.h" #include "gba-audio.h" +const uint32_t GBA_ARM7TDMI_FREQUENCY; + enum GBAIRQ { IRQ_VBLANK = 0x0, IRQ_HBLANK = 0x1, diff --git a/src/util/circle-buffer.c b/src/util/circle-buffer.c new file mode 100644 index 000000000..232a80ebd --- /dev/null +++ b/src/util/circle-buffer.c @@ -0,0 +1,59 @@ +#include "circle-buffer.h" + +#include +#include + +void CircleBufferInit(struct CircleBuffer* buffer, unsigned capacity) { + buffer->data = malloc(capacity); + buffer->capacity = capacity; + buffer->readPtr = buffer->data; + buffer->writePtr = (int8_t*) buffer->data + capacity; +} + +void CircleBufferDeinit(struct CircleBuffer* buffer) { + free(buffer->data); + buffer->data = 0; +} + +unsigned CircleBufferSize(const struct CircleBuffer* buffer) { + ptrdiff_t size = (int8_t*) buffer->readPtr - (int8_t*) buffer->writePtr; + if (size < 0) { + return buffer->capacity - size; + } + return size; +} + +int CircleBufferWrite32(struct CircleBuffer* buffer, int32_t value) { + uint32_t* data = buffer->writePtr; + if ((int8_t*) buffer->writePtr + 1 == buffer->readPtr) { + return 0; + } + if ((int8_t*) buffer->writePtr == (int8_t*) buffer->data + buffer->capacity - 1 && buffer->readPtr == buffer->data) { + return 0; + } + *data = value; + ++data; + ptrdiff_t size = (int8_t*) data - (int8_t*) buffer->data; + if (size < buffer->capacity) { + buffer->writePtr = data; + } else { + buffer->writePtr = buffer->data; + } + return 1; +} + +int CircleBufferRead32(struct CircleBuffer* buffer, int32_t* value) { + uint32_t* data = buffer->readPtr; + if (buffer->readPtr == buffer->writePtr) { + return 0; + } + *value = *data; + ++data; + ptrdiff_t size = (int8_t*) data - (int8_t*) buffer->data; + if (size < buffer->capacity) { + buffer->readPtr = data; + } else { + buffer->readPtr = buffer->data; + } + return 1; +} diff --git a/src/util/circle-buffer.h b/src/util/circle-buffer.h new file mode 100644 index 000000000..47c23f0de --- /dev/null +++ b/src/util/circle-buffer.h @@ -0,0 +1,19 @@ +#ifndef CIRCLE_BUFFER_H +#define CIRCLE_BUFFER_H + +#include + +struct CircleBuffer { + void* data; + unsigned capacity; + void* readPtr; + void* writePtr; +}; + +void CircleBufferInit(struct CircleBuffer* buffer, unsigned capacity); +void CircleBufferDeinit(struct CircleBuffer* buffer); +unsigned CircleBufferSize(const struct CircleBuffer* buffer); +int CircleBufferWrite32(struct CircleBuffer* buffer, int32_t value); +int CircleBufferRead32(struct CircleBuffer* buffer, int32_t* value); + +#endif