GBA Audio: Sample FIFOs at SOUNDBIAS-set frequency

This commit is contained in:
Vicki Pfau 2022-06-01 18:40:53 -07:00
parent 29dbb55c80
commit 3c0d9f7197
4 changed files with 47 additions and 15 deletions

View File

@ -29,6 +29,7 @@ Emulation fixes:
- GBA: Fix expected entry point for multiboot ELFs (fixes mgba.io/i/2450)
- GBA: Fix booting multiboot ROMs with no JOY entrypoint
- GBA Audio: Adjust PSG sampling rate with SOUNDBIAS
- GBA Audio: Sample FIFOs at SOUNDBIAS-set frequency
- GBA BIOS: Work around IRQ handling hiccup in Mario & Luigi (fixes mgba.io/i/1059)
- GBA BIOS: Initial HLE timing estimation of UnLz77 functions (fixes mgba.io/i/2141)
- GBA DMA: Fix DMA source direction bits being cleared (fixes mgba.io/i/2410)

View File

@ -34,7 +34,7 @@ struct GBAAudioFIFO {
uint32_t internalSample;
int internalRemaining;
int dmaSource;
int8_t sample;
int8_t samples[8];
};
DECL_BITFIELD(GBARegisterSOUNDCNT_HI, uint16_t);

View File

@ -224,7 +224,10 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
* 0x00320 - 0x00323: Next IRQ event
* 0x00324 - 0x00327: Interruptable BIOS stall cycles
* 0x00328 - 0x00367: Matrix memory mapping table
* 0x00368 - 0x003FF: Reserved (leave zero)
* 0x00368 - 0x0036F: Reserved (leave zero)
* 0x00370 - 0x00377: Audio FIFO A samples
* 0x00378 - 0x0037F: Audio FIFO B samples
* 0x00380 - 0x003FF: Reserved (leave zero)
* 0x00400 - 0x007FF: I/O memory
* 0x00800 - 0x00BFF: Palette
* 0x00C00 - 0x00FFF: OAM
@ -381,8 +384,14 @@ struct GBASerializedState {
int32_t biosStall;
uint32_t matrixMappings[16];
uint32_t reservedMatrix[2];
uint32_t reserved[38];
struct {
int8_t chA[8];
int8_t chB[8];
} samples;
uint32_t reserved[32];
uint16_t io[SIZE_IO >> 1];
uint16_t pram[SIZE_PALETTE_RAM >> 1];

View File

@ -67,13 +67,16 @@ void GBAAudioReset(struct GBAAudio* audio) {
audio->chA.internalSample = 0;
audio->chA.internalRemaining = 0;
memset(audio->chA.fifo, 0, sizeof(audio->chA.fifo));
audio->chA.sample = 0;
audio->chB.fifoWrite = 0;
audio->chB.fifoRead = 0;
audio->chB.internalSample = 0;
audio->chB.internalRemaining = 0;
memset(audio->chB.fifo, 0, sizeof(audio->chB.fifo));
audio->chB.sample = 0;
int i;
for (i = 0; i < 8; ++i) {
audio->chA.samples[i] = 0;
audio->chB.samples[i] = 0;
}
audio->sampleRate = 0x8000;
audio->soundbias = 0x200;
audio->volume = 0;
@ -301,7 +304,17 @@ void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles) {
channel->fifoRead = 0;
}
}
channel->sample = channel->internalSample;
int32_t until = mTimingUntil(&audio->p->timing, &audio->sampleEvent) - 1;
int bits = 1 << GBARegisterSOUNDBIASGetResolution(audio->soundbias);
until += 1 << (9 - GBARegisterSOUNDBIASGetResolution(audio->soundbias));
until >>= 9 - GBARegisterSOUNDBIASGetResolution(audio->soundbias);
int i;
for (i = bits - until; i < bits; ++i) {
if (i < 0 || i >= bits) {
abort();
}
channel->samples[i] = channel->internalSample;
}
if (channel->internalRemaining) {
channel->internalSample >>= 8;
--channel->internalRemaining;
@ -339,21 +352,21 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
if (!audio->externalMixing) {
if (!audio->forceDisableChA) {
if (audio->chALeft) {
sampleLeft += (audio->chA.sample << 2) >> !audio->volumeChA;
sampleLeft += (audio->chA.samples[sample] << 2) >> !audio->volumeChA;
}
if (audio->chARight) {
sampleRight += (audio->chA.sample << 2) >> !audio->volumeChA;
sampleRight += (audio->chA.samples[sample] << 2) >> !audio->volumeChA;
}
}
if (!audio->forceDisableChB) {
if (audio->chBLeft) {
sampleLeft += (audio->chB.sample << 2) >> !audio->volumeChB;
sampleLeft += (audio->chB.samples[sample] << 2) >> !audio->volumeChB;
}
if (audio->chBRight) {
sampleRight += (audio->chB.sample << 2) >> !audio->volumeChB;
sampleRight += (audio->chB.samples[sample] << 2) >> !audio->volumeChB;
}
}
}
@ -364,12 +377,19 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
samplesRight[sample] = sampleRight;
}
memset(audio->chA.samples, audio->chA.samples[sample - 1], sizeof(audio->chA.samples));
memset(audio->chB.samples, audio->chB.samples[sample - 1], sizeof(audio->chB.samples));
mCoreSyncLockAudio(audio->p->sync);
unsigned produced;
int32_t sampleSumLeft = 0;
int32_t sampleSumRight = 0;
int i;
for (i = 0; i < sample; ++i) {
int16_t sampleLeft = samplesLeft[i];
int16_t sampleRight = samplesRight[i];
sampleSumLeft += sampleLeft;
sampleSumRight += sampleRight;
if ((size_t) blip_samples_avail(audio->psg.left) < audio->samples) {
blip_add_delta(audio->psg.left, audio->clock, sampleLeft - audio->lastLeft);
blip_add_delta(audio->psg.right, audio->clock, sampleRight - audio->lastRight);
@ -385,7 +405,9 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
}
// TODO: Post all frames
if (audio->p->stream && audio->p->stream->postAudioFrame) {
audio->p->stream->postAudioFrame(audio->p->stream, samplesLeft[sample - 1], samplesRight[sample - 1]);
sampleSumLeft /= sample;
sampleSumRight /= sample;
audio->p->stream->postAudioFrame(audio->p->stream, sampleSumLeft, sampleSumRight);
}
produced = blip_samples_avail(audio->psg.left);
bool wait = produced >= audio->samples;
@ -406,8 +428,8 @@ void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState*
STORE_32(audio->chA.internalSample, 0, &state->audio.internalA);
STORE_32(audio->chB.internalSample, 0, &state->audio.internalB);
state->audio.sampleA = audio->chA.sample;
state->audio.sampleB = audio->chB.sample;
memcpy(state->samples.chA, audio->chA.samples, sizeof(audio->chA.samples));
memcpy(state->samples.chB, audio->chB.samples, sizeof(audio->chB.samples));
int readA = audio->chA.fifoRead;
int readB = audio->chB.fifoRead;
@ -453,8 +475,8 @@ void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState
LOAD_32(audio->chA.internalSample, 0, &state->audio.internalA);
LOAD_32(audio->chB.internalSample, 0, &state->audio.internalB);
audio->chA.sample = state->audio.sampleA;
audio->chB.sample = state->audio.sampleB;
memcpy(audio->chA.samples, state->samples.chA, sizeof(audio->chA.samples));
memcpy(audio->chB.samples, state->samples.chB, sizeof(audio->chB.samples));
int readA = 0;
int readB = 0;