GB Audio: Add channel 4 batching back (fixes #1313)

This commit is contained in:
Vicki Pfau 2021-03-15 23:10:57 -07:00
parent dd850b8d33
commit c5b78f9354
4 changed files with 46 additions and 50 deletions

View File

@ -113,6 +113,7 @@ Misc:
- GB: Allow pausing event loop while CPU is blocked
- GB: Add support for sleep and shutdown callbacks
- GB: Redo double speed emulation (closes mgba.io/i/1515)
- GB Audio: Add channel 4 batching back (fixes mgba.io/i/1313)
- GB Core: Return the current number of banks for ROM/SRAM, not theoretical max
- GB I/O: Implement preliminary support for PCM12/PCM34 (closes mgba.io/i/1468)
- GBA: Allow pausing event loop while CPU is blocked

View File

@ -240,6 +240,7 @@ void GBAudioWriteNR51(struct GBAudio* audio, uint8_t);
void GBAudioWriteNR52(struct GBAudio* audio, uint8_t);
void GBAudioUpdateFrame(struct GBAudio* audio);
void GBAudioUpdateChannel4(struct GBAudio* audio);
void GBAudioSamplePSG(struct GBAudio* audio, int16_t* left, int16_t* right);

View File

@ -44,7 +44,6 @@ static void _updateChannel1(struct mTiming* timing, void* user, uint32_t cyclesL
static void _updateChannel2(struct mTiming* timing, void* user, uint32_t cyclesLate);
static void _updateChannel3(struct mTiming* timing, void* user, uint32_t cyclesLate);
static void _fadeChannel3(struct mTiming* timing, void* user, uint32_t cyclesLate);
static void _updateChannel4(struct mTiming* timing, void* user, uint32_t cyclesLate);
static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate);
void GBAudioInit(struct GBAudio* audio, size_t samples, uint8_t* nr52, enum GBAudioStyle style) {
@ -90,7 +89,7 @@ void GBAudioInit(struct GBAudio* audio, size_t samples, uint8_t* nr52, enum GBAu
audio->ch3Fade.priority = 0x14;
audio->ch4Event.context = audio;
audio->ch4Event.name = "GB Audio Channel 4";
audio->ch4Event.callback = _updateChannel4;
audio->ch4Event.callback = NULL; // This is pending removal, so calling it will crash
audio->ch4Event.priority = 0x15;
audio->sampleEvent.context = audio;
audio->sampleEvent.name = "GB Audio Sample";
@ -346,32 +345,33 @@ void GBAudioWriteNR34(struct GBAudio* audio, uint8_t value) {
}
void GBAudioWriteNR41(struct GBAudio* audio, uint8_t value) {
GBAudioUpdateChannel4(audio);
_writeDuty(&audio->ch4.envelope, value);
audio->ch4.length = 64 - audio->ch4.envelope.length;
}
void GBAudioWriteNR42(struct GBAudio* audio, uint8_t value) {
GBAudioUpdateChannel4(audio);
if (!_writeEnvelope(&audio->ch4.envelope, value, audio->style)) {
mTimingDeschedule(audio->timing, &audio->ch4Event);
audio->playingCh4 = false;
*audio->nr52 &= ~0x0008;
}
}
void GBAudioWriteNR43(struct GBAudio* audio, uint8_t value) {
// TODO: Reschedule event
GBAudioUpdateChannel4(audio);
audio->ch4.ratio = GBAudioRegisterNoiseFeedbackGetRatio(value);
audio->ch4.frequency = GBAudioRegisterNoiseFeedbackGetFrequency(value);
audio->ch4.power = GBAudioRegisterNoiseFeedbackGetPower(value);
}
void GBAudioWriteNR44(struct GBAudio* audio, uint8_t value) {
GBAudioUpdateChannel4(audio);
bool wasStop = audio->ch4.stop;
audio->ch4.stop = GBAudioRegisterNoiseControlGetStop(value);
if (!wasStop && audio->ch4.stop && audio->ch4.length && !(audio->frame & 1)) {
--audio->ch4.length;
if (audio->ch4.length == 0) {
mTimingDeschedule(audio->timing, &audio->ch4Event);
audio->playingCh4 = false;
}
}
@ -391,8 +391,6 @@ void GBAudioWriteNR44(struct GBAudio* audio, uint8_t value) {
}
if (audio->playingCh4 && audio->ch4.envelope.dead != 2) {
audio->ch4.lastEvent = mTimingCurrentTime(audio->timing);
mTimingDeschedule(audio->timing, &audio->ch4Event);
mTimingSchedule(audio->timing, &audio->ch4Event, 0);
}
}
*audio->nr52 &= ~0x0008;
@ -550,7 +548,7 @@ void GBAudioUpdateFrame(struct GBAudio* audio) {
if (audio->ch4.length && audio->ch4.stop) {
--audio->ch4.length;
if (audio->ch4.length == 0) {
mTimingDeschedule(audio->timing, &audio->ch4Event);
GBAudioUpdateChannel4(audio);
audio->playingCh4 = 0;
*audio->nr52 &= ~0x0008;
}
@ -582,11 +580,9 @@ void GBAudioUpdateFrame(struct GBAudio* audio) {
if (audio->playingCh4 && !audio->ch4.envelope.dead) {
--audio->ch4.envelope.nextStep;
if (audio->ch4.envelope.nextStep == 0) {
GBAudioUpdateChannel4(audio);
int8_t sample = audio->ch4.sample;
_updateEnvelope(&audio->ch4.envelope);
if (audio->ch4.envelope.dead == 2) {
mTimingDeschedule(audio->timing, &audio->ch4Event);
}
audio->ch4.sample = (sample > 0) * audio->ch4.envelope.currentVolume;
if (audio->ch4.nSamples) {
audio->ch4.samples -= sample;
@ -598,6 +594,31 @@ void GBAudioUpdateFrame(struct GBAudio* audio) {
}
}
void GBAudioUpdateChannel4(struct GBAudio* audio) {
struct GBAudioNoiseChannel* ch = &audio->ch4;
if (ch->envelope.dead == 2 || !audio->playingCh4) {
return;
}
int32_t cycles = ch->ratio ? 2 * ch->ratio : 1;
cycles <<= ch->frequency;
cycles *= 8 * audio->timingFactor;
uint32_t last = 0;
uint32_t now = mTimingCurrentTime(audio->timing) - ch->lastEvent;
for (; last + cycles <= 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);
}
ch->lastEvent += last;
}
void GBAudioSamplePSG(struct GBAudio* audio, int16_t* left, int16_t* right) {
int dcOffset = audio->style == GB_AUDIO_GBA ? 0 : -0x8;
int sampleLeft = dcOffset;
@ -637,6 +658,7 @@ void GBAudioSamplePSG(struct GBAudio* audio, int16_t* left, int16_t* right) {
sampleRight <<= 3;
if (!audio->forceDisableCh[3]) {
GBAudioUpdateChannel4(audio);
int16_t sample = audio->style == GB_AUDIO_GBA ? (audio->ch4.sample << 3) : _coalesceNoiseChannel(&audio->ch4);
if (audio->ch4Left) {
sampleLeft += sample;
@ -770,7 +792,7 @@ static int32_t _updateSquareChannel(struct GBAudioSquareChannel* ch) {
}
static int16_t _coalesceNoiseChannel(struct GBAudioNoiseChannel* ch) {
if (!ch->nSamples) {
if (ch->nSamples <= 1) {
return ch->sample << 3;
}
// TODO keep track of timing
@ -926,42 +948,6 @@ static void _fadeChannel3(struct mTiming* timing, void* user, uint32_t cyclesLat
audio->ch3.readable = false;
}
static void _updateChannel4(struct mTiming* timing, void* user, uint32_t cyclesLate) {
struct GBAudio* audio = user;
struct GBAudioNoiseChannel* ch = &audio->ch4;
int32_t cycles = ch->ratio ? 2 * ch->ratio : 1;
cycles <<= ch->frequency;
cycles *= 8 * audio->timingFactor;
uint32_t last = 0;
uint32_t now = cycles;
int32_t next = cycles - cyclesLate;
if (audio->style == GB_AUDIO_GBA) {
last = ch->lastEvent;
now = mTimingCurrentTime(timing) - cyclesLate;
ch->lastEvent = now;
now -= last;
last = 0;
if (audio->sampleInterval > next) {
// 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) {
uint32_t flags = 0;
uint32_t sweep = 0;
@ -1007,7 +993,11 @@ void GBAudioPSGSerialize(const struct GBAudio* audio, struct GBSerializedPSGStat
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);
int32_t cycles = audio->ch4.ratio ? 2 * audio->ch4.ratio : 1;
cycles <<= audio->ch4.frequency;
cycles *= 8 * audio->timingFactor;
STORE_32LE(audio->ch4.lastEvent + cycles, 0, &state->ch4.nextEvent);
STORE_32LE(flags, 0, flagsOut);
}
@ -1095,7 +1085,6 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt
cycles *= 8 * audio->timingFactor;
audio->ch4.lastEvent = currentTime + (when & (cycles - 1)) - cycles;
}
mTimingSchedule(audio->timing, &audio->ch4Event, when);
}
}

View File

@ -166,6 +166,10 @@ void GBIOReset(struct GB* gb) {
GBIOWrite(gb, GB_REG_TMA, 0);
GBIOWrite(gb, GB_REG_TAC, 0);
GBIOWrite(gb, GB_REG_IF, 1);
gb->audio.playingCh1 = false;
gb->audio.playingCh2 = false;
gb->audio.playingCh3 = false;
gb->audio.playingCh4 = false;
GBIOWrite(gb, GB_REG_NR52, 0xF1);
GBIOWrite(gb, GB_REG_NR14, 0x3F);
GBIOWrite(gb, GB_REG_NR10, 0x80);
@ -633,6 +637,7 @@ uint8_t GBIORead(struct GB* gb, unsigned address) {
if (gb->model < GB_MODEL_CGB) {
mLOG(GB_IO, GAME_ERROR, "Reading from CGB register FF%02X in DMG mode", address);
} else if (gb->audio.enable) {
GBAudioUpdateChannel4(&gb->audio);
return (gb->audio.ch3.sample) | (gb->audio.ch4.sample << 4);
}
break;