From cbbcf7478ebc9532d9e9a02e07c38bed19ef9de4 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 1 Jun 2022 02:13:16 -0700 Subject: [PATCH] GBA Audio: Adjust PSG sampling rate with SOUNDBIAS --- CHANGES | 1 + src/gb/audio.c | 2 +- src/gba/audio.c | 97 +++++++++++++++++++++++++++++-------------------- 3 files changed, 59 insertions(+), 41 deletions(-) diff --git a/CHANGES b/CHANGES index 25bb5172e..d781669d3 100644 --- a/CHANGES +++ b/CHANGES @@ -28,6 +28,7 @@ Emulation fixes: - GBA: Improve timing when not booting from BIOS - 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 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/src/gb/audio.c b/src/gb/audio.c index 1601c734b..b1a3f68dd 100644 --- a/src/gb/audio.c +++ b/src/gb/audio.c @@ -674,7 +674,6 @@ void GBAudioUpdateFrame(struct GBAudio* audio) { } void GBAudioSamplePSG(struct GBAudio* audio, int16_t* left, int16_t* right) { - GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0xF); int dcOffset = audio->style == GB_AUDIO_GBA ? 0 : -0x8; int sampleLeft = dcOffset; int sampleRight = dcOffset; @@ -731,6 +730,7 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) { struct GBAudio* audio = user; int16_t sampleLeft = 0; int16_t sampleRight = 0; + GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0xF); GBAudioSamplePSG(audio, &sampleLeft, &sampleRight); sampleLeft = (sampleLeft * audio->masterVolume * 6) >> 7; sampleRight = (sampleRight * audio->masterVolume * 6) >> 7; diff --git a/src/gba/audio.c b/src/gba/audio.c index addac27ef..05f062719 100644 --- a/src/gba/audio.c +++ b/src/gba/audio.c @@ -25,6 +25,7 @@ mLOG_DEFINE_CATEGORY(GBA_AUDIO, "GBA Audio", "gba.audio"); const unsigned GBA_AUDIO_SAMPLES = 2048; const int GBA_AUDIO_VOLUME_MAX = 0x100; +static const int SAMPLE_INTERVAL = GBA_ARM7TDMI_FREQUENCY / 0x8000; static const int CLOCKS_PER_FRAME = 0x800; static int _applyBias(struct GBAAudio* audio, int sample); @@ -218,6 +219,7 @@ void GBAAudioWriteSOUNDCNT_X(struct GBAAudio* audio, uint16_t value) { void GBAAudioWriteSOUNDBIAS(struct GBAAudio* audio, uint16_t value) { audio->soundbias = value; + audio->sampleInterval = 0x200 >> GBARegisterSOUNDBIASGetResolution(value); } void GBAAudioWriteWaveRAM(struct GBAAudio* audio, int address, uint32_t value) { @@ -318,59 +320,74 @@ static int _applyBias(struct GBAAudio* audio, int sample) { static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) { struct GBAAudio* audio = user; - int16_t sampleLeft = 0; - int16_t sampleRight = 0; - int psgShift = 4 - audio->volume; - GBAudioSamplePSG(&audio->psg, &sampleLeft, &sampleRight); - sampleLeft >>= psgShift; - sampleRight >>= psgShift; + int16_t samplesLeft[8]; + int16_t samplesRight[8]; + int32_t timestamp = mTimingCurrentTime(&audio->p->timing) - cyclesLate - SAMPLE_INTERVAL; + int sample; + for (sample = 0; sample * audio->sampleInterval < (int32_t) SAMPLE_INTERVAL; ++sample) { + int16_t sampleLeft = 0; + int16_t sampleRight = 0; + int psgShift = 4 - audio->volume; + GBAudioRun(&audio->psg, timestamp + (sample + 1) * audio->sampleInterval, 0xF); + GBAudioSamplePSG(&audio->psg, &sampleLeft, &sampleRight); + sampleLeft >>= psgShift; + sampleRight >>= psgShift; - if (audio->mixer) { - audio->mixer->step(audio->mixer); - } - if (!audio->externalMixing) { - if (!audio->forceDisableChA) { - if (audio->chALeft) { - sampleLeft += (audio->chA.sample << 2) >> !audio->volumeChA; + if (audio->mixer) { + audio->mixer->step(audio->mixer); + } + if (!audio->externalMixing) { + if (!audio->forceDisableChA) { + if (audio->chALeft) { + sampleLeft += (audio->chA.sample << 2) >> !audio->volumeChA; + } + + if (audio->chARight) { + sampleRight += (audio->chA.sample << 2) >> !audio->volumeChA; + } } - if (audio->chARight) { - sampleRight += (audio->chA.sample << 2) >> !audio->volumeChA; + if (!audio->forceDisableChB) { + if (audio->chBLeft) { + sampleLeft += (audio->chB.sample << 2) >> !audio->volumeChB; + } + + if (audio->chBRight) { + sampleRight += (audio->chB.sample << 2) >> !audio->volumeChB; + } } } - if (!audio->forceDisableChB) { - if (audio->chBLeft) { - sampleLeft += (audio->chB.sample << 2) >> !audio->volumeChB; - } - - if (audio->chBRight) { - sampleRight += (audio->chB.sample << 2) >> !audio->volumeChB; - } - } + sampleLeft = _applyBias(audio, sampleLeft); + sampleRight = _applyBias(audio, sampleRight); + samplesLeft[sample] = sampleLeft; + samplesRight[sample] = sampleRight; } - sampleLeft = _applyBias(audio, sampleLeft); - sampleRight = _applyBias(audio, sampleRight); - mCoreSyncLockAudio(audio->p->sync); unsigned produced; - 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); - audio->lastLeft = sampleLeft; - audio->lastRight = sampleRight; - audio->clock += audio->sampleInterval; - if (audio->clock >= CLOCKS_PER_FRAME) { - blip_end_frame(audio->psg.left, CLOCKS_PER_FRAME); - blip_end_frame(audio->psg.right, CLOCKS_PER_FRAME); - audio->clock -= CLOCKS_PER_FRAME; + int i; + for (i = 0; i < sample; ++i) { + int16_t sampleLeft = samplesLeft[i]; + int16_t sampleRight = samplesRight[i]; + 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); + audio->lastLeft = sampleLeft; + audio->lastRight = sampleRight; + audio->clock += audio->sampleInterval; + if (audio->clock >= CLOCKS_PER_FRAME) { + blip_end_frame(audio->psg.left, CLOCKS_PER_FRAME); + blip_end_frame(audio->psg.right, CLOCKS_PER_FRAME); + audio->clock -= CLOCKS_PER_FRAME; + } } } - produced = blip_samples_avail(audio->psg.left); + // TODO: Post all frames if (audio->p->stream && audio->p->stream->postAudioFrame) { - audio->p->stream->postAudioFrame(audio->p->stream, sampleLeft, sampleRight); + audio->p->stream->postAudioFrame(audio->p->stream, samplesLeft[sample - 1], samplesRight[sample - 1]); } + produced = blip_samples_avail(audio->psg.left); bool wait = produced >= audio->samples; if (!mCoreSyncProduceAudio(audio->p->sync, audio->psg.left, audio->samples)) { // Interrupted @@ -381,7 +398,7 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) { audio->p->stream->postAudioBuffer(audio->p->stream, audio->psg.left, audio->psg.right); } - mTimingSchedule(timing, &audio->sampleEvent, audio->sampleInterval - cyclesLate); + mTimingSchedule(timing, &audio->sampleEvent, SAMPLE_INTERVAL - cyclesLate); } void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* state) {