Build up DMA channel audio infrastructure from GBA.js
Jeffrey Pfau jeffrey@endrift.com
Tue, 01 Oct 2013 23:56:10 -0700
8 files changed,
256 insertions(+),
9 deletions(-)
M
CMakeLists.txt
→
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 @@ include_directories(${SDL_INCLUDE_DIR} ${OPENGL_INCLUDE_DIR})
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})
M
src/gba/gba-audio.c
→
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 @@ audio->ch4.control.packed = 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 @@ GBALog(audio->p, GBA_LOG_STUB, "Audio unimplemented");
} 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); +}
M
src/gba/gba-audio.h
→
src/gba/gba-audio.h
@@ -1,7 +1,11 @@
#ifndef GBA_AUDIO_H #define GBA_AUDIO_H +#include "circle-buffer.h" + #include <stdint.h> + +struct GBADMA; union GBAAudioWave { struct {@@ -94,7 +98,9 @@ } control;
}; struct GBAAudioFIFO { - + struct CircleBuffer fifo; + int dmaSource; + int32_t sample; }; struct GBAAudio {@@ -107,10 +113,73 @@ struct GBAAudioChannel4 ch4;
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);@@ -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
M
src/gba/gba-memory.c
→
src/gba/gba-memory.c
@@ -634,7 +634,7 @@ GBALog(memory->p, GBA_LOG_WARN, "Discarding invalid DMA0 scheduling");
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);
M
src/gba/gba.c
→
src/gba/gba.c
@@ -14,6 +14,8 @@ #include <string.h>
#include <sys/mman.h> #include <sys/stat.h> +const uint32_t GBA_ARM7TDMI_FREQUENCY = 0x1000000; + enum { SP_BASE_SYSTEM = 0x03FFFF00, SP_BASE_IRQ = 0x03FFFFA0,@@ -91,6 +93,11 @@ gbaBoard->p->springIRQ = 0;
} testEvent = GBAVideoProcessEvents(&gbaBoard->p->video, cycles); + if (testEvent < nextEvent) { + nextEvent = testEvent; + } + + testEvent = GBAAudioProcessEvents(&gbaBoard->p->audio, cycles); if (testEvent < nextEvent) { nextEvent = testEvent; }
M
src/gba/gba.h
→
src/gba/gba.h
@@ -7,6 +7,8 @@ #include "gba-memory.h"
#include "gba-video.h" #include "gba-audio.h" +const uint32_t GBA_ARM7TDMI_FREQUENCY; + enum GBAIRQ { IRQ_VBLANK = 0x0, IRQ_HBLANK = 0x1,
A
src/util/circle-buffer.c
@@ -0,0 +1,59 @@
+#include "circle-buffer.h" + +#include <stddef.h> +#include <stdlib.h> + +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; +}
A
src/util/circle-buffer.h
@@ -0,0 +1,19 @@
+#ifndef CIRCLE_BUFFER_H +#define CIRCLE_BUFFER_H + +#include <stdint.h> + +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