From 6159c5a70b6a92cb18948e9e1d7d214b3cdc9a48 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 15 Jun 2022 20:34:06 -0700 Subject: [PATCH] GBA Audio: Decrunchify GB audio --- include/mgba/internal/gba/audio.h | 8 +- include/mgba/internal/gba/serialize.h | 37 +++++++--- src/gb/audio.c | 19 +++-- src/gba/audio.c | 102 ++++++++++++++++++++------ src/gba/serialize.c | 2 +- src/gba/video.c | 7 +- 6 files changed, 125 insertions(+), 50 deletions(-) diff --git a/include/mgba/internal/gba/audio.h b/include/mgba/internal/gba/audio.h index 26fe69992..8b212dd6a 100644 --- a/include/mgba/internal/gba/audio.h +++ b/include/mgba/internal/gba/audio.h @@ -80,14 +80,16 @@ struct GBAAudio { bool enable; size_t samples; - unsigned sampleRate; - GBARegisterSOUNDBIAS soundbias; struct GBAAudioMixer* mixer; bool externalMixing; int32_t sampleInterval; + int32_t lastSample; + int sampleIndex; + struct mStereoSample currentSamples[GBA_MAX_SAMPLES]; + bool forceDisableChA; bool forceDisableChB; int masterVolume; @@ -305,6 +307,8 @@ uint32_t GBAAudioReadWaveRAM(struct GBAAudio* audio, int address); uint32_t GBAAudioWriteFIFO(struct GBAAudio* audio, int address, uint32_t value); void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles); +void GBAAudioSample(struct GBAAudio* audio, int32_t timestamp); + struct GBASerializedState; void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* state); void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState* state); diff --git a/include/mgba/internal/gba/serialize.h b/include/mgba/internal/gba/serialize.h index f3ed332dc..07223ea3f 100644 --- a/include/mgba/internal/gba/serialize.h +++ b/include/mgba/internal/gba/serialize.h @@ -71,7 +71,7 @@ mLOG_DECLARE_CATEGORY(GBA_STATE); * | 0x00188 - 0x0018B: Next event * 0x0018C - 0x001AB: Audio FIFO 1 * 0x001AC - 0x001CB: Audio FIFO 2 - * 0x001CC - 0x001DF: Audio miscellaneous state + * 0x001CC - 0x001EF: Audio miscellaneous state * | 0x001CC - 0x001CF: Channel A internal audio samples * | 0x001D0 - 0x001D3: Channel B internal audio samples * | 0x001D4 - 0x001D7: Next sample @@ -104,9 +104,13 @@ mLOG_DECLARE_CATEGORY(GBA_STATE); * | bit 3: Is channel 3's memory readable? * | bit 4: Skip frame * | bits 5 - 7: Reserved - * 0x001E0 - 0x001FF: Video miscellaneous state - * | 0x001E0 - 0x001E3: Next event - * | 0x001E4 - 0x001F7: Reserved + * | 0x001E0 - 0x001E3: Last sample + * | 0x001E4 - 0x001E7: Additional audio flags + * | bits 0 - 3: Current sample index + * | 0x001E8 - 0x001EF: Reserved + * 0x001F0 - 0x001FF: Video miscellaneous state + * | 0x001F0 - 0x001F3: Reserved + * | 0x001F4 - 0x001F7: Next event * | 0x001F8 - 0x001FB: Miscellaneous flags * | 0x001FC - 0x001FF: Frame counter * 0x00200 - 0x00213: Timer 0 @@ -227,7 +231,8 @@ mLOG_DECLARE_CATEGORY(GBA_STATE); * 0x00368 - 0x0036F: Reserved (leave zero) * 0x00370 - 0x0037F: Audio FIFO A samples * 0x00380 - 0x0038F: Audio FIFO B samples - * 0x00390 - 0x003FF: Reserved (leave zero) + * 0x00390 - 0x003CF: Audio rendered samples + * 0x003D0 - 0x003FF: Reserved (leave zero) * 0x00400 - 0x007FF: I/O memory * 0x00800 - 0x00BFF: Palette * 0x00C00 - 0x00FFF: OAM @@ -243,6 +248,9 @@ DECL_BITS(GBASerializedAudioFlags, FIFOSamplesB, 2, 3); // Yay legacy? DECL_BITS(GBASerializedAudioFlags, FIFOInternalSamplesA, 5, 2); DECL_BITS(GBASerializedAudioFlags, FIFOSamplesA, 7, 3); +DECL_BITFIELD(GBASerializedAudioFlags2, uint32_t); +DECL_BITS(GBASerializedAudioFlags2, SampleIndex, 0, 4); + DECL_BITFIELD(GBASerializedVideoFlags, uint32_t); DECL_BITS(GBASerializedVideoFlags, Mode, 0, 2); @@ -303,11 +311,14 @@ struct GBASerializedState { int8_t sampleB; GBASerializedAudioFlags gbaFlags; GBSerializedAudioFlags flags; + int32_t lastSample; + GBASerializedAudioFlags2 gbaFlags2; + int32_t reserved[2]; } audio; struct { + int32_t reserved; int32_t nextEvent; - int32_t reserved[5]; GBASerializedVideoFlags flags; uint32_t frameCounter; } video; @@ -384,14 +395,16 @@ struct GBASerializedState { int32_t biosStall; uint32_t matrixMappings[16]; - uint32_t reservedMatrix[2]; + uint32_t reservedMatrix[2]; - struct { - int8_t chA[16]; - int8_t chB[16]; - } samples; + struct { + int8_t chA[16]; + int8_t chB[16]; + } samples; - uint32_t reserved[28]; + struct mStereoSample currentSamples[16]; + + uint32_t reserved[12]; uint16_t io[SIZE_IO >> 1]; uint16_t pram[SIZE_PALETTE_RAM >> 1]; diff --git a/src/gb/audio.c b/src/gb/audio.c index 47911f794..94a1c40c4 100644 --- a/src/gb/audio.c +++ b/src/gb/audio.c @@ -11,6 +11,9 @@ #include #include #include +#ifdef M_CORE_GBA +#include +#endif #ifdef __3DS__ #define blip_add_delta blip_add_delta_fast @@ -69,7 +72,6 @@ void GBAudioInit(struct GBAudio* audio, size_t samples, uint8_t* nr52, enum GBAu audio->timingFactor = 2; } - audio->frameEvent.context = audio; audio->frameEvent.name = "GB Audio Frame Sequencer"; audio->frameEvent.callback = _updateFrame; audio->frameEvent.priority = 0x10; @@ -85,14 +87,10 @@ void GBAudioDeinit(struct GBAudio* audio) { } void GBAudioReset(struct GBAudio* audio) { - mTimingDeschedule(audio->timing, &audio->frameEvent); mTimingDeschedule(audio->timing, &audio->sampleEvent); if (audio->style != GB_AUDIO_GBA) { mTimingSchedule(audio->timing, &audio->sampleEvent, 0); } - if (audio->style == GB_AUDIO_GBA) { - mTimingSchedule(audio->timing, &audio->frameEvent, 0); - } audio->ch1 = (struct GBAudioSquareChannel) { .sweep = { .time = 8 }, .envelope = { .dead = 2 } }; audio->ch2 = (struct GBAudioSquareChannel) { .envelope = { .dead = 2 } }; audio->ch3 = (struct GBAudioWaveChannel) { .bank = 0 }; @@ -458,11 +456,12 @@ void GBAudioWriteNR52(struct GBAudio* audio, uint8_t value) { } void _updateFrame(struct mTiming* timing, void* user, uint32_t cyclesLate) { - struct GBAudio* audio = user; - GBAudioUpdateFrame(audio); - if (audio->style == GB_AUDIO_GBA) { - mTimingSchedule(timing, &audio->frameEvent, audio->timingFactor * FRAME_CYCLES - cyclesLate); - } +#ifdef M_CORE_GBA + struct GBAAudio* audio = user; + GBAAudioSample(audio, mTimingCurrentTime(timing)); + mTimingSchedule(timing, &audio->psg.frameEvent, audio->psg.timingFactor * FRAME_CYCLES - cyclesLate); + GBAudioUpdateFrame(&audio->psg); +#endif } void GBAudioRun(struct GBAudio* audio, int32_t timestamp, int channels) { diff --git a/src/gba/audio.c b/src/gba/audio.c index 00c20bd46..c4f989f63 100644 --- a/src/gba/audio.c +++ b/src/gba/audio.c @@ -44,6 +44,7 @@ void GBAAudioInit(struct GBAAudio* audio, size_t samples) { GBAudioInit(&audio->psg, 0, nr52, GB_AUDIO_GBA); audio->psg.timing = &audio->p->timing; audio->psg.clockRate = GBA_ARM7TDMI_FREQUENCY; + audio->psg.frameEvent.context = audio; audio->samples = samples; // Guess too large; we hang producing extra samples if we guess too low blip_set_rates(audio->psg.left, GBA_ARM7TDMI_FREQUENCY, 96000); @@ -58,6 +59,8 @@ void GBAAudioInit(struct GBAAudio* audio, size_t samples) { void GBAAudioReset(struct GBAAudio* audio) { GBAudioReset(&audio->psg); + mTimingDeschedule(&audio->p->timing, &audio->psg.frameEvent); + mTimingSchedule(&audio->p->timing, &audio->psg.frameEvent, 0); mTimingDeschedule(&audio->p->timing, &audio->sampleEvent); mTimingSchedule(&audio->p->timing, &audio->sampleEvent, 0); audio->chA.dmaSource = 1; @@ -77,11 +80,12 @@ void GBAAudioReset(struct GBAAudio* audio) { audio->chA.samples[i] = 0; audio->chB.samples[i] = 0; } - audio->sampleRate = 0x8000; audio->soundbias = 0x200; audio->volume = 0; audio->volumeChA = false; audio->volumeChB = false; + audio->lastSample = 0; + audio->sampleIndex = 0; audio->chARight = false; audio->chALeft = false; audio->chATimer = false; @@ -89,7 +93,7 @@ void GBAAudioReset(struct GBAAudio* audio) { audio->chBLeft = false; audio->chBTimer = false; audio->enable = false; - audio->sampleInterval = GBA_ARM7TDMI_FREQUENCY / audio->sampleRate; + audio->sampleInterval = GBA_ARM7TDMI_FREQUENCY / 0x8000; audio->psg.sampleInterval = audio->sampleInterval; blip_clear(audio->psg.left); @@ -141,56 +145,67 @@ void GBAAudioScheduleFifoDma(struct GBAAudio* audio, int number, struct GBADMA* } void GBAAudioWriteSOUND1CNT_LO(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR10(&audio->psg, value); } void GBAAudioWriteSOUND1CNT_HI(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR11(&audio->psg, value); GBAudioWriteNR12(&audio->psg, value >> 8); } void GBAAudioWriteSOUND1CNT_X(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR13(&audio->psg, value); GBAudioWriteNR14(&audio->psg, value >> 8); } void GBAAudioWriteSOUND2CNT_LO(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR21(&audio->psg, value); GBAudioWriteNR22(&audio->psg, value >> 8); } void GBAAudioWriteSOUND2CNT_HI(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR23(&audio->psg, value); GBAudioWriteNR24(&audio->psg, value >> 8); } void GBAAudioWriteSOUND3CNT_LO(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); audio->psg.ch3.size = GBAudioRegisterBankGetSize(value); audio->psg.ch3.bank = GBAudioRegisterBankGetBank(value); GBAudioWriteNR30(&audio->psg, value); } void GBAAudioWriteSOUND3CNT_HI(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR31(&audio->psg, value); audio->psg.ch3.volume = GBAudioRegisterBankVolumeGetVolumeGBA(value >> 8); } void GBAAudioWriteSOUND3CNT_X(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR33(&audio->psg, value); GBAudioWriteNR34(&audio->psg, value >> 8); } void GBAAudioWriteSOUND4CNT_LO(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR41(&audio->psg, value); GBAudioWriteNR42(&audio->psg, value >> 8); } void GBAAudioWriteSOUND4CNT_HI(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR43(&audio->psg, value); GBAudioWriteNR44(&audio->psg, value >> 8); } void GBAAudioWriteSOUNDCNT_LO(struct GBAAudio* audio, uint16_t value) { + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing)); GBAudioWriteNR50(&audio->psg, value); GBAudioWriteNR51(&audio->psg, value >> 8); } @@ -328,17 +343,17 @@ static int _applyBias(struct GBAAudio* audio, int sample) { return ((sample - GBARegisterSOUNDBIASGetBias(audio->soundbias)) * audio->masterVolume * 3) >> 4; } -static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) { - struct GBAAudio* audio = user; - int16_t samplesLeft[GBA_MAX_SAMPLES]; - int16_t samplesRight[GBA_MAX_SAMPLES]; - int32_t timestamp = mTimingCurrentTime(&audio->p->timing) - cyclesLate - SAMPLE_INTERVAL; +void GBAAudioSample(struct GBAAudio* audio, int32_t timestamp) { + timestamp -= audio->lastSample; + timestamp -= audio->sampleIndex * audio->sampleInterval; // TODO: This can break if the interval changes between samples + + int maxSample = 2 << GBARegisterSOUNDBIASGetResolution(audio->soundbias); int sample; - for (sample = 0; sample * audio->sampleInterval < (int32_t) SAMPLE_INTERVAL; ++sample) { + for (sample = audio->sampleIndex; timestamp >= audio->sampleInterval && sample < maxSample; ++sample, timestamp -= audio->sampleInterval) { int16_t sampleLeft = 0; int16_t sampleRight = 0; int psgShift = 4 - audio->volume; - GBAudioRun(&audio->psg, timestamp + (sample + 1) * audio->sampleInterval, 0xF); + GBAudioRun(&audio->psg, sample * audio->sampleInterval + audio->lastSample, 0xF); GBAudioSamplePSG(&audio->psg, &sampleLeft, &sampleRight); sampleLeft >>= psgShift; sampleRight >>= psgShift; @@ -370,21 +385,34 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) { sampleLeft = _applyBias(audio, sampleLeft); sampleRight = _applyBias(audio, sampleRight); - samplesLeft[sample] = sampleLeft; - samplesRight[sample] = sampleRight; + audio->currentSamples[sample].left = sampleLeft; + audio->currentSamples[sample].right = 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)); + audio->sampleIndex = sample; + if (sample == maxSample) { + audio->lastSample += SAMPLE_INTERVAL; + audio->sampleIndex = 0; + } +} + +static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) { + struct GBAAudio* audio = user; + GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing) - cyclesLate); + + int samples = 2 << GBARegisterSOUNDBIASGetResolution(audio->soundbias); + int sampleMask = 1 << GBARegisterSOUNDBIASGetResolution(audio->soundbias); + memset(audio->chA.samples, audio->chA.samples[samples - 1], sizeof(audio->chA.samples)); + memset(audio->chB.samples, audio->chB.samples[samples - 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]; + for (i = 0; i < samples; ++i) { + int16_t sampleLeft = audio->currentSamples[i].left; + int16_t sampleRight = audio->currentSamples[i].right; sampleSumLeft += sampleLeft; sampleSumRight += sampleRight; if ((size_t) blip_samples_avail(audio->psg.left) < audio->samples) { @@ -399,12 +427,14 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) { audio->clock -= CLOCKS_PER_FRAME; } } - } - // TODO: Post all frames - if (audio->p->stream && audio->p->stream->postAudioFrame) { - sampleSumLeft /= sample; - sampleSumRight /= sample; - audio->p->stream->postAudioFrame(audio->p->stream, sampleSumLeft, sampleSumRight); + // TODO: Post all frames + if (audio->p->stream && audio->p->stream->postAudioFrame && (i & (sampleMask - 1)) == sampleMask - 1) { + sampleSumLeft /= sampleMask; + sampleSumRight /= sampleMask; + audio->p->stream->postAudioFrame(audio->p->stream, sampleSumLeft, sampleSumRight); + sampleSumLeft = 0; + sampleSumRight = 0; + } } produced = blip_samples_avail(audio->psg.left); bool wait = produced >= audio->samples; @@ -428,9 +458,15 @@ void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* memcpy(state->samples.chA, audio->chA.samples, sizeof(audio->chA.samples)); memcpy(state->samples.chB, audio->chB.samples, sizeof(audio->chB.samples)); + size_t i; + for (i = 0; i < GBA_MAX_SAMPLES; ++i) { + STORE_16(audio->currentSamples[i].left, 0, &state->currentSamples[i].left); + STORE_16(audio->currentSamples[i].right, 0, &state->currentSamples[i].right); + } + STORE_32(audio->lastSample, 0, &state->audio.lastSample); + int readA = audio->chA.fifoRead; int readB = audio->chB.fifoRead; - size_t i; for (i = 0; i < GBA_AUDIO_FIFO_SIZE; ++i) { STORE_32(audio->chA.fifo[readA], i << 2, state->audio.fifoA); STORE_32(audio->chB.fifo[readB], i << 2, state->audio.fifoB); @@ -464,6 +500,11 @@ void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* flags = GBASerializedAudioFlagsSetFIFOInternalSamplesA(flags, audio->chA.internalRemaining); flags = GBASerializedAudioFlagsSetFIFOInternalSamplesB(flags, audio->chB.internalRemaining); STORE_16(flags, 0, &state->audio.gbaFlags); + + GBASerializedAudioFlags2 flags2 = 0; + flags2 = GBASerializedAudioFlags2SetSampleIndex(flags2, audio->sampleIndex); + STORE_32(flags2, 0, &state->audio.gbaFlags2); + STORE_32(audio->sampleEvent.when - mTimingCurrentTime(&audio->p->timing), 0, &state->audio.nextSample); } @@ -475,9 +516,15 @@ void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState memcpy(audio->chA.samples, state->samples.chA, sizeof(audio->chA.samples)); memcpy(audio->chB.samples, state->samples.chB, sizeof(audio->chB.samples)); + size_t i; + for (i = 0; i < GBA_MAX_SAMPLES; ++i) { + LOAD_16(audio->currentSamples[i].left, 0, &state->currentSamples[i].left); + LOAD_16(audio->currentSamples[i].right, 0, &state->currentSamples[i].right); + } + LOAD_32(audio->lastSample, 0, &state->audio.lastSample); + int readA = 0; int readB = 0; - size_t i; for (i = 0; i < GBA_AUDIO_FIFO_SIZE; ++i) { LOAD_32(audio->chA.fifo[readA], i << 2, state->audio.fifoA); LOAD_32(audio->chB.fifo[readB], i << 2, state->audio.fifoB); @@ -494,8 +541,15 @@ void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState audio->chA.internalRemaining = GBASerializedAudioFlagsGetFIFOInternalSamplesA(flags); audio->chB.internalRemaining = GBASerializedAudioFlagsGetFIFOInternalSamplesB(flags); + GBASerializedAudioFlags2 flags2; + LOAD_32(flags2, 0, &state->audio.gbaFlags2); + audio->sampleIndex = GBASerializedAudioFlags2GetSampleIndex(flags2); + uint32_t when; LOAD_32(when, 0, &state->audio.nextSample); + if (state->versionMagic < 0x01000007) { + audio->lastSample = when - SAMPLE_INTERVAL; + } mTimingSchedule(&audio->p->timing, &audio->sampleEvent, when); } diff --git a/src/gba/serialize.c b/src/gba/serialize.c index 9c5416e72..c64d7c22b 100644 --- a/src/gba/serialize.c +++ b/src/gba/serialize.c @@ -15,7 +15,7 @@ #include MGBA_EXPORT const uint32_t GBASavestateMagic = 0x01000000; -MGBA_EXPORT const uint32_t GBASavestateVersion = 0x00000006; +MGBA_EXPORT const uint32_t GBASavestateVersion = 0x00000007; mLOG_DEFINE_CATEGORY(GBA_STATE, "GBA Savestate", "gba.serialize"); diff --git a/src/gba/video.c b/src/gba/video.c index 659313575..7268f19de 100644 --- a/src/gba/video.c +++ b/src/gba/video.c @@ -377,7 +377,12 @@ void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState break; } uint32_t when; - LOAD_32(when, 0, &state->video.nextEvent); + if (state->versionMagic < 0x01000007) { + // This field was moved in v7 + LOAD_32(when, 0, &state->audio.lastSample); + } else { + LOAD_32(when, 0, &state->video.nextEvent); + } mTimingSchedule(&video->p->timing, &video->event, when); LOAD_16(video->vcount, REG_VCOUNT, state->io);