GBA Audio: Redo channel 4 batching for GBA only
Vicki Pfau vi@endrift.com
Wed, 01 Jan 2020 16:59:57 -0800
5 files changed,
35 insertions(+),
10 deletions(-)
M
CHANGES
→
CHANGES
@@ -110,6 +110,7 @@ - Qt: Fix sprite view using wrong base address (fixes mgba.io/i/1603)
- 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
M
include/mgba/internal/gb/audio.h
→
include/mgba/internal/gb/audio.h
@@ -139,6 +139,7 @@
uint32_t lfsr; int nSamples; int samples; + uint32_t lastEvent; int8_t sample; };
M
include/mgba/internal/gb/serialize.h
→
include/mgba/internal/gb/serialize.h
@@ -79,7 +79,7 @@ * | 0x00098 - 0x0009B: Envelepe timing
* | 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 @@ } ch3;
struct { int32_t lfsr; GBSerializedAudioEnvelope envelope; - int32_t reserved; + int32_t lastEvent; uint32_t nextEvent; } ch4; };
M
include/mgba/internal/gba/serialize.h
→
include/mgba/internal/gba/serialize.h
@@ -61,7 +61,7 @@ * | 0x00180 - 0x00183: Envelepe timing
* | 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
M
src/gb/audio.c
→
src/gb/audio.c
@@ -359,6 +359,7 @@ }
} 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 @@ int32_t cycles = ch->ratio ? 2 * ch->ratio : 1;
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 @@ STORE_32LE(audio->ch4.lfsr, 0, &state->ch4.lfsr);
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 @@ audio->ch4.envelope.dead = GBSerializedAudioFlagsGetCh4Dead(flags);
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); } }