diff --git a/CHANGES b/CHANGES index 4918ad25b..b7c9a8bbb 100644 --- a/CHANGES +++ b/CHANGES @@ -18,6 +18,7 @@ Misc: - Qt: Disable sync to video by default - GBA: Exit cleanly on FATAL if the port supports it - Qt: Handle a game crash without crashing + - GBA Audio: Change internal audio sample buffer from 32-bit to 16-bit samples 0.1.0: (2014-12-13) - Initial release diff --git a/src/gba/gba-audio.c b/src/gba/gba-audio.c index 3d5a7c0b1..6024686e8 100644 --- a/src/gba/gba-audio.c +++ b/src/gba/gba-audio.c @@ -27,8 +27,8 @@ static int _applyBias(struct GBAAudio* audio, int sample); static void _sample(struct GBAAudio* audio); void GBAAudioInit(struct GBAAudio* audio, size_t samples) { - CircleBufferInit(&audio->left, samples * sizeof(int32_t)); - CircleBufferInit(&audio->right, samples * sizeof(int32_t)); + CircleBufferInit(&audio->left, samples * sizeof(int16_t)); + CircleBufferInit(&audio->right, samples * sizeof(int16_t)); CircleBufferInit(&audio->chA.fifo, GBA_AUDIO_FIFO_SIZE); CircleBufferInit(&audio->chB.fifo, GBA_AUDIO_FIFO_SIZE); } @@ -96,28 +96,28 @@ void GBAAudioResizeBuffer(struct GBAAudio* audio, size_t samples) { } GBASyncLockAudio(audio->p->sync); - int32_t buffer[GBA_AUDIO_SAMPLES]; - int32_t dummy; + int16_t buffer[GBA_AUDIO_SAMPLES]; + int16_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]); + CircleBufferInit(&audio->left, samples * sizeof(int16_t)); + for (i = 0; i * sizeof(int16_t) < read; ++i) { + if (!CircleBufferWrite16(&audio->left, buffer[i])) { + CircleBufferRead16(&audio->left, &dummy); + CircleBufferWrite16(&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]); + CircleBufferInit(&audio->right, samples * sizeof(int16_t)); + for (i = 0; i * sizeof(int16_t) < read; ++i) { + if (!CircleBufferWrite16(&audio->right, buffer[i])) { + CircleBufferRead16(&audio->right, &dummy); + CircleBufferWrite16(&audio->right, buffer[i]); } } @@ -476,16 +476,16 @@ unsigned GBAAudioCopy(struct GBAAudio* audio, void* left, void* right, unsigned GBASyncLockAudio(audio->p->sync); unsigned read = 0; if (left) { - unsigned readL = CircleBufferRead(&audio->left, left, nSamples * sizeof(int32_t)) >> 2; + unsigned readL = CircleBufferRead(&audio->left, left, nSamples * sizeof(int16_t)) >> 1; if (readL < nSamples) { - memset((int32_t*) left + readL, 0, nSamples - readL); + memset((int16_t*) left + readL, 0, nSamples - readL); } read = readL; } if (right) { - unsigned readR = CircleBufferRead(&audio->right, right, nSamples * sizeof(int32_t)) >> 2; + unsigned readR = CircleBufferRead(&audio->right, right, nSamples * sizeof(int16_t)) >> 1; if (readR < nSamples) { - memset((int32_t*) right + readR, 0, nSamples - readR); + memset((int16_t*) right + readR, 0, nSamples - readR); } read = read >= readR ? read : readR; } @@ -494,8 +494,8 @@ unsigned GBAAudioCopy(struct GBAAudio* audio, void* left, void* right, unsigned } unsigned GBAAudioResampleNN(struct GBAAudio* audio, float ratio, float* drift, struct GBAStereoSample* output, unsigned nSamples) { - int32_t left[GBA_AUDIO_SAMPLES]; - int32_t right[GBA_AUDIO_SAMPLES]; + int16_t left[GBA_AUDIO_SAMPLES]; + int16_t right[GBA_AUDIO_SAMPLES]; // toRead is in GBA samples // TODO: Do this with fixed-point math @@ -685,12 +685,12 @@ static int _applyBias(struct GBAAudio* audio, int sample) { } else if (sample < 0) { sample = 0; } - return (sample - GBARegisterSOUNDBIASGetBias(audio->soundbias)) << 6; + return (sample - GBARegisterSOUNDBIASGetBias(audio->soundbias)) << 5; } static void _sample(struct GBAAudio* audio) { - int32_t sampleLeft = 0; - int32_t sampleRight = 0; + int16_t sampleLeft = 0; + int16_t sampleRight = 0; int psgShift = 6 - audio->volume; if (audio->ch1Left) { @@ -748,8 +748,8 @@ static void _sample(struct GBAAudio* audio) { sampleRight = _applyBias(audio, sampleRight); GBASyncLockAudio(audio->p->sync); - CircleBufferWrite32(&audio->left, sampleLeft); - CircleBufferWrite32(&audio->right, sampleRight); + CircleBufferWrite16(&audio->left, sampleLeft); + CircleBufferWrite16(&audio->right, sampleRight); unsigned produced = CircleBufferSize(&audio->left); struct GBAThread* thread = GBAThreadGetContext(); if (thread && thread->stream) { diff --git a/src/util/circle-buffer.c b/src/util/circle-buffer.c index a6bc87405..5220ced44 100644 --- a/src/util/circle-buffer.c +++ b/src/util/circle-buffer.c @@ -97,6 +97,34 @@ int CircleBufferWrite32(struct CircleBuffer* buffer, int32_t value) { return 4; } +int CircleBufferWrite16(struct CircleBuffer* buffer, int16_t value) { + int16_t* data = buffer->writePtr; + if (buffer->size + sizeof(int16_t) > buffer->capacity) { + return 0; + } + if ((intptr_t) data & 0x3) { + int written = 0; + written += CircleBufferWrite8(buffer, ((int8_t*) &value)[0]); + written += CircleBufferWrite8(buffer, ((int8_t*) &value)[1]); + return written; + } + *data = value; + ++data; + size_t size = (int8_t*) data - (int8_t*) buffer->data; + if (size < buffer->capacity) { + buffer->writePtr = data; + } else { + buffer->writePtr = buffer->data; + } + buffer->size += sizeof(int16_t); +#ifndef NDEBUG + if (!_checkIntegrity(buffer)) { + abort(); + } +#endif + return 2; +} + int CircleBufferRead8(struct CircleBuffer* buffer, int8_t* value) { int8_t* data = buffer->readPtr; if (buffer->size < sizeof(int8_t)) { @@ -119,6 +147,34 @@ int CircleBufferRead8(struct CircleBuffer* buffer, int8_t* value) { return 1; } +int CircleBufferRead16(struct CircleBuffer* buffer, int16_t* value) { + int16_t* data = buffer->readPtr; + if (buffer->size < sizeof(int16_t)) { + return 0; + } + if ((intptr_t) data & 0x3) { + int read = 0; + read += CircleBufferRead8(buffer, &((int8_t*) value)[0]); + read += CircleBufferRead8(buffer, &((int8_t*) value)[1]); + return read; + } + *value = *data; + ++data; + size_t size = (int8_t*) data - (int8_t*) buffer->data; + if (size < buffer->capacity) { + buffer->readPtr = data; + } else { + buffer->readPtr = buffer->data; + } + buffer->size -= sizeof(int16_t); +#ifndef NDEBUG + if (!_checkIntegrity(buffer)) { + abort(); + } +#endif + return 2; +} + int CircleBufferRead32(struct CircleBuffer* buffer, int32_t* value) { int32_t* data = buffer->readPtr; if (buffer->size < sizeof(int32_t)) { diff --git a/src/util/circle-buffer.h b/src/util/circle-buffer.h index 97e5fdc09..77650ca84 100644 --- a/src/util/circle-buffer.h +++ b/src/util/circle-buffer.h @@ -22,8 +22,10 @@ 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 CircleBufferWrite16(struct CircleBuffer* buffer, int16_t value); int CircleBufferWrite32(struct CircleBuffer* buffer, int32_t value); int CircleBufferRead8(struct CircleBuffer* buffer, int8_t* value); +int CircleBufferRead16(struct CircleBuffer* buffer, int16_t* value); int CircleBufferRead32(struct CircleBuffer* buffer, int32_t* value); size_t CircleBufferRead(struct CircleBuffer* buffer, void* output, size_t length); size_t CircleBufferDump(const struct CircleBuffer* buffer, void* output, size_t length);