mirror of https://github.com/mgba-emu/mgba.git
GB Audio: Convert audio to mTiming
This commit is contained in:
parent
5b50c43857
commit
bb1c47c7e8
425
src/gb/audio.c
425
src/gb/audio.c
|
@ -35,11 +35,14 @@ static bool _updateSweep(struct GBAudioSquareChannel* sweep, bool initial);
|
|||
|
||||
static void _updateSquareSample(struct GBAudioSquareChannel* ch);
|
||||
static int32_t _updateSquareChannel(struct GBAudioSquareChannel* ch);
|
||||
static int32_t _updateWaveChannel(struct GBAudioWaveChannel* ch, enum GBAudioStyle style);
|
||||
static int32_t _updateNoiseChannel(struct GBAudioNoiseChannel* ch);
|
||||
|
||||
static void _sample(struct GBAudio* audio, int32_t cycles);
|
||||
static void _scheduleEvent(struct GBAudio* audio);
|
||||
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 _updateChannel4(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) {
|
||||
audio->samples = samples;
|
||||
|
@ -56,6 +59,33 @@ void GBAudioInit(struct GBAudio* audio, size_t samples, uint8_t* nr52, enum GBAu
|
|||
audio->masterVolume = GB_AUDIO_VOLUME_MAX;
|
||||
audio->nr52 = nr52;
|
||||
audio->style = style;
|
||||
if (style == GB_AUDIO_GBA) {
|
||||
audio->timingFactor = 4;
|
||||
} else {
|
||||
audio->timingFactor = 1;
|
||||
}
|
||||
|
||||
audio->frameEvent.context = audio;
|
||||
audio->frameEvent.name = "GB Audio Frame Sequencer";
|
||||
audio->frameEvent.callback = _updateFrame;
|
||||
audio->ch1Event.context = audio;
|
||||
audio->ch1Event.name = "GB Audio Channel 1";
|
||||
audio->ch1Event.callback = _updateChannel1;
|
||||
audio->ch2Event.context = audio;
|
||||
audio->ch2Event.name = "GB Audio Channel 2";
|
||||
audio->ch2Event.callback = _updateChannel2;
|
||||
audio->ch3Event.context = audio;
|
||||
audio->ch3Event.name = "GB Audio Channel 3";
|
||||
audio->ch3Event.callback = _updateChannel3;
|
||||
audio->ch3Fade.context = audio;
|
||||
audio->ch3Fade.name = "GB Audio Channel 3 Memory";
|
||||
audio->ch3Fade.callback = _fadeChannel3;
|
||||
audio->ch4Event.context = audio;
|
||||
audio->ch4Event.name = "GB Audio Channel 4";
|
||||
audio->ch4Event.callback = _updateChannel4;
|
||||
audio->sampleEvent.context = audio;
|
||||
audio->sampleEvent.name = "GB Audio Sample";
|
||||
audio->sampleEvent.callback = _sample;
|
||||
}
|
||||
|
||||
void GBAudioDeinit(struct GBAudio* audio) {
|
||||
|
@ -64,12 +94,17 @@ void GBAudioDeinit(struct GBAudio* audio) {
|
|||
}
|
||||
|
||||
void GBAudioReset(struct GBAudio* audio) {
|
||||
audio->nextEvent = 0;
|
||||
audio->nextCh1 = 0;
|
||||
audio->nextCh2 = 0;
|
||||
audio->nextCh3 = 0;
|
||||
audio->fadeCh3 = 0;
|
||||
audio->nextCh4 = 0;
|
||||
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);
|
||||
mTimingDeschedule(audio->timing, &audio->sampleEvent);
|
||||
mTimingSchedule(audio->timing, &audio->frameEvent, 0);
|
||||
if (audio->style != GB_AUDIO_GBA) {
|
||||
mTimingSchedule(audio->timing, &audio->sampleEvent, 0);
|
||||
}
|
||||
audio->ch1 = (struct GBAudioSquareChannel) { .envelope = { .dead = 2 } };
|
||||
audio->ch2 = (struct GBAudioSquareChannel) { .envelope = { .dead = 2 } };
|
||||
audio->ch3 = (struct GBAudioWaveChannel) { .bank = 0 };
|
||||
|
@ -91,10 +126,7 @@ void GBAudioReset(struct GBAudio* audio) {
|
|||
audio->ch3.wavedata8[14] = 0x00;
|
||||
audio->ch3.wavedata8[15] = 0xFF;
|
||||
audio->ch4 = (struct GBAudioNoiseChannel) { .envelope = { .dead = 2 } };
|
||||
audio->eventDiff = 0;
|
||||
audio->nextFrame = 0;
|
||||
audio->frame = 0;
|
||||
audio->nextSample = 0;
|
||||
audio->sampleInterval = 128;
|
||||
audio->lastLeft = 0;
|
||||
audio->lastRight = 0;
|
||||
|
@ -126,6 +158,7 @@ void GBAudioResizeBuffer(struct GBAudio* audio, size_t samples) {
|
|||
|
||||
void GBAudioWriteNR10(struct GBAudio* audio, uint8_t value) {
|
||||
if (!_writeSweep(&audio->ch1.sweep, value)) {
|
||||
mTimingDeschedule(audio->timing, &audio->ch1Event);
|
||||
audio->playingCh1 = false;
|
||||
*audio->nr52 &= ~0x0001;
|
||||
}
|
||||
|
@ -138,6 +171,7 @@ void GBAudioWriteNR11(struct GBAudio* audio, uint8_t value) {
|
|||
|
||||
void GBAudioWriteNR12(struct GBAudio* audio, uint8_t value) {
|
||||
if (!_writeEnvelope(&audio->ch1.envelope, value)) {
|
||||
mTimingDeschedule(audio->timing, &audio->ch1Event);
|
||||
audio->playingCh1 = false;
|
||||
*audio->nr52 &= ~0x0001;
|
||||
}
|
||||
|
@ -156,20 +190,17 @@ 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)) {
|
||||
audio->playingCh1 = _resetEnvelope(&audio->ch1.envelope);
|
||||
|
||||
if (audio->nextEvent == INT_MAX) {
|
||||
audio->eventDiff = 0;
|
||||
}
|
||||
if (audio->playingCh1) {
|
||||
audio->ch1.control.hi = 0;
|
||||
_updateSquareSample(&audio->ch1);
|
||||
}
|
||||
audio->nextCh1 = audio->eventDiff;
|
||||
|
||||
audio->ch1.sweep.realFrequency = audio->ch1.control.frequency;
|
||||
_resetSweep(&audio->ch1.sweep);
|
||||
|
@ -182,7 +213,10 @@ void GBAudioWriteNR14(struct GBAudio* audio, uint8_t value) {
|
|||
--audio->ch1.control.length;
|
||||
}
|
||||
}
|
||||
_scheduleEvent(audio);
|
||||
mTimingDeschedule(audio->timing, &audio->ch1Event);
|
||||
if (audio->playingCh1 && audio->ch1.envelope.dead != 2) {
|
||||
mTimingSchedule(audio->timing, &audio->ch1Event, 0);
|
||||
}
|
||||
}
|
||||
*audio->nr52 &= ~0x0001;
|
||||
*audio->nr52 |= audio->playingCh1;
|
||||
|
@ -195,6 +229,7 @@ void GBAudioWriteNR21(struct GBAudio* audio, uint8_t value) {
|
|||
|
||||
void GBAudioWriteNR22(struct GBAudio* audio, uint8_t value) {
|
||||
if (!_writeEnvelope(&audio->ch2.envelope, value)) {
|
||||
mTimingDeschedule(audio->timing, &audio->ch2Event);
|
||||
audio->playingCh2 = false;
|
||||
*audio->nr52 &= ~0x0002;
|
||||
}
|
||||
|
@ -213,15 +248,13 @@ 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)) {
|
||||
audio->playingCh2 = _resetEnvelope(&audio->ch2.envelope);
|
||||
|
||||
if (audio->nextEvent == INT_MAX) {
|
||||
audio->eventDiff = 0;
|
||||
}
|
||||
if (audio->playingCh2) {
|
||||
audio->ch2.control.hi = 0;
|
||||
_updateSquareSample(&audio->ch2);
|
||||
|
@ -233,8 +266,10 @@ void GBAudioWriteNR24(struct GBAudio* audio, uint8_t value) {
|
|||
--audio->ch2.control.length;
|
||||
}
|
||||
}
|
||||
audio->nextCh2 = audio->eventDiff;
|
||||
_scheduleEvent(audio);
|
||||
mTimingDeschedule(audio->timing, &audio->ch2Event);
|
||||
if (audio->playingCh2 && audio->ch2.envelope.dead != 2) {
|
||||
mTimingSchedule(audio->timing, &audio->ch2Event, 0);
|
||||
}
|
||||
}
|
||||
*audio->nr52 &= ~0x0002;
|
||||
*audio->nr52 |= audio->playingCh2 << 1;
|
||||
|
@ -294,14 +329,11 @@ void GBAudioWriteNR34(struct GBAudio* audio, uint8_t value) {
|
|||
}
|
||||
audio->ch3.window = 0;
|
||||
}
|
||||
mTimingDeschedule(audio->timing, &audio->ch3Event);
|
||||
if (audio->playingCh3) {
|
||||
if (audio->nextEvent == INT_MAX) {
|
||||
audio->eventDiff = 0;
|
||||
}
|
||||
audio->ch3.readable = audio->style != GB_AUDIO_DMG;
|
||||
_scheduleEvent(audio);
|
||||
// TODO: Where does this cycle delay come from?
|
||||
audio->nextCh3 = audio->eventDiff + audio->nextEvent + 4 + 2 * (2048 - audio->ch3.rate);
|
||||
mTimingSchedule(audio->timing, &audio->ch3Event, audio->timingFactor * 4 + 2 * (2048 - audio->ch3.rate));
|
||||
}
|
||||
*audio->nr52 &= ~0x0004;
|
||||
*audio->nr52 |= audio->playingCh3 << 2;
|
||||
|
@ -314,6 +346,7 @@ void GBAudioWriteNR41(struct GBAudio* audio, uint8_t value) {
|
|||
|
||||
void GBAudioWriteNR42(struct GBAudio* audio, uint8_t value) {
|
||||
if (!_writeEnvelope(&audio->ch4.envelope, value)) {
|
||||
mTimingDeschedule(audio->timing, &audio->ch4Event);
|
||||
audio->playingCh4 = false;
|
||||
*audio->nr52 &= ~0x0008;
|
||||
}
|
||||
|
@ -331,6 +364,7 @@ void GBAudioWriteNR44(struct GBAudio* audio, uint8_t value) {
|
|||
if (!wasStop && audio->ch4.stop && audio->ch4.length && !(audio->frame & 1)) {
|
||||
--audio->ch4.length;
|
||||
if (audio->ch4.length == 0) {
|
||||
mTimingDeschedule(audio->timing, &audio->ch4Event);
|
||||
audio->playingCh4 = false;
|
||||
}
|
||||
}
|
||||
|
@ -342,17 +376,16 @@ void GBAudioWriteNR44(struct GBAudio* audio, uint8_t value) {
|
|||
} else {
|
||||
audio->ch4.lfsr = 0x4000;
|
||||
}
|
||||
if (audio->nextEvent == INT_MAX) {
|
||||
audio->eventDiff = 0;
|
||||
}
|
||||
audio->nextCh4 = audio->eventDiff;
|
||||
if (!audio->ch4.length) {
|
||||
audio->ch4.length = 64;
|
||||
if (audio->ch4.stop && !(audio->frame & 1)) {
|
||||
--audio->ch4.length;
|
||||
}
|
||||
}
|
||||
_scheduleEvent(audio);
|
||||
mTimingDeschedule(audio->timing, &audio->ch4Event);
|
||||
if (audio->playingCh4 && audio->ch4.envelope.dead != 2) {
|
||||
mTimingSchedule(audio->timing, &audio->ch4Event, 0);
|
||||
}
|
||||
}
|
||||
*audio->nr52 &= ~0x0008;
|
||||
*audio->nr52 |= audio->playingCh4 << 3;
|
||||
|
@ -438,171 +471,106 @@ void GBAudioWriteNR52(struct GBAudio* audio, uint8_t value) {
|
|||
}
|
||||
}
|
||||
|
||||
int32_t GBAudioProcessEvents(struct GBAudio* audio, int32_t cycles) {
|
||||
if (audio->nextEvent == INT_MAX) {
|
||||
return INT_MAX;
|
||||
}
|
||||
audio->nextEvent -= cycles;
|
||||
audio->eventDiff += cycles;
|
||||
while (audio->nextEvent <= 0) {
|
||||
audio->nextEvent = INT_MAX;
|
||||
if (audio->enable) {
|
||||
audio->nextFrame -= audio->eventDiff;
|
||||
int frame = -1;
|
||||
if (audio->nextFrame <= 0) {
|
||||
frame = (audio->frame + 1) & 7;
|
||||
audio->frame = frame;
|
||||
audio->nextFrame += FRAME_CYCLES;
|
||||
if (audio->nextFrame < audio->nextEvent) {
|
||||
audio->nextEvent = audio->nextFrame;
|
||||
}
|
||||
void _updateFrame(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
||||
struct GBAudio* audio = user;
|
||||
|
||||
int frame = (audio->frame + 1) & 7;
|
||||
audio->frame = frame;
|
||||
|
||||
switch (frame) {
|
||||
case 2:
|
||||
case 6:
|
||||
if (audio->ch1.sweep.enable) {
|
||||
--audio->ch1.sweep.step;
|
||||
if (audio->ch1.sweep.step == 0) {
|
||||
audio->playingCh1 = _updateSweep(&audio->ch1, false);
|
||||
*audio->nr52 &= ~0x0001;
|
||||
*audio->nr52 |= audio->playingCh1;
|
||||
}
|
||||
|
||||
if (audio->playingCh1) {
|
||||
audio->nextCh1 -= audio->eventDiff;
|
||||
if (!audio->ch1.envelope.dead && frame == 7) {
|
||||
--audio->ch1.envelope.nextStep;
|
||||
if (audio->ch1.envelope.nextStep == 0) {
|
||||
_updateEnvelope(&audio->ch1.envelope);
|
||||
_updateSquareSample(&audio->ch1);
|
||||
}
|
||||
}
|
||||
|
||||
if (audio->ch1.sweep.enable && (frame & 3) == 2) {
|
||||
--audio->ch1.sweep.step;
|
||||
if (audio->ch1.sweep.step == 0) {
|
||||
audio->playingCh1 = _updateSweep(&audio->ch1, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (audio->ch1.envelope.dead != 2) {
|
||||
if (audio->nextCh1 <= 0) {
|
||||
audio->nextCh1 += _updateSquareChannel(&audio->ch1);
|
||||
}
|
||||
if (audio->nextCh1 < audio->nextEvent) {
|
||||
audio->nextEvent = audio->nextCh1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (audio->ch1.control.length && audio->ch1.control.stop && !(frame & 1)) {
|
||||
--audio->ch1.control.length;
|
||||
if (audio->ch1.control.length == 0) {
|
||||
audio->playingCh1 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (audio->playingCh2) {
|
||||
audio->nextCh2 -= audio->eventDiff;
|
||||
if (!audio->ch2.envelope.dead && frame == 7) {
|
||||
--audio->ch2.envelope.nextStep;
|
||||
if (audio->ch2.envelope.nextStep == 0) {
|
||||
_updateEnvelope(&audio->ch2.envelope);
|
||||
_updateSquareSample(&audio->ch2);
|
||||
}
|
||||
}
|
||||
|
||||
if (audio->ch2.envelope.dead != 2) {
|
||||
if (audio->nextCh2 <= 0) {
|
||||
audio->nextCh2 += _updateSquareChannel(&audio->ch2);
|
||||
}
|
||||
if (audio->nextCh2 < audio->nextEvent) {
|
||||
audio->nextEvent = audio->nextCh2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (audio->ch2.control.length && audio->ch2.control.stop && !(frame & 1)) {
|
||||
--audio->ch2.control.length;
|
||||
if (audio->ch2.control.length == 0) {
|
||||
audio->playingCh2 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (audio->playingCh3) {
|
||||
audio->nextCh3 -= audio->eventDiff;
|
||||
audio->fadeCh3 -= audio->eventDiff;
|
||||
if (audio->fadeCh3 <= 0) {
|
||||
audio->ch3.readable = false;
|
||||
audio->fadeCh3 = INT_MAX;
|
||||
}
|
||||
if (audio->nextCh3 <= 0) {
|
||||
if (audio->style == GB_AUDIO_DMG) {
|
||||
audio->fadeCh3 = audio->nextCh3 + 2;
|
||||
}
|
||||
audio->nextCh3 += _updateWaveChannel(&audio->ch3, audio->style);
|
||||
audio->ch3.readable = true;
|
||||
}
|
||||
if (audio->fadeCh3 < audio->nextEvent) {
|
||||
audio->nextEvent = audio->fadeCh3;
|
||||
}
|
||||
if (audio->nextCh3 < audio->nextEvent) {
|
||||
audio->nextEvent = audio->nextCh3;
|
||||
}
|
||||
}
|
||||
|
||||
if (audio->ch3.length && audio->ch3.stop && !(frame & 1)) {
|
||||
--audio->ch3.length;
|
||||
if (audio->ch3.length == 0) {
|
||||
audio->playingCh3 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (audio->playingCh4) {
|
||||
audio->nextCh4 -= audio->eventDiff;
|
||||
if (!audio->ch4.envelope.dead && frame == 7) {
|
||||
--audio->ch4.envelope.nextStep;
|
||||
if (audio->ch4.envelope.nextStep == 0) {
|
||||
int8_t sample = (audio->ch4.sample >> 7) * 0x8;
|
||||
_updateEnvelope(&audio->ch4.envelope);
|
||||
audio->ch4.sample = sample * audio->ch4.envelope.currentVolume;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (audio->ch4.length && audio->ch4.stop && !(frame & 1)) {
|
||||
--audio->ch4.length;
|
||||
if (audio->ch4.length == 0) {
|
||||
audio->playingCh4 = 0;
|
||||
}
|
||||
}
|
||||
// Fall through
|
||||
case 0:
|
||||
case 4:
|
||||
if (audio->ch1.control.length && audio->ch1.control.stop) {
|
||||
--audio->ch1.control.length;
|
||||
if (audio->ch1.control.length == 0) {
|
||||
mTimingDeschedule(timing, &audio->ch1Event);
|
||||
audio->playingCh1 = 0;
|
||||
*audio->nr52 &= ~0x0001;
|
||||
}
|
||||
}
|
||||
|
||||
*audio->nr52 &= ~0x000F;
|
||||
*audio->nr52 |= audio->playingCh1;
|
||||
*audio->nr52 |= audio->playingCh2 << 1;
|
||||
*audio->nr52 |= audio->playingCh3 << 2;
|
||||
*audio->nr52 |= audio->playingCh4 << 3;
|
||||
|
||||
if (audio->p) {
|
||||
audio->nextSample -= audio->eventDiff;
|
||||
if (audio->nextSample <= 0) {
|
||||
_sample(audio, audio->sampleInterval);
|
||||
audio->nextSample += audio->sampleInterval;
|
||||
}
|
||||
|
||||
if (audio->nextSample < audio->nextEvent) {
|
||||
audio->nextEvent = audio->nextSample;
|
||||
if (audio->ch2.control.length && audio->ch2.control.stop) {
|
||||
--audio->ch2.control.length;
|
||||
if (audio->ch2.control.length == 0) {
|
||||
mTimingDeschedule(timing, &audio->ch2Event);
|
||||
audio->playingCh2 = 0;
|
||||
*audio->nr52 &= ~0x0002;
|
||||
}
|
||||
}
|
||||
audio->eventDiff = 0;
|
||||
|
||||
if (audio->ch3.length && audio->ch3.stop) {
|
||||
--audio->ch3.length;
|
||||
if (audio->ch3.length == 0) {
|
||||
mTimingDeschedule(timing, &audio->ch3Event);
|
||||
audio->playingCh3 = 0;
|
||||
*audio->nr52 &= ~0x0004;
|
||||
}
|
||||
}
|
||||
|
||||
if (audio->ch4.length && audio->ch4.stop) {
|
||||
--audio->ch4.length;
|
||||
if (audio->ch4.length == 0) {
|
||||
mTimingDeschedule(timing, &audio->ch4Event);
|
||||
audio->playingCh4 = 0;
|
||||
*audio->nr52 &= ~0x0008;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
if (audio->playingCh1 && !audio->ch1.envelope.dead) {
|
||||
--audio->ch1.envelope.nextStep;
|
||||
if (audio->ch1.envelope.nextStep == 0) {
|
||||
_updateEnvelope(&audio->ch1.envelope);
|
||||
if (audio->ch1.envelope.dead == 2) {
|
||||
mTimingDeschedule(timing, &audio->ch1Event);
|
||||
}
|
||||
_updateSquareSample(&audio->ch1);
|
||||
}
|
||||
}
|
||||
|
||||
if (audio->playingCh2 && !audio->ch2.envelope.dead) {
|
||||
--audio->ch2.envelope.nextStep;
|
||||
if (audio->ch2.envelope.nextStep == 0) {
|
||||
_updateEnvelope(&audio->ch2.envelope);
|
||||
if (audio->ch2.envelope.dead == 2) {
|
||||
mTimingDeschedule(timing, &audio->ch2Event);
|
||||
}
|
||||
_updateSquareSample(&audio->ch1);
|
||||
}
|
||||
}
|
||||
|
||||
if (audio->playingCh4 && !audio->ch4.envelope.dead) {
|
||||
--audio->ch4.envelope.nextStep;
|
||||
if (audio->ch4.envelope.nextStep == 0) {
|
||||
int8_t sample = (audio->ch4.sample >> 7) * 0x8;
|
||||
_updateEnvelope(&audio->ch4.envelope);
|
||||
if (audio->ch4.envelope.dead == 2) {
|
||||
mTimingDeschedule(timing, &audio->ch4Event);
|
||||
}
|
||||
audio->ch4.sample = sample * audio->ch4.envelope.currentVolume;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return audio->nextEvent;
|
||||
|
||||
mTimingSchedule(timing, &audio->frameEvent, audio->timingFactor * FRAME_CYCLES - cyclesLate);
|
||||
}
|
||||
|
||||
void GBAudioSamplePSG(struct GBAudio* audio, int16_t* left, int16_t* right) {
|
||||
int sampleLeft = 0;
|
||||
int sampleRight = 0;
|
||||
|
||||
if (audio->ch4.envelope.dead != 2) {
|
||||
while (audio->nextCh4 <= 0) {
|
||||
audio->nextCh4 += _updateNoiseChannel(&audio->ch4);
|
||||
}
|
||||
if (audio->nextCh4 < audio->nextEvent) {
|
||||
audio->nextEvent = audio->nextCh4;
|
||||
}
|
||||
}
|
||||
|
||||
if (audio->playingCh1 && !audio->forceDisableCh[0]) {
|
||||
if (audio->ch1Left) {
|
||||
sampleLeft += audio->ch1.sample;
|
||||
|
@ -647,7 +615,8 @@ void GBAudioSamplePSG(struct GBAudio* audio, int16_t* left, int16_t* right) {
|
|||
*right = sampleRight * (1 + audio->volumeRight);
|
||||
}
|
||||
|
||||
void _sample(struct GBAudio* audio, int32_t cycles) {
|
||||
static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
||||
struct GBAudio* audio = user;
|
||||
int16_t sampleLeft = 0;
|
||||
int16_t sampleRight = 0;
|
||||
GBAudioSamplePSG(audio, &sampleLeft, &sampleRight);
|
||||
|
@ -661,7 +630,7 @@ void _sample(struct GBAudio* audio, int32_t cycles) {
|
|||
blip_add_delta(audio->right, audio->clock, sampleRight - audio->lastRight);
|
||||
audio->lastLeft = sampleLeft;
|
||||
audio->lastRight = sampleRight;
|
||||
audio->clock += cycles;
|
||||
audio->clock += audio->sampleInterval;
|
||||
if (audio->clock >= CLOCKS_PER_BLIP_FRAME) {
|
||||
blip_end_frame(audio->left, audio->clock);
|
||||
blip_end_frame(audio->right, audio->clock);
|
||||
|
@ -678,6 +647,7 @@ void _sample(struct GBAudio* audio, int32_t cycles) {
|
|||
if (wait && audio->p->stream && audio->p->stream->postAudioBuffer) {
|
||||
audio->p->stream->postAudioBuffer(audio->p->stream, audio->left, audio->right);
|
||||
}
|
||||
mTimingSchedule(timing, &audio->sampleEvent, audio->sampleInterval * audio->timingFactor - cyclesLate);
|
||||
}
|
||||
|
||||
bool _resetEnvelope(struct GBAudioEnvelope* envelope) {
|
||||
|
@ -722,7 +692,7 @@ bool _writeEnvelope(struct GBAudioEnvelope* envelope, uint8_t value) {
|
|||
envelope->initialVolume = GBAudioRegisterSweepGetInitialVolume(value);
|
||||
_updateEnvelopeDead(envelope);
|
||||
envelope->nextStep = envelope->stepTime;
|
||||
return envelope->initialVolume || envelope->direction;
|
||||
return (envelope->initialVolume || envelope->direction) && envelope->dead != 2;
|
||||
}
|
||||
|
||||
static void _updateSquareSample(struct GBAudioSquareChannel* ch) {
|
||||
|
@ -806,7 +776,23 @@ static bool _updateSweep(struct GBAudioSquareChannel* ch, bool initial) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static int32_t _updateWaveChannel(struct GBAudioWaveChannel* ch, enum GBAudioStyle style) {
|
||||
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;
|
||||
int i;
|
||||
int volume;
|
||||
switch (ch->volume) {
|
||||
|
@ -826,9 +812,9 @@ static int32_t _updateWaveChannel(struct GBAudioWaveChannel* ch, enum GBAudioSty
|
|||
volume = 3;
|
||||
break;
|
||||
}
|
||||
switch (style) {
|
||||
int start;
|
||||
int end;
|
||||
int start;
|
||||
int end;
|
||||
switch (audio->style) {
|
||||
case GB_AUDIO_DMG:
|
||||
default:
|
||||
++ch->window;
|
||||
|
@ -863,29 +849,32 @@ static int32_t _updateWaveChannel(struct GBAudioWaveChannel* ch, enum GBAudioSty
|
|||
}
|
||||
ch->sample -= 8;
|
||||
ch->sample *= volume * 4;
|
||||
return 2 * (2048 - ch->rate);
|
||||
audio->ch3.readable = true;
|
||||
if (audio->style == GB_AUDIO_DMG) {
|
||||
mTimingSchedule(timing, &audio->ch3Fade, 2 - cyclesLate);
|
||||
}
|
||||
int cycles = 2 * (2048 - ch->rate);
|
||||
mTimingSchedule(timing, &audio->ch3Event, audio->timingFactor * cycles - cyclesLate);
|
||||
}
|
||||
static void _fadeChannel3(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
||||
UNUSED(timing);
|
||||
UNUSED(cyclesLate);
|
||||
struct GBAudio* audio = user;
|
||||
audio->ch3.readable = false;
|
||||
}
|
||||
|
||||
static int32_t _updateNoiseChannel(struct GBAudioNoiseChannel* ch) {
|
||||
static void _updateChannel4(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
||||
struct GBAudio* audio = user;
|
||||
struct GBAudioNoiseChannel* ch = &audio->ch4;
|
||||
int lsb = ch->lfsr & 1;
|
||||
ch->sample = lsb * 0x10 - 0x8;
|
||||
ch->sample *= ch->envelope.currentVolume;
|
||||
ch->lfsr >>= 1;
|
||||
ch->lfsr ^= (lsb * 0x60) << (ch->power ? 0 : 8);
|
||||
int timing = ch->ratio ? 2 * ch->ratio : 1;
|
||||
timing <<= ch->frequency;
|
||||
timing *= 8;
|
||||
return timing;
|
||||
}
|
||||
|
||||
void _scheduleEvent(struct GBAudio* audio) {
|
||||
// TODO: Don't need p
|
||||
if (audio->p) {
|
||||
audio->nextEvent = audio->p->cpu->cycles >> audio->p->doubleSpeed;
|
||||
audio->p->cpu->nextEvent = audio->p->cpu->cycles;
|
||||
} else {
|
||||
audio->nextEvent = 0;
|
||||
}
|
||||
int cycles = ch->ratio ? 2 * ch->ratio : 1;
|
||||
cycles <<= ch->frequency;
|
||||
cycles *= 8;
|
||||
mTimingSchedule(timing, &audio->ch4Event, audio->timingFactor * cycles - cyclesLate);
|
||||
}
|
||||
|
||||
void GBAudioPSGSerialize(const struct GBAudio* audio, struct GBSerializedPSGState* state, uint32_t* flagsOut) {
|
||||
|
@ -905,8 +894,6 @@ void GBAudioPSGSerialize(const struct GBAudio* audio, struct GBSerializedPSGStat
|
|||
ch1Flags = GBSerializedAudioEnvelopeSetNextStep(ch1Flags, audio->ch1.envelope.nextStep);
|
||||
ch1Flags = GBSerializedAudioEnvelopeSetFrequency(ch1Flags, audio->ch1.sweep.realFrequency);
|
||||
STORE_32LE(ch1Flags, 0, &state->ch1.envelope);
|
||||
STORE_32LE(audio->nextFrame, 0, &state->ch1.nextFrame);
|
||||
STORE_32LE(audio->nextCh1, 0, &state->ch1.nextEvent);
|
||||
|
||||
flags = GBSerializedAudioFlagsSetCh2Volume(flags, audio->ch2.envelope.currentVolume);
|
||||
flags = GBSerializedAudioFlagsSetCh2Dead(flags, audio->ch2.envelope.dead);
|
||||
|
@ -914,12 +901,9 @@ void GBAudioPSGSerialize(const struct GBAudio* audio, struct GBSerializedPSGStat
|
|||
ch2Flags = GBSerializedAudioEnvelopeSetLength(ch2Flags, audio->ch2.control.length);
|
||||
ch2Flags = GBSerializedAudioEnvelopeSetNextStep(ch2Flags, audio->ch2.envelope.nextStep);
|
||||
STORE_32LE(ch2Flags, 0, &state->ch2.envelope);
|
||||
STORE_32LE(audio->nextCh2, 0, &state->ch2.nextEvent);
|
||||
|
||||
memcpy(state->ch3.wavebanks, audio->ch3.wavedata32, sizeof(state->ch3.wavebanks));
|
||||
STORE_16LE(audio->ch3.length, 0, &state->ch3.length);
|
||||
STORE_32LE(audio->nextCh3, 0, &state->ch3.nextEvent);
|
||||
STORE_32LE(audio->fadeCh3, 0, &state->ch1.nextCh3Fade);
|
||||
|
||||
flags = GBSerializedAudioFlagsSetCh4Volume(flags, audio->ch4.envelope.currentVolume);
|
||||
flags = GBSerializedAudioFlagsSetCh4Dead(flags, audio->ch4.envelope.dead);
|
||||
|
@ -927,7 +911,6 @@ void GBAudioPSGSerialize(const struct GBAudio* audio, struct GBSerializedPSGStat
|
|||
ch4Flags = GBSerializedAudioEnvelopeSetLength(ch4Flags, audio->ch4.length);
|
||||
ch4Flags = GBSerializedAudioEnvelopeSetNextStep(ch4Flags, audio->ch4.envelope.nextStep);
|
||||
STORE_32LE(ch4Flags, 0, &state->ch4.envelope);
|
||||
STORE_32LE(audio->nextCh4, 0, &state->ch4.nextEvent);
|
||||
|
||||
STORE_32LE(flags, 0, flagsOut);
|
||||
}
|
||||
|
@ -954,8 +937,6 @@ 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(audio->nextFrame, 0, &state->ch1.nextFrame);
|
||||
LOAD_32LE(audio->nextCh1, 0, &state->ch1.nextEvent);
|
||||
|
||||
LOAD_32LE(ch2Flags, 0, &state->ch2.envelope);
|
||||
audio->ch2.envelope.currentVolume = GBSerializedAudioFlagsGetCh2Volume(flags);
|
||||
|
@ -963,14 +944,11 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt
|
|||
audio->ch2.control.hi = GBSerializedAudioFlagsGetCh2Hi(flags);
|
||||
audio->ch2.control.length = GBSerializedAudioEnvelopeGetLength(ch2Flags);
|
||||
audio->ch2.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch2Flags);
|
||||
LOAD_32LE(audio->nextCh2, 0, &state->ch2.nextEvent);
|
||||
|
||||
audio->ch3.readable = GBSerializedAudioFlagsGetCh3Readable(flags);
|
||||
// TODO: Big endian?
|
||||
memcpy(audio->ch3.wavedata32, state->ch3.wavebanks, sizeof(audio->ch3.wavedata32));
|
||||
LOAD_16LE(audio->ch3.length, 0, &state->ch3.length);
|
||||
LOAD_32LE(audio->nextCh3, 0, &state->ch3.nextEvent);
|
||||
LOAD_32LE(audio->fadeCh3, 0, &state->ch1.nextCh3Fade);
|
||||
|
||||
LOAD_32LE(ch4Flags, 0, &state->ch4.envelope);
|
||||
audio->ch4.envelope.currentVolume = GBSerializedAudioFlagsGetCh4Volume(flags);
|
||||
|
@ -978,21 +956,12 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt
|
|||
audio->ch4.length = GBSerializedAudioEnvelopeGetLength(ch4Flags);
|
||||
audio->ch4.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch4Flags);
|
||||
LOAD_32LE(audio->ch4.lfsr, 0, &state->ch4.lfsr);
|
||||
LOAD_32LE(audio->nextCh4, 0, &state->ch4.nextEvent);
|
||||
}
|
||||
|
||||
void GBAudioSerialize(const struct GBAudio* audio, struct GBSerializedState* state) {
|
||||
GBAudioPSGSerialize(audio, &state->audio.psg, &state->audio.flags);
|
||||
|
||||
STORE_32LE(audio->nextEvent, 0, &state->audio.nextEvent);
|
||||
STORE_32LE(audio->eventDiff, 0, &state->audio.eventDiff);
|
||||
STORE_32LE(audio->nextSample, 0, &state->audio.nextSample);
|
||||
}
|
||||
|
||||
void GBAudioDeserialize(struct GBAudio* audio, const struct GBSerializedState* state) {
|
||||
GBAudioPSGDeserialize(audio, &state->audio.psg, &state->audio.flags);
|
||||
|
||||
LOAD_32LE(audio->nextEvent, 0, &state->audio.nextEvent);
|
||||
LOAD_32LE(audio->eventDiff, 0, &state->audio.eventDiff);
|
||||
LOAD_32LE(audio->nextSample, 0, &state->audio.nextSample);
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "util/common.h"
|
||||
|
||||
#include "core/timing.h"
|
||||
#include "third-party/blip_buf/blip_buf.h"
|
||||
|
||||
DECL_BITFIELD(GBAudioRegisterDuty, uint8_t);
|
||||
|
@ -147,6 +148,8 @@ enum GBAudioStyle {
|
|||
|
||||
struct GBAudio {
|
||||
struct GB* p;
|
||||
struct mTiming* timing;
|
||||
unsigned timingFactor;
|
||||
struct GBAudioSquareChannel ch1;
|
||||
struct GBAudioSquareChannel ch2;
|
||||
struct GBAudioWaveChannel ch3;
|
||||
|
@ -176,20 +179,18 @@ struct GBAudio {
|
|||
bool playingCh4;
|
||||
uint8_t* nr52;
|
||||
|
||||
int32_t nextEvent;
|
||||
int32_t eventDiff;
|
||||
int32_t nextFrame;
|
||||
int frame;
|
||||
int32_t nextSample;
|
||||
|
||||
int32_t sampleInterval;
|
||||
enum GBAudioStyle style;
|
||||
|
||||
int32_t nextCh1;
|
||||
int32_t nextCh2;
|
||||
int32_t nextCh3;
|
||||
int32_t fadeCh3;
|
||||
int32_t nextCh4;
|
||||
struct mTimingEvent frameEvent;
|
||||
struct mTimingEvent ch1Event;
|
||||
struct mTimingEvent ch2Event;
|
||||
struct mTimingEvent ch3Event;
|
||||
struct mTimingEvent ch3Fade;
|
||||
struct mTimingEvent ch4Event;
|
||||
struct mTimingEvent sampleEvent;
|
||||
bool enable;
|
||||
|
||||
size_t samples;
|
||||
|
@ -229,7 +230,6 @@ void GBAudioWriteNR50(struct GBAudio* audio, uint8_t);
|
|||
void GBAudioWriteNR51(struct GBAudio* audio, uint8_t);
|
||||
void GBAudioWriteNR52(struct GBAudio* audio, uint8_t);
|
||||
|
||||
int32_t GBAudioProcessEvents(struct GBAudio* audio, int32_t cycles);
|
||||
void GBAudioSamplePSG(struct GBAudio* audio, int16_t* left, int16_t* right);
|
||||
|
||||
struct GBSerializedPSGState;
|
||||
|
|
11
src/gb/gb.c
11
src/gb/gb.c
|
@ -84,6 +84,7 @@ static void GBInit(void* cpu, struct mCPUComponent* component) {
|
|||
gb->stream = NULL;
|
||||
|
||||
mTimingInit(&gb->timing, &gb->cpu->cycles, &gb->cpu->nextEvent);
|
||||
gb->audio.timing = &gb->timing;
|
||||
}
|
||||
|
||||
static void GBDeinit(struct mCPUComponent* component) {
|
||||
|
@ -530,7 +531,6 @@ void GBProcessEvents(struct LR35902Core* cpu) {
|
|||
do {
|
||||
int32_t cycles = cpu->cycles;
|
||||
int32_t nextEvent;
|
||||
int32_t testEvent;
|
||||
|
||||
cpu->cycles = 0;
|
||||
cpu->nextEvent = INT_MAX;
|
||||
|
@ -549,14 +549,6 @@ void GBProcessEvents(struct LR35902Core* cpu) {
|
|||
mTimingTick(&gb->timing, cycles);
|
||||
nextEvent = cpu->nextEvent;
|
||||
|
||||
testEvent = GBAudioProcessEvents(&gb->audio, cycles >> gb->doubleSpeed);
|
||||
if (testEvent != INT_MAX) {
|
||||
testEvent <<= gb->doubleSpeed;
|
||||
if (testEvent < nextEvent) {
|
||||
nextEvent = testEvent;
|
||||
}
|
||||
}
|
||||
|
||||
cpu->nextEvent = nextEvent;
|
||||
|
||||
if (cpu->halted) {
|
||||
|
@ -596,6 +588,7 @@ void GBStop(struct LR35902Core* cpu) {
|
|||
}
|
||||
if (gb->memory.io[REG_KEY1] & 1) {
|
||||
gb->doubleSpeed ^= 1;
|
||||
gb->audio.timingFactor = gb->doubleSpeed + 1;
|
||||
gb->memory.io[REG_KEY1] = 0;
|
||||
gb->memory.io[REG_KEY1] |= gb->doubleSpeed << 7;
|
||||
} else if (cpu->bus) {
|
||||
|
|
|
@ -177,6 +177,7 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
gb->cpu->condition = GBSerializedCpuFlagsGetCondition(flags);
|
||||
gb->cpu->irqPending = GBSerializedCpuFlagsGetIrqPending(flags);
|
||||
gb->doubleSpeed = GBSerializedCpuFlagsGetDoubleSpeed(flags);
|
||||
gb->audio.timingFactor = gb->doubleSpeed + 1;
|
||||
|
||||
LOAD_32LE(gb->cpu->cycles, 0, &state->cpu.cycles);
|
||||
LOAD_32LE(gb->cpu->nextEvent, 0, &state->cpu.nextEvent);
|
||||
|
|
|
@ -33,8 +33,9 @@ void GBAAudioInit(struct GBAAudio* audio, size_t samples) {
|
|||
++nr52;
|
||||
#endif
|
||||
GBAudioInit(&audio->psg, 0, nr52, GB_AUDIO_GBA);
|
||||
audio->samples = samples;
|
||||
audio->psg.timing = &audio->p->timing;
|
||||
audio->psg.clockRate = GBA_ARM7TDMI_FREQUENCY;
|
||||
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);
|
||||
blip_set_rates(audio->psg.right, GBA_ARM7TDMI_FREQUENCY, 96000);
|
||||
|
@ -96,13 +97,6 @@ int32_t GBAAudioProcessEvents(struct GBAAudio* audio, int32_t cycles) {
|
|||
audio->eventDiff += cycles;
|
||||
while (audio->nextEvent <= 0) {
|
||||
audio->nextEvent = INT_MAX;
|
||||
if (audio->enable) {
|
||||
audio->nextEvent = GBAudioProcessEvents(&audio->psg, audio->eventDiff / 4);
|
||||
if (audio->nextEvent != INT_MAX) {
|
||||
audio->nextEvent *= 4;
|
||||
}
|
||||
}
|
||||
|
||||
audio->nextSample -= audio->eventDiff;
|
||||
if (audio->nextSample <= 0) {
|
||||
_sample(audio);
|
||||
|
|
Loading…
Reference in New Issue