GB Audio: Redo channels 1 and 2

This commit is contained in:
Vicki Pfau 2022-05-31 20:40:14 -07:00
parent 5e6d063aa1
commit 68f628a211
7 changed files with 76 additions and 114 deletions

View File

@ -87,7 +87,6 @@ struct GBAudioSquareControl {
int frequency;
int length;
bool stop;
int hi;
};
struct GBAudioSweep {
@ -104,6 +103,8 @@ struct GBAudioSquareChannel {
struct GBAudioSweep sweep;
struct GBAudioEnvelope envelope;
struct GBAudioSquareControl control;
int32_t lastUpdate;
uint8_t index;
int8_t sample;
};
@ -194,8 +195,6 @@ struct GBAudio {
enum GBAudioStyle style;
struct mTimingEvent frameEvent;
struct mTimingEvent ch1Event;
struct mTimingEvent ch2Event;
struct mTimingEvent ch3Event;
struct mTimingEvent ch3Fade;
struct mTimingEvent ch4Event;
@ -239,6 +238,7 @@ void GBAudioWriteNR50(struct GBAudio* audio, uint8_t);
void GBAudioWriteNR51(struct GBAudio* audio, uint8_t);
void GBAudioWriteNR52(struct GBAudio* audio, uint8_t);
void GBAudioRun(struct GBAudio* audio, int32_t timestamp);
void GBAudioUpdateFrame(struct GBAudio* audio);
void GBAudioUpdateChannel4(struct GBAudio* audio);

View File

@ -19,7 +19,7 @@ extern MGBA_EXPORT const uint32_t GBSavestateVersion;
mLOG_DECLARE_CATEGORY(GB_STATE);
/* Savestate format:
* 0x00000 - 0x00003: Version Magic (0x01000002)
* 0x00000 - 0x00003: Version Magic (0x00400003)
* 0x00004 - 0x00007: ROM CRC32
* 0x00008: Game Boy model
* 0x00009 - 0x0000B: Reserved (leave zero)
@ -56,20 +56,23 @@ mLOG_DECLARE_CATEGORY(GB_STATE);
* | bits 0 - 6: Remaining length
* | bits 7 - 9: Next step
* | bits 10 - 20: Shadow frequency register
* | bits 21 - 31: Reserved
* | bits 21 - 23: Duty index
* | bits 24 - 31: Reserved
* | 0x0004C - 0x0004F: Next frame
* | 0x00050 - 0x00053: Next channel 3 fade
* | 0x00054 - 0x00057: Sweep state
* | bits 0 - 2: Timesteps
* | bits 3 - 31: Reserved
* | 0x00058 - 0x0005B: Next event
* | 0x00058 - 0x0005B: Last update
* 0x0005C - 0x0006B: Audio channel 2 state
* | 0x0005C - 0x0005F: Envelepe timing
* | bits 0 - 2: Remaining length
* | bits 3 - 5: Next step
* | bits 6 - 31: Reserved
* | bits 6 - 20: Reserved
* | bits 21 - 23: Duty index
* | bits 24 - 31: Reserved
* | 0x00060 - 0x00067: Reserved
* | 0x00068 - 0x0006B: Next event
* | 0x00068 - 0x0006B: Last update
* 0x0006C - 0x00093: Audio channel 3 state
* | 0x0006C - 0x0008B: Wave banks
* | 0x0008C - 0x0008D: Remaining length
@ -208,7 +211,7 @@ DECL_BITFIELD(GBSerializedAudioEnvelope, uint32_t);
DECL_BITS(GBSerializedAudioEnvelope, Length, 0, 7);
DECL_BITS(GBSerializedAudioEnvelope, NextStep, 7, 3);
DECL_BITS(GBSerializedAudioEnvelope, Frequency, 10, 11);
DECL_BITS(GBSerializedAudioEnvelope, DutyIndex, 21, 3);
DECL_BITFIELD(GBSerializedAudioSweep, uint32_t);
DECL_BITS(GBSerializedAudioSweep, Time, 0, 3);
@ -219,12 +222,12 @@ struct GBSerializedPSGState {
int32_t nextFrame;
int32_t nextCh3Fade;
GBSerializedAudioSweep sweep;
uint32_t nextEvent;
uint32_t lastUpdate;
} ch1;
struct {
GBSerializedAudioEnvelope envelope;
int32_t reserved[2];
int32_t nextEvent;
uint32_t lastUpdate;
} ch2;
struct {
uint32_t wavebanks[8];

View File

@ -20,7 +20,7 @@ extern MGBA_EXPORT const uint32_t GBASavestateVersion;
mLOG_DECLARE_CATEGORY(GBA_STATE);
/* Savestate format:
* 0x00000 - 0x00003: Version Magic (0x01000004)
* 0x00000 - 0x00003: Version Magic (0x01000005)
* 0x00004 - 0x00007: BIOS checksum (e.g. 0xBAAE187F for official BIOS)
* 0x00008 - 0x0000B: ROM CRC32
* 0x0000C - 0x0000F: Master cycles
@ -39,25 +39,28 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
* | bits 0 - 6: Remaining length
* | bits 7 - 9: Next step
* | bits 10 - 20: Shadow frequency register
* | bits 21 - 31: Reserved
* | bits 21 - 23: Duty index
* | bits 24 - 31: Reserved
* | 0x00134 - 0x00137: Next frame
* | 0x00138 - 0x0013B: Next channel 3 fade
* | 0x0013C - 0x0013F: Sweep state
* | bits 0 - 2: Timesteps
* | bits 3 - 7: Reserved
* | 0x00140 - 0x00143: Next event
* | 0x00140 - 0x00143: Last update
* 0x00144 - 0x00153: Audio channel 2 state
* | 0x00144 - 0x00147: Envelepe timing
* | bits 0 - 2: Remaining length
* | bits 3 - 5: Next step
* | bits 6 - 31: Reserved
* | bits 6 - 20: Reserved
* | bits 21 - 23: Duty index
* | bits 24 - 31: Reserved
* | 0x00148 - 0x0014F: Reserved
* | 0x00150 - 0x00153: Next event
* | 0x00150 - 0x00153: Last update
* 0x00154 - 0x0017B: Audio channel 3 state
* | 0x00154 - 0x00173: Wave banks
* | 0x00174 - 0x00175: Remaining length
* | 0x00176 - 0x00177: Reserved
* | 0x00178 - 0x0017B: Next event
* | 0x00178 - 0x0017B: Last update
* 0x0017C - 0x0018B: Audio channel 4 state
* | 0x0017C - 0x0017F: Linear feedback shift register state
* | 0x00180 - 0x00183: Envelepe timing

View File

@ -35,19 +35,21 @@ static void _updateEnvelopeDead(struct GBAudioEnvelope* envelope);
static bool _updateSweep(struct GBAudioSquareChannel* sweep, bool initial);
static void _updateSquareSample(struct GBAudioSquareChannel* ch);
static int32_t _updateSquareChannel(struct GBAudioSquareChannel* ch);
static int32_t _cyclesToInvert(struct GBAudioSquareChannel* ch);
static int16_t _coalesceNoiseChannel(struct GBAudioNoiseChannel* ch);
static void _updateFrame(struct mTiming* timing, void* user, uint32_t cyclesLate);
static void _updateChannel1(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 _fadeChannel3(struct mTiming* timing, void* user, uint32_t cyclesLate);
static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate);
static const int _squareChannelDuty[4][8] = {
{ 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 1, 1, 1 },
{ 0, 1, 1, 1, 1, 1, 1, 0 },
};
void GBAudioInit(struct GBAudio* audio, size_t samples, uint8_t* nr52, enum GBAudioStyle style) {
audio->samples = samples;
audio->left = blip_new(BLIP_BUFFER_SIZE);
@ -73,14 +75,6 @@ void GBAudioInit(struct GBAudio* audio, size_t samples, uint8_t* nr52, enum GBAu
audio->frameEvent.name = "GB Audio Frame Sequencer";
audio->frameEvent.callback = _updateFrame;
audio->frameEvent.priority = 0x10;
audio->ch1Event.context = audio;
audio->ch1Event.name = "GB Audio Channel 1";
audio->ch1Event.callback = _updateChannel1;
audio->ch1Event.priority = 0x11;
audio->ch2Event.context = audio;
audio->ch2Event.name = "GB Audio Channel 2";
audio->ch2Event.callback = _updateChannel2;
audio->ch2Event.priority = 0x12;
audio->ch3Event.context = audio;
audio->ch3Event.name = "GB Audio Channel 3";
audio->ch3Event.callback = _updateChannel3;
@ -106,8 +100,6 @@ void GBAudioDeinit(struct GBAudio* audio) {
void GBAudioReset(struct GBAudio* audio) {
mTimingDeschedule(audio->timing, &audio->frameEvent);
mTimingDeschedule(audio->timing, &audio->ch1Event);
mTimingDeschedule(audio->timing, &audio->ch2Event);
mTimingDeschedule(audio->timing, &audio->ch3Event);
mTimingDeschedule(audio->timing, &audio->ch3Fade);
mTimingDeschedule(audio->timing, &audio->ch4Event);
@ -168,32 +160,35 @@ void GBAudioResizeBuffer(struct GBAudio* audio, size_t samples) {
}
void GBAudioWriteNR10(struct GBAudio* audio, uint8_t value) {
GBAudioRun(audio, mTimingCurrentTime(audio->timing));
if (!_writeSweep(&audio->ch1.sweep, value)) {
mTimingDeschedule(audio->timing, &audio->ch1Event);
audio->playingCh1 = false;
*audio->nr52 &= ~0x0001;
}
}
void GBAudioWriteNR11(struct GBAudio* audio, uint8_t value) {
GBAudioRun(audio, mTimingCurrentTime(audio->timing));
_writeDuty(&audio->ch1.envelope, value);
audio->ch1.control.length = 64 - audio->ch1.envelope.length;
}
void GBAudioWriteNR12(struct GBAudio* audio, uint8_t value) {
GBAudioRun(audio, mTimingCurrentTime(audio->timing));
if (!_writeEnvelope(&audio->ch1.envelope, value, audio->style)) {
mTimingDeschedule(audio->timing, &audio->ch1Event);
audio->playingCh1 = false;
*audio->nr52 &= ~0x0001;
}
}
void GBAudioWriteNR13(struct GBAudio* audio, uint8_t value) {
GBAudioRun(audio, mTimingCurrentTime(audio->timing));
audio->ch1.control.frequency &= 0x700;
audio->ch1.control.frequency |= GBAudioRegisterControlGetFrequency(value);
}
void GBAudioWriteNR14(struct GBAudio* audio, uint8_t value) {
GBAudioRun(audio, mTimingCurrentTime(audio->timing));
audio->ch1.control.frequency &= 0xFF;
audio->ch1.control.frequency |= GBAudioRegisterControlGetFrequency(value << 8);
bool wasStop = audio->ch1.control.stop;
@ -201,12 +196,10 @@ void GBAudioWriteNR14(struct GBAudio* audio, uint8_t value) {
if (!wasStop && audio->ch1.control.stop && audio->ch1.control.length && !(audio->frame & 1)) {
--audio->ch1.control.length;
if (audio->ch1.control.length == 0) {
mTimingDeschedule(audio->timing, &audio->ch1Event);
audio->playingCh1 = false;
}
}
if (GBAudioRegisterControlIsRestart(value << 8)) {
bool wasDead = !audio->playingCh1;
audio->playingCh1 = _resetEnvelope(&audio->ch1.envelope);
audio->ch1.sweep.realFrequency = audio->ch1.control.frequency;
_resetSweep(&audio->ch1.sweep);
@ -220,35 +213,33 @@ void GBAudioWriteNR14(struct GBAudio* audio, uint8_t value) {
}
}
_updateSquareSample(&audio->ch1);
if (wasDead && audio->playingCh1) {
mTimingSchedule(audio->timing, &audio->ch1Event, _cyclesToInvert(&audio->ch1));
} else if (!audio->playingCh1) {
mTimingDeschedule(audio->timing, &audio->ch1Event);
}
}
*audio->nr52 &= ~0x0001;
*audio->nr52 |= audio->playingCh1;
}
void GBAudioWriteNR21(struct GBAudio* audio, uint8_t value) {
GBAudioRun(audio, mTimingCurrentTime(audio->timing));
_writeDuty(&audio->ch2.envelope, value);
audio->ch2.control.length = 64 - audio->ch2.envelope.length;
}
void GBAudioWriteNR22(struct GBAudio* audio, uint8_t value) {
GBAudioRun(audio, mTimingCurrentTime(audio->timing));
if (!_writeEnvelope(&audio->ch2.envelope, value, audio->style)) {
mTimingDeschedule(audio->timing, &audio->ch2Event);
audio->playingCh2 = false;
*audio->nr52 &= ~0x0002;
}
}
void GBAudioWriteNR23(struct GBAudio* audio, uint8_t value) {
GBAudioRun(audio, mTimingCurrentTime(audio->timing));
audio->ch2.control.frequency &= 0x700;
audio->ch2.control.frequency |= GBAudioRegisterControlGetFrequency(value);
}
void GBAudioWriteNR24(struct GBAudio* audio, uint8_t value) {
GBAudioRun(audio, mTimingCurrentTime(audio->timing));
audio->ch2.control.frequency &= 0xFF;
audio->ch2.control.frequency |= GBAudioRegisterControlGetFrequency(value << 8);
bool wasStop = audio->ch2.control.stop;
@ -256,12 +247,10 @@ void GBAudioWriteNR24(struct GBAudio* audio, uint8_t value) {
if (!wasStop && audio->ch2.control.stop && audio->ch2.control.length && !(audio->frame & 1)) {
--audio->ch2.control.length;
if (audio->ch2.control.length == 0) {
mTimingDeschedule(audio->timing, &audio->ch2Event);
audio->playingCh2 = false;
}
}
if (GBAudioRegisterControlIsRestart(value << 8)) {
bool wasDead = !audio->playingCh2;
audio->playingCh2 = _resetEnvelope(&audio->ch2.envelope);
if (!audio->ch2.control.length) {
@ -271,11 +260,6 @@ void GBAudioWriteNR24(struct GBAudio* audio, uint8_t value) {
}
}
_updateSquareSample(&audio->ch2);
if (wasDead && audio->playingCh2) {
mTimingSchedule(audio->timing, &audio->ch2Event, _cyclesToInvert(&audio->ch2));
} else if (!audio->playingCh2) {
mTimingDeschedule(audio->timing, &audio->ch2Event);
}
}
*audio->nr52 &= ~0x0002;
*audio->nr52 |= audio->playingCh2 << 1;
@ -496,6 +480,26 @@ void _updateFrame(struct mTiming* timing, void* user, uint32_t cyclesLate) {
}
}
void GBAudioRun(struct GBAudio* audio, int32_t timestamp) {
if (!audio->enable) {
return;
}
if (audio->playingCh1) {
int period = 4 * (2048 - audio->ch1.control.frequency) * audio->timingFactor;
int32_t diff = (timestamp - audio->ch1.lastUpdate) / period;
audio->ch1.index = (audio->ch1.index + diff) & 7;
audio->ch1.lastUpdate += diff * period;
_updateSquareSample(&audio->ch1);
}
if (audio->playingCh2) {
int period = 4 * (2048 - audio->ch2.control.frequency) * audio->timingFactor;
int32_t diff = (timestamp - audio->ch2.lastUpdate) / period;
audio->ch2.index = (audio->ch2.index + diff) & 7;
audio->ch2.lastUpdate += diff * period;
_updateSquareSample(&audio->ch2);
}
}
void GBAudioUpdateFrame(struct GBAudio* audio) {
if (!audio->enable) {
return;
@ -504,6 +508,8 @@ void GBAudioUpdateFrame(struct GBAudio* audio) {
audio->skipFrame = false;
return;
}
GBAudioRun(audio, mTimingCurrentTime(audio->timing));
int frame = (audio->frame + 1) & 7;
audio->frame = frame;
@ -516,9 +522,6 @@ void GBAudioUpdateFrame(struct GBAudio* audio) {
audio->playingCh1 = _updateSweep(&audio->ch1, false);
*audio->nr52 &= ~0x0001;
*audio->nr52 |= audio->playingCh1;
if (!audio->playingCh1) {
mTimingDeschedule(audio->timing, &audio->ch1Event);
}
}
}
// Fall through
@ -527,7 +530,6 @@ void GBAudioUpdateFrame(struct GBAudio* audio) {
if (audio->ch1.control.length && audio->ch1.control.stop) {
--audio->ch1.control.length;
if (audio->ch1.control.length == 0) {
mTimingDeschedule(audio->timing, &audio->ch1Event);
audio->playingCh1 = 0;
*audio->nr52 &= ~0x0001;
}
@ -536,7 +538,6 @@ void GBAudioUpdateFrame(struct GBAudio* audio) {
if (audio->ch2.control.length && audio->ch2.control.stop) {
--audio->ch2.control.length;
if (audio->ch2.control.length == 0) {
mTimingDeschedule(audio->timing, &audio->ch2Event);
audio->playingCh2 = 0;
*audio->nr52 &= ~0x0002;
}
@ -565,9 +566,6 @@ void GBAudioUpdateFrame(struct GBAudio* audio) {
--audio->ch1.envelope.nextStep;
if (audio->ch1.envelope.nextStep == 0) {
_updateEnvelope(&audio->ch1.envelope);
if (audio->ch1.envelope.dead == 2) {
mTimingDeschedule(audio->timing, &audio->ch1Event);
}
_updateSquareSample(&audio->ch1);
}
}
@ -576,9 +574,6 @@ void GBAudioUpdateFrame(struct GBAudio* audio) {
--audio->ch2.envelope.nextStep;
if (audio->ch2.envelope.nextStep == 0) {
_updateEnvelope(&audio->ch2.envelope);
if (audio->ch2.envelope.dead == 2) {
mTimingDeschedule(audio->timing, &audio->ch2Event);
}
_updateSquareSample(&audio->ch2);
}
}
@ -626,6 +621,7 @@ void GBAudioUpdateChannel4(struct GBAudio* audio) {
}
void GBAudioSamplePSG(struct GBAudio* audio, int16_t* left, int16_t* right) {
GBAudioRun(audio, mTimingCurrentTime(audio->timing));
int dcOffset = audio->style == GB_AUDIO_GBA ? 0 : -0x8;
int sampleLeft = dcOffset;
int sampleRight = dcOffset;
@ -775,30 +771,7 @@ bool _writeEnvelope(struct GBAudioEnvelope* envelope, uint8_t value, enum GBAudi
}
static void _updateSquareSample(struct GBAudioSquareChannel* ch) {
ch->sample = ch->control.hi * ch->envelope.currentVolume;
}
static int32_t _updateSquareChannel(struct GBAudioSquareChannel* ch) {
ch->control.hi = !ch->control.hi;
_updateSquareSample(ch);
return _cyclesToInvert(ch);
}
static int32_t _cyclesToInvert(struct GBAudioSquareChannel* ch) {
int period = 4 * (2048 - ch->control.frequency);
switch (ch->envelope.duty) {
case 0:
return ch->control.hi ? period : period * 7;
case 1:
return ch->control.hi ? period * 2 : period * 6;
case 2:
return period * 4;
case 3:
return ch->control.hi ? period * 6 : period * 2;
default:
// This should never be hit
return period * 4;
}
ch->sample = _squareChannelDuty[ch->envelope.duty][ch->index] * ch->envelope.currentVolume;
}
static int16_t _coalesceNoiseChannel(struct GBAudioNoiseChannel* ch) {
@ -870,20 +843,6 @@ static bool _updateSweep(struct GBAudioSquareChannel* ch, bool initial) {
return true;
}
static void _updateChannel1(struct mTiming* timing, void* user, uint32_t cyclesLate) {
struct GBAudio* audio = user;
struct GBAudioSquareChannel* ch = &audio->ch1;
int cycles = _updateSquareChannel(ch);
mTimingSchedule(timing, &audio->ch1Event, audio->timingFactor * cycles - cyclesLate);
}
static void _updateChannel2(struct mTiming* timing, void* user, uint32_t cyclesLate) {
struct GBAudio* audio = user;
struct GBAudioSquareChannel* ch = &audio->ch2;
int cycles = _updateSquareChannel(ch);
mTimingSchedule(timing, &audio->ch2Event, audio->timingFactor * cycles - cyclesLate);
}
static void _updateChannel3(struct mTiming* timing, void* user, uint32_t cyclesLate) {
struct GBAudio* audio = user;
struct GBAudioWaveChannel* ch = &audio->ch3;
@ -971,24 +930,24 @@ void GBAudioPSGSerialize(const struct GBAudio* audio, struct GBSerializedPSGStat
flags = GBSerializedAudioFlagsSetCh1Volume(flags, audio->ch1.envelope.currentVolume);
flags = GBSerializedAudioFlagsSetCh1Dead(flags, audio->ch1.envelope.dead);
flags = GBSerializedAudioFlagsSetCh1Hi(flags, audio->ch1.control.hi);
flags = GBSerializedAudioFlagsSetCh1SweepEnabled(flags, audio->ch1.sweep.enable);
flags = GBSerializedAudioFlagsSetCh1SweepOccurred(flags, audio->ch1.sweep.occurred);
ch1Flags = GBSerializedAudioEnvelopeSetLength(ch1Flags, audio->ch1.control.length);
ch1Flags = GBSerializedAudioEnvelopeSetNextStep(ch1Flags, audio->ch1.envelope.nextStep);
ch1Flags = GBSerializedAudioEnvelopeSetFrequency(ch1Flags, audio->ch1.sweep.realFrequency);
ch1Flags = GBSerializedAudioEnvelopeSetDutyIndex(ch1Flags, audio->ch1.index);
sweep = GBSerializedAudioSweepSetTime(sweep, audio->ch1.sweep.time & 7);
STORE_32LE(ch1Flags, 0, &state->ch1.envelope);
STORE_32LE(sweep, 0, &state->ch1.sweep);
STORE_32LE(audio->ch1Event.when - mTimingCurrentTime(audio->timing), 0, &state->ch1.nextEvent);
STORE_32LE(audio->ch1.lastUpdate - mTimingCurrentTime(audio->timing), 0, &state->ch1.lastUpdate);
flags = GBSerializedAudioFlagsSetCh2Volume(flags, audio->ch2.envelope.currentVolume);
flags = GBSerializedAudioFlagsSetCh2Dead(flags, audio->ch2.envelope.dead);
flags = GBSerializedAudioFlagsSetCh2Hi(flags, audio->ch2.control.hi);
ch2Flags = GBSerializedAudioEnvelopeSetLength(ch2Flags, audio->ch2.control.length);
ch2Flags = GBSerializedAudioEnvelopeSetNextStep(ch2Flags, audio->ch2.envelope.nextStep);
ch2Flags = GBSerializedAudioEnvelopeSetDutyIndex(ch2Flags, audio->ch2.index);
STORE_32LE(ch2Flags, 0, &state->ch2.envelope);
STORE_32LE(audio->ch2Event.when - mTimingCurrentTime(audio->timing), 0, &state->ch2.nextEvent);
STORE_32LE(audio->ch2.lastUpdate - mTimingCurrentTime(audio->timing), 0, &state->ch2.lastUpdate);
flags = GBSerializedAudioFlagsSetCh3Readable(flags, audio->ch3.readable);
memcpy(state->ch3.wavebanks, audio->ch3.wavedata32, sizeof(state->ch3.wavebanks));
@ -1039,7 +998,6 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt
LOAD_32LE(sweep, 0, &state->ch1.sweep);
audio->ch1.envelope.currentVolume = GBSerializedAudioFlagsGetCh1Volume(flags);
audio->ch1.envelope.dead = GBSerializedAudioFlagsGetCh1Dead(flags);
audio->ch1.control.hi = GBSerializedAudioFlagsGetCh1Hi(flags);
audio->ch1.sweep.enable = GBSerializedAudioFlagsGetCh1SweepEnabled(flags);
audio->ch1.sweep.occurred = GBSerializedAudioFlagsGetCh1SweepOccurred(flags);
audio->ch1.sweep.time = GBSerializedAudioSweepGetTime(sweep);
@ -1049,21 +1007,18 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt
audio->ch1.control.length = GBSerializedAudioEnvelopeGetLength(ch1Flags);
audio->ch1.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch1Flags);
audio->ch1.sweep.realFrequency = GBSerializedAudioEnvelopeGetFrequency(ch1Flags);
LOAD_32LE(when, 0, &state->ch1.nextEvent);
if (audio->ch1.envelope.dead < 2 && audio->playingCh1) {
mTimingSchedule(audio->timing, &audio->ch1Event, when);
}
audio->ch1.index = GBSerializedAudioEnvelopeGetDutyIndex(ch1Flags);
LOAD_32LE(audio->ch1.lastUpdate, 0, &state->ch1.lastUpdate);
audio->ch1.lastUpdate += mTimingCurrentTime(audio->timing);
LOAD_32LE(ch2Flags, 0, &state->ch2.envelope);
audio->ch2.envelope.currentVolume = GBSerializedAudioFlagsGetCh2Volume(flags);
audio->ch2.envelope.dead = GBSerializedAudioFlagsGetCh2Dead(flags);
audio->ch2.control.hi = GBSerializedAudioFlagsGetCh2Hi(flags);
audio->ch2.control.length = GBSerializedAudioEnvelopeGetLength(ch2Flags);
audio->ch2.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch2Flags);
LOAD_32LE(when, 0, &state->ch2.nextEvent);
if (audio->ch2.envelope.dead < 2 && audio->playingCh2) {
mTimingSchedule(audio->timing, &audio->ch2Event, when);
}
audio->ch2.index = GBSerializedAudioEnvelopeGetDutyIndex(ch2Flags);
LOAD_32LE(audio->ch2.lastUpdate, 0, &state->ch2.lastUpdate);
audio->ch2.lastUpdate += mTimingCurrentTime(audio->timing);
audio->ch3.readable = GBSerializedAudioFlagsGetCh3Readable(flags);
// TODO: Big endian?

View File

@ -620,6 +620,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) {
GBAudioRun(&gb->audio, mTimingCurrentTime(gb->audio.timing));
return (gb->audio.ch1.sample) | (gb->audio.ch2.sample << 4);
}
break;

View File

@ -14,7 +14,7 @@
mLOG_DEFINE_CATEGORY(GB_STATE, "GB Savestate", "gb.serialize");
MGBA_EXPORT const uint32_t GBSavestateMagic = 0x00400000;
MGBA_EXPORT const uint32_t GBSavestateVersion = 0x00000002;
MGBA_EXPORT const uint32_t GBSavestateVersion = 0x00000003;
void GBSerialize(struct GB* gb, struct GBSerializedState* state) {
STORE_32LE(GBSavestateMagic + GBSavestateVersion, 0, &state->versionMagic);

View File

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