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: Allow pausing event loop while CPU is blocked
- GB: Add support for sleep and shutdown callbacks - GB: Add support for sleep and shutdown callbacks
- GB: Redo double speed emulation (closes mgba.io/i/1515) - 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 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) - GB I/O: Implement preliminary support for PCM12/PCM34 (closes mgba.io/i/1468)
- GBA: Allow pausing event loop while CPU is blocked - 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 GBAudioWriteNR52(struct GBAudio* audio, uint8_t);
void GBAudioUpdateFrame(struct GBAudio* audio); void GBAudioUpdateFrame(struct GBAudio* audio);
void GBAudioUpdateChannel4(struct GBAudio* audio);
void GBAudioSamplePSG(struct GBAudio* audio, int16_t* left, int16_t* right); 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 _updateChannel2(struct mTiming* timing, void* user, uint32_t cyclesLate);
static void _updateChannel3(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 _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); 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) { 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->ch3Fade.priority = 0x14;
audio->ch4Event.context = audio; audio->ch4Event.context = audio;
audio->ch4Event.name = "GB Audio Channel 4"; 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->ch4Event.priority = 0x15;
audio->sampleEvent.context = audio; audio->sampleEvent.context = audio;
audio->sampleEvent.name = "GB Audio Sample"; 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) { void GBAudioWriteNR41(struct GBAudio* audio, uint8_t value) {
GBAudioUpdateChannel4(audio);
_writeDuty(&audio->ch4.envelope, value); _writeDuty(&audio->ch4.envelope, value);
audio->ch4.length = 64 - audio->ch4.envelope.length; audio->ch4.length = 64 - audio->ch4.envelope.length;
} }
void GBAudioWriteNR42(struct GBAudio* audio, uint8_t value) { void GBAudioWriteNR42(struct GBAudio* audio, uint8_t value) {
GBAudioUpdateChannel4(audio);
if (!_writeEnvelope(&audio->ch4.envelope, value, audio->style)) { if (!_writeEnvelope(&audio->ch4.envelope, value, audio->style)) {
mTimingDeschedule(audio->timing, &audio->ch4Event);
audio->playingCh4 = false; audio->playingCh4 = false;
*audio->nr52 &= ~0x0008; *audio->nr52 &= ~0x0008;
} }
} }
void GBAudioWriteNR43(struct GBAudio* audio, uint8_t value) { void GBAudioWriteNR43(struct GBAudio* audio, uint8_t value) {
// TODO: Reschedule event GBAudioUpdateChannel4(audio);
audio->ch4.ratio = GBAudioRegisterNoiseFeedbackGetRatio(value); audio->ch4.ratio = GBAudioRegisterNoiseFeedbackGetRatio(value);
audio->ch4.frequency = GBAudioRegisterNoiseFeedbackGetFrequency(value); audio->ch4.frequency = GBAudioRegisterNoiseFeedbackGetFrequency(value);
audio->ch4.power = GBAudioRegisterNoiseFeedbackGetPower(value); audio->ch4.power = GBAudioRegisterNoiseFeedbackGetPower(value);
} }
void GBAudioWriteNR44(struct GBAudio* audio, uint8_t value) { void GBAudioWriteNR44(struct GBAudio* audio, uint8_t value) {
GBAudioUpdateChannel4(audio);
bool wasStop = audio->ch4.stop; bool wasStop = audio->ch4.stop;
audio->ch4.stop = GBAudioRegisterNoiseControlGetStop(value); audio->ch4.stop = GBAudioRegisterNoiseControlGetStop(value);
if (!wasStop && audio->ch4.stop && audio->ch4.length && !(audio->frame & 1)) { if (!wasStop && audio->ch4.stop && audio->ch4.length && !(audio->frame & 1)) {
--audio->ch4.length; --audio->ch4.length;
if (audio->ch4.length == 0) { if (audio->ch4.length == 0) {
mTimingDeschedule(audio->timing, &audio->ch4Event);
audio->playingCh4 = false; audio->playingCh4 = false;
} }
} }
@ -391,8 +391,6 @@ void GBAudioWriteNR44(struct GBAudio* audio, uint8_t value) {
} }
if (audio->playingCh4 && audio->ch4.envelope.dead != 2) { if (audio->playingCh4 && audio->ch4.envelope.dead != 2) {
audio->ch4.lastEvent = mTimingCurrentTime(audio->timing); audio->ch4.lastEvent = mTimingCurrentTime(audio->timing);
mTimingDeschedule(audio->timing, &audio->ch4Event);
mTimingSchedule(audio->timing, &audio->ch4Event, 0);
} }
} }
*audio->nr52 &= ~0x0008; *audio->nr52 &= ~0x0008;
@ -550,7 +548,7 @@ void GBAudioUpdateFrame(struct GBAudio* audio) {
if (audio->ch4.length && audio->ch4.stop) { if (audio->ch4.length && audio->ch4.stop) {
--audio->ch4.length; --audio->ch4.length;
if (audio->ch4.length == 0) { if (audio->ch4.length == 0) {
mTimingDeschedule(audio->timing, &audio->ch4Event); GBAudioUpdateChannel4(audio);
audio->playingCh4 = 0; audio->playingCh4 = 0;
*audio->nr52 &= ~0x0008; *audio->nr52 &= ~0x0008;
} }
@ -582,11 +580,9 @@ void GBAudioUpdateFrame(struct GBAudio* audio) {
if (audio->playingCh4 && !audio->ch4.envelope.dead) { if (audio->playingCh4 && !audio->ch4.envelope.dead) {
--audio->ch4.envelope.nextStep; --audio->ch4.envelope.nextStep;
if (audio->ch4.envelope.nextStep == 0) { if (audio->ch4.envelope.nextStep == 0) {
GBAudioUpdateChannel4(audio);
int8_t sample = audio->ch4.sample; int8_t sample = audio->ch4.sample;
_updateEnvelope(&audio->ch4.envelope); _updateEnvelope(&audio->ch4.envelope);
if (audio->ch4.envelope.dead == 2) {
mTimingDeschedule(audio->timing, &audio->ch4Event);
}
audio->ch4.sample = (sample > 0) * audio->ch4.envelope.currentVolume; audio->ch4.sample = (sample > 0) * audio->ch4.envelope.currentVolume;
if (audio->ch4.nSamples) { if (audio->ch4.nSamples) {
audio->ch4.samples -= sample; 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) { void GBAudioSamplePSG(struct GBAudio* audio, int16_t* left, int16_t* right) {
int dcOffset = audio->style == GB_AUDIO_GBA ? 0 : -0x8; int dcOffset = audio->style == GB_AUDIO_GBA ? 0 : -0x8;
int sampleLeft = dcOffset; int sampleLeft = dcOffset;
@ -637,6 +658,7 @@ void GBAudioSamplePSG(struct GBAudio* audio, int16_t* left, int16_t* right) {
sampleRight <<= 3; sampleRight <<= 3;
if (!audio->forceDisableCh[3]) { if (!audio->forceDisableCh[3]) {
GBAudioUpdateChannel4(audio);
int16_t sample = audio->style == GB_AUDIO_GBA ? (audio->ch4.sample << 3) : _coalesceNoiseChannel(&audio->ch4); int16_t sample = audio->style == GB_AUDIO_GBA ? (audio->ch4.sample << 3) : _coalesceNoiseChannel(&audio->ch4);
if (audio->ch4Left) { if (audio->ch4Left) {
sampleLeft += sample; sampleLeft += sample;
@ -770,7 +792,7 @@ static int32_t _updateSquareChannel(struct GBAudioSquareChannel* ch) {
} }
static int16_t _coalesceNoiseChannel(struct GBAudioNoiseChannel* ch) { static int16_t _coalesceNoiseChannel(struct GBAudioNoiseChannel* ch) {
if (!ch->nSamples) { if (ch->nSamples <= 1) {
return ch->sample << 3; return ch->sample << 3;
} }
// TODO keep track of timing // TODO keep track of timing
@ -926,42 +948,6 @@ static void _fadeChannel3(struct mTiming* timing, void* user, uint32_t cyclesLat
audio->ch3.readable = false; 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) { void GBAudioPSGSerialize(const struct GBAudio* audio, struct GBSerializedPSGState* state, uint32_t* flagsOut) {
uint32_t flags = 0; uint32_t flags = 0;
uint32_t sweep = 0; uint32_t sweep = 0;
@ -1007,7 +993,11 @@ void GBAudioPSGSerialize(const struct GBAudio* audio, struct GBSerializedPSGStat
ch4Flags = GBSerializedAudioEnvelopeSetNextStep(ch4Flags, audio->ch4.envelope.nextStep); ch4Flags = GBSerializedAudioEnvelopeSetNextStep(ch4Flags, audio->ch4.envelope.nextStep);
STORE_32LE(ch4Flags, 0, &state->ch4.envelope); STORE_32LE(ch4Flags, 0, &state->ch4.envelope);
STORE_32LE(audio->ch4.lastEvent, 0, &state->ch4.lastEvent); 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); STORE_32LE(flags, 0, flagsOut);
} }
@ -1095,7 +1085,6 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt
cycles *= 8 * audio->timingFactor; cycles *= 8 * audio->timingFactor;
audio->ch4.lastEvent = currentTime + (when & (cycles - 1)) - cycles; 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_TMA, 0);
GBIOWrite(gb, GB_REG_TAC, 0); GBIOWrite(gb, GB_REG_TAC, 0);
GBIOWrite(gb, GB_REG_IF, 1); 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_NR52, 0xF1);
GBIOWrite(gb, GB_REG_NR14, 0x3F); GBIOWrite(gb, GB_REG_NR14, 0x3F);
GBIOWrite(gb, GB_REG_NR10, 0x80); GBIOWrite(gb, GB_REG_NR10, 0x80);
@ -633,6 +637,7 @@ uint8_t GBIORead(struct GB* gb, unsigned address) {
if (gb->model < GB_MODEL_CGB) { if (gb->model < GB_MODEL_CGB) {
mLOG(GB_IO, GAME_ERROR, "Reading from CGB register FF%02X in DMG mode", address); mLOG(GB_IO, GAME_ERROR, "Reading from CGB register FF%02X in DMG mode", address);
} else if (gb->audio.enable) { } else if (gb->audio.enable) {
GBAudioUpdateChannel4(&gb->audio);
return (gb->audio.ch3.sample) | (gb->audio.ch4.sample << 4); return (gb->audio.ch3.sample) | (gb->audio.ch4.sample << 4);
} }
break; break;