diff --git a/CHANGES b/CHANGES index 93251cacd..aafff65ed 100644 --- a/CHANGES +++ b/CHANGES @@ -110,6 +110,7 @@ Other fixes: - Qt: Fix inability to clear default keybindings Misc: - GB Memory: Support manual SRAM editing (fixes mgba.io/i/1580) + - GBA Audio: Redo channel 4 batching for GBA only - GBA I/O: Stop logging several harmless invalid register reads - Debugger: Separate aliases from main commands - Debugger: Print break-/watchpoint ID when breaking in CLI diff --git a/include/mgba/internal/gb/audio.h b/include/mgba/internal/gb/audio.h index ed3a76325..e01e60a3f 100644 --- a/include/mgba/internal/gb/audio.h +++ b/include/mgba/internal/gb/audio.h @@ -139,6 +139,7 @@ struct GBAudioNoiseChannel { uint32_t lfsr; int nSamples; int samples; + uint32_t lastEvent; int8_t sample; }; diff --git a/include/mgba/internal/gb/serialize.h b/include/mgba/internal/gb/serialize.h index a7f321358..ef2d5c4bc 100644 --- a/include/mgba/internal/gb/serialize.h +++ b/include/mgba/internal/gb/serialize.h @@ -79,7 +79,7 @@ mLOG_DECLARE_CATEGORY(GB_STATE); * | bits 0 - 2: Remaining length * | bits 3 - 5: Next step * | bits 6 - 31: Reserved - * | 0x00098 - 0x0009F: Reserved + * | 0x0009C - 0x0009F: Last event * | 0x000A0 - 0x000A3: Next event * 0x000A4 - 0x000B7: Audio miscellaneous state * | TODO: Fix this, they're in big-endian order, but field is little-endian @@ -224,7 +224,7 @@ struct GBSerializedPSGState { struct { int32_t lfsr; GBSerializedAudioEnvelope envelope; - int32_t reserved; + int32_t lastEvent; uint32_t nextEvent; } ch4; }; diff --git a/include/mgba/internal/gba/serialize.h b/include/mgba/internal/gba/serialize.h index b95a07a1f..3d2e0e69c 100644 --- a/include/mgba/internal/gba/serialize.h +++ b/include/mgba/internal/gba/serialize.h @@ -61,7 +61,7 @@ mLOG_DECLARE_CATEGORY(GBA_STATE); * | bits 0 - 2: Remaining length * | bits 3 - 5: Next step * | bits 6 - 31: Reserved - * | 0x00184 - 0x00187: Reserved + * | 0x00184 - 0x00187: Last event * | 0x00188 - 0x0018B: Next event * 0x0018C - 0x001AB: Audio FIFO 1 * 0x001AC - 0x001CB: Audio FIFO 2 diff --git a/src/gb/audio.c b/src/gb/audio.c index 06414fcf6..c4c199663 100644 --- a/src/gb/audio.c +++ b/src/gb/audio.c @@ -359,6 +359,7 @@ void GBAudioWriteNR42(struct GBAudio* audio, uint8_t value) { } void GBAudioWriteNR43(struct GBAudio* audio, uint8_t value) { + // TODO: Reschedule event audio->ch4.ratio = GBAudioRegisterNoiseFeedbackGetRatio(value); audio->ch4.frequency = GBAudioRegisterNoiseFeedbackGetFrequency(value); audio->ch4.power = GBAudioRegisterNoiseFeedbackGetPower(value); @@ -933,14 +934,30 @@ static void _updateChannel4(struct mTiming* timing, void* user, uint32_t cyclesL cycles <<= ch->frequency; cycles *= 8 * audio->timingFactor; - int lsb = ch->lfsr & 1; - ch->sample = lsb * ch->envelope.currentVolume; - ++ch->nSamples; - ch->samples += ch->sample; - ch->lfsr >>= 1; - ch->lfsr ^= (lsb * 0x60) << (ch->power ? 0 : 8); + uint32_t last = 0; + uint32_t now = cycles; + uint32_t next = cycles - cyclesLate; - mTimingSchedule(timing, &audio->ch4Event, cycles - cyclesLate); + if (audio->style == GB_AUDIO_GBA) { + last = ch->lastEvent; + now = mTimingCurrentTime(timing) - cyclesLate; + ch->lastEvent = now; + now -= last; + last = 0; + // TODO: Make batching work when descheduled + next = audio->sampleInterval; + } + + for (; last < now; last += cycles) { + int lsb = ch->lfsr & 1; + ch->sample = lsb * ch->envelope.currentVolume; + ++ch->nSamples; + ch->samples += ch->sample; + ch->lfsr >>= 1; + ch->lfsr ^= (lsb * 0x60) << (ch->power ? 0 : 8); + } + + mTimingSchedule(timing, &audio->ch4Event, next); } void GBAudioPSGSerialize(const struct GBAudio* audio, struct GBSerializedPSGState* state, uint32_t* flagsOut) { @@ -984,6 +1001,7 @@ void GBAudioPSGSerialize(const struct GBAudio* audio, struct GBSerializedPSGStat ch4Flags = GBSerializedAudioEnvelopeSetLength(ch4Flags, audio->ch4.length); ch4Flags = GBSerializedAudioEnvelopeSetNextStep(ch4Flags, audio->ch4.envelope.nextStep); STORE_32LE(ch4Flags, 0, &state->ch4.envelope); + STORE_32LE(audio->ch4.lastEvent, 0, &state->ch4.lastEvent); STORE_32LE(audio->ch4Event.when - mTimingCurrentTime(audio->timing), 0, &state->ch4.nextEvent); STORE_32LE(flags, 0, flagsOut); @@ -1055,8 +1073,13 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt audio->ch4.length = GBSerializedAudioEnvelopeGetLength(ch4Flags); audio->ch4.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch4Flags); LOAD_32LE(audio->ch4.lfsr, 0, &state->ch4.lfsr); + LOAD_32LE(audio->ch4.lastEvent, 0, &state->ch4.lastEvent); LOAD_32LE(when, 0, &state->ch4.nextEvent); if (audio->ch4.envelope.dead < 2 && audio->playingCh4) { + if (when - audio->ch4.lastEvent > (uint32_t) audio->sampleInterval) { + // Back-compat: fake this value + audio->ch4.lastEvent = when - audio->sampleInterval; + } mTimingSchedule(audio->timing, &audio->ch4Event, when); } }