From 81c0a8ae615e6291de7d5a56466ba67c174dfd06 Mon Sep 17 00:00:00 2001 From: Gregory Heskett Date: Mon, 5 Jun 2023 18:09:03 -0400 Subject: [PATCH] Improve BETTER_REVERB runtime by about 15-20% overall (#619) Refactored everything to be more memory efficient and execute fewer function calls than prior Also accidentally discovered a significant bug with the audio modes always being forced to stereo so this is definitely 2.1 scope lmao --- src/audio/data.c | 5 +- src/audio/external.c | 1 - src/audio/heap.c | 4 +- src/audio/internal.h | 4 +- src/audio/load.c | 3 +- src/audio/synthesis.c | 359 ++++++++++++++++-------------------------- src/audio/synthesis.h | 9 +- 7 files changed, 152 insertions(+), 233 deletions(-) diff --git a/src/audio/data.c b/src/audio/data.c index 97c843cf..105dda6b 100644 --- a/src/audio/data.c +++ b/src/audio/data.c @@ -65,8 +65,7 @@ u32 delaysArr[][NUM_ALLPASS] = { }; // Each entry represents an array of multipliers applied to the final output of each group of 3 filters. -// These values are u8s in spirit, but are set as s32 values to slightly increase performance during calculations. -s32 reverbMultsArr[][NUM_ALLPASS / 3] = { +u8 reverbMultsArr[][NUM_ALLPASS / 3] = { /* 0 */ {0x00, 0x00, 0x00, 0x00}, /* 1 */ {0xD7, 0x6F, 0x36, 0x22}, /* 2 */ {0xCF, 0x73, 0x38, 0x1F}, @@ -125,7 +124,7 @@ struct BetterReverbSettings gBetterReverbSettings[] = { .reverbMultsL = reverbMultsArr[1], // Ignored with lightweight settings .reverbMultsR = reverbMultsArr[2], // Ignored with lightweight settings }, - { /* Preset 2 - Sample Emulator Configuration (RCVI Hack Only) */ + { /* Preset 2 - Sample Emulator Configuration (RCVI Hack or Emulator CPU Overclocking Required!) */ .useLightweightSettings = FALSE, .downsampleRate = 1, .isMono = FALSE, diff --git a/src/audio/external.c b/src/audio/external.c index 3502da1e..e5a95217 100644 --- a/src/audio/external.c +++ b/src/audio/external.c @@ -1985,7 +1985,6 @@ void sound_init(void) { sLowerBackgroundMusicVolume = FALSE; sSoundBanksThatLowerBackgroundMusic = 0; sCurrentBackgroundMusicSeqId = 0xff; - gSoundMode = SOUND_MODE_STEREO; sBackgroundMusicQueueSize = 0; sBackgroundMusicMaxTargetVolume = TARGET_VOLUME_UNSET; D_80332120 = 0; diff --git a/src/audio/heap.c b/src/audio/heap.c index 3a0703fc..2239bb2b 100644 --- a/src/audio/heap.c +++ b/src/audio/heap.c @@ -1058,8 +1058,8 @@ void init_reverb_us(s32 presetId) { betterReverbWindowsSize = betterReverbPreset->windowSize; betterReverbRevIndex = betterReverbPreset->reverbIndex; betterReverbGainIndex = betterReverbPreset->gainIndex; - gReverbMultsL = betterReverbPreset->reverbMultsL; - gReverbMultsR = betterReverbPreset->reverbMultsR; + gReverbMults[SYNTH_CHANNEL_LEFT] = betterReverbPreset->reverbMultsL; + gReverbMults[SYNTH_CHANNEL_RIGHT] = betterReverbPreset->reverbMultsR; if (betterReverbDownsampleRate <= 0) { toggleBetterReverb = FALSE; diff --git a/src/audio/internal.h b/src/audio/internal.h index 0a612f61..4f110c08 100644 --- a/src/audio/internal.h +++ b/src/audio/internal.h @@ -744,8 +744,8 @@ struct BetterReverbSettings { u8 reverbIndex; u32 *delaysL; u32 *delaysR; - s32 *reverbMultsL; - s32 *reverbMultsR; + u8 *reverbMultsL; + u8 *reverbMultsR; }; #endif diff --git a/src/audio/load.c b/src/audio/load.c index 7fbb0f77..295490e9 100644 --- a/src/audio/load.c +++ b/src/audio/load.c @@ -86,7 +86,7 @@ s16 gTempoInternalToExternal; s8 gAudioUpdatesPerFrame; #endif -s8 gSoundMode; +s8 gSoundMode = SOUND_MODE_STEREO; #if defined(VERSION_EU) s8 gAudioUpdatesPerFrame; @@ -855,7 +855,6 @@ void audio_init() { gAudioFrameCount = 0; gAudioTaskIndex = 0; gCurrAiBufferIndex = 0; - gSoundMode = 0; gAudioTask = NULL; gAudioTasks[0].task.t.data_size = 0; gAudioTasks[1].task.t.data_size = 0; diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index b932b30a..7e946e37 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -45,23 +45,17 @@ u8 toggleBetterReverb = FALSE; u8 betterReverbLightweight = FALSE; u8 monoReverb; s8 betterReverbDownsampleRate; -static u8 reverbMultsL[NUM_ALLPASS / 3] = {0}; -static u8 reverbMultsR[NUM_ALLPASS / 3] = {0}; -static s32 allpassIdxL[NUM_ALLPASS] = {0}; -static s32 allpassIdxR[NUM_ALLPASS] = {0}; -static s32 delaysL[NUM_ALLPASS] = {0}; -static s32 delaysR[NUM_ALLPASS] = {0}; -static s32 **delayBufsL; -static s32 **delayBufsR; -static s32 lastDelayLightL; -static s32 lastDelayLightR; +static s32 reverbMults[SYNTH_CHANNEL_STEREO_COUNT][NUM_ALLPASS / 3] = {0}; +static s32 allpassIdx[SYNTH_CHANNEL_STEREO_COUNT][NUM_ALLPASS] = {0}; +static s32 betterReverbDelays[SYNTH_CHANNEL_STEREO_COUNT][NUM_ALLPASS] = {0}; +static s32 **delayBufs[SYNTH_CHANNEL_STEREO_COUNT]; +static s32 lastDelayLight[SYNTH_CHANNEL_STEREO_COUNT]; +u8 *gReverbMults[SYNTH_CHANNEL_STEREO_COUNT]; s32 reverbLastFilterIndex; s32 reverbFilterCount; s32 betterReverbWindowsSize; s32 betterReverbRevIndex; // This one is okay to adjust whenever s32 betterReverbGainIndex; // This one is okay to adjust whenever -s32 *gReverbMultsL; -s32 *gReverbMultsR; #endif @@ -97,149 +91,97 @@ u8 sAudioSynthesisPad[0x20]; #endif #ifdef BETTER_REVERB -static void reverb_samples(s16 *outSampleL, s16 *outSampleR, s32 inSampleL, s32 inSampleR) { - s32 *curDelaySampleL; - s32 *curDelaySampleR; - s32 historySampleL; - s32 historySampleR; - s32 outTmpL = 0; - s32 outTmpR = 0; - s32 tmpCarryoverL = ((delayBufsL[reverbLastFilterIndex][allpassIdxL[reverbLastFilterIndex]] * betterReverbRevIndex) >> 8) + inSampleL; - s32 tmpCarryoverR = ((delayBufsR[reverbLastFilterIndex][allpassIdxR[reverbLastFilterIndex]] * betterReverbRevIndex) >> 8) + inSampleR; - s32 i = 0; - s32 j = 0; - s32 k = 0; +static void reverb_samples(s16 *start, s16 *end, s16 *downsampleBuffer, s32 channel) { + s32 *curDelaySample; + s32 historySample; + s32 tmpCarryover; + s32 outSampleTotal; + s32 i; + s32 j; + s32 k; - for (; i <= reverbLastFilterIndex; ++i, ++j) { - curDelaySampleL = &delayBufsL[i][allpassIdxL[i]]; - curDelaySampleR = &delayBufsR[i][allpassIdxR[i]]; - historySampleL = *curDelaySampleL; - historySampleR = *curDelaySampleR; + s32 downsampleIncrement = gReverbDownsampleRate; + s32 **delayBufsLocal = delayBufs[channel]; + s32 *delaysLocal = betterReverbDelays[channel]; + s32 *reverbMultsLocal = reverbMults[channel]; + s32 *allpassIdxLocal = allpassIdx[channel]; - if (j == 2) { - j = -1; - outTmpL += (historySampleL * reverbMultsL[k ]) >> 8; - outTmpR += (historySampleR * reverbMultsR[k++]) >> 8; - *curDelaySampleL = tmpCarryoverL; - *curDelaySampleR = tmpCarryoverR; - if (i != reverbLastFilterIndex) { - tmpCarryoverL = ((historySampleL * betterReverbRevIndex) >> 8)/* + inSampleL*/; - tmpCarryoverR = ((historySampleR * betterReverbRevIndex) >> 8)/* + inSampleR*/; + s32 lastFilterIndex = reverbLastFilterIndex; + s32 revIndex = betterReverbRevIndex; + s32 gainIndex = betterReverbGainIndex; + + // Set j outside of the loop only. Because we're forcing filter count to always be a multiple of 3, we can count on j always being 0 when exiting the second for loop. + j = 0; + + for (; start < end; start++, downsampleBuffer += downsampleIncrement) { + tmpCarryover = ((delayBufsLocal[lastFilterIndex][allpassIdxLocal[lastFilterIndex]] * revIndex) >> 8) + *downsampleBuffer; + outSampleTotal = 0; + i = 0; + k = 0; + + for (; i <= lastFilterIndex; ++i, ++j) { + curDelaySample = &delayBufsLocal[i][allpassIdxLocal[i]]; + historySample = *curDelaySample; + + if (j == 2) { + j = -1; + outSampleTotal += ((historySample * reverbMultsLocal[k++]) >> 8); + *curDelaySample = tmpCarryover; + if (i != lastFilterIndex) + tmpCarryover = ((historySample * revIndex) >> 8)/* + *downsampleBuffer*/; + } else { + *curDelaySample = ((historySample * (-gainIndex)) >> 8) + tmpCarryover; + tmpCarryover = ((*curDelaySample * gainIndex) >> 8) + historySample; } - } else { - *curDelaySampleL = ((historySampleL * (-betterReverbGainIndex)) >> 8) + tmpCarryoverL; - *curDelaySampleR = ((historySampleR * (-betterReverbGainIndex)) >> 8) + tmpCarryoverR; - tmpCarryoverL = ((*curDelaySampleL * betterReverbGainIndex) >> 8) + historySampleL; - tmpCarryoverR = ((*curDelaySampleR * betterReverbGainIndex) >> 8) + historySampleR; + + if (++allpassIdxLocal[i] == delaysLocal[i]) allpassIdxLocal[i] = 0; } - if (++allpassIdxL[i] == delaysL[i]) allpassIdxL[i] = 0; - if (++allpassIdxR[i] == delaysR[i]) allpassIdxR[i] = 0; + *start = CLAMP_S16(outSampleTotal); } - - *outSampleL = CLAMP_S16(outTmpL); - *outSampleR = CLAMP_S16(outTmpR); } -static void reverb_mono_sample(s16 *outSample, s32 inSample) { - s32 *curDelaySample; - s32 historySample; - s32 outTmp = 0; - s32 tmpCarryover = ((delayBufsL[reverbLastFilterIndex][allpassIdxL[reverbLastFilterIndex]] * betterReverbRevIndex) >> 8) + inSample; - s32 i = 0; - s32 j = 0; - s32 k = 0; - - for (; i <= reverbLastFilterIndex; ++i, ++j) { - curDelaySample = &delayBufsL[i][allpassIdxL[i]]; - historySample = *curDelaySample; - - if (j == 2) { - j = -1; - outTmp += ((historySample * reverbMultsL[k++]) >> 8); - *curDelaySample = tmpCarryover; - if (i != reverbLastFilterIndex) - tmpCarryover = ((historySample * betterReverbRevIndex) >> 8)/* + inSample*/; - } else { - *curDelaySample = ((historySample * (-betterReverbGainIndex)) >> 8) + tmpCarryover; - tmpCarryover = ((*curDelaySample * betterReverbGainIndex) >> 8) + historySample; - } - - if (++allpassIdxL[i] == delaysL[i]) allpassIdxL[i] = 0; - } - - *outSample = CLAMP_S16(outTmp); -} - -// Light reverb processing functions! #define FILTERS_MINUS_1 (BETTER_REVERB_FILTER_COUNT_LIGHT - 1) -static void reverb_samples_light(s16 *outSampleL, s16 *outSampleR, s32 inSampleL, s32 inSampleR) { - s32 *curDelaySampleL; - s32 *curDelaySampleR; - s32 historySampleL; - s32 historySampleR; - s32 tmpCarryoverL = (((delayBufsL[FILTERS_MINUS_1][allpassIdxL[FILTERS_MINUS_1]] * BETTER_REVERB_REVERB_INDEX_LIGHT) >> 8) + inSampleL); - s32 tmpCarryoverR = (((delayBufsR[FILTERS_MINUS_1][allpassIdxR[FILTERS_MINUS_1]] * BETTER_REVERB_REVERB_INDEX_LIGHT) >> 8) + inSampleR); - s32 i = 0; - - for (; i < FILTERS_MINUS_1; ++i) { - curDelaySampleL = &delayBufsL[i][allpassIdxL[i]]; - curDelaySampleR = &delayBufsR[i][allpassIdxR[i]]; - historySampleL = *curDelaySampleL; - historySampleR = *curDelaySampleR; - - *curDelaySampleL = (((historySampleL * (-BETTER_REVERB_GAIN_INDEX_LIGHT)) >> 8) + tmpCarryoverL); - *curDelaySampleR = (((historySampleR * (-BETTER_REVERB_GAIN_INDEX_LIGHT)) >> 8) + tmpCarryoverR); - tmpCarryoverL = (((*curDelaySampleL * BETTER_REVERB_GAIN_INDEX_LIGHT) >> 8) + historySampleL); - tmpCarryoverR = (((*curDelaySampleR * BETTER_REVERB_GAIN_INDEX_LIGHT) >> 8) + historySampleR); - - if (++allpassIdxL[i] == delaysL[i]) allpassIdxL[i] = 0; - if (++allpassIdxR[i] == delaysR[i]) allpassIdxR[i] = 0; - } - - curDelaySampleL = &delayBufsL[FILTERS_MINUS_1][allpassIdxL[FILTERS_MINUS_1]]; - curDelaySampleR = &delayBufsR[FILTERS_MINUS_1][allpassIdxR[FILTERS_MINUS_1]]; - historySampleL = ((*curDelaySampleL * BETTER_REVERB_MULTIPLE_LIGHT) >> 8); // outTmpL variable not needed, as there is no sample addition happening here. Not really a history sample though. - historySampleR = ((*curDelaySampleR * BETTER_REVERB_MULTIPLE_LIGHT) >> 8); // outTmpR variable not needed, as there is no sample addition happening here. Not really a history sample though. - *curDelaySampleL = tmpCarryoverL; - *curDelaySampleR = tmpCarryoverR; - - if (++allpassIdxL[FILTERS_MINUS_1] == lastDelayLightL) allpassIdxL[FILTERS_MINUS_1] = 0; - if (++allpassIdxR[FILTERS_MINUS_1] == lastDelayLightR) allpassIdxR[FILTERS_MINUS_1] = 0; - - *outSampleL = CLAMP_S16(historySampleL); - *outSampleR = CLAMP_S16(historySampleR); -} - -static void reverb_mono_sample_light(s16 *outSample, s32 inSample) { +static void reverb_samples_light(s16 *start, s16 *end, s16 *downsampleBuffer, s32 channel) { s32 *curDelaySample; s32 historySample; - s32 tmpCarryover = (((delayBufsL[FILTERS_MINUS_1][allpassIdxL[FILTERS_MINUS_1]] * BETTER_REVERB_REVERB_INDEX_LIGHT) >> 8) + inSample); - s32 i = 0; + s32 tmpCarryover; + s32 i; - for (; i < FILTERS_MINUS_1; ++i) { - curDelaySample = &delayBufsL[i][allpassIdxL[i]]; - historySample = *curDelaySample; + s32 downsampleIncrement = gReverbDownsampleRate; + s32 **delayBufsLocal = delayBufs[channel]; + s32 *delaysLocal = betterReverbDelays[channel]; + s32 *allpassIdxLocal = allpassIdx[channel]; + s32 lastDelayLightLocal = lastDelayLight[channel]; - *curDelaySample = (((historySample * (-BETTER_REVERB_GAIN_INDEX_LIGHT)) >> 8) + tmpCarryover); - tmpCarryover = (((*curDelaySample * BETTER_REVERB_GAIN_INDEX_LIGHT) >> 8) + historySample); + for (; start < end; start++, downsampleBuffer += downsampleIncrement) { + tmpCarryover = (((delayBufsLocal[FILTERS_MINUS_1][allpassIdxLocal[FILTERS_MINUS_1]] * BETTER_REVERB_REVERB_INDEX_LIGHT) >> 8) + *downsampleBuffer); + i = 0; - if (++allpassIdxL[i] == delaysL[i]) allpassIdxL[i] = 0; + for (; i < FILTERS_MINUS_1; ++i) { + curDelaySample = &delayBufsLocal[i][allpassIdxLocal[i]]; + historySample = *curDelaySample; + + *curDelaySample = (((historySample * (-BETTER_REVERB_GAIN_INDEX_LIGHT)) >> 8) + tmpCarryover); + tmpCarryover = (((*curDelaySample * BETTER_REVERB_GAIN_INDEX_LIGHT) >> 8) + historySample); + + if (++allpassIdxLocal[i] == delaysLocal[i]) allpassIdxLocal[i] = 0; + } + + curDelaySample = &delayBufsLocal[FILTERS_MINUS_1][allpassIdxLocal[FILTERS_MINUS_1]]; + historySample = ((*curDelaySample * BETTER_REVERB_MULTIPLE_LIGHT) >> 8); // outSampleTotal variable not needed, as there is no sample addition happening here. Not really a history sample though. + *curDelaySample = tmpCarryover; + + if (++allpassIdxLocal[FILTERS_MINUS_1] == lastDelayLightLocal) allpassIdxLocal[FILTERS_MINUS_1] = 0; + + *start = CLAMP_S16(historySample); } - - curDelaySample = &delayBufsL[FILTERS_MINUS_1][allpassIdxL[FILTERS_MINUS_1]]; - historySample = ((*curDelaySample * BETTER_REVERB_MULTIPLE_LIGHT) >> 8); // outTmp variable not needed, as there is no sample addition happening here. Not really a history sample though. - *curDelaySample = tmpCarryover; - - if (++allpassIdxL[FILTERS_MINUS_1] == lastDelayLightL) allpassIdxL[FILTERS_MINUS_1] = 0; - - *outSample = CLAMP_S16(historySample); } #undef FILTERS_MINUS_1 void initialize_better_reverb_buffers(void) { - delayBufsL = (s32**) soundAlloc(&gBetterReverbPool, BETTER_REVERB_PTR_SIZE); - delayBufsR = &delayBufsL[NUM_ALLPASS]; + delayBufs[SYNTH_CHANNEL_LEFT] = (s32**) soundAlloc(&gBetterReverbPool, BETTER_REVERB_PTR_SIZE); + delayBufs[SYNTH_CHANNEL_RIGHT] = &delayBufs[SYNTH_CHANNEL_LEFT][NUM_ALLPASS]; } void set_better_reverb_buffers(u32 *inputDelaysL, u32 *inputDelaysR) { @@ -259,21 +201,20 @@ void set_better_reverb_buffers(u32 *inputDelaysL, u32 *inputDelaysR) { // NOTE: Using filterCount over NUM_ALLPASS will report less memory usage with fewer filters, but poses an additional // risk to anybody testing on console with performance compromises, as emulator can be easily overlooked. for (i = 0; i < filterCount; ++i) { - delaysL[i] = (s32) (inputDelaysL[i] / gReverbDownsampleRate); - delaysR[i] = (s32) (inputDelaysR[i] / gReverbDownsampleRate); - delayBufsL[i] = soundAlloc(&gBetterReverbPool, delaysL[i] * sizeof(s32)); - bufOffset += delaysL[i]; - delayBufsR[i] = soundAlloc(&gBetterReverbPool, delaysR[i] * sizeof(s32)); - bufOffset += delaysR[i]; + betterReverbDelays[SYNTH_CHANNEL_LEFT][i] = (s32) (inputDelaysL[i] / gReverbDownsampleRate); + betterReverbDelays[SYNTH_CHANNEL_RIGHT][i] = (s32) (inputDelaysR[i] / gReverbDownsampleRate); + delayBufs[SYNTH_CHANNEL_LEFT][i] = soundAlloc(&gBetterReverbPool, betterReverbDelays[SYNTH_CHANNEL_LEFT][i] * sizeof(s32)); + bufOffset += betterReverbDelays[SYNTH_CHANNEL_LEFT][i]; + delayBufs[SYNTH_CHANNEL_RIGHT][i] = soundAlloc(&gBetterReverbPool, betterReverbDelays[SYNTH_CHANNEL_RIGHT][i] * sizeof(s32)); + bufOffset += betterReverbDelays[SYNTH_CHANNEL_RIGHT][i]; } aggress(bufOffset * sizeof(s32) <= BETTER_REVERB_SIZE - ALIGN16(BETTER_REVERB_PTR_SIZE), "BETTER_REVERB_SIZE is too small for this preset!"); - lastDelayLightL = delaysL[filterCount-1]; - lastDelayLightR = delaysR[filterCount-1]; + lastDelayLight[SYNTH_CHANNEL_LEFT] = betterReverbDelays[SYNTH_CHANNEL_LEFT][filterCount-1]; + lastDelayLight[SYNTH_CHANNEL_RIGHT] = betterReverbDelays[SYNTH_CHANNEL_RIGHT][filterCount-1]; - bzero(allpassIdxL, sizeof(allpassIdxL)); - bzero(allpassIdxR, sizeof(allpassIdxR)); + bzero(allpassIdx, sizeof(allpassIdx)); } #endif @@ -376,81 +317,57 @@ void prepare_reverb_ring_buffer(s32 chunkLen, u32 updateIndex) { } #ifdef BETTER_REVERB else if (toggleBetterReverb) { - s32 firstLoopMax; - s32 secondLoopMax; + s32 loopCounts[2]; + + s16 *betterReverbDownampleBuffers[SYNTH_CHANNEL_STEREO_COUNT][ARRAY_COUNT(loopCounts)]; // StartA and StartB for both channels + s16 *betterReverbSampleBuffers[SYNTH_CHANNEL_STEREO_COUNT][ARRAY_COUNT(loopCounts)]; // Output reverb buffers + void (*reverbFunc)(s16*, s16*, s16*, s32) = betterReverbLightweight ? reverb_samples_light : reverb_samples; // Function pointers for both heavy and lightweight reverb functions + item = &gSynthesisReverb.items[gSynthesisReverb.curFrame][updateIndex]; - dstPos = item->startPos; - firstLoopMax = ((item->lengthA / 2) + item->startPos); - secondLoopMax = (item->lengthB / 2); + loopCounts[0] = item->lengthA / 2; + loopCounts[1] = item->lengthB / 2; - // This block could be simplified probably with function pointers or rewriting reverb_mono_sample to support L and R individually. Another idea would be to process more than one sample at a time - // for each function call. In practice with HackerSM64, these ideas either aren't super trivial to implement efficiently or don't benefit performance at all, so I'm leaving this as it is for now. - if (betterReverbLightweight) { - if (gSoundMode == SOUND_MODE_MONO || monoReverb) { - if (gReverbDownsampleRate != 1) { - osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH); - for (srcPos = 0; dstPos < firstLoopMax; srcPos += gReverbDownsampleRate, dstPos++) - reverb_mono_sample_light(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) item->toDownsampleLeft[srcPos] + (s32) item->toDownsampleRight[srcPos]) / 2); - bcopy(&gSynthesisReverb.ringBuffer.left[item->startPos], &gSynthesisReverb.ringBuffer.right[item->startPos], (dstPos - item->startPos) * sizeof(s16)); + if (gReverbDownsampleRate != 1) { + // NOTE: / HACKERSM64 TODO: Commenting this check seems to improve runtime by about 100 microseconds (per 30fps frame), + // but idk enough about why it was added here in vanilla to comfortably remove it. Is it supposed to act as an + // optimization (that isn't actually an optimization) or is it a safety measure since it's loaded from the RSP? + osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH); + } + + betterReverbSampleBuffers[SYNTH_CHANNEL_LEFT][0] = &gSynthesisReverb.ringBuffer.left[item->startPos]; + betterReverbSampleBuffers[SYNTH_CHANNEL_LEFT][1] = &gSynthesisReverb.ringBuffer.left[0]; + betterReverbSampleBuffers[SYNTH_CHANNEL_RIGHT][0] = &gSynthesisReverb.ringBuffer.right[item->startPos]; + betterReverbSampleBuffers[SYNTH_CHANNEL_RIGHT][1] = &gSynthesisReverb.ringBuffer.right[0]; + if (gReverbDownsampleRate > 1) { + betterReverbDownampleBuffers[SYNTH_CHANNEL_LEFT][0] = &item->toDownsampleLeft[0]; + betterReverbDownampleBuffers[SYNTH_CHANNEL_LEFT][1] = &item->toDownsampleLeft[loopCounts[0] * gReverbDownsampleRate]; + betterReverbDownampleBuffers[SYNTH_CHANNEL_RIGHT][0] = &item->toDownsampleRight[0]; + betterReverbDownampleBuffers[SYNTH_CHANNEL_RIGHT][1] = &item->toDownsampleRight[loopCounts[0] * gReverbDownsampleRate]; + } else { + betterReverbDownampleBuffers[SYNTH_CHANNEL_LEFT][0] = betterReverbSampleBuffers[SYNTH_CHANNEL_LEFT][0]; + betterReverbDownampleBuffers[SYNTH_CHANNEL_LEFT][1] = betterReverbSampleBuffers[SYNTH_CHANNEL_LEFT][1]; + betterReverbDownampleBuffers[SYNTH_CHANNEL_RIGHT][0] = betterReverbSampleBuffers[SYNTH_CHANNEL_RIGHT][0]; + betterReverbDownampleBuffers[SYNTH_CHANNEL_RIGHT][1] = betterReverbSampleBuffers[SYNTH_CHANNEL_RIGHT][1]; + } - for (dstPos = 0; dstPos < secondLoopMax; srcPos += gReverbDownsampleRate, dstPos++) - reverb_mono_sample_light(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) item->toDownsampleLeft[srcPos] + (s32) item->toDownsampleRight[srcPos]) / 2); - bcopy(gSynthesisReverb.ringBuffer.left, gSynthesisReverb.ringBuffer.right, dstPos * sizeof(s16)); - } else { - for (; dstPos < firstLoopMax; dstPos++) - reverb_mono_sample_light(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) gSynthesisReverb.ringBuffer.left[dstPos] + (s32) gSynthesisReverb.ringBuffer.right[dstPos]) / 2); - bcopy(&gSynthesisReverb.ringBuffer.left[item->startPos], &gSynthesisReverb.ringBuffer.right[item->startPos], (dstPos - item->startPos) * sizeof(s16)); - - for (dstPos = 0; dstPos < secondLoopMax; dstPos++) - reverb_mono_sample_light(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) gSynthesisReverb.ringBuffer.left[dstPos] + (s32) gSynthesisReverb.ringBuffer.right[dstPos]) / 2); - bcopy(gSynthesisReverb.ringBuffer.left, gSynthesisReverb.ringBuffer.right, dstPos * sizeof(s16)); - } - } else { - if (gReverbDownsampleRate != 1) { - osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH); - for (srcPos = 0; dstPos < firstLoopMax; srcPos += gReverbDownsampleRate, dstPos++) - reverb_samples_light(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], item->toDownsampleLeft[srcPos], item->toDownsampleRight[srcPos]); - for (dstPos = 0; dstPos < secondLoopMax; srcPos += gReverbDownsampleRate, dstPos++) - reverb_samples_light(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], item->toDownsampleLeft[srcPos], item->toDownsampleRight[srcPos]); - } else { - for (; dstPos < firstLoopMax; dstPos++) - reverb_samples_light(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], gSynthesisReverb.ringBuffer.left[dstPos], gSynthesisReverb.ringBuffer.right[dstPos]); - for (dstPos = 0; dstPos < secondLoopMax; dstPos++) - reverb_samples_light(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], gSynthesisReverb.ringBuffer.left[dstPos], gSynthesisReverb.ringBuffer.right[dstPos]); + if (gSoundMode == SOUND_MODE_MONO || monoReverb) { + for (srcPos = 0; srcPos < ARRAY_COUNT(loopCounts); srcPos++) { // LengthA and LengthB processing + s16 *downsampleBufferL = betterReverbDownampleBuffers[SYNTH_CHANNEL_LEFT][srcPos]; + s16 *downsampleBufferR = betterReverbDownampleBuffers[SYNTH_CHANNEL_RIGHT][srcPos]; + for (dstPos = 0; dstPos < loopCounts[srcPos]; dstPos += gReverbDownsampleRate) { // Individual sample processing + downsampleBufferL[dstPos] = ((s32) downsampleBufferL[dstPos] + (s32) downsampleBufferR[dstPos]) >> 1; // Merge stereo samples into left channel } } + for (srcPos = 0; srcPos < ARRAY_COUNT(loopCounts); srcPos++) { // LengthA and LengthB processing + // Call core reverb processing function, either reverb_samples() or reverb_samples_light() + (*reverbFunc)(betterReverbSampleBuffers[SYNTH_CHANNEL_LEFT][srcPos], betterReverbSampleBuffers[SYNTH_CHANNEL_LEFT][srcPos] + loopCounts[srcPos], betterReverbDownampleBuffers[SYNTH_CHANNEL_LEFT][srcPos], SYNTH_CHANNEL_LEFT); + bcopy(betterReverbSampleBuffers[SYNTH_CHANNEL_LEFT][srcPos], betterReverbSampleBuffers[SYNTH_CHANNEL_RIGHT][srcPos], loopCounts[srcPos] * sizeof(s16)); + } } else { - if (gSoundMode == SOUND_MODE_MONO || monoReverb) { - if (gReverbDownsampleRate != 1) { - osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH); - for (srcPos = 0; dstPos < firstLoopMax; srcPos += gReverbDownsampleRate, dstPos++) - reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) item->toDownsampleLeft[srcPos] + (s32) item->toDownsampleRight[srcPos]) / 2); - bcopy(&gSynthesisReverb.ringBuffer.left[item->startPos], &gSynthesisReverb.ringBuffer.right[item->startPos], (dstPos - item->startPos) * sizeof(s16)); - - for (dstPos = 0; dstPos < secondLoopMax; srcPos += gReverbDownsampleRate, dstPos++) - reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) item->toDownsampleLeft[srcPos] + (s32) item->toDownsampleRight[srcPos]) / 2); - bcopy(gSynthesisReverb.ringBuffer.left, gSynthesisReverb.ringBuffer.right, dstPos * sizeof(s16)); - } else { - for (; dstPos < firstLoopMax; dstPos++) - reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) gSynthesisReverb.ringBuffer.left[dstPos] + (s32) gSynthesisReverb.ringBuffer.right[dstPos]) / 2); - bcopy(&gSynthesisReverb.ringBuffer.left[item->startPos], &gSynthesisReverb.ringBuffer.right[item->startPos], (dstPos - item->startPos) * sizeof(s16)); - - for (dstPos = 0; dstPos < secondLoopMax; dstPos++) - reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) gSynthesisReverb.ringBuffer.left[dstPos] + (s32) gSynthesisReverb.ringBuffer.right[dstPos]) / 2); - bcopy(gSynthesisReverb.ringBuffer.left, gSynthesisReverb.ringBuffer.right, dstPos * sizeof(s16)); - } - } else { - if (gReverbDownsampleRate != 1) { - osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH); - for (srcPos = 0; dstPos < firstLoopMax; srcPos += gReverbDownsampleRate, dstPos++) - reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], item->toDownsampleLeft[srcPos], item->toDownsampleRight[srcPos]); - for (dstPos = 0; dstPos < secondLoopMax; srcPos += gReverbDownsampleRate, dstPos++) - reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], item->toDownsampleLeft[srcPos], item->toDownsampleRight[srcPos]); - } else { - for (; dstPos < firstLoopMax; dstPos++) - reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], gSynthesisReverb.ringBuffer.left[dstPos], gSynthesisReverb.ringBuffer.right[dstPos]); - for (dstPos = 0; dstPos < secondLoopMax; dstPos++) - reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], gSynthesisReverb.ringBuffer.left[dstPos], gSynthesisReverb.ringBuffer.right[dstPos]); + for (dstPos = 0; dstPos < SYNTH_CHANNEL_STEREO_COUNT; dstPos++) { // left and right channels + for (srcPos = 0; srcPos < ARRAY_COUNT(loopCounts); srcPos++) { // LengthA and LengthB processing + // Call core reverb processing function, either reverb_samples() or reverb_samples_light() + (*reverbFunc)(betterReverbSampleBuffers[dstPos][srcPos], betterReverbSampleBuffers[dstPos][srcPos] + loopCounts[srcPos], betterReverbDownampleBuffers[dstPos][srcPos], dstPos); } } } @@ -601,18 +518,18 @@ u64 *synthesis_execute(u64 *cmdBuf, s32 *writtenCmds, s16 *aiBuf, s32 bufLen) { reverbLastFilterIndex = reverbFilterCount - 1; - // Update reverbMultsL every audio frame just in case gReverbMults is ever to change. - if (gReverbMultsL != NULL && gReverbMultsR != NULL) { + // Update reverbMults every audio frame just in case gReverbMults is ever to change. + if (gReverbMults[SYNTH_CHANNEL_LEFT] != NULL && gReverbMults[SYNTH_CHANNEL_RIGHT] != NULL) { for (i = 0; i < filterCountDiv3; ++i) { - reverbMultsL[i] = gReverbMultsL[i]; - reverbMultsR[i] = gReverbMultsR[i]; + reverbMults[SYNTH_CHANNEL_LEFT][i] = gReverbMults[SYNTH_CHANNEL_LEFT][i]; + reverbMults[SYNTH_CHANNEL_RIGHT][i] = gReverbMults[SYNTH_CHANNEL_RIGHT][i]; } } // If there's only one reverb multiplier set, adjust these to match so one channel doesn't end up potentially overpowering the other. if (filterCountDiv3 == 1) { - reverbMultsL[0] = (reverbMultsR[0] + reverbMultsL[0]) / 2; - reverbMultsR[0] = reverbMultsL[0]; + reverbMults[SYNTH_CHANNEL_LEFT][0] = (reverbMults[SYNTH_CHANNEL_RIGHT][0] + reverbMults[SYNTH_CHANNEL_LEFT][0]) / 2; + reverbMults[SYNTH_CHANNEL_RIGHT][0] = reverbMults[SYNTH_CHANNEL_LEFT][0]; } #endif diff --git a/src/audio/synthesis.h b/src/audio/synthesis.h index c6564e3c..075914d5 100644 --- a/src/audio/synthesis.h +++ b/src/audio/synthesis.h @@ -17,6 +17,12 @@ #define MAX_UPDATES_PER_FRAME 4 #endif +enum ChannelIndexes { + SYNTH_CHANNEL_LEFT, + SYNTH_CHANNEL_RIGHT, + SYNTH_CHANNEL_STEREO_COUNT, +}; + #ifdef BETTER_REVERB #define REVERB_WINDOW_SIZE_MAX 0x2000 @@ -56,8 +62,7 @@ extern s32 reverbFilterCount; extern s32 betterReverbWindowsSize; extern s32 betterReverbRevIndex; extern s32 betterReverbGainIndex; -extern s32 *gReverbMultsL; -extern s32 *gReverbMultsR; +extern u8 *gReverbMults[SYNTH_CHANNEL_STEREO_COUNT]; /* ------------ BETTER REVERB EXTERNED FUNCTIONS ------------ */