diff --git a/CHANGES b/CHANGES index d781669d3..302e1685b 100644 --- a/CHANGES +++ b/CHANGES @@ -29,6 +29,7 @@ Emulation fixes: - GBA: Fix expected entry point for multiboot ELFs (fixes mgba.io/i/2450) - GBA: Fix booting multiboot ROMs with no JOY entrypoint - GBA Audio: Adjust PSG sampling rate with SOUNDBIAS + - GBA Audio: Sample FIFOs at SOUNDBIAS-set frequency - GBA BIOS: Work around IRQ handling hiccup in Mario & Luigi (fixes mgba.io/i/1059) - GBA BIOS: Initial HLE timing estimation of UnLz77 functions (fixes mgba.io/i/2141) - GBA DMA: Fix DMA source direction bits being cleared (fixes mgba.io/i/2410) diff --git a/include/mgba/internal/gba/audio.h b/include/mgba/internal/gba/audio.h index c9fab3ae1..2bf6c14a6 100644 --- a/include/mgba/internal/gba/audio.h +++ b/include/mgba/internal/gba/audio.h @@ -34,7 +34,7 @@ struct GBAAudioFIFO { uint32_t internalSample; int internalRemaining; int dmaSource; - int8_t sample; + int8_t samples[8]; }; DECL_BITFIELD(GBARegisterSOUNDCNT_HI, uint16_t); diff --git a/include/mgba/internal/gba/serialize.h b/include/mgba/internal/gba/serialize.h index 364563195..20d0719ab 100644 --- a/include/mgba/internal/gba/serialize.h +++ b/include/mgba/internal/gba/serialize.h @@ -224,7 +224,10 @@ mLOG_DECLARE_CATEGORY(GBA_STATE); * 0x00320 - 0x00323: Next IRQ event * 0x00324 - 0x00327: Interruptable BIOS stall cycles * 0x00328 - 0x00367: Matrix memory mapping table - * 0x00368 - 0x003FF: Reserved (leave zero) + * 0x00368 - 0x0036F: Reserved (leave zero) + * 0x00370 - 0x00377: Audio FIFO A samples + * 0x00378 - 0x0037F: Audio FIFO B samples + * 0x00380 - 0x003FF: Reserved (leave zero) * 0x00400 - 0x007FF: I/O memory * 0x00800 - 0x00BFF: Palette * 0x00C00 - 0x00FFF: OAM @@ -381,8 +384,14 @@ struct GBASerializedState { int32_t biosStall; uint32_t matrixMappings[16]; + uint32_t reservedMatrix[2]; - uint32_t reserved[38]; + struct { + int8_t chA[8]; + int8_t chB[8]; + } samples; + + uint32_t reserved[32]; uint16_t io[SIZE_IO >> 1]; uint16_t pram[SIZE_PALETTE_RAM >> 1]; diff --git a/src/gba/audio.c b/src/gba/audio.c index 05f062719..8cd58dc78 100644 --- a/src/gba/audio.c +++ b/src/gba/audio.c @@ -67,13 +67,16 @@ void GBAAudioReset(struct GBAAudio* audio) { audio->chA.internalSample = 0; audio->chA.internalRemaining = 0; memset(audio->chA.fifo, 0, sizeof(audio->chA.fifo)); - audio->chA.sample = 0; audio->chB.fifoWrite = 0; audio->chB.fifoRead = 0; audio->chB.internalSample = 0; audio->chB.internalRemaining = 0; memset(audio->chB.fifo, 0, sizeof(audio->chB.fifo)); - audio->chB.sample = 0; + int i; + for (i = 0; i < 8; ++i) { + audio->chA.samples[i] = 0; + audio->chB.samples[i] = 0; + } audio->sampleRate = 0x8000; audio->soundbias = 0x200; audio->volume = 0; @@ -301,7 +304,17 @@ void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles) { channel->fifoRead = 0; } } - channel->sample = channel->internalSample; + int32_t until = mTimingUntil(&audio->p->timing, &audio->sampleEvent) - 1; + int bits = 1 << GBARegisterSOUNDBIASGetResolution(audio->soundbias); + until += 1 << (9 - GBARegisterSOUNDBIASGetResolution(audio->soundbias)); + until >>= 9 - GBARegisterSOUNDBIASGetResolution(audio->soundbias); + int i; + for (i = bits - until; i < bits; ++i) { + if (i < 0 || i >= bits) { + abort(); + } + channel->samples[i] = channel->internalSample; + } if (channel->internalRemaining) { channel->internalSample >>= 8; --channel->internalRemaining; @@ -339,21 +352,21 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) { if (!audio->externalMixing) { if (!audio->forceDisableChA) { if (audio->chALeft) { - sampleLeft += (audio->chA.sample << 2) >> !audio->volumeChA; + sampleLeft += (audio->chA.samples[sample] << 2) >> !audio->volumeChA; } if (audio->chARight) { - sampleRight += (audio->chA.sample << 2) >> !audio->volumeChA; + sampleRight += (audio->chA.samples[sample] << 2) >> !audio->volumeChA; } } if (!audio->forceDisableChB) { if (audio->chBLeft) { - sampleLeft += (audio->chB.sample << 2) >> !audio->volumeChB; + sampleLeft += (audio->chB.samples[sample] << 2) >> !audio->volumeChB; } if (audio->chBRight) { - sampleRight += (audio->chB.sample << 2) >> !audio->volumeChB; + sampleRight += (audio->chB.samples[sample] << 2) >> !audio->volumeChB; } } } @@ -364,12 +377,19 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) { samplesRight[sample] = sampleRight; } + memset(audio->chA.samples, audio->chA.samples[sample - 1], sizeof(audio->chA.samples)); + memset(audio->chB.samples, audio->chB.samples[sample - 1], sizeof(audio->chB.samples)); + mCoreSyncLockAudio(audio->p->sync); unsigned produced; + int32_t sampleSumLeft = 0; + int32_t sampleSumRight = 0; int i; for (i = 0; i < sample; ++i) { int16_t sampleLeft = samplesLeft[i]; int16_t sampleRight = samplesRight[i]; + sampleSumLeft += sampleLeft; + sampleSumRight += sampleRight; if ((size_t) blip_samples_avail(audio->psg.left) < audio->samples) { blip_add_delta(audio->psg.left, audio->clock, sampleLeft - audio->lastLeft); blip_add_delta(audio->psg.right, audio->clock, sampleRight - audio->lastRight); @@ -385,7 +405,9 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) { } // TODO: Post all frames if (audio->p->stream && audio->p->stream->postAudioFrame) { - audio->p->stream->postAudioFrame(audio->p->stream, samplesLeft[sample - 1], samplesRight[sample - 1]); + sampleSumLeft /= sample; + sampleSumRight /= sample; + audio->p->stream->postAudioFrame(audio->p->stream, sampleSumLeft, sampleSumRight); } produced = blip_samples_avail(audio->psg.left); bool wait = produced >= audio->samples; @@ -406,8 +428,8 @@ void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* STORE_32(audio->chA.internalSample, 0, &state->audio.internalA); STORE_32(audio->chB.internalSample, 0, &state->audio.internalB); - state->audio.sampleA = audio->chA.sample; - state->audio.sampleB = audio->chB.sample; + memcpy(state->samples.chA, audio->chA.samples, sizeof(audio->chA.samples)); + memcpy(state->samples.chB, audio->chB.samples, sizeof(audio->chB.samples)); int readA = audio->chA.fifoRead; int readB = audio->chB.fifoRead; @@ -453,8 +475,8 @@ void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState LOAD_32(audio->chA.internalSample, 0, &state->audio.internalA); LOAD_32(audio->chB.internalSample, 0, &state->audio.internalB); - audio->chA.sample = state->audio.sampleA; - audio->chB.sample = state->audio.sampleB; + memcpy(audio->chA.samples, state->samples.chA, sizeof(audio->chA.samples)); + memcpy(audio->chB.samples, state->samples.chB, sizeof(audio->chB.samples)); int readA = 0; int readB = 0;