mirror of https://github.com/mgba-emu/mgba.git
GBA Audio: Decrunchify GB audio
This commit is contained in:
parent
33b3d33da2
commit
6159c5a70b
|
@ -80,14 +80,16 @@ struct GBAAudio {
|
||||||
bool enable;
|
bool enable;
|
||||||
|
|
||||||
size_t samples;
|
size_t samples;
|
||||||
unsigned sampleRate;
|
|
||||||
|
|
||||||
GBARegisterSOUNDBIAS soundbias;
|
GBARegisterSOUNDBIAS soundbias;
|
||||||
|
|
||||||
struct GBAAudioMixer* mixer;
|
struct GBAAudioMixer* mixer;
|
||||||
bool externalMixing;
|
bool externalMixing;
|
||||||
int32_t sampleInterval;
|
int32_t sampleInterval;
|
||||||
|
|
||||||
|
int32_t lastSample;
|
||||||
|
int sampleIndex;
|
||||||
|
struct mStereoSample currentSamples[GBA_MAX_SAMPLES];
|
||||||
|
|
||||||
bool forceDisableChA;
|
bool forceDisableChA;
|
||||||
bool forceDisableChB;
|
bool forceDisableChB;
|
||||||
int masterVolume;
|
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);
|
uint32_t GBAAudioWriteFIFO(struct GBAAudio* audio, int address, uint32_t value);
|
||||||
void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles);
|
void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles);
|
||||||
|
|
||||||
|
void GBAAudioSample(struct GBAAudio* audio, int32_t timestamp);
|
||||||
|
|
||||||
struct GBASerializedState;
|
struct GBASerializedState;
|
||||||
void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* state);
|
void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* state);
|
||||||
void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState* state);
|
void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState* state);
|
||||||
|
|
|
@ -71,7 +71,7 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
|
||||||
* | 0x00188 - 0x0018B: Next event
|
* | 0x00188 - 0x0018B: Next event
|
||||||
* 0x0018C - 0x001AB: Audio FIFO 1
|
* 0x0018C - 0x001AB: Audio FIFO 1
|
||||||
* 0x001AC - 0x001CB: Audio FIFO 2
|
* 0x001AC - 0x001CB: Audio FIFO 2
|
||||||
* 0x001CC - 0x001DF: Audio miscellaneous state
|
* 0x001CC - 0x001EF: Audio miscellaneous state
|
||||||
* | 0x001CC - 0x001CF: Channel A internal audio samples
|
* | 0x001CC - 0x001CF: Channel A internal audio samples
|
||||||
* | 0x001D0 - 0x001D3: Channel B internal audio samples
|
* | 0x001D0 - 0x001D3: Channel B internal audio samples
|
||||||
* | 0x001D4 - 0x001D7: Next sample
|
* | 0x001D4 - 0x001D7: Next sample
|
||||||
|
@ -104,9 +104,13 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
|
||||||
* | bit 3: Is channel 3's memory readable?
|
* | bit 3: Is channel 3's memory readable?
|
||||||
* | bit 4: Skip frame
|
* | bit 4: Skip frame
|
||||||
* | bits 5 - 7: Reserved
|
* | bits 5 - 7: Reserved
|
||||||
* 0x001E0 - 0x001FF: Video miscellaneous state
|
* | 0x001E0 - 0x001E3: Last sample
|
||||||
* | 0x001E0 - 0x001E3: Next event
|
* | 0x001E4 - 0x001E7: Additional audio flags
|
||||||
* | 0x001E4 - 0x001F7: Reserved
|
* | 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
|
* | 0x001F8 - 0x001FB: Miscellaneous flags
|
||||||
* | 0x001FC - 0x001FF: Frame counter
|
* | 0x001FC - 0x001FF: Frame counter
|
||||||
* 0x00200 - 0x00213: Timer 0
|
* 0x00200 - 0x00213: Timer 0
|
||||||
|
@ -227,7 +231,8 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
|
||||||
* 0x00368 - 0x0036F: Reserved (leave zero)
|
* 0x00368 - 0x0036F: Reserved (leave zero)
|
||||||
* 0x00370 - 0x0037F: Audio FIFO A samples
|
* 0x00370 - 0x0037F: Audio FIFO A samples
|
||||||
* 0x00380 - 0x0038F: Audio FIFO B 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
|
* 0x00400 - 0x007FF: I/O memory
|
||||||
* 0x00800 - 0x00BFF: Palette
|
* 0x00800 - 0x00BFF: Palette
|
||||||
* 0x00C00 - 0x00FFF: OAM
|
* 0x00C00 - 0x00FFF: OAM
|
||||||
|
@ -243,6 +248,9 @@ DECL_BITS(GBASerializedAudioFlags, FIFOSamplesB, 2, 3); // Yay legacy?
|
||||||
DECL_BITS(GBASerializedAudioFlags, FIFOInternalSamplesA, 5, 2);
|
DECL_BITS(GBASerializedAudioFlags, FIFOInternalSamplesA, 5, 2);
|
||||||
DECL_BITS(GBASerializedAudioFlags, FIFOSamplesA, 7, 3);
|
DECL_BITS(GBASerializedAudioFlags, FIFOSamplesA, 7, 3);
|
||||||
|
|
||||||
|
DECL_BITFIELD(GBASerializedAudioFlags2, uint32_t);
|
||||||
|
DECL_BITS(GBASerializedAudioFlags2, SampleIndex, 0, 4);
|
||||||
|
|
||||||
DECL_BITFIELD(GBASerializedVideoFlags, uint32_t);
|
DECL_BITFIELD(GBASerializedVideoFlags, uint32_t);
|
||||||
DECL_BITS(GBASerializedVideoFlags, Mode, 0, 2);
|
DECL_BITS(GBASerializedVideoFlags, Mode, 0, 2);
|
||||||
|
|
||||||
|
@ -303,11 +311,14 @@ struct GBASerializedState {
|
||||||
int8_t sampleB;
|
int8_t sampleB;
|
||||||
GBASerializedAudioFlags gbaFlags;
|
GBASerializedAudioFlags gbaFlags;
|
||||||
GBSerializedAudioFlags flags;
|
GBSerializedAudioFlags flags;
|
||||||
|
int32_t lastSample;
|
||||||
|
GBASerializedAudioFlags2 gbaFlags2;
|
||||||
|
int32_t reserved[2];
|
||||||
} audio;
|
} audio;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
int32_t reserved;
|
||||||
int32_t nextEvent;
|
int32_t nextEvent;
|
||||||
int32_t reserved[5];
|
|
||||||
GBASerializedVideoFlags flags;
|
GBASerializedVideoFlags flags;
|
||||||
uint32_t frameCounter;
|
uint32_t frameCounter;
|
||||||
} video;
|
} video;
|
||||||
|
@ -384,14 +395,16 @@ struct GBASerializedState {
|
||||||
int32_t biosStall;
|
int32_t biosStall;
|
||||||
|
|
||||||
uint32_t matrixMappings[16];
|
uint32_t matrixMappings[16];
|
||||||
uint32_t reservedMatrix[2];
|
uint32_t reservedMatrix[2];
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
int8_t chA[16];
|
int8_t chA[16];
|
||||||
int8_t chB[16];
|
int8_t chB[16];
|
||||||
} samples;
|
} samples;
|
||||||
|
|
||||||
uint32_t reserved[28];
|
struct mStereoSample currentSamples[16];
|
||||||
|
|
||||||
|
uint32_t reserved[12];
|
||||||
|
|
||||||
uint16_t io[SIZE_IO >> 1];
|
uint16_t io[SIZE_IO >> 1];
|
||||||
uint16_t pram[SIZE_PALETTE_RAM >> 1];
|
uint16_t pram[SIZE_PALETTE_RAM >> 1];
|
||||||
|
|
|
@ -11,6 +11,9 @@
|
||||||
#include <mgba/internal/gb/gb.h>
|
#include <mgba/internal/gb/gb.h>
|
||||||
#include <mgba/internal/gb/serialize.h>
|
#include <mgba/internal/gb/serialize.h>
|
||||||
#include <mgba/internal/gb/io.h>
|
#include <mgba/internal/gb/io.h>
|
||||||
|
#ifdef M_CORE_GBA
|
||||||
|
#include <mgba/internal/gba/audio.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __3DS__
|
#ifdef __3DS__
|
||||||
#define blip_add_delta blip_add_delta_fast
|
#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->timingFactor = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
audio->frameEvent.context = audio;
|
|
||||||
audio->frameEvent.name = "GB Audio Frame Sequencer";
|
audio->frameEvent.name = "GB Audio Frame Sequencer";
|
||||||
audio->frameEvent.callback = _updateFrame;
|
audio->frameEvent.callback = _updateFrame;
|
||||||
audio->frameEvent.priority = 0x10;
|
audio->frameEvent.priority = 0x10;
|
||||||
|
@ -85,14 +87,10 @@ void GBAudioDeinit(struct GBAudio* audio) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAudioReset(struct GBAudio* audio) {
|
void GBAudioReset(struct GBAudio* audio) {
|
||||||
mTimingDeschedule(audio->timing, &audio->frameEvent);
|
|
||||||
mTimingDeschedule(audio->timing, &audio->sampleEvent);
|
mTimingDeschedule(audio->timing, &audio->sampleEvent);
|
||||||
if (audio->style != GB_AUDIO_GBA) {
|
if (audio->style != GB_AUDIO_GBA) {
|
||||||
mTimingSchedule(audio->timing, &audio->sampleEvent, 0);
|
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->ch1 = (struct GBAudioSquareChannel) { .sweep = { .time = 8 }, .envelope = { .dead = 2 } };
|
||||||
audio->ch2 = (struct GBAudioSquareChannel) { .envelope = { .dead = 2 } };
|
audio->ch2 = (struct GBAudioSquareChannel) { .envelope = { .dead = 2 } };
|
||||||
audio->ch3 = (struct GBAudioWaveChannel) { .bank = 0 };
|
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) {
|
void _updateFrame(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
||||||
struct GBAudio* audio = user;
|
#ifdef M_CORE_GBA
|
||||||
GBAudioUpdateFrame(audio);
|
struct GBAAudio* audio = user;
|
||||||
if (audio->style == GB_AUDIO_GBA) {
|
GBAAudioSample(audio, mTimingCurrentTime(timing));
|
||||||
mTimingSchedule(timing, &audio->frameEvent, audio->timingFactor * FRAME_CYCLES - cyclesLate);
|
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) {
|
void GBAudioRun(struct GBAudio* audio, int32_t timestamp, int channels) {
|
||||||
|
|
102
src/gba/audio.c
102
src/gba/audio.c
|
@ -44,6 +44,7 @@ void GBAAudioInit(struct GBAAudio* audio, size_t samples) {
|
||||||
GBAudioInit(&audio->psg, 0, nr52, GB_AUDIO_GBA);
|
GBAudioInit(&audio->psg, 0, nr52, GB_AUDIO_GBA);
|
||||||
audio->psg.timing = &audio->p->timing;
|
audio->psg.timing = &audio->p->timing;
|
||||||
audio->psg.clockRate = GBA_ARM7TDMI_FREQUENCY;
|
audio->psg.clockRate = GBA_ARM7TDMI_FREQUENCY;
|
||||||
|
audio->psg.frameEvent.context = audio;
|
||||||
audio->samples = samples;
|
audio->samples = samples;
|
||||||
// Guess too large; we hang producing extra samples if we guess too low
|
// Guess too large; we hang producing extra samples if we guess too low
|
||||||
blip_set_rates(audio->psg.left, GBA_ARM7TDMI_FREQUENCY, 96000);
|
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) {
|
void GBAAudioReset(struct GBAAudio* audio) {
|
||||||
GBAudioReset(&audio->psg);
|
GBAudioReset(&audio->psg);
|
||||||
|
mTimingDeschedule(&audio->p->timing, &audio->psg.frameEvent);
|
||||||
|
mTimingSchedule(&audio->p->timing, &audio->psg.frameEvent, 0);
|
||||||
mTimingDeschedule(&audio->p->timing, &audio->sampleEvent);
|
mTimingDeschedule(&audio->p->timing, &audio->sampleEvent);
|
||||||
mTimingSchedule(&audio->p->timing, &audio->sampleEvent, 0);
|
mTimingSchedule(&audio->p->timing, &audio->sampleEvent, 0);
|
||||||
audio->chA.dmaSource = 1;
|
audio->chA.dmaSource = 1;
|
||||||
|
@ -77,11 +80,12 @@ void GBAAudioReset(struct GBAAudio* audio) {
|
||||||
audio->chA.samples[i] = 0;
|
audio->chA.samples[i] = 0;
|
||||||
audio->chB.samples[i] = 0;
|
audio->chB.samples[i] = 0;
|
||||||
}
|
}
|
||||||
audio->sampleRate = 0x8000;
|
|
||||||
audio->soundbias = 0x200;
|
audio->soundbias = 0x200;
|
||||||
audio->volume = 0;
|
audio->volume = 0;
|
||||||
audio->volumeChA = false;
|
audio->volumeChA = false;
|
||||||
audio->volumeChB = false;
|
audio->volumeChB = false;
|
||||||
|
audio->lastSample = 0;
|
||||||
|
audio->sampleIndex = 0;
|
||||||
audio->chARight = false;
|
audio->chARight = false;
|
||||||
audio->chALeft = false;
|
audio->chALeft = false;
|
||||||
audio->chATimer = false;
|
audio->chATimer = false;
|
||||||
|
@ -89,7 +93,7 @@ void GBAAudioReset(struct GBAAudio* audio) {
|
||||||
audio->chBLeft = false;
|
audio->chBLeft = false;
|
||||||
audio->chBTimer = false;
|
audio->chBTimer = false;
|
||||||
audio->enable = false;
|
audio->enable = false;
|
||||||
audio->sampleInterval = GBA_ARM7TDMI_FREQUENCY / audio->sampleRate;
|
audio->sampleInterval = GBA_ARM7TDMI_FREQUENCY / 0x8000;
|
||||||
audio->psg.sampleInterval = audio->sampleInterval;
|
audio->psg.sampleInterval = audio->sampleInterval;
|
||||||
|
|
||||||
blip_clear(audio->psg.left);
|
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) {
|
void GBAAudioWriteSOUND1CNT_LO(struct GBAAudio* audio, uint16_t value) {
|
||||||
|
GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing));
|
||||||
GBAudioWriteNR10(&audio->psg, value);
|
GBAudioWriteNR10(&audio->psg, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAAudioWriteSOUND1CNT_HI(struct GBAAudio* audio, uint16_t value) {
|
void GBAAudioWriteSOUND1CNT_HI(struct GBAAudio* audio, uint16_t value) {
|
||||||
|
GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing));
|
||||||
GBAudioWriteNR11(&audio->psg, value);
|
GBAudioWriteNR11(&audio->psg, value);
|
||||||
GBAudioWriteNR12(&audio->psg, value >> 8);
|
GBAudioWriteNR12(&audio->psg, value >> 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAAudioWriteSOUND1CNT_X(struct GBAAudio* audio, uint16_t value) {
|
void GBAAudioWriteSOUND1CNT_X(struct GBAAudio* audio, uint16_t value) {
|
||||||
|
GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing));
|
||||||
GBAudioWriteNR13(&audio->psg, value);
|
GBAudioWriteNR13(&audio->psg, value);
|
||||||
GBAudioWriteNR14(&audio->psg, value >> 8);
|
GBAudioWriteNR14(&audio->psg, value >> 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAAudioWriteSOUND2CNT_LO(struct GBAAudio* audio, uint16_t value) {
|
void GBAAudioWriteSOUND2CNT_LO(struct GBAAudio* audio, uint16_t value) {
|
||||||
|
GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing));
|
||||||
GBAudioWriteNR21(&audio->psg, value);
|
GBAudioWriteNR21(&audio->psg, value);
|
||||||
GBAudioWriteNR22(&audio->psg, value >> 8);
|
GBAudioWriteNR22(&audio->psg, value >> 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAAudioWriteSOUND2CNT_HI(struct GBAAudio* audio, uint16_t value) {
|
void GBAAudioWriteSOUND2CNT_HI(struct GBAAudio* audio, uint16_t value) {
|
||||||
|
GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing));
|
||||||
GBAudioWriteNR23(&audio->psg, value);
|
GBAudioWriteNR23(&audio->psg, value);
|
||||||
GBAudioWriteNR24(&audio->psg, value >> 8);
|
GBAudioWriteNR24(&audio->psg, value >> 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAAudioWriteSOUND3CNT_LO(struct GBAAudio* audio, uint16_t value) {
|
void GBAAudioWriteSOUND3CNT_LO(struct GBAAudio* audio, uint16_t value) {
|
||||||
|
GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing));
|
||||||
audio->psg.ch3.size = GBAudioRegisterBankGetSize(value);
|
audio->psg.ch3.size = GBAudioRegisterBankGetSize(value);
|
||||||
audio->psg.ch3.bank = GBAudioRegisterBankGetBank(value);
|
audio->psg.ch3.bank = GBAudioRegisterBankGetBank(value);
|
||||||
GBAudioWriteNR30(&audio->psg, value);
|
GBAudioWriteNR30(&audio->psg, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAAudioWriteSOUND3CNT_HI(struct GBAAudio* audio, uint16_t value) {
|
void GBAAudioWriteSOUND3CNT_HI(struct GBAAudio* audio, uint16_t value) {
|
||||||
|
GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing));
|
||||||
GBAudioWriteNR31(&audio->psg, value);
|
GBAudioWriteNR31(&audio->psg, value);
|
||||||
audio->psg.ch3.volume = GBAudioRegisterBankVolumeGetVolumeGBA(value >> 8);
|
audio->psg.ch3.volume = GBAudioRegisterBankVolumeGetVolumeGBA(value >> 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAAudioWriteSOUND3CNT_X(struct GBAAudio* audio, uint16_t value) {
|
void GBAAudioWriteSOUND3CNT_X(struct GBAAudio* audio, uint16_t value) {
|
||||||
|
GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing));
|
||||||
GBAudioWriteNR33(&audio->psg, value);
|
GBAudioWriteNR33(&audio->psg, value);
|
||||||
GBAudioWriteNR34(&audio->psg, value >> 8);
|
GBAudioWriteNR34(&audio->psg, value >> 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAAudioWriteSOUND4CNT_LO(struct GBAAudio* audio, uint16_t value) {
|
void GBAAudioWriteSOUND4CNT_LO(struct GBAAudio* audio, uint16_t value) {
|
||||||
|
GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing));
|
||||||
GBAudioWriteNR41(&audio->psg, value);
|
GBAudioWriteNR41(&audio->psg, value);
|
||||||
GBAudioWriteNR42(&audio->psg, value >> 8);
|
GBAudioWriteNR42(&audio->psg, value >> 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAAudioWriteSOUND4CNT_HI(struct GBAAudio* audio, uint16_t value) {
|
void GBAAudioWriteSOUND4CNT_HI(struct GBAAudio* audio, uint16_t value) {
|
||||||
|
GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing));
|
||||||
GBAudioWriteNR43(&audio->psg, value);
|
GBAudioWriteNR43(&audio->psg, value);
|
||||||
GBAudioWriteNR44(&audio->psg, value >> 8);
|
GBAudioWriteNR44(&audio->psg, value >> 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAAudioWriteSOUNDCNT_LO(struct GBAAudio* audio, uint16_t value) {
|
void GBAAudioWriteSOUNDCNT_LO(struct GBAAudio* audio, uint16_t value) {
|
||||||
|
GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing));
|
||||||
GBAudioWriteNR50(&audio->psg, value);
|
GBAudioWriteNR50(&audio->psg, value);
|
||||||
GBAudioWriteNR51(&audio->psg, value >> 8);
|
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;
|
return ((sample - GBARegisterSOUNDBIASGetBias(audio->soundbias)) * audio->masterVolume * 3) >> 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
void GBAAudioSample(struct GBAAudio* audio, int32_t timestamp) {
|
||||||
struct GBAAudio* audio = user;
|
timestamp -= audio->lastSample;
|
||||||
int16_t samplesLeft[GBA_MAX_SAMPLES];
|
timestamp -= audio->sampleIndex * audio->sampleInterval; // TODO: This can break if the interval changes between samples
|
||||||
int16_t samplesRight[GBA_MAX_SAMPLES];
|
|
||||||
int32_t timestamp = mTimingCurrentTime(&audio->p->timing) - cyclesLate - SAMPLE_INTERVAL;
|
int maxSample = 2 << GBARegisterSOUNDBIASGetResolution(audio->soundbias);
|
||||||
int sample;
|
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 sampleLeft = 0;
|
||||||
int16_t sampleRight = 0;
|
int16_t sampleRight = 0;
|
||||||
int psgShift = 4 - audio->volume;
|
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);
|
GBAudioSamplePSG(&audio->psg, &sampleLeft, &sampleRight);
|
||||||
sampleLeft >>= psgShift;
|
sampleLeft >>= psgShift;
|
||||||
sampleRight >>= psgShift;
|
sampleRight >>= psgShift;
|
||||||
|
@ -370,21 +385,34 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
||||||
|
|
||||||
sampleLeft = _applyBias(audio, sampleLeft);
|
sampleLeft = _applyBias(audio, sampleLeft);
|
||||||
sampleRight = _applyBias(audio, sampleRight);
|
sampleRight = _applyBias(audio, sampleRight);
|
||||||
samplesLeft[sample] = sampleLeft;
|
audio->currentSamples[sample].left = sampleLeft;
|
||||||
samplesRight[sample] = sampleRight;
|
audio->currentSamples[sample].right = sampleRight;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(audio->chA.samples, audio->chA.samples[sample - 1], sizeof(audio->chA.samples));
|
audio->sampleIndex = sample;
|
||||||
memset(audio->chB.samples, audio->chB.samples[sample - 1], sizeof(audio->chB.samples));
|
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);
|
mCoreSyncLockAudio(audio->p->sync);
|
||||||
unsigned produced;
|
unsigned produced;
|
||||||
int32_t sampleSumLeft = 0;
|
int32_t sampleSumLeft = 0;
|
||||||
int32_t sampleSumRight = 0;
|
int32_t sampleSumRight = 0;
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < sample; ++i) {
|
for (i = 0; i < samples; ++i) {
|
||||||
int16_t sampleLeft = samplesLeft[i];
|
int16_t sampleLeft = audio->currentSamples[i].left;
|
||||||
int16_t sampleRight = samplesRight[i];
|
int16_t sampleRight = audio->currentSamples[i].right;
|
||||||
sampleSumLeft += sampleLeft;
|
sampleSumLeft += sampleLeft;
|
||||||
sampleSumRight += sampleRight;
|
sampleSumRight += sampleRight;
|
||||||
if ((size_t) blip_samples_avail(audio->psg.left) < audio->samples) {
|
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;
|
audio->clock -= CLOCKS_PER_FRAME;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
// TODO: Post all frames
|
||||||
// TODO: Post all frames
|
if (audio->p->stream && audio->p->stream->postAudioFrame && (i & (sampleMask - 1)) == sampleMask - 1) {
|
||||||
if (audio->p->stream && audio->p->stream->postAudioFrame) {
|
sampleSumLeft /= sampleMask;
|
||||||
sampleSumLeft /= sample;
|
sampleSumRight /= sampleMask;
|
||||||
sampleSumRight /= sample;
|
audio->p->stream->postAudioFrame(audio->p->stream, sampleSumLeft, sampleSumRight);
|
||||||
audio->p->stream->postAudioFrame(audio->p->stream, sampleSumLeft, sampleSumRight);
|
sampleSumLeft = 0;
|
||||||
|
sampleSumRight = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
produced = blip_samples_avail(audio->psg.left);
|
produced = blip_samples_avail(audio->psg.left);
|
||||||
bool wait = produced >= audio->samples;
|
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.chA, audio->chA.samples, sizeof(audio->chA.samples));
|
||||||
memcpy(state->samples.chB, audio->chB.samples, sizeof(audio->chB.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 readA = audio->chA.fifoRead;
|
||||||
int readB = audio->chB.fifoRead;
|
int readB = audio->chB.fifoRead;
|
||||||
size_t i;
|
|
||||||
for (i = 0; i < GBA_AUDIO_FIFO_SIZE; ++i) {
|
for (i = 0; i < GBA_AUDIO_FIFO_SIZE; ++i) {
|
||||||
STORE_32(audio->chA.fifo[readA], i << 2, state->audio.fifoA);
|
STORE_32(audio->chA.fifo[readA], i << 2, state->audio.fifoA);
|
||||||
STORE_32(audio->chB.fifo[readB], i << 2, state->audio.fifoB);
|
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 = GBASerializedAudioFlagsSetFIFOInternalSamplesA(flags, audio->chA.internalRemaining);
|
||||||
flags = GBASerializedAudioFlagsSetFIFOInternalSamplesB(flags, audio->chB.internalRemaining);
|
flags = GBASerializedAudioFlagsSetFIFOInternalSamplesB(flags, audio->chB.internalRemaining);
|
||||||
STORE_16(flags, 0, &state->audio.gbaFlags);
|
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);
|
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->chA.samples, state->samples.chA, sizeof(audio->chA.samples));
|
||||||
memcpy(audio->chB.samples, state->samples.chB, sizeof(audio->chB.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 readA = 0;
|
||||||
int readB = 0;
|
int readB = 0;
|
||||||
size_t i;
|
|
||||||
for (i = 0; i < GBA_AUDIO_FIFO_SIZE; ++i) {
|
for (i = 0; i < GBA_AUDIO_FIFO_SIZE; ++i) {
|
||||||
LOAD_32(audio->chA.fifo[readA], i << 2, state->audio.fifoA);
|
LOAD_32(audio->chA.fifo[readA], i << 2, state->audio.fifoA);
|
||||||
LOAD_32(audio->chB.fifo[readB], i << 2, state->audio.fifoB);
|
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->chA.internalRemaining = GBASerializedAudioFlagsGetFIFOInternalSamplesA(flags);
|
||||||
audio->chB.internalRemaining = GBASerializedAudioFlagsGetFIFOInternalSamplesB(flags);
|
audio->chB.internalRemaining = GBASerializedAudioFlagsGetFIFOInternalSamplesB(flags);
|
||||||
|
|
||||||
|
GBASerializedAudioFlags2 flags2;
|
||||||
|
LOAD_32(flags2, 0, &state->audio.gbaFlags2);
|
||||||
|
audio->sampleIndex = GBASerializedAudioFlags2GetSampleIndex(flags2);
|
||||||
|
|
||||||
uint32_t when;
|
uint32_t when;
|
||||||
LOAD_32(when, 0, &state->audio.nextSample);
|
LOAD_32(when, 0, &state->audio.nextSample);
|
||||||
|
if (state->versionMagic < 0x01000007) {
|
||||||
|
audio->lastSample = when - SAMPLE_INTERVAL;
|
||||||
|
}
|
||||||
mTimingSchedule(&audio->p->timing, &audio->sampleEvent, when);
|
mTimingSchedule(&audio->p->timing, &audio->sampleEvent, when);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
MGBA_EXPORT const uint32_t GBASavestateMagic = 0x01000000;
|
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");
|
mLOG_DEFINE_CATEGORY(GBA_STATE, "GBA Savestate", "gba.serialize");
|
||||||
|
|
||||||
|
|
|
@ -377,7 +377,12 @@ void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
uint32_t when;
|
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);
|
mTimingSchedule(&video->p->timing, &video->event, when);
|
||||||
|
|
||||||
LOAD_16(video->vcount, REG_VCOUNT, state->io);
|
LOAD_16(video->vcount, REG_VCOUNT, state->io);
|
||||||
|
|
Loading…
Reference in New Issue