From 5ef0e8c9f6b6c49150e2e1e392b064b1e54ad782 Mon Sep 17 00:00:00 2001 From: Gregory Heskett Date: Fri, 17 Jun 2022 01:05:40 -0400 Subject: [PATCH] Bugfix, refactor, and slightly improve BETTER_REVERB runtime (#391) * Buxfix, refactor, and slightly improve BETTER_REVERB runtime * Update BETTER_REVERB defaults and add some more customizability * Improve BETTER_REVERB runtime even further * Rename some reverb variables to make more sense in context --- include/config/config_audio.h | 10 +- src/audio/heap.c | 18 +--- src/audio/heap.h | 3 + src/audio/synthesis.c | 169 ++++++++++++++++++++++------------ src/audio/synthesis.h | 18 ++-- 5 files changed, 132 insertions(+), 86 deletions(-) diff --git a/include/config/config_audio.h b/include/config/config_audio.h index a65b1818..e4136c25 100644 --- a/include/config/config_audio.h +++ b/include/config/config_audio.h @@ -9,6 +9,11 @@ */ #define CASTLE_MUSIC_FIX +/** + * Do not restart the music on cap grabs + */ +#define PERSISTENT_CAP_MUSIC + /** * Increase audio heap size to allow for larger/more custom sequences/banks/sfx to be imported without causing issues (not supported for SH). */ @@ -28,8 +33,3 @@ * Reverb parameters can be configured in audio/synthesis.c to meet desired aesthetic/performance needs. Currently US/JP only. Hurts emulator and console performance. */ // #define BETTER_REVERB - -/** - * Do not restart the music on cap grabs - */ -#define PERSISTENT_CAP_MUSIC diff --git a/src/audio/heap.c b/src/audio/heap.c index 573b3d4e..21379fe5 100644 --- a/src/audio/heap.c +++ b/src/audio/heap.c @@ -1059,7 +1059,6 @@ void init_reverb_us(s32 presetId) { s32 i; #ifdef BETTER_REVERB s8 reverbConsole; - s32 bufOffset = 0; #endif s32 reverbWindowSize = gReverbSettings[presetId].windowSize; @@ -1112,9 +1111,7 @@ void init_reverb_us(s32 presetId) { gSynthesisReverb.items[1][i].toDownsampleRight = (mem + (DEFAULT_LEN_1CH / sizeof(s16))); } #ifdef BETTER_REVERB - delayBufsL = (s32**) soundAlloc(&gBetterReverbPool, BETTER_REVERB_PTR_SIZE); - delayBufsR = &delayBufsL[NUM_ALLPASS]; - delayBufsL[0] = (s32*) soundAlloc(&gBetterReverbPool, BETTER_REVERB_SIZE - BETTER_REVERB_PTR_SIZE); + initialize_better_reverb_buffers(); #endif } else { bzero(gSynthesisReverb.ringBuffer.left, (REVERB_WINDOW_SIZE_MAX * 2 * sizeof(s16))); @@ -1146,17 +1143,10 @@ void init_reverb_us(s32 presetId) { #ifdef BETTER_REVERB if (toggleBetterReverb) { if (sAudioIsInitialized) { - bzero(delayBufsL[0], (BETTER_REVERB_SIZE - BETTER_REVERB_PTR_SIZE)); - } - - for (i = 0; i < NUM_ALLPASS; ++i) { - delaysL[i] = (delaysBaselineL[i] / gReverbDownsampleRate); - delaysR[i] = (delaysBaselineR[i] / gReverbDownsampleRate); - delayBufsL[i] = (s32*) &delayBufsL[0][bufOffset]; - bufOffset += delaysL[i]; - delayBufsR[i] = (s32*) &delayBufsL[0][bufOffset]; // L and R buffers are interpolated adjacently in memory; not a bug - bufOffset += delaysR[i]; + clear_better_reverb_buffers(); } + + set_better_reverb_buffers(); } #endif } diff --git a/src/audio/heap.h b/src/audio/heap.h index f1fb3455..57107418 100644 --- a/src/audio/heap.h +++ b/src/audio/heap.h @@ -93,6 +93,9 @@ extern struct SoundAllocPool gAudioInitPool; extern struct SoundAllocPool gNotesAndBuffersPool; extern struct SoundAllocPool gPersistentCommonPool; extern struct SoundAllocPool gTemporaryCommonPool; +#ifdef BETTER_REVERB +extern struct SoundAllocPool gBetterReverbPool; +#endif extern struct SoundMultiPool gSeqLoadedPool; extern struct SoundMultiPool gBankLoadedPool; #ifdef VERSION_SH diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index 7800ca81..09c5e636 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -69,7 +69,7 @@ // You can change this value before audio_reset_session gets called if different levels can tolerate the demand better than others or just have different reverb goals. // A higher downsample value hits the game's frequency limit sooner, which can cause the reverb sometimes to be off pitch. This is a vanilla level issue (and also counter intuitive). // Higher downsample values also result in slightly shorter reverb decay times. -s8 betterReverbDownsampleConsole = 3; +s8 betterReverbDownsampleConsole = 2; // Most emulators can handle a default value of 2, but 3 may be advisable in some cases if targeting older emulators (e.g. PJ64 1.6). Setting this to -1 also uses vanilla reverb. // Using a value of 1 is not recommended on emulator unless reducing other parameters to compensate. If you do decide to use 1 here, you must adjust BETTER_REVERB_SIZE appropriately. @@ -80,22 +80,22 @@ s8 betterReverbDownsampleEmulator = 2; // This value represents the number of filters to use with the reverb. This can be decreased to improve performance, but at the cost of a lesser presence of reverb in the final audio. // Filter count should always be a multiple of 3. Never ever set this value to be greater than NUM_ALLPASS. -// Setting it to anything less than 3 will disable reverb outright. +// This value cannot be less than 3. Setting it to anything lower will act as if it was set to 3. // This can be changed at any time, but is best set immediately before calling audio_reset_session. -u32 reverbFilterCountConsole = (NUM_ALLPASS - 6); +s32 reverbFilterCountConsole = (NUM_ALLPASS - 9); // This value represents the number of filters to use with the reverb. This can be decreased to improve performance, but at the cost of a lesser presence of reverb in the final audio. // Filter count should always be a multiple of 3. Never ever set this value to be greater than NUM_ALLPASS. -// Setting it to anything less than 3 will disable reverb outright. +// This value cannot be less than 3. Setting it to anything lower will act as if it was set to 3. // This can be changed at any time, but is best set immediately before calling audio_reset_session. -u32 reverbFilterCountEmulator = NUM_ALLPASS; +s32 reverbFilterCountEmulator = NUM_ALLPASS; -// Set this to TRUE to use mono over stereo for reverb. This should increase performance, but at the cost of a less fullfilling reverb experience. +// Set this to TRUE to use mono over stereo for reverb. This should increase performance, but at the cost of a less fulfilling reverb experience. // If performance is desirable, it is recommended to change reverbFilterCountConsole or betterReverbDownsampleConsole first. // This can be changed at any time, but is best set immediately before calling audio_reset_session. u8 monoReverbConsole = FALSE; -// Set this to TRUE to use mono over stereo for reverb. This should increase performance, but at the cost of a less fullfilling reverb experience. +// Set this to TRUE to use mono over stereo for reverb. This should increase performance, but at the cost of a less fulfilling reverb experience. // If performance is desirable, it is recommended to change reverbFilterCountEmulator or betterReverbDownsampleEmulator first. // This can be changed at any time, but is best set immediately before calling audio_reset_session. u8 monoReverbEmulator = FALSE; @@ -111,7 +111,7 @@ s32 betterReverbWindowsSize = -1; // Setting these to values larger than 0xFF (255) or less than 0 may cause issues and is not recommended. #define REVERB_REV_INDEX 0x60 // Affects decay time mostly (large values can cause terrible feedback!); can be messed with at any time #define REVERB_GAIN_INDEX 0xA0 // Affects signal immediately retransmitted back into buffers (mid-high values yield the strongest effect); can be messed with at any time -#define REVERB_WET_SIGNAL 0xE8 // Amount of reverb specific output in final signal (also affects decay); can be messed with at any time, also very easy to control +#define REVERB_WET_SIGNAL 0xE0 // Amount of reverb specific output in final signal (also affects decay); can be messed with at any time, also very easy to control // #define REVERB_DRY_SIGNAL = 0x00; // Amount of original input in final signal (large values can cause terrible feedback!); declaration and uses commented out by default to improve compiler optimization @@ -136,26 +136,25 @@ s32 delaysBaselineR[NUM_ALLPASS] = { 1200, 1432, 1232 }; -// These values affect reverb decay depending on the filter index; can be messed with at any time -s32 reverbMultsL[NUM_ALLPASS / 3] = {0xD7, 0x6F, 0x36, 0x22}; -s32 reverbMultsR[NUM_ALLPASS / 3] = {0xCF, 0x73, 0x38, 0x1F}; +// These values affect reverb decay depending on the filter index; can be messed with at any time, and will have effects updated in real time +s32 gReverbMultsL[NUM_ALLPASS / 3] = {0xD7, 0x6F, 0x36, 0x22}; +s32 gReverbMultsR[NUM_ALLPASS / 3] = {0xCF, 0x73, 0x38, 0x1F}; /* -----------------------------------------------------------------------END REVERB PARAMETERS----------------------------------------------------------------------- */ // Do not touch these values manually, unless you want potential for problems. -s32 reverbFilterCount = NUM_ALLPASS; -s32 reverbFilterCountm1 = (NUM_ALLPASS - 1); -u8 monoReverb = FALSE; u8 toggleBetterReverb = TRUE; -s32 allpassIdxL[NUM_ALLPASS] = {0}; -s32 allpassIdxR[NUM_ALLPASS] = {0}; -s32 tmpBufL[NUM_ALLPASS] = {0}; -s32 tmpBufR[NUM_ALLPASS] = {0}; -s32 delaysL[NUM_ALLPASS] = {0}; -s32 delaysR[NUM_ALLPASS] = {0}; -s32 **delayBufsL; -s32 **delayBufsR; +static u8 monoReverb = FALSE; +static s32 reverbFilterCount = NUM_ALLPASS; +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 u8 reverbMultsL[NUM_ALLPASS / 3] = {0}; +static u8 reverbMultsR[NUM_ALLPASS / 3] = {0}; +static s32 **delayBufsL; +static s32 **delayBufsR; #endif @@ -193,72 +192,109 @@ u8 sAudioSynthesisPad[0x20]; #endif #ifdef BETTER_REVERB -static inline void reverb_samples(s16 *outSampleL, s16 *outSampleR, s32 inSampleL, s32 inSampleR) { +static void reverb_samples(s16 *outSampleL, s16 *outSampleR, s32 inSampleL, s32 inSampleR) { + s32 *curDelaySampleL; + s32 *curDelaySampleR; + s32 historySampleL; + s32 historySampleR; s32 i = 0; s32 j = 0; s32 k = 0; s32 outTmpL = 0; s32 outTmpR = 0; - s32 tmpCarryoverL = (((tmpBufL[reverbFilterCountm1] * REVERB_REV_INDEX) / 256) + inSampleL); - s32 tmpCarryoverR = (((tmpBufR[reverbFilterCountm1] * REVERB_REV_INDEX) / 256) + inSampleR); + s32 tmpCarryoverL = (((delayBufsL[reverbFilterCount][allpassIdxL[reverbFilterCount]] * REVERB_REV_INDEX) >> 8) + inSampleL); + s32 tmpCarryoverR = (((delayBufsR[reverbFilterCount][allpassIdxR[reverbFilterCount]] * REVERB_REV_INDEX) >> 8) + inSampleR); - for (; i != reverbFilterCount; ++i, ++j) { - tmpBufL[i] = delayBufsL[i][allpassIdxL[i]]; - tmpBufR[i] = delayBufsR[i][allpassIdxR[i]]; + for (; i <= reverbFilterCount; ++i, ++j) { + curDelaySampleL = &delayBufsL[i][allpassIdxL[i]]; + curDelaySampleR = &delayBufsR[i][allpassIdxR[i]]; + historySampleL = *curDelaySampleL; + historySampleR = *curDelaySampleR; if (j == 2) { j = -1; - outTmpL += ((tmpBufL[i] * reverbMultsL[k ]) / 256); - outTmpR += ((tmpBufR[i] * reverbMultsR[k++]) / 256); - delayBufsL[i][allpassIdxL[i]] = tmpCarryoverL; - delayBufsR[i][allpassIdxR[i]] = tmpCarryoverR; - if (i != reverbFilterCountm1) { - tmpCarryoverL = ((tmpBufL[i] * REVERB_REV_INDEX) / 256); - tmpCarryoverR = ((tmpBufR[i] * REVERB_REV_INDEX) / 256); + outTmpL += ((historySampleL * reverbMultsL[k ]) >> 8); + outTmpR += ((historySampleR * reverbMultsR[k++]) >> 8); + *curDelaySampleL = tmpCarryoverL; + *curDelaySampleR = tmpCarryoverR; + if (i != reverbFilterCount) { + tmpCarryoverL = ((historySampleL * REVERB_REV_INDEX) >> 8); + tmpCarryoverR = ((historySampleR * REVERB_REV_INDEX) >> 8); } } else { - delayBufsL[i][allpassIdxL[i]] = (((tmpBufL[i] * (-REVERB_GAIN_INDEX)) / 256) + tmpCarryoverL); - delayBufsR[i][allpassIdxR[i]] = (((tmpBufR[i] * (-REVERB_GAIN_INDEX)) / 256) + tmpCarryoverR); - tmpCarryoverL = (((delayBufsL[i][allpassIdxL[i]] * REVERB_GAIN_INDEX) / 256) + tmpBufL[i]); - tmpCarryoverR = (((delayBufsR[i][allpassIdxR[i]] * REVERB_GAIN_INDEX) / 256) + tmpBufR[i]); + *curDelaySampleL = (((historySampleL * (-REVERB_GAIN_INDEX)) >> 8) + tmpCarryoverL); + *curDelaySampleR = (((historySampleR * (-REVERB_GAIN_INDEX)) >> 8) + tmpCarryoverR); + tmpCarryoverL = (((*curDelaySampleL * REVERB_GAIN_INDEX) >> 8) + historySampleL); + tmpCarryoverR = (((*curDelaySampleR * REVERB_GAIN_INDEX) >> 8) + historySampleR); } if (++allpassIdxL[i] == delaysL[i]) allpassIdxL[i] = 0; if (++allpassIdxR[i] == delaysR[i]) allpassIdxR[i] = 0; } - s32 outUnclamped = ((outTmpL * REVERB_WET_SIGNAL/* + inSampleL * REVERB_DRY_SIGNAL*/) / 256); + + s32 outUnclamped = ((outTmpL * REVERB_WET_SIGNAL/* + inSampleL * REVERB_DRY_SIGNAL*/) >> 8); *outSampleL = CLAMP_S16(outUnclamped); - outUnclamped = ((outTmpR * REVERB_WET_SIGNAL/* + inSampleL * REVERB_DRY_SIGNAL*/) / 256); + outUnclamped = ((outTmpR * REVERB_WET_SIGNAL/* + inSampleL * REVERB_DRY_SIGNAL*/) >> 8); *outSampleR = CLAMP_S16(outUnclamped); } -static inline void reverb_mono_sample(s16 *outSample, s32 inSample) { +static void reverb_mono_sample(s16 *outSample, s32 inSample) { + s32 *curDelaySample; + s32 historySample; s32 i = 0; s32 j = 0; s32 k = 0; s32 outTmp = 0; - s32 tmpCarryover = (((tmpBufL[reverbFilterCountm1] * REVERB_REV_INDEX) / 256) + inSample); + s32 tmpCarryover = (((delayBufsL[reverbFilterCount][allpassIdxL[reverbFilterCount]] * REVERB_REV_INDEX) >> 8) + inSample); - for (; i != reverbFilterCount; ++i, ++j) { - tmpBufL[i] = delayBufsL[i][allpassIdxL[i]]; + for (; i <= reverbFilterCount; ++i, ++j) { + curDelaySample = &delayBufsL[i][allpassIdxL[i]]; + historySample = *curDelaySample; if (j == 2) { j = -1; - outTmp += ((tmpBufL[i] * reverbMultsL[k++]) / 256); - delayBufsL[i][allpassIdxL[i]] = tmpCarryover; - if (i != reverbFilterCountm1) - tmpCarryover = ((tmpBufL[i] * REVERB_REV_INDEX) / 256); + outTmp += ((historySample * reverbMultsL[k++]) >> 8); + *curDelaySample = tmpCarryover; + if (i != reverbFilterCount) + tmpCarryover = ((historySample * REVERB_REV_INDEX) >> 8); } else { - delayBufsL[i][allpassIdxL[i]] = (((tmpBufL[i] * (-REVERB_GAIN_INDEX)) / 256) + tmpCarryover); - tmpCarryover = (((delayBufsL[i][allpassIdxL[i]] * REVERB_GAIN_INDEX) / 256) + tmpBufL[i]); + *curDelaySample = (((historySample * (-REVERB_GAIN_INDEX)) >> 8) + tmpCarryover); + tmpCarryover = (((*curDelaySample * REVERB_GAIN_INDEX) >> 8) + historySample); } - if (++allpassIdxL[i] == delaysL[i]) - allpassIdxL[i] = 0; + if (++allpassIdxL[i] == delaysL[i]) allpassIdxL[i] = 0; } - s32 outUnclamped = ((outTmp * REVERB_WET_SIGNAL/* + inSample * REVERB_DRY_SIGNAL*/) / 256); + + s32 outUnclamped = ((outTmp * REVERB_WET_SIGNAL/* + inSample * REVERB_DRY_SIGNAL*/) >> 8); *outSample = CLAMP_S16(outUnclamped); } + +void initialize_better_reverb_buffers(void) { + delayBufsL = (s32**) soundAlloc(&gBetterReverbPool, BETTER_REVERB_PTR_SIZE); + delayBufsR = &delayBufsL[NUM_ALLPASS]; + delayBufsL[0] = (s32*) soundAlloc(&gBetterReverbPool, BETTER_REVERB_SIZE - BETTER_REVERB_PTR_SIZE); +} + +void clear_better_reverb_buffers(void) { + bzero(delayBufsL[0], (BETTER_REVERB_SIZE - BETTER_REVERB_PTR_SIZE)); + + bzero(allpassIdxL, sizeof(allpassIdxL)); + bzero(allpassIdxR, sizeof(allpassIdxR)); +} + +void set_better_reverb_buffers(void) { + s32 bufOffset = 0; + s32 i; + + for (i = 0; i < NUM_ALLPASS; ++i) { + delaysL[i] = (delaysBaselineL[i] / gReverbDownsampleRate); + delaysR[i] = (delaysBaselineR[i] / gReverbDownsampleRate); + delayBufsL[i] = (s32*) &delayBufsL[0][bufOffset]; + bufOffset += delaysL[i]; + delayBufsR[i] = (s32*) &delayBufsL[0][bufOffset]; // L and R buffers are interpolated adjacently in memory; not a bug + bufOffset += delaysR[i]; + } +} #endif #ifdef VERSION_EU @@ -361,6 +397,7 @@ void prepare_reverb_ring_buffer(s32 chunkLen, u32 updateIndex) { } #ifdef BETTER_REVERB else if (toggleBetterReverb) { + reverbFilterCount--; // Temporarily lower filter count for optimized bulk processing item = &gSynthesisReverb.items[gSynthesisReverb.curFrame][updateIndex]; if (gSoundMode == SOUND_MODE_MONO || monoReverb) { if (gReverbDownsampleRate != 1) { @@ -397,6 +434,7 @@ void prepare_reverb_ring_buffer(s32 chunkLen, u32 updateIndex) { reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], gSynthesisReverb.ringBuffer.left[dstPos], gSynthesisReverb.ringBuffer.right[dstPos]); } } + reverbFilterCount++; // Reset filter count to accurate numbers } #endif item = &gSynthesisReverb.items[gSynthesisReverb.curFrame][updateIndex]; @@ -556,18 +594,31 @@ u64 *synthesis_execute(u64 *cmdBuf, s32 *writtenCmds, s16 *aiBuf, s32 bufLen) { #ifdef BETTER_REVERB if (gIsConsole) { - reverbFilterCount = (s32) reverbFilterCountConsole; + reverbFilterCount = reverbFilterCountConsole; monoReverb = monoReverbConsole; } else { - reverbFilterCount = (s32) reverbFilterCountEmulator; + reverbFilterCount = reverbFilterCountEmulator; monoReverb = monoReverbEmulator; } if (reverbFilterCount > NUM_ALLPASS) { reverbFilterCount = NUM_ALLPASS; + } else if (reverbFilterCount < 3) { + reverbFilterCount = 3; } - reverbFilterCountm1 = (reverbFilterCount - 1); - if (reverbFilterCount < 3) { - reverbFilterCountm1 = 0; + + s32 filterCountDiv3 = reverbFilterCount / 3; + reverbFilterCount = filterCountDiv3 * 3; // reverbFilterCount should always be a multiple of 3. + + // Update reverbMultsL every audio frame just in case gReverbMults is ever to change. + for (i = 0; i < filterCountDiv3; ++i) { + reverbMultsL[i] = gReverbMultsL[i]; + reverbMultsR[i] = gReverbMultsR[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]; } #endif diff --git a/src/audio/synthesis.h b/src/audio/synthesis.h index 54403016..95236321 100644 --- a/src/audio/synthesis.h +++ b/src/audio/synthesis.h @@ -33,20 +33,16 @@ extern s8 betterReverbDownsampleConsole; extern s8 betterReverbDownsampleEmulator; -extern u32 reverbFilterCountConsole; -extern u32 reverbFilterCountEmulator; extern u8 monoReverbConsole; extern u8 monoReverbEmulator; +extern s32 reverbFilterCountConsole; +extern s32 reverbFilterCountEmulator; extern s32 betterReverbWindowsSize; extern s32 delaysBaselineL[NUM_ALLPASS]; extern s32 delaysBaselineR[NUM_ALLPASS]; -extern s32 delaysL[NUM_ALLPASS]; -extern s32 delaysR[NUM_ALLPASS]; -extern s32 reverbMultsL[NUM_ALLPASS / 3]; -extern s32 reverbMultsR[NUM_ALLPASS / 3]; -extern s32 **delayBufsL; -extern s32 **delayBufsR; +extern s32 gReverbMultsL[NUM_ALLPASS / 3]; +extern s32 gReverbMultsR[NUM_ALLPASS / 3]; extern u8 toggleBetterReverb; #define REVERB_WINDOW_SIZE_MAX 0x2000 @@ -149,6 +145,12 @@ extern struct SynthesisReverb gSynthesisReverb; extern s16 D_SH_803479B4; #endif +#ifdef BETTER_REVERB +void initialize_better_reverb_buffers(void); +void clear_better_reverb_buffers(void); +void set_better_reverb_buffers(void); +#endif + u64 *synthesis_execute(u64 *cmdBuf, s32 *writtenCmds, s16 *aiBuf, s32 bufLen); #if defined(VERSION_JP) || defined(VERSION_US) void note_init_volume(struct Note *note);