GB Audio: Improve channel 4 accuracy
Vicki Pfau vi@endrift.com
Fri, 21 Sep 2018 21:41:57 -0700
2 files changed,
32 insertions(+),
13 deletions(-)
M
include/mgba/internal/gb/audio.h
→
include/mgba/internal/gb/audio.h
@@ -137,6 +137,9 @@ bool stop;
int length; uint32_t lfsr; + int nSamples; + int samples; + int8_t sample; };
M
src/gb/audio.c
→
src/gb/audio.c
@@ -37,6 +37,8 @@
static void _updateSquareSample(struct GBAudioSquareChannel* ch); static int32_t _updateSquareChannel(struct GBAudioSquareChannel* ch); +static int8_t _coalesceNoiseChannel(struct GBAudioNoiseChannel* ch); + static void _updateFrame(struct mTiming* timing, void* user, uint32_t cyclesLate); static void _updateChannel1(struct mTiming* timing, void* user, uint32_t cyclesLate); static void _updateChannel2(struct mTiming* timing, void* user, uint32_t cyclesLate);@@ -118,6 +120,7 @@ }
audio->ch1 = (struct GBAudioSquareChannel) { .envelope = { .dead = 2 } }; audio->ch2 = (struct GBAudioSquareChannel) { .envelope = { .dead = 2 } }; audio->ch3 = (struct GBAudioWaveChannel) { .bank = 0 }; + audio->ch4 = (struct GBAudioNoiseChannel) { .nSamples = 0 }; // TODO: DMG randomness audio->ch3.wavedata8[0] = 0x00; audio->ch3.wavedata8[1] = 0xFF;@@ -564,11 +567,13 @@ if (audio->playingCh4 && !audio->ch4.envelope.dead) {
--audio->ch4.envelope.nextStep; if (audio->ch4.envelope.nextStep == 0) { int8_t sample = audio->ch4.sample > 0; + audio->ch4.samples -= audio->ch4.sample; _updateEnvelope(&audio->ch4.envelope); if (audio->ch4.envelope.dead == 2) { mTimingDeschedule(timing, &audio->ch4Event); } audio->ch4.sample = sample * audio->ch4.envelope.currentVolume; + audio->ch4.samples += audio->ch4.sample; } } break;@@ -611,12 +616,13 @@ }
} if (audio->playingCh4 && !audio->forceDisableCh[3]) { + int8_t sample = _coalesceNoiseChannel(&audio->ch4); if (audio->ch4Left) { - sampleLeft += audio->ch4.sample; + sampleLeft += sample; } if (audio->ch4Right) { - sampleRight += audio->ch4.sample; + sampleRight += sample; } }@@ -745,6 +751,17 @@ return period * 4;
} } +static int8_t _coalesceNoiseChannel(struct GBAudioNoiseChannel* ch) { + if (!ch->nSamples) { + return ch->sample; + } + // TODO keep track of timing + int8_t sample = ch->samples / ch->nSamples; + ch->nSamples = 0; + ch->samples = 0; + return sample; +} + static void _updateEnvelope(struct GBAudioEnvelope* envelope) { if (envelope->direction) { ++envelope->currentVolume;@@ -895,18 +912,17 @@ static void _updateChannel4(struct mTiming* timing, void* user, uint32_t cyclesLate) {
struct GBAudio* audio = user; struct GBAudioNoiseChannel* ch = &audio->ch4; - int32_t baseCycles = ch->ratio ? 2 * ch->ratio : 1; - baseCycles <<= ch->frequency; - baseCycles *= 8 * audio->timingFactor; - int32_t cycles = 0; + 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); - do { - int lsb = ch->lfsr & 1; - ch->sample = lsb * ch->envelope.currentVolume; - ch->lfsr >>= 1; - ch->lfsr ^= (lsb * 0x60) << (ch->power ? 0 : 8); - cycles += baseCycles; - } while (cycles + baseCycles < audio->sampleInterval); mTimingSchedule(timing, &audio->ch4Event, cycles - cyclesLate); }