GB Audio: Minor channel 3 revamp
@@ -23,11 +23,11 @@ static void _updateEnvelope(struct GBAudioEnvelope* envelope);
static bool _updateSweep(struct GBAudioChannel1* ch, bool initial); static int32_t _updateChannel1(struct GBAudioChannel1* ch); static int32_t _updateChannel2(struct GBAudioChannel2* ch); -static int32_t _updateChannel3(struct GBAudioChannel3* ch); +static int32_t _updateChannel3(struct GBAudioChannel3* ch, enum GBAudioStyle style); static int32_t _updateChannel4(struct GBAudioChannel4* ch); static void _sample(struct GBAudio* audio, int32_t cycles); -void GBAudioInit(struct GBAudio* audio, size_t samples, uint8_t* nr52) { +void GBAudioInit(struct GBAudio* audio, size_t samples, uint8_t* nr52, enum GBAudioStyle style) { audio->samples = samples; audio->left = blip_new(BLIP_BUFFER_SIZE); audio->right = blip_new(BLIP_BUFFER_SIZE);@@ -41,6 +41,7 @@ audio->forceDisableCh[2] = false;
audio->forceDisableCh[3] = false; audio->masterVolume = GB_AUDIO_VOLUME_MAX; audio->nr52 = nr52; + audio->style = style; } void GBAudioDeinit(struct GBAudio* audio) {@@ -156,10 +157,10 @@ if (audio->ch1.control.stop && !(audio->frame & 1)) {
--audio->ch1.control.length; } } - audio->nextEvent = audio->eventDiff; + audio->nextEvent = audio->p->cpu->cycles; if (audio->p) { // TODO: Don't need p - audio->p->cpu->nextEvent = audio->eventDiff; + audio->p->cpu->nextEvent = audio->nextEvent; } } *audio->nr52 &= ~0x0001;@@ -212,10 +213,10 @@ if (audio->ch2.control.stop && !(audio->frame & 1)) {
--audio->ch2.control.length; } } - audio->nextEvent = audio->eventDiff; + audio->nextEvent = audio->p->cpu->cycles; if (audio->p) { // TODO: Don't need p - audio->p->cpu->nextEvent = audio->eventDiff; + audio->p->cpu->nextEvent = audio->nextEvent; } } *audio->nr52 &= ~0x0002;@@ -254,6 +255,7 @@ if (audio->ch3.length == 0) {
audio->playingCh3 = false; } } + bool wasEnable = audio->playingCh3; if (GBAudioRegisterControlIsRestart(value << 8)) { audio->playingCh3 = audio->ch3.enable; if (!audio->ch3.length) {@@ -262,16 +264,29 @@ if (audio->ch3.stop && !(audio->frame & 1)) {
--audio->ch3.length; } } + + if (audio->style == GB_AUDIO_DMG && wasEnable && audio->playingCh3 && audio->ch3.readable) { + if (audio->ch3.window < 8) { + audio->ch3.wavedata8[0] = audio->ch3.wavedata8[audio->ch3.window >> 1]; + } else { + audio->ch3.wavedata8[0] = audio->ch3.wavedata8[((audio->ch3.window >> 1) & ~3)]; + audio->ch3.wavedata8[1] = audio->ch3.wavedata8[((audio->ch3.window >> 1) & ~3) + 1]; + audio->ch3.wavedata8[2] = audio->ch3.wavedata8[((audio->ch3.window >> 1) & ~3) + 2]; + audio->ch3.wavedata8[3] = audio->ch3.wavedata8[((audio->ch3.window >> 1) & ~3) + 3]; + } + } + audio->ch3.window = 0; } if (audio->playingCh3) { if (audio->nextEvent == INT_MAX) { audio->eventDiff = 0; } - audio->nextCh3 = audio->eventDiff; - audio->nextEvent = audio->eventDiff; + audio->nextCh3 = audio->eventDiff + audio->p->cpu->cycles + 2 + 2 * (2048 - audio->ch3.rate); + audio->ch3.readable = false; + audio->nextEvent = audio->p->cpu->cycles; if (audio->p) { // TODO: Don't need p - audio->p->cpu->nextEvent = audio->eventDiff; + audio->p->cpu->nextEvent = audio->nextEvent; } } *audio->nr52 &= ~0x0004;@@ -328,10 +343,10 @@ if (audio->ch4.stop && !(audio->frame & 1)) {
--audio->ch4.length; } } - audio->nextEvent = audio->eventDiff; + audio->nextEvent = audio->p->cpu->cycles; if (audio->p) { // TODO: Don't need p - audio->p->cpu->nextEvent = audio->eventDiff; + audio->p->cpu->nextEvent = audio->nextEvent; } } *audio->nr52 &= ~0x0008;@@ -494,8 +509,18 @@ }
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) { - audio->nextCh3 += _updateChannel3(&audio->ch3); + audio->fadeCh3 = audio->nextCh3 + 2; + audio->nextCh3 += _updateChannel3(&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;@@ -741,10 +766,8 @@ ch->sample *= ch->envelope.currentVolume;
return timing; } -static int32_t _updateChannel3(struct GBAudioChannel3* ch) { +static int32_t _updateChannel3(struct GBAudioChannel3* ch, enum GBAudioStyle style) { int i; - int start; - int end; int volume; switch (ch->volume) { case 0:@@ -763,25 +786,42 @@ default:
volume = 3; break; } - if (ch->size) { - start = 7; - end = 0; - } else if (ch->bank) { - start = 7; - end = 4; - } else { - start = 3; - end = 0; - } - uint32_t bitsCarry = ch->wavedata[end] & 0x000000F0; - uint32_t bits; - for (i = start; i >= end; --i) { - bits = ch->wavedata[i] & 0x000000F0; - ch->wavedata[i] = ((ch->wavedata[i] & 0x0F0F0F0F) << 4) | ((ch->wavedata[i] & 0xF0F0F000) >> 12); - ch->wavedata[i] |= bitsCarry << 20; - bitsCarry = bits; + switch (style) { + int start; + int end; + case GB_AUDIO_DMG: + default: + ++ch->window; + ch->window &= 0x1F; + ch->sample = ch->wavedata8[ch->window >> 1]; + if (ch->window & 1) { + ch->sample &= 0xF; + } else { + ch->sample >>= 4; + } + break; + case GB_AUDIO_GBA: + if (ch->size) { + start = 7; + end = 0; + } else if (ch->bank) { + start = 7; + end = 4; + } else { + start = 3; + end = 0; + } + uint32_t bitsCarry = ch->wavedata32[end] & 0x000000F0; + uint32_t bits; + for (i = start; i >= end; --i) { + bits = ch->wavedata32[i] & 0x000000F0; + ch->wavedata32[i] = ((ch->wavedata32[i] & 0x0F0F0F0F) << 4) | ((ch->wavedata32[i] & 0xF0F0F000) >> 12); + ch->wavedata32[i] |= bitsCarry << 20; + bitsCarry = bits; + } + ch->sample = bitsCarry >> 4; + break; } - ch->sample = bitsCarry >> 4; ch->sample -= 8; ch->sample *= volume * 4; return 2 * (2048 - ch->rate);
@@ -119,7 +119,12 @@
int rate; bool stop; - uint32_t wavedata[8]; + int window; + bool readable; + union { + uint32_t wavedata32[8]; + uint8_t wavedata8[16]; + }; int8_t sample; };@@ -136,6 +141,13 @@ uint32_t lfsr;
int8_t sample; }; +enum GBAudioStyle { + GB_AUDIO_DMG, + GB_AUDIO_CGB, + GB_AUDIO_AGB, // GB in GBA + GB_AUDIO_GBA, // GBA PSG +}; + struct GBAudio { struct GB* p; struct GBAudioChannel1 ch1;@@ -174,10 +186,12 @@ 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; bool enable;@@ -186,7 +200,7 @@ bool forceDisableCh[4];
int masterVolume; }; -void GBAudioInit(struct GBAudio* audio, size_t samples, uint8_t* nr52); +void GBAudioInit(struct GBAudio* audio, size_t samples, uint8_t* nr52, enum GBAudioStyle style); void GBAudioDeinit(struct GBAudio* audio); void GBAudioReset(struct GBAudio* audio);
@@ -45,7 +45,7 @@ gb->video.p = gb;
GBVideoInit(&gb->video); gb->audio.p = gb; - GBAudioInit(&gb->audio, 2048, &gb->memory.io[REG_NR52]); // TODO: Remove magic constant + GBAudioInit(&gb->audio, 2048, &gb->memory.io[REG_NR52], GB_AUDIO_DMG); // TODO: Remove magic constant gb->timer.p = gb;
@@ -243,7 +243,9 @@ case REG_WAVE_C:
case REG_WAVE_D: case REG_WAVE_E: case REG_WAVE_F: - ((uint8_t*) gb->audio.ch3.wavedata)[address - REG_WAVE_0] = value; // TODO: Big endian + if (!gb->audio.playingCh3 || gb->audio.ch3.readable) { + gb->audio.ch3.wavedata8[address - REG_WAVE_0] = value; + } break; case REG_JOYP: case REG_TIMA:@@ -318,6 +320,32 @@ // TODO
break; case REG_IE: return gb->memory.ie; + case REG_WAVE_0: + case REG_WAVE_1: + case REG_WAVE_2: + case REG_WAVE_3: + case REG_WAVE_4: + case REG_WAVE_5: + case REG_WAVE_6: + case REG_WAVE_7: + case REG_WAVE_8: + case REG_WAVE_9: + case REG_WAVE_A: + case REG_WAVE_B: + case REG_WAVE_C: + case REG_WAVE_D: + case REG_WAVE_E: + case REG_WAVE_F: + if (gb->audio.playingCh3) { + if (gb->audio.ch3.readable) { + return gb->audio.ch3.wavedata8[gb->audio.ch3.window >> 1]; + } else { + return 0xFF; + } + } else { + return gb->audio.ch3.wavedata8[address - REG_WAVE_0]; + } + break; case REG_IF: case REG_NR10: case REG_NR11:@@ -336,22 +364,6 @@ case REG_NR44:
case REG_NR50: case REG_NR51: case REG_NR52: - case REG_WAVE_0: - case REG_WAVE_1: - case REG_WAVE_2: - case REG_WAVE_3: - case REG_WAVE_4: - case REG_WAVE_5: - case REG_WAVE_6: - case REG_WAVE_7: - case REG_WAVE_8: - case REG_WAVE_9: - case REG_WAVE_A: - case REG_WAVE_B: - case REG_WAVE_C: - case REG_WAVE_D: - case REG_WAVE_E: - case REG_WAVE_F: case REG_DIV: case REG_TIMA: case REG_TMA:
@@ -28,7 +28,7 @@ uint8_t* nr52 = (uint8_t*) &audio->p->memory.io[REG_SOUNDCNT_X >> 1];
#ifdef __BIG_ENDIAN__ ++n52; #endif - GBAudioInit(&audio->psg, 0, nr52); + GBAudioInit(&audio->psg, 0, nr52, GB_AUDIO_GBA); audio->samples = samples; audio->psg.clockRate = GBA_ARM7TDMI_FREQUENCY; // Guess too large; we hang producing extra samples if we guess too low@@ -209,7 +209,7 @@ audio->soundbias = value;
} void GBAAudioWriteWaveRAM(struct GBAAudio* audio, int address, uint32_t value) { - audio->psg.ch3.wavedata[address | (!audio->psg.ch3.bank * 4)] = value; + audio->psg.ch3.wavedata32[address | (!audio->psg.ch3.bank * 4)] = value; } void GBAAudioWriteFIFO(struct GBAAudio* audio, int address, uint32_t value) {@@ -353,7 +353,7 @@ ch2Flags = GBASerializedAudioEnvelopeSetNextStep(ch2Flags, audio->psg.ch2.envelope.nextStep);
STORE_32(ch2Flags, 0, &state->audio.ch2.envelope); STORE_32(audio->psg.nextCh2, 0, &state->audio.ch2.nextEvent); - memcpy(state->audio.ch3.wavebanks, audio->psg.ch3.wavedata, sizeof(state->audio.ch3.wavebanks)); + memcpy(state->audio.ch3.wavebanks, audio->psg.ch3.wavedata32, sizeof(state->audio.ch3.wavebanks)); STORE_16(audio->psg.ch3.length, 0, &state->audio.ch3.length); STORE_32(audio->psg.nextCh3, 0, &state->audio.ch3.nextEvent);@@ -407,7 +407,7 @@ audio->psg.ch2.envelope.nextStep = GBASerializedAudioEnvelopeGetNextStep(ch2Flags);
LOAD_32(audio->psg.nextCh2, 0, &state->audio.ch2.nextEvent); // TODO: Big endian? - memcpy(audio->psg.ch3.wavedata, state->audio.ch3.wavebanks, sizeof(audio->psg.ch3.wavedata)); + memcpy(audio->psg.ch3.wavedata32, state->audio.ch3.wavebanks, sizeof(audio->psg.ch3.wavedata32)); LOAD_16(audio->psg.ch3.length, 0, &state->audio.ch3.length); LOAD_32(audio->psg.nextCh3, 0, &state->audio.ch3.nextEvent);
@@ -87,7 +87,7 @@ cpu->memory.store16(cpu, BASE_IO | REG_SOUNDCNT_LO, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_SOUNDCNT_HI, 0, 0); cpu->memory.store16(cpu, BASE_IO | REG_SOUNDCNT_X, 0, 0); cpu->memory.store16(cpu, BASE_IO | REG_SOUNDBIAS, 0x200, 0); - memset(gba->audio.psg.ch3.wavedata, 0, sizeof(gba->audio.psg.ch3.wavedata)); + memset(gba->audio.psg.ch3.wavedata32, 0, sizeof(gba->audio.psg.ch3.wavedata32)); } if (registers & 0x80) { cpu->memory.store16(cpu, BASE_IO | 0x00, 0, 0);