GBA Audio: Decrunchify GB audio

This commit is contained in:
Vicki Pfau 2022-06-15 20:34:06 -07:00
parent 33b3d33da2
commit 6159c5a70b
6 changed files with 125 additions and 50 deletions

View File

@ -80,14 +80,16 @@ struct GBAAudio {
bool enable;
size_t samples;
unsigned sampleRate;
GBARegisterSOUNDBIAS soundbias;
struct GBAAudioMixer* mixer;
bool externalMixing;
int32_t sampleInterval;
int32_t lastSample;
int sampleIndex;
struct mStereoSample currentSamples[GBA_MAX_SAMPLES];
bool forceDisableChA;
bool forceDisableChB;
int masterVolume;
@ -305,6 +307,8 @@ uint32_t GBAAudioReadWaveRAM(struct GBAAudio* audio, int address);
uint32_t GBAAudioWriteFIFO(struct GBAAudio* audio, int address, uint32_t value);
void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles);
void GBAAudioSample(struct GBAAudio* audio, int32_t timestamp);
struct GBASerializedState;
void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* state);
void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState* state);

View File

@ -71,7 +71,7 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
* | 0x00188 - 0x0018B: Next event
* 0x0018C - 0x001AB: Audio FIFO 1
* 0x001AC - 0x001CB: Audio FIFO 2
* 0x001CC - 0x001DF: Audio miscellaneous state
* 0x001CC - 0x001EF: Audio miscellaneous state
* | 0x001CC - 0x001CF: Channel A internal audio samples
* | 0x001D0 - 0x001D3: Channel B internal audio samples
* | 0x001D4 - 0x001D7: Next sample
@ -104,9 +104,13 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
* | bit 3: Is channel 3's memory readable?
* | bit 4: Skip frame
* | bits 5 - 7: Reserved
* 0x001E0 - 0x001FF: Video miscellaneous state
* | 0x001E0 - 0x001E3: Next event
* | 0x001E4 - 0x001F7: Reserved
* | 0x001E0 - 0x001E3: Last sample
* | 0x001E4 - 0x001E7: Additional audio flags
* | bits 0 - 3: Current sample index
* | 0x001E8 - 0x001EF: Reserved
* 0x001F0 - 0x001FF: Video miscellaneous state
* | 0x001F0 - 0x001F3: Reserved
* | 0x001F4 - 0x001F7: Next event
* | 0x001F8 - 0x001FB: Miscellaneous flags
* | 0x001FC - 0x001FF: Frame counter
* 0x00200 - 0x00213: Timer 0
@ -227,7 +231,8 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
* 0x00368 - 0x0036F: Reserved (leave zero)
* 0x00370 - 0x0037F: Audio FIFO A samples
* 0x00380 - 0x0038F: Audio FIFO B samples
* 0x00390 - 0x003FF: Reserved (leave zero)
* 0x00390 - 0x003CF: Audio rendered samples
* 0x003D0 - 0x003FF: Reserved (leave zero)
* 0x00400 - 0x007FF: I/O memory
* 0x00800 - 0x00BFF: Palette
* 0x00C00 - 0x00FFF: OAM
@ -243,6 +248,9 @@ DECL_BITS(GBASerializedAudioFlags, FIFOSamplesB, 2, 3); // Yay legacy?
DECL_BITS(GBASerializedAudioFlags, FIFOInternalSamplesA, 5, 2);
DECL_BITS(GBASerializedAudioFlags, FIFOSamplesA, 7, 3);
DECL_BITFIELD(GBASerializedAudioFlags2, uint32_t);
DECL_BITS(GBASerializedAudioFlags2, SampleIndex, 0, 4);
DECL_BITFIELD(GBASerializedVideoFlags, uint32_t);
DECL_BITS(GBASerializedVideoFlags, Mode, 0, 2);
@ -303,11 +311,14 @@ struct GBASerializedState {
int8_t sampleB;
GBASerializedAudioFlags gbaFlags;
GBSerializedAudioFlags flags;
int32_t lastSample;
GBASerializedAudioFlags2 gbaFlags2;
int32_t reserved[2];
} audio;
struct {
int32_t reserved;
int32_t nextEvent;
int32_t reserved[5];
GBASerializedVideoFlags flags;
uint32_t frameCounter;
} video;
@ -384,14 +395,16 @@ struct GBASerializedState {
int32_t biosStall;
uint32_t matrixMappings[16];
uint32_t reservedMatrix[2];
uint32_t reservedMatrix[2];
struct {
int8_t chA[16];
int8_t chB[16];
} samples;
struct {
int8_t chA[16];
int8_t chB[16];
} samples;
uint32_t reserved[28];
struct mStereoSample currentSamples[16];
uint32_t reserved[12];
uint16_t io[SIZE_IO >> 1];
uint16_t pram[SIZE_PALETTE_RAM >> 1];

View File

@ -11,6 +11,9 @@
#include <mgba/internal/gb/gb.h>
#include <mgba/internal/gb/serialize.h>
#include <mgba/internal/gb/io.h>
#ifdef M_CORE_GBA
#include <mgba/internal/gba/audio.h>
#endif
#ifdef __3DS__
#define blip_add_delta blip_add_delta_fast
@ -69,7 +72,6 @@ void GBAudioInit(struct GBAudio* audio, size_t samples, uint8_t* nr52, enum GBAu
audio->timingFactor = 2;
}
audio->frameEvent.context = audio;
audio->frameEvent.name = "GB Audio Frame Sequencer";
audio->frameEvent.callback = _updateFrame;
audio->frameEvent.priority = 0x10;
@ -85,14 +87,10 @@ void GBAudioDeinit(struct GBAudio* audio) {
}
void GBAudioReset(struct GBAudio* audio) {
mTimingDeschedule(audio->timing, &audio->frameEvent);
mTimingDeschedule(audio->timing, &audio->sampleEvent);
if (audio->style != GB_AUDIO_GBA) {
mTimingSchedule(audio->timing, &audio->sampleEvent, 0);
}
if (audio->style == GB_AUDIO_GBA) {
mTimingSchedule(audio->timing, &audio->frameEvent, 0);
}
audio->ch1 = (struct GBAudioSquareChannel) { .sweep = { .time = 8 }, .envelope = { .dead = 2 } };
audio->ch2 = (struct GBAudioSquareChannel) { .envelope = { .dead = 2 } };
audio->ch3 = (struct GBAudioWaveChannel) { .bank = 0 };
@ -458,11 +456,12 @@ void GBAudioWriteNR52(struct GBAudio* audio, uint8_t value) {
}
void _updateFrame(struct mTiming* timing, void* user, uint32_t cyclesLate) {
struct GBAudio* audio = user;
GBAudioUpdateFrame(audio);
if (audio->style == GB_AUDIO_GBA) {
mTimingSchedule(timing, &audio->frameEvent, audio->timingFactor * FRAME_CYCLES - cyclesLate);
}
#ifdef M_CORE_GBA
struct GBAAudio* audio = user;
GBAAudioSample(audio, mTimingCurrentTime(timing));
mTimingSchedule(timing, &audio->psg.frameEvent, audio->psg.timingFactor * FRAME_CYCLES - cyclesLate);
GBAudioUpdateFrame(&audio->psg);
#endif
}
void GBAudioRun(struct GBAudio* audio, int32_t timestamp, int channels) {

View File

@ -44,6 +44,7 @@ void GBAAudioInit(struct GBAAudio* audio, size_t samples) {
GBAudioInit(&audio->psg, 0, nr52, GB_AUDIO_GBA);
audio->psg.timing = &audio->p->timing;
audio->psg.clockRate = GBA_ARM7TDMI_FREQUENCY;
audio->psg.frameEvent.context = audio;
audio->samples = samples;
// Guess too large; we hang producing extra samples if we guess too low
blip_set_rates(audio->psg.left, GBA_ARM7TDMI_FREQUENCY, 96000);
@ -58,6 +59,8 @@ void GBAAudioInit(struct GBAAudio* audio, size_t samples) {
void GBAAudioReset(struct GBAAudio* audio) {
GBAudioReset(&audio->psg);
mTimingDeschedule(&audio->p->timing, &audio->psg.frameEvent);
mTimingSchedule(&audio->p->timing, &audio->psg.frameEvent, 0);
mTimingDeschedule(&audio->p->timing, &audio->sampleEvent);
mTimingSchedule(&audio->p->timing, &audio->sampleEvent, 0);
audio->chA.dmaSource = 1;
@ -77,11 +80,12 @@ void GBAAudioReset(struct GBAAudio* audio) {
audio->chA.samples[i] = 0;
audio->chB.samples[i] = 0;
}
audio->sampleRate = 0x8000;
audio->soundbias = 0x200;
audio->volume = 0;
audio->volumeChA = false;
audio->volumeChB = false;
audio->lastSample = 0;
audio->sampleIndex = 0;
audio->chARight = false;
audio->chALeft = false;
audio->chATimer = false;
@ -89,7 +93,7 @@ void GBAAudioReset(struct GBAAudio* audio) {
audio->chBLeft = false;
audio->chBTimer = false;
audio->enable = false;
audio->sampleInterval = GBA_ARM7TDMI_FREQUENCY / audio->sampleRate;
audio->sampleInterval = GBA_ARM7TDMI_FREQUENCY / 0x8000;
audio->psg.sampleInterval = audio->sampleInterval;
blip_clear(audio->psg.left);
@ -141,56 +145,67 @@ void GBAAudioScheduleFifoDma(struct GBAAudio* audio, int number, struct GBADMA*
}
void GBAAudioWriteSOUND1CNT_LO(struct GBAAudio* audio, uint16_t value) {
GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing));
GBAudioWriteNR10(&audio->psg, value);
}
void GBAAudioWriteSOUND1CNT_HI(struct GBAAudio* audio, uint16_t value) {
GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing));
GBAudioWriteNR11(&audio->psg, value);
GBAudioWriteNR12(&audio->psg, value >> 8);
}
void GBAAudioWriteSOUND1CNT_X(struct GBAAudio* audio, uint16_t value) {
GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing));
GBAudioWriteNR13(&audio->psg, value);
GBAudioWriteNR14(&audio->psg, value >> 8);
}
void GBAAudioWriteSOUND2CNT_LO(struct GBAAudio* audio, uint16_t value) {
GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing));
GBAudioWriteNR21(&audio->psg, value);
GBAudioWriteNR22(&audio->psg, value >> 8);
}
void GBAAudioWriteSOUND2CNT_HI(struct GBAAudio* audio, uint16_t value) {
GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing));
GBAudioWriteNR23(&audio->psg, value);
GBAudioWriteNR24(&audio->psg, value >> 8);
}
void GBAAudioWriteSOUND3CNT_LO(struct GBAAudio* audio, uint16_t value) {
GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing));
audio->psg.ch3.size = GBAudioRegisterBankGetSize(value);
audio->psg.ch3.bank = GBAudioRegisterBankGetBank(value);
GBAudioWriteNR30(&audio->psg, value);
}
void GBAAudioWriteSOUND3CNT_HI(struct GBAAudio* audio, uint16_t value) {
GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing));
GBAudioWriteNR31(&audio->psg, value);
audio->psg.ch3.volume = GBAudioRegisterBankVolumeGetVolumeGBA(value >> 8);
}
void GBAAudioWriteSOUND3CNT_X(struct GBAAudio* audio, uint16_t value) {
GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing));
GBAudioWriteNR33(&audio->psg, value);
GBAudioWriteNR34(&audio->psg, value >> 8);
}
void GBAAudioWriteSOUND4CNT_LO(struct GBAAudio* audio, uint16_t value) {
GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing));
GBAudioWriteNR41(&audio->psg, value);
GBAudioWriteNR42(&audio->psg, value >> 8);
}
void GBAAudioWriteSOUND4CNT_HI(struct GBAAudio* audio, uint16_t value) {
GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing));
GBAudioWriteNR43(&audio->psg, value);
GBAudioWriteNR44(&audio->psg, value >> 8);
}
void GBAAudioWriteSOUNDCNT_LO(struct GBAAudio* audio, uint16_t value) {
GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing));
GBAudioWriteNR50(&audio->psg, value);
GBAudioWriteNR51(&audio->psg, value >> 8);
}
@ -328,17 +343,17 @@ static int _applyBias(struct GBAAudio* audio, int sample) {
return ((sample - GBARegisterSOUNDBIASGetBias(audio->soundbias)) * audio->masterVolume * 3) >> 4;
}
static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
struct GBAAudio* audio = user;
int16_t samplesLeft[GBA_MAX_SAMPLES];
int16_t samplesRight[GBA_MAX_SAMPLES];
int32_t timestamp = mTimingCurrentTime(&audio->p->timing) - cyclesLate - SAMPLE_INTERVAL;
void GBAAudioSample(struct GBAAudio* audio, int32_t timestamp) {
timestamp -= audio->lastSample;
timestamp -= audio->sampleIndex * audio->sampleInterval; // TODO: This can break if the interval changes between samples
int maxSample = 2 << GBARegisterSOUNDBIASGetResolution(audio->soundbias);
int sample;
for (sample = 0; sample * audio->sampleInterval < (int32_t) SAMPLE_INTERVAL; ++sample) {
for (sample = audio->sampleIndex; timestamp >= audio->sampleInterval && sample < maxSample; ++sample, timestamp -= audio->sampleInterval) {
int16_t sampleLeft = 0;
int16_t sampleRight = 0;
int psgShift = 4 - audio->volume;
GBAudioRun(&audio->psg, timestamp + (sample + 1) * audio->sampleInterval, 0xF);
GBAudioRun(&audio->psg, sample * audio->sampleInterval + audio->lastSample, 0xF);
GBAudioSamplePSG(&audio->psg, &sampleLeft, &sampleRight);
sampleLeft >>= psgShift;
sampleRight >>= psgShift;
@ -370,21 +385,34 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
sampleLeft = _applyBias(audio, sampleLeft);
sampleRight = _applyBias(audio, sampleRight);
samplesLeft[sample] = sampleLeft;
samplesRight[sample] = sampleRight;
audio->currentSamples[sample].left = sampleLeft;
audio->currentSamples[sample].right = 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));
audio->sampleIndex = sample;
if (sample == maxSample) {
audio->lastSample += SAMPLE_INTERVAL;
audio->sampleIndex = 0;
}
}
static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
struct GBAAudio* audio = user;
GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing) - cyclesLate);
int samples = 2 << GBARegisterSOUNDBIASGetResolution(audio->soundbias);
int sampleMask = 1 << GBARegisterSOUNDBIASGetResolution(audio->soundbias);
memset(audio->chA.samples, audio->chA.samples[samples - 1], sizeof(audio->chA.samples));
memset(audio->chB.samples, audio->chB.samples[samples - 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];
for (i = 0; i < samples; ++i) {
int16_t sampleLeft = audio->currentSamples[i].left;
int16_t sampleRight = audio->currentSamples[i].right;
sampleSumLeft += sampleLeft;
sampleSumRight += sampleRight;
if ((size_t) blip_samples_avail(audio->psg.left) < audio->samples) {
@ -399,12 +427,14 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
audio->clock -= CLOCKS_PER_FRAME;
}
}
}
// TODO: Post all frames
if (audio->p->stream && audio->p->stream->postAudioFrame) {
sampleSumLeft /= sample;
sampleSumRight /= sample;
audio->p->stream->postAudioFrame(audio->p->stream, sampleSumLeft, sampleSumRight);
// TODO: Post all frames
if (audio->p->stream && audio->p->stream->postAudioFrame && (i & (sampleMask - 1)) == sampleMask - 1) {
sampleSumLeft /= sampleMask;
sampleSumRight /= sampleMask;
audio->p->stream->postAudioFrame(audio->p->stream, sampleSumLeft, sampleSumRight);
sampleSumLeft = 0;
sampleSumRight = 0;
}
}
produced = blip_samples_avail(audio->psg.left);
bool wait = produced >= audio->samples;
@ -428,9 +458,15 @@ void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState*
memcpy(state->samples.chA, audio->chA.samples, sizeof(audio->chA.samples));
memcpy(state->samples.chB, audio->chB.samples, sizeof(audio->chB.samples));
size_t i;
for (i = 0; i < GBA_MAX_SAMPLES; ++i) {
STORE_16(audio->currentSamples[i].left, 0, &state->currentSamples[i].left);
STORE_16(audio->currentSamples[i].right, 0, &state->currentSamples[i].right);
}
STORE_32(audio->lastSample, 0, &state->audio.lastSample);
int readA = audio->chA.fifoRead;
int readB = audio->chB.fifoRead;
size_t i;
for (i = 0; i < GBA_AUDIO_FIFO_SIZE; ++i) {
STORE_32(audio->chA.fifo[readA], i << 2, state->audio.fifoA);
STORE_32(audio->chB.fifo[readB], i << 2, state->audio.fifoB);
@ -464,6 +500,11 @@ void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState*
flags = GBASerializedAudioFlagsSetFIFOInternalSamplesA(flags, audio->chA.internalRemaining);
flags = GBASerializedAudioFlagsSetFIFOInternalSamplesB(flags, audio->chB.internalRemaining);
STORE_16(flags, 0, &state->audio.gbaFlags);
GBASerializedAudioFlags2 flags2 = 0;
flags2 = GBASerializedAudioFlags2SetSampleIndex(flags2, audio->sampleIndex);
STORE_32(flags2, 0, &state->audio.gbaFlags2);
STORE_32(audio->sampleEvent.when - mTimingCurrentTime(&audio->p->timing), 0, &state->audio.nextSample);
}
@ -475,9 +516,15 @@ void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState
memcpy(audio->chA.samples, state->samples.chA, sizeof(audio->chA.samples));
memcpy(audio->chB.samples, state->samples.chB, sizeof(audio->chB.samples));
size_t i;
for (i = 0; i < GBA_MAX_SAMPLES; ++i) {
LOAD_16(audio->currentSamples[i].left, 0, &state->currentSamples[i].left);
LOAD_16(audio->currentSamples[i].right, 0, &state->currentSamples[i].right);
}
LOAD_32(audio->lastSample, 0, &state->audio.lastSample);
int readA = 0;
int readB = 0;
size_t i;
for (i = 0; i < GBA_AUDIO_FIFO_SIZE; ++i) {
LOAD_32(audio->chA.fifo[readA], i << 2, state->audio.fifoA);
LOAD_32(audio->chB.fifo[readB], i << 2, state->audio.fifoB);
@ -494,8 +541,15 @@ void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState
audio->chA.internalRemaining = GBASerializedAudioFlagsGetFIFOInternalSamplesA(flags);
audio->chB.internalRemaining = GBASerializedAudioFlagsGetFIFOInternalSamplesB(flags);
GBASerializedAudioFlags2 flags2;
LOAD_32(flags2, 0, &state->audio.gbaFlags2);
audio->sampleIndex = GBASerializedAudioFlags2GetSampleIndex(flags2);
uint32_t when;
LOAD_32(when, 0, &state->audio.nextSample);
if (state->versionMagic < 0x01000007) {
audio->lastSample = when - SAMPLE_INTERVAL;
}
mTimingSchedule(&audio->p->timing, &audio->sampleEvent, when);
}

View File

@ -15,7 +15,7 @@
#include <fcntl.h>
MGBA_EXPORT const uint32_t GBASavestateMagic = 0x01000000;
MGBA_EXPORT const uint32_t GBASavestateVersion = 0x00000006;
MGBA_EXPORT const uint32_t GBASavestateVersion = 0x00000007;
mLOG_DEFINE_CATEGORY(GBA_STATE, "GBA Savestate", "gba.serialize");

View File

@ -377,7 +377,12 @@ void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState
break;
}
uint32_t when;
LOAD_32(when, 0, &state->video.nextEvent);
if (state->versionMagic < 0x01000007) {
// This field was moved in v7
LOAD_32(when, 0, &state->audio.lastSample);
} else {
LOAD_32(when, 0, &state->video.nextEvent);
}
mTimingSchedule(&video->p->timing, &video->event, when);
LOAD_16(video->vcount, REG_VCOUNT, state->io);