From 8031963125a553903b5b144bd8af4c5b04055f13 Mon Sep 17 00:00:00 2001 From: gheskett Date: Wed, 4 Aug 2021 18:15:38 -0500 Subject: [PATCH 01/11] Add mono parameter for reverb --- src/audio/synthesis.c | 16 +++++++++++++--- src/audio/synthesis.h | 14 +++++++++----- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index 347efc87..ef8a2f88 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -37,6 +37,7 @@ #define AUDIO_ALIGN(val, amnt) (((val) + (1 << amnt) - 1) & ~((1 << amnt) - 1)) +#ifdef BETTER_REVERB /* ----------------------------------------------------------------------BEGIN REVERB PARAMETERS---------------------------------------------------------------------- */ @@ -47,6 +48,8 @@ * To take advantage of the reverb effect, you can change the echo parameters set in levels/level_defines.h to tailor the reverb to each specific level area. * To adjust reverb presence with individual sound effects, apply the .set_reverb command within sound/sequences/00_sound_player.s (see examples of other sounds that use it). * To use with M64 sequences, set the Effect parameter for each channel accordingly (CC 91 for MIDI files). + * + * Most parameter configuration is to be done here, though BETTER_REVERB_SIZE can be adjusted in audio/synthesis.h. */ @@ -63,6 +66,10 @@ s8 betterReverbConsoleDownsample = 3; // Higher downsample values also result in slightly shorter reverb decay times. s8 betterReverbEmulatorDownsample = 2; +// 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. +// This can be changed at any time, but is best set when calling audio_reset_session. +u8 monoReverb = FALSE; + s32 gReverbRevIndex = 0x5F; // Affects decay time mostly (large values can cause terrible feedback!); can be messed with at any time s32 gReverbGainIndex = 0x9F; // Affects signal immediately retransmitted back into buffers (mid-high values yield the strongest effect); can be messed with at any time s32 gReverbWetSignal = 0xE7; // Amount of reverb specific output in final signal (also affects decay); can be messed with at any time, also very easy to control @@ -119,6 +126,7 @@ s32 allpassIdx[2][NUM_ALLPASS] = { s32 tmpBufL[NUM_ALLPASS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; s32 tmpBufR[NUM_ALLPASS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; s32 ***delayBufs; +#endif struct VolumeChange { @@ -154,6 +162,7 @@ struct SynthesisReverb gSynthesisReverb; u8 sAudioSynthesisPad[0x20]; #endif +#ifdef BETTER_REVERB inline s16 clamp16(s32 x) { if (x >= 32767) return 32767; @@ -237,7 +246,7 @@ inline s16 reverb_sample_left(s32 inSample) { return clamp16((outTmp * gReverbWetSignal + inSample * gReverbDrySignal) / 256); } - +/* UNUSED inline s16 reverb_sample_right(s32 inSample) { u32 i = 0; s32 j = 0; @@ -269,7 +278,8 @@ inline s16 reverb_sample_right(s32 inSample) { } return clamp16((outTmp * gReverbWetSignal + inSample * gReverbDrySignal) / 256); -} +}*/ +#endif #ifdef VERSION_EU s16 gVolume; @@ -380,7 +390,7 @@ void prepare_reverb_ring_buffer(s32 chunkLen, u32 updateIndex) { #ifdef BETTER_REVERB else if (toggleBetterReverb) { item = &gSynthesisReverb.items[gSynthesisReverb.curFrame][updateIndex]; - if (gSoundMode == SOUND_MODE_MONO) { + if (gSoundMode == SOUND_MODE_MONO || monoReverb) { if (gReverbDownsampleRate != 1) { osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH); for (srcPos = 0, dstPos = item->startPos; dstPos < item->lengthA / 2 + item->startPos; srcPos += gReverbDownsampleRate, dstPos++) { diff --git a/src/audio/synthesis.h b/src/audio/synthesis.h index 19f341ab..de636143 100644 --- a/src/audio/synthesis.h +++ b/src/audio/synthesis.h @@ -18,11 +18,12 @@ #endif #ifdef BETTER_REVERB -#define BETTER_REVERB_SIZE 0xF200 // Size determined by ((all delaysBaseline values * 16) / (2 ^ Minimum Downsample Factor)) + array pointers; can be increased if needed -// #define BETTER_REVERB_SIZE 0x1E200 // For use with a downsampling value of 1 (i.e. no downsampling at all) -#else -#define BETTER_REVERB_SIZE 0 -#endif + // Size determined by ((all delaysBaseline values * 16) / (2 ^ Minimum Downsample Factor)) + array pointers. + // The default value can be increased or decreased in conjunction with the values in delaysBaseline +#define BETTER_REVERB_SIZE 0xF200 + +// #define BETTER_REVERB_SIZE 0x7A00 // Default for use only with a downsampling value of 3 (i.e. double the emulator default) +// #define BETTER_REVERB_SIZE 0x1E200 // Default for use with a downsampling value of 1 (i.e. no downsampling at all) #define NUM_ALLPASS 12 // Number of delay filters to use with better reverb; do not change this value if you don't know what you're doing. @@ -34,6 +35,9 @@ extern s32 ***delayBufs; extern u8 toggleBetterReverb; extern s8 betterReverbConsoleDownsample; extern s8 betterReverbEmulatorDownsample; +#else +#define BETTER_REVERB_SIZE 0 +#endif struct ReverbRingBufferItem { From 646c3aea47d6aeadba4a0268461fcb58e54d2c1c Mon Sep 17 00:00:00 2001 From: gheskett Date: Wed, 4 Aug 2021 23:05:39 -0500 Subject: [PATCH 02/11] More slight better reverb optimization --- src/audio/synthesis.c | 121 +++++++++++------------------------------- 1 file changed, 32 insertions(+), 89 deletions(-) diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index ef8a2f88..d412c469 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -70,11 +70,6 @@ s8 betterReverbEmulatorDownsample = 2; // This can be changed at any time, but is best set when calling audio_reset_session. u8 monoReverb = FALSE; -s32 gReverbRevIndex = 0x5F; // Affects decay time mostly (large values can cause terrible feedback!); can be messed with at any time -s32 gReverbGainIndex = 0x9F; // Affects signal immediately retransmitted back into buffers (mid-high values yield the strongest effect); can be messed with at any time -s32 gReverbWetSignal = 0xE7; // Amount of reverb specific output in final signal (also affects decay); can be messed with at any time, also very easy to control -s32 gReverbDrySignal = 0x00; // Amount of original input in final signal (large values can cause terrible feedback!); can be messed with at any time - // This value controls the size of the reverb buffer. It affects the global reverb delay time. This variable is one of the easiest to control. // It is not recommended setting this to values greater than 0x1000 * 2^(downsample factor - 1), as you run the risk of running into a memory issue (though this is far from a guarantee). // Setting the value lower than the downsample buffer size will destroy the game audio. This is taken into account automatically, but also means the value set here isn't what always gets used. @@ -82,6 +77,12 @@ s32 gReverbDrySignal = 0x00; // Amount of original input in final signal (large // Set to -1 to use a default preset instead. Higher values represent more audio delay (usually better for echoey spaces). s32 betterReverbWindowsSize = -1; +s32 gReverbRevIndex = 0x5F; // Affects decay time mostly (large values can cause terrible feedback!); can be messed with at any time +s32 gReverbGainIndex = 0x9F; // Affects signal immediately retransmitted back into buffers (mid-high values yield the strongest effect); can be messed with at any time +s32 gReverbWetSignal = 0xE7; // Amount of reverb specific output in final signal (also affects decay); can be messed with at any time, also very easy to control + +// s32 gReverbDrySignal = 0x00; // Amount of original input in final signal (large values can cause terrible feedback!); disabled by default to improve compiler optimization + /* ---------------------------------------------------------------------ADVANCED REVERB PARAMETERS-------------------------------------------------------------------- */ @@ -119,10 +120,7 @@ const s32 reverbMults[2][NUM_ALLPASS / 3] = { // Do not touch these values. u8 toggleBetterReverb = TRUE; -s32 allpassIdx[2][NUM_ALLPASS] = { - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} -}; +s32 allpassIdx[NUM_ALLPASS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; s32 tmpBufL[NUM_ALLPASS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; s32 tmpBufR[NUM_ALLPASS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; s32 ***delayBufs; @@ -182,43 +180,40 @@ inline void reverb_samples(s16 *outSampleL, s16 *outSampleR, s32 inSampleL, s32 s32 tmpCarryoverR = ((tmpBufR[NUM_ALLPASS-1] * gReverbRevIndex) / 256); for (; i < NUM_ALLPASS; ++i, ++j) { - tmpBufL[i] = delayBufs[0][i][allpassIdx[0][i]]; - tmpBufR[i] = delayBufs[1][i][allpassIdx[1][i]]; + tmpBufL[i] = delayBufs[0][i][allpassIdx[i]]; + tmpBufR[i] = delayBufs[1][i][allpassIdx[i]]; if (j == 2) { j = -1; outTmpL += (tmpBufL[i] * reverbMults[0][k]) / 256; outTmpR += (tmpBufR[i] * reverbMults[1][k++]) / 256; - delayBufs[0][i][allpassIdx[0][i]] = tmpCarryoverL; - delayBufs[1][i][allpassIdx[1][i]] = tmpCarryoverR; + delayBufs[0][i][allpassIdx[i]] = tmpCarryoverL; + delayBufs[1][i][allpassIdx[i]] = tmpCarryoverR; if (i != NUM_ALLPASS - 1) { tmpCarryoverL = (tmpBufL[i] * gReverbRevIndex) / 256; tmpCarryoverR = (tmpBufR[i] * gReverbRevIndex) / 256; } } else { - delayBufs[0][i][allpassIdx[0][i]] = (tmpBufL[i] * (-gReverbGainIndex)) / 256 + tmpCarryoverL; - delayBufs[1][i][allpassIdx[1][i]] = (tmpBufR[i] * (-gReverbGainIndex)) / 256 + tmpCarryoverR; + delayBufs[0][i][allpassIdx[i]] = (tmpBufL[i] * (-gReverbGainIndex)) / 256 + tmpCarryoverL; + delayBufs[1][i][allpassIdx[i]] = (tmpBufR[i] * (-gReverbGainIndex)) / 256 + tmpCarryoverR; if (i == 6) - delayBufs[1][i][allpassIdx[1][i]] += inSampleR; // Unique to right channel + delayBufs[1][i][allpassIdx[i]] += inSampleR; // Unique to right channel - tmpCarryoverL = (delayBufs[0][i][allpassIdx[0][i]] * gReverbGainIndex) / 256 + tmpBufL[i]; - tmpCarryoverR = (delayBufs[1][i][allpassIdx[1][i]] * gReverbGainIndex) / 256 + tmpBufR[i]; + tmpCarryoverL = (delayBufs[0][i][allpassIdx[i]] * gReverbGainIndex) / 256 + tmpBufL[i]; + tmpCarryoverR = (delayBufs[1][i][allpassIdx[i]] * gReverbGainIndex) / 256 + tmpBufR[i]; } - if (++allpassIdx[0][i] == delays[i]) { - allpassIdx[0][i] = 0; - allpassIdx[1][i] = -1; // To avoid an else branch - } - ++allpassIdx[1][i]; + if (++allpassIdx[i] == delays[i]) + allpassIdx[i] = 0; } - *outSampleL = clamp16((outTmpL * gReverbWetSignal + inSampleL * gReverbDrySignal) / 256); - *outSampleR = clamp16((outTmpR * gReverbWetSignal + inSampleR * gReverbDrySignal) / 256); + *outSampleL = clamp16((outTmpL * gReverbWetSignal/* + inSampleL * gReverbDrySignal*/) / 256); + *outSampleR = clamp16((outTmpR * gReverbWetSignal/* + inSampleR * gReverbDrySignal*/) / 256); } -inline s16 reverb_sample_left(s32 inSample) { +inline void reverb_mono_sample(s16 *outSample, s32 inSample) { u32 i = 0; s32 j = 0; u8 k = 0; @@ -226,59 +221,26 @@ inline s16 reverb_sample_left(s32 inSample) { s32 tmpCarryover = ((tmpBufL[NUM_ALLPASS-1] * gReverbRevIndex) / 256) + inSample; for (; i < NUM_ALLPASS; ++i, ++j) { - tmpBufL[i] = delayBufs[0][i][allpassIdx[0][i]]; + tmpBufL[i] = delayBufs[0][i][allpassIdx[i]]; if (j == 2) { j = -1; outTmp += (tmpBufL[i] * reverbMults[0][k++]) / 256; - delayBufs[0][i][allpassIdx[0][i]] = tmpCarryover; + delayBufs[0][i][allpassIdx[i]] = tmpCarryover; if (i != NUM_ALLPASS - 1) tmpCarryover = (tmpBufL[i] * gReverbRevIndex) / 256; } else { - delayBufs[0][i][allpassIdx[0][i]] = (tmpBufL[i] * (-gReverbGainIndex)) / 256 + tmpCarryover; - tmpCarryover = (delayBufs[0][i][allpassIdx[0][i]] * gReverbGainIndex) / 256 + tmpBufL[i]; + delayBufs[0][i][allpassIdx[i]] = (tmpBufL[i] * (-gReverbGainIndex)) / 256 + tmpCarryover; + tmpCarryover = (delayBufs[0][i][allpassIdx[i]] * gReverbGainIndex) / 256 + tmpBufL[i]; } - if (++allpassIdx[0][i] == delays[i]) - allpassIdx[0][i] = 0; + if (++allpassIdx[i] == delays[i]) + allpassIdx[i] = 0; } - return clamp16((outTmp * gReverbWetSignal + inSample * gReverbDrySignal) / 256); + *outSample = clamp16((outTmp * gReverbWetSignal/* + inSample * gReverbDrySignal*/) / 256); } -/* UNUSED -inline s16 reverb_sample_right(s32 inSample) { - u32 i = 0; - s32 j = 0; - u8 k = 0; - s32 outTmp = 0; - s32 tmpCarryover = ((tmpBufR[NUM_ALLPASS-1] * gReverbRevIndex) / 256); - - for (; i < NUM_ALLPASS; ++i, ++j) { - tmpBufR[i] = delayBufs[1][i][allpassIdx[1][i]]; - - if (j == 2) { - j = -1; - outTmp += (tmpBufR[i] * reverbMults[1][k++]) / 256; - delayBufs[1][i][allpassIdx[1][i]] = tmpCarryover; - if (i != NUM_ALLPASS - 1) - tmpCarryover = (tmpBufR[i] * gReverbRevIndex) / 256; - } - else { - delayBufs[1][i][allpassIdx[1][i]] = (tmpBufR[i] * (-gReverbGainIndex)) / 256 + tmpCarryover; - - if (i == 6) - delayBufs[1][i][allpassIdx[1][i]] += inSample; - - tmpCarryover = (delayBufs[1][i][allpassIdx[1][i]] * gReverbGainIndex) / 256 + tmpBufR[i]; - } - - if (++allpassIdx[1][i] == delays[i]) - allpassIdx[1][i] = 0; - } - - return clamp16((outTmp * gReverbWetSignal + inSample * gReverbDrySignal) / 256); -}*/ #endif #ifdef VERSION_EU @@ -394,21 +356,21 @@ void prepare_reverb_ring_buffer(s32 chunkLen, u32 updateIndex) { if (gReverbDownsampleRate != 1) { osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH); for (srcPos = 0, dstPos = item->startPos; dstPos < item->lengthA / 2 + item->startPos; srcPos += gReverbDownsampleRate, dstPos++) { - gSynthesisReverb.ringBuffer.left[dstPos] = reverb_sample_left(((s32) item->toDownsampleLeft[srcPos] + (s32) item->toDownsampleRight[srcPos]) / 2); + reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) item->toDownsampleLeft[srcPos] + (s32) item->toDownsampleRight[srcPos]) / 2); gSynthesisReverb.ringBuffer.right[dstPos] = gSynthesisReverb.ringBuffer.left[dstPos]; } for (dstPos = 0; dstPos < item->lengthB / 2; srcPos += gReverbDownsampleRate, dstPos++) { - gSynthesisReverb.ringBuffer.left[dstPos] = reverb_sample_left(((s32) item->toDownsampleLeft[srcPos] + (s32) item->toDownsampleRight[srcPos]) / 2); + reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) item->toDownsampleLeft[srcPos] + (s32) item->toDownsampleRight[srcPos]) / 2); gSynthesisReverb.ringBuffer.right[dstPos] = gSynthesisReverb.ringBuffer.left[dstPos]; } } else { // Too slow for practical use, not recommended most of the time. for (dstPos = item->startPos; dstPos < item->lengthA / 2 + item->startPos; dstPos++) { - gSynthesisReverb.ringBuffer.left[dstPos] = reverb_sample_left(((s32) gSynthesisReverb.ringBuffer.left[dstPos] + (s32) gSynthesisReverb.ringBuffer.right[dstPos]) / 2); + reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) gSynthesisReverb.ringBuffer.left[dstPos] + (s32) gSynthesisReverb.ringBuffer.right[dstPos]) / 2); gSynthesisReverb.ringBuffer.right[dstPos] = gSynthesisReverb.ringBuffer.left[dstPos]; } for (dstPos = 0; dstPos < item->lengthB / 2; dstPos++) { - gSynthesisReverb.ringBuffer.left[dstPos] = reverb_sample_left(((s32) gSynthesisReverb.ringBuffer.left[dstPos] + (s32) gSynthesisReverb.ringBuffer.right[dstPos]) / 2); + reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) gSynthesisReverb.ringBuffer.left[dstPos] + (s32) gSynthesisReverb.ringBuffer.right[dstPos]) / 2); gSynthesisReverb.ringBuffer.right[dstPos] = gSynthesisReverb.ringBuffer.left[dstPos]; } } @@ -420,31 +382,12 @@ void prepare_reverb_ring_buffer(s32 chunkLen, u32 updateIndex) { reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], item->toDownsampleLeft[srcPos], item->toDownsampleRight[srcPos]); for (dstPos = 0; dstPos < item->lengthB / 2; srcPos += gReverbDownsampleRate, dstPos++) reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], item->toDownsampleLeft[srcPos], item->toDownsampleRight[srcPos]); - - // for (srcPos = 0, dstPos = item->startPos; dstPos < item->lengthA / 2 + item->startPos; - // srcPos += gReverbDownsampleRate, dstPos++) { - // gSynthesisReverb.ringBuffer.left[dstPos] = reverb_sample_left(item->toDownsampleLeft[srcPos]); - // gSynthesisReverb.ringBuffer.right[dstPos] = reverb_sample_right(item->toDownsampleRight[srcPos]); - // } - // for (dstPos = 0; dstPos < item->lengthB / 2; srcPos += gReverbDownsampleRate, dstPos++) { - // gSynthesisReverb.ringBuffer.left[dstPos] = reverb_sample_left(item->toDownsampleLeft[srcPos]); - // gSynthesisReverb.ringBuffer.right[dstPos] = reverb_sample_right(item->toDownsampleRight[srcPos]); - // } } else { // Too slow for practical use, not recommended most of the time. for (dstPos = item->startPos; dstPos < item->lengthA / 2 + item->startPos; dstPos++) reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], gSynthesisReverb.ringBuffer.left[dstPos], gSynthesisReverb.ringBuffer.right[dstPos]); for (dstPos = 0; dstPos < item->lengthB / 2; dstPos++) reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], gSynthesisReverb.ringBuffer.left[dstPos], gSynthesisReverb.ringBuffer.right[dstPos]); - - // for (dstPos = item->startPos; dstPos < item->lengthA / 2 + item->startPos; dstPos++) { - // gSynthesisReverb.ringBuffer.left[dstPos] = reverb_sample_left(gSynthesisReverb.ringBuffer.left[dstPos]); - // gSynthesisReverb.ringBuffer.right[dstPos] = reverb_sample_right(gSynthesisReverb.ringBuffer.right[dstPos]); - // } - // for (dstPos = 0; dstPos < item->lengthB / 2; srcPos += gReverbDownsampleRate, dstPos++) { - // gSynthesisReverb.ringBuffer.left[dstPos] = reverb_sample_left(gSynthesisReverb.ringBuffer.left[dstPos]); - // gSynthesisReverb.ringBuffer.right[dstPos] = reverb_sample_right(gSynthesisReverb.ringBuffer.right[dstPos]); - // } } } } From 82334f790b4c0d252628035aeb61be9281b2a949 Mon Sep 17 00:00:00 2001 From: gheskett Date: Wed, 4 Aug 2021 23:16:03 -0500 Subject: [PATCH 03/11] delayBufs now 2 variables --- src/audio/heap.c | 9 ++++----- src/audio/synthesis.c | 29 +++++++++++++++-------------- src/audio/synthesis.h | 3 ++- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/audio/heap.c b/src/audio/heap.c index 27423a10..65daf79f 100644 --- a/src/audio/heap.c +++ b/src/audio/heap.c @@ -1460,12 +1460,11 @@ void audio_reset_session(void) { for (i = 0; i < NUM_ALLPASS; ++i) delays[i] = delaysBaseline[i] / gReverbDownsampleRate; - delayBufs = (s32***) soundAlloc(&gAudioSessionPool, 2 * sizeof(s32**)); - delayBufs[0] = (s32**) soundAlloc(&gAudioSessionPool, NUM_ALLPASS * sizeof(s32*)); - delayBufs[1] = (s32**) soundAlloc(&gAudioSessionPool, NUM_ALLPASS * sizeof(s32*)); + delayBufsL = (s32**) soundAlloc(&gAudioSessionPool, NUM_ALLPASS * sizeof(s32*)); + delayBufsR = (s32**) soundAlloc(&gAudioSessionPool, NUM_ALLPASS * sizeof(s32*)); for (i = 0; i < NUM_ALLPASS; ++i) { - delayBufs[0][i] = (s32*) soundAlloc(&gAudioSessionPool, delays[i] * sizeof(s32)); - delayBufs[1][i] = (s32*) soundAlloc(&gAudioSessionPool, delays[i] * sizeof(s32)); + delayBufsL[i] = (s32*) soundAlloc(&gAudioSessionPool, delays[i] * sizeof(s32)); + delayBufsR[i] = (s32*) soundAlloc(&gAudioSessionPool, delays[i] * sizeof(s32)); } } #endif diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index d412c469..ac60faad 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -123,7 +123,8 @@ u8 toggleBetterReverb = TRUE; s32 allpassIdx[NUM_ALLPASS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; s32 tmpBufL[NUM_ALLPASS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; s32 tmpBufR[NUM_ALLPASS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -s32 ***delayBufs; +s32 **delayBufsL; +s32 **delayBufsR; #endif @@ -180,29 +181,29 @@ inline void reverb_samples(s16 *outSampleL, s16 *outSampleR, s32 inSampleL, s32 s32 tmpCarryoverR = ((tmpBufR[NUM_ALLPASS-1] * gReverbRevIndex) / 256); for (; i < NUM_ALLPASS; ++i, ++j) { - tmpBufL[i] = delayBufs[0][i][allpassIdx[i]]; - tmpBufR[i] = delayBufs[1][i][allpassIdx[i]]; + tmpBufL[i] = delayBufsL[i][allpassIdx[i]]; + tmpBufR[i] = delayBufsR[i][allpassIdx[i]]; if (j == 2) { j = -1; outTmpL += (tmpBufL[i] * reverbMults[0][k]) / 256; outTmpR += (tmpBufR[i] * reverbMults[1][k++]) / 256; - delayBufs[0][i][allpassIdx[i]] = tmpCarryoverL; - delayBufs[1][i][allpassIdx[i]] = tmpCarryoverR; + delayBufsL[i][allpassIdx[i]] = tmpCarryoverL; + delayBufsR[i][allpassIdx[i]] = tmpCarryoverR; if (i != NUM_ALLPASS - 1) { tmpCarryoverL = (tmpBufL[i] * gReverbRevIndex) / 256; tmpCarryoverR = (tmpBufR[i] * gReverbRevIndex) / 256; } } else { - delayBufs[0][i][allpassIdx[i]] = (tmpBufL[i] * (-gReverbGainIndex)) / 256 + tmpCarryoverL; - delayBufs[1][i][allpassIdx[i]] = (tmpBufR[i] * (-gReverbGainIndex)) / 256 + tmpCarryoverR; + delayBufsL[i][allpassIdx[i]] = (tmpBufL[i] * (-gReverbGainIndex)) / 256 + tmpCarryoverL; + delayBufsR[i][allpassIdx[i]] = (tmpBufR[i] * (-gReverbGainIndex)) / 256 + tmpCarryoverR; if (i == 6) - delayBufs[1][i][allpassIdx[i]] += inSampleR; // Unique to right channel + delayBufsR[i][allpassIdx[i]] += inSampleR; // Unique to right channel - tmpCarryoverL = (delayBufs[0][i][allpassIdx[i]] * gReverbGainIndex) / 256 + tmpBufL[i]; - tmpCarryoverR = (delayBufs[1][i][allpassIdx[i]] * gReverbGainIndex) / 256 + tmpBufR[i]; + tmpCarryoverL = (delayBufsL[i][allpassIdx[i]] * gReverbGainIndex) / 256 + tmpBufL[i]; + tmpCarryoverR = (delayBufsR[i][allpassIdx[i]] * gReverbGainIndex) / 256 + tmpBufR[i]; } if (++allpassIdx[i] == delays[i]) @@ -221,18 +222,18 @@ inline void reverb_mono_sample(s16 *outSample, s32 inSample) { s32 tmpCarryover = ((tmpBufL[NUM_ALLPASS-1] * gReverbRevIndex) / 256) + inSample; for (; i < NUM_ALLPASS; ++i, ++j) { - tmpBufL[i] = delayBufs[0][i][allpassIdx[i]]; + tmpBufL[i] = delayBufsL[i][allpassIdx[i]]; if (j == 2) { j = -1; outTmp += (tmpBufL[i] * reverbMults[0][k++]) / 256; - delayBufs[0][i][allpassIdx[i]] = tmpCarryover; + delayBufsL[i][allpassIdx[i]] = tmpCarryover; if (i != NUM_ALLPASS - 1) tmpCarryover = (tmpBufL[i] * gReverbRevIndex) / 256; } else { - delayBufs[0][i][allpassIdx[i]] = (tmpBufL[i] * (-gReverbGainIndex)) / 256 + tmpCarryover; - tmpCarryover = (delayBufs[0][i][allpassIdx[i]] * gReverbGainIndex) / 256 + tmpBufL[i]; + delayBufsL[i][allpassIdx[i]] = (tmpBufL[i] * (-gReverbGainIndex)) / 256 + tmpCarryover; + tmpCarryover = (delayBufsL[i][allpassIdx[i]] * gReverbGainIndex) / 256 + tmpBufL[i]; } if (++allpassIdx[i] == delays[i]) diff --git a/src/audio/synthesis.h b/src/audio/synthesis.h index de636143..6d1f5916 100644 --- a/src/audio/synthesis.h +++ b/src/audio/synthesis.h @@ -30,7 +30,8 @@ extern s32 betterReverbWindowsSize; extern const s32 delaysBaseline[NUM_ALLPASS]; extern s32 delays[NUM_ALLPASS]; -extern s32 ***delayBufs; +extern s32 **delayBufsL; +extern s32 **delayBufsR; extern u8 toggleBetterReverb; extern s8 betterReverbConsoleDownsample; From 382b50541a4753fac5b5a5154ee9adc2b805b599 Mon Sep 17 00:00:00 2001 From: gheskett Date: Thu, 5 Aug 2021 00:02:00 -0500 Subject: [PATCH 04/11] More configuration parameters to help console performance --- include/config.h | 2 +- src/audio/heap.c | 4 +-- src/audio/synthesis.c | 71 +++++++++++++++++++++++++++++++------------ src/audio/synthesis.h | 15 +++++++-- 4 files changed, 68 insertions(+), 24 deletions(-) diff --git a/include/config.h b/include/config.h index c9002a94..43944115 100644 --- a/include/config.h +++ b/include/config.h @@ -155,7 +155,7 @@ #define IA8_COINS // Use a much better implementation of reverb over vanilla's fake echo reverb. Great for caves or eerie levels, as well as just a better audio experience in general. // Reverb parameters can be configured in audio/synthesis.c to meet desired aesthetic/performance needs. -//#define BETTER_REVERB +#define BETTER_REVERB // If you want to change the extended boundaries mode, go to engine/extended_bounds.h and change EXTENDED_BOUNDS_MODE diff --git a/src/audio/heap.c b/src/audio/heap.c index 65daf79f..4312f3c1 100644 --- a/src/audio/heap.c +++ b/src/audio/heap.c @@ -1240,9 +1240,9 @@ void audio_reset_session(void) { gReverbDownsampleRate = preset->reverbDownsampleRate; #ifdef BETTER_REVERB if (gIsConsole) - reverbConsole = betterReverbConsoleDownsample; // Console! + reverbConsole = betterReverbDownsampleConsole; // Console! else - reverbConsole = betterReverbEmulatorDownsample; // Setting this to 1 is REALLY slow, please use sparingly! + reverbConsole = betterReverbDownsampleEmulator; // Setting this to 1 is REALLY slow, please use sparingly! if (reverbConsole <= 0) { reverbConsole = 1; diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index ac60faad..26940d5f 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -8,6 +8,7 @@ #include "seqplayer.h" #include "internal.h" #include "external.h" +#include "game/game_init.h" #define DMEM_ADDR_TEMP 0x0 @@ -57,18 +58,34 @@ // 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 betterReverbConsoleDownsample = 3; +s8 betterReverbDownsampleConsole = 3; // 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 except in very specific situations. If you do decide to use 1 here, you must adjust BETTER_REVERB_SIZE appropriately. // 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 betterReverbEmulatorDownsample = 2; +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 this to 3 will only generate reverb in one channel, and setting it to anything less 0 will disable reverb outright. +// This can be changed at any time, but is best set when calling audio_reset_session. +u32 reverbFilterCountConsole = NUM_ALLPASS - 3; + +// 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 this to 3 will only generate reverb in one channel, and setting it to anything less 0 will disable reverb outright. +// This can be changed at any time, but is best set when calling audio_reset_session. +u32 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. // This can be changed at any time, but is best set when calling audio_reset_session. -u8 monoReverb = FALSE; +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. +// This can be changed at any time, but is best set when calling audio_reset_session. +u8 monoReverbEmulator = FALSE; // This value controls the size of the reverb buffer. It affects the global reverb delay time. This variable is one of the easiest to control. // It is not recommended setting this to values greater than 0x1000 * 2^(downsample factor - 1), as you run the risk of running into a memory issue (though this is far from a guarantee). @@ -110,15 +127,16 @@ const s32 delaysBaseline[NUM_ALLPASS] = { }; // These values affect reverb decay depending on the filter index; can be messed with at any time -const s32 reverbMults[2][NUM_ALLPASS / 3] = { - {0xD2, 0x6E, 0x36, 0x1F}, // Left Channel - {0x38, 0x26, 0xCF, 0x71} // Right Channel -}; +s32 reverbMultsL[NUM_ALLPASS / 3] = {0xD2, 0x6E, 0x36, 0x1F}; +s32 reverbMultsR[NUM_ALLPASS / 3] = {0x26, 0xCF, 0x71, 0x38}; /* -----------------------------------------------------------------------END REVERB PARAMETERS----------------------------------------------------------------------- */ -// Do not touch these values. +// Do not touch these values manually, unless you want potential for problems. +u32 reverbFilterCount = NUM_ALLPASS; +u32 reverbFilterRightChIdx = 6; +u8 monoReverb = FALSE; u8 toggleBetterReverb = TRUE; s32 allpassIdx[NUM_ALLPASS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; s32 tmpBufL[NUM_ALLPASS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -177,20 +195,20 @@ inline void reverb_samples(s16 *outSampleL, s16 *outSampleR, s32 inSampleL, s32 u8 k = 0; s32 outTmpL = 0; s32 outTmpR = 0; - s32 tmpCarryoverL = ((tmpBufL[NUM_ALLPASS-1] * gReverbRevIndex) / 256) + inSampleL; // Unique to left channel - s32 tmpCarryoverR = ((tmpBufR[NUM_ALLPASS-1] * gReverbRevIndex) / 256); + s32 tmpCarryoverL = ((tmpBufL[reverbFilterCount-1] * gReverbRevIndex) / 256) + inSampleL; // Unique to left channel + s32 tmpCarryoverR = ((tmpBufR[reverbFilterCount-1] * gReverbRevIndex) / 256); - for (; i < NUM_ALLPASS; ++i, ++j) { + for (; i < reverbFilterCount; ++i, ++j) { tmpBufL[i] = delayBufsL[i][allpassIdx[i]]; tmpBufR[i] = delayBufsR[i][allpassIdx[i]]; if (j == 2) { j = -1; - outTmpL += (tmpBufL[i] * reverbMults[0][k]) / 256; - outTmpR += (tmpBufR[i] * reverbMults[1][k++]) / 256; + outTmpL += (tmpBufL[i] * reverbMultsL[k]) / 256; + outTmpR += (tmpBufR[i] * reverbMultsR[k++]) / 256; delayBufsL[i][allpassIdx[i]] = tmpCarryoverL; delayBufsR[i][allpassIdx[i]] = tmpCarryoverR; - if (i != NUM_ALLPASS - 1) { + if (i != reverbFilterCount - 1) { tmpCarryoverL = (tmpBufL[i] * gReverbRevIndex) / 256; tmpCarryoverR = (tmpBufR[i] * gReverbRevIndex) / 256; } @@ -199,7 +217,7 @@ inline void reverb_samples(s16 *outSampleL, s16 *outSampleR, s32 inSampleL, s32 delayBufsL[i][allpassIdx[i]] = (tmpBufL[i] * (-gReverbGainIndex)) / 256 + tmpCarryoverL; delayBufsR[i][allpassIdx[i]] = (tmpBufR[i] * (-gReverbGainIndex)) / 256 + tmpCarryoverR; - if (i == 6) + if (i == reverbFilterRightChIdx) delayBufsR[i][allpassIdx[i]] += inSampleR; // Unique to right channel tmpCarryoverL = (delayBufsL[i][allpassIdx[i]] * gReverbGainIndex) / 256 + tmpBufL[i]; @@ -219,16 +237,16 @@ inline void reverb_mono_sample(s16 *outSample, s32 inSample) { s32 j = 0; u8 k = 0; s32 outTmp = 0; - s32 tmpCarryover = ((tmpBufL[NUM_ALLPASS-1] * gReverbRevIndex) / 256) + inSample; + s32 tmpCarryover = ((tmpBufL[reverbFilterCount-1] * gReverbRevIndex) / 256) + inSample; - for (; i < NUM_ALLPASS; ++i, ++j) { + for (; i < reverbFilterCount; ++i, ++j) { tmpBufL[i] = delayBufsL[i][allpassIdx[i]]; if (j == 2) { j = -1; - outTmp += (tmpBufL[i] * reverbMults[0][k++]) / 256; + outTmp += (tmpBufL[i] * reverbMultsL[k++]) / 256; delayBufsL[i][allpassIdx[i]] = tmpCarryover; - if (i != NUM_ALLPASS - 1) + if (i != reverbFilterCount - 1) tmpCarryover = (tmpBufL[i] * gReverbRevIndex) / 256; } else { @@ -551,6 +569,21 @@ u64 *synthesis_execute(u64 *cmdBuf, s32 *writtenCmds, s16 *aiBuf, s32 bufLen) { aSegment(cmdBuf, 0, 0); +#ifdef BETTER_REVERB + if (gIsConsole) { + reverbFilterCount = reverbFilterCountConsole; + monoReverb = monoReverbConsole; + } + else { + reverbFilterCount = reverbFilterCountEmulator; + monoReverb = monoReverbEmulator; + } + if (reverbFilterCount >= 6) + reverbFilterRightChIdx = 6; + else + reverbFilterRightChIdx = 3; +#endif + for (i = gAudioUpdatesPerFrame; i > 0; i--) { if (i == 1) { // 'bufLen' will automatically be divisible by 8, no need to round diff --git a/src/audio/synthesis.h b/src/audio/synthesis.h index 6d1f5916..83440eaf 100644 --- a/src/audio/synthesis.h +++ b/src/audio/synthesis.h @@ -27,15 +27,26 @@ #define NUM_ALLPASS 12 // Number of delay filters to use with better reverb; do not change this value if you don't know what you're doing. +extern s8 betterReverbDownsampleConsole; +extern s8 betterReverbDownsampleEmulator; +extern u32 reverbFilterCountConsole; +extern u32 reverbFilterCountEmulator; +extern u8 monoReverbConsole; +extern u8 monoReverbEmulator; extern s32 betterReverbWindowsSize; +extern s32 gReverbRevIndex; +extern s32 gReverbGainIndex; +extern s32 gReverbWetSigna; +// extern s32 gReverbDrySignal; + extern const s32 delaysBaseline[NUM_ALLPASS]; extern s32 delays[NUM_ALLPASS]; +extern s32 reverbMultsL[NUM_ALLPASS / 3]; +extern s32 reverbMultsR[NUM_ALLPASS / 3]; extern s32 **delayBufsL; extern s32 **delayBufsR; extern u8 toggleBetterReverb; -extern s8 betterReverbConsoleDownsample; -extern s8 betterReverbEmulatorDownsample; #else #define BETTER_REVERB_SIZE 0 #endif From 450e5ed8291dcf242d887c65c1d7fc1a8f5bf3b6 Mon Sep 17 00:00:00 2001 From: gheskett Date: Thu, 5 Aug 2021 00:03:42 -0500 Subject: [PATCH 05/11] Forgot to disable reverb --- include/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/config.h b/include/config.h index 43944115..c9002a94 100644 --- a/include/config.h +++ b/include/config.h @@ -155,7 +155,7 @@ #define IA8_COINS // Use a much better implementation of reverb over vanilla's fake echo reverb. Great for caves or eerie levels, as well as just a better audio experience in general. // Reverb parameters can be configured in audio/synthesis.c to meet desired aesthetic/performance needs. -#define BETTER_REVERB +//#define BETTER_REVERB // If you want to change the extended boundaries mode, go to engine/extended_bounds.h and change EXTENDED_BOUNDS_MODE From 29cc8055bae42f7ef2dfc9a209870434c385f84e Mon Sep 17 00:00:00 2001 From: gheskett Date: Thu, 5 Aug 2021 01:14:01 -0500 Subject: [PATCH 06/11] Configure delays parameters to allow for reduced filter count to not disrupt the right channel --- src/audio/heap.c | 10 +++-- src/audio/synthesis.c | 88 +++++++++++++++++++++++++------------------ src/audio/synthesis.h | 10 +++-- 3 files changed, 63 insertions(+), 45 deletions(-) diff --git a/src/audio/heap.c b/src/audio/heap.c index 4312f3c1..d7580eed 100644 --- a/src/audio/heap.c +++ b/src/audio/heap.c @@ -1457,14 +1457,16 @@ void audio_reset_session(void) { // However, reseting this allows for proper clearing of the reverb buffers, as well as dynamic customization of the delays array. #ifdef BETTER_REVERB if (toggleBetterReverb) { - for (i = 0; i < NUM_ALLPASS; ++i) - delays[i] = delaysBaseline[i] / gReverbDownsampleRate; + for (i = 0; i < NUM_ALLPASS; ++i) { + delaysL[i] = delaysBaselineL[i] / gReverbDownsampleRate; + delaysR[i] = delaysBaselineR[i] / gReverbDownsampleRate; + } delayBufsL = (s32**) soundAlloc(&gAudioSessionPool, NUM_ALLPASS * sizeof(s32*)); delayBufsR = (s32**) soundAlloc(&gAudioSessionPool, NUM_ALLPASS * sizeof(s32*)); for (i = 0; i < NUM_ALLPASS; ++i) { - delayBufsL[i] = (s32*) soundAlloc(&gAudioSessionPool, delays[i] * sizeof(s32)); - delayBufsR[i] = (s32*) soundAlloc(&gAudioSessionPool, delays[i] * sizeof(s32)); + delayBufsL[i] = (s32*) soundAlloc(&gAudioSessionPool, delaysL[i] * sizeof(s32)); + delayBufsR[i] = (s32*) soundAlloc(&gAudioSessionPool, delaysR[i] * sizeof(s32)); } } #endif diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index 26940d5f..dc870ff9 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -69,21 +69,23 @@ 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 this to 3 will only generate reverb in one channel, and setting it to anything less 0 will disable reverb outright. +// Setting this to 3 will only generate reverb in one channel (using mono is recommended here), and setting it to anything less 0 will disable reverb outright. // This can be changed at any time, but is best set when calling audio_reset_session. u32 reverbFilterCountConsole = NUM_ALLPASS - 3; // 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 this to 3 will only generate reverb in one channel, and setting it to anything less 0 will disable reverb outright. +// Ssetting it to anything less 3 will disable reverb outright. // This can be changed at any time, but is best set when calling audio_reset_session. u32 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. +// If performance is desirable, it is recommended to change reverbFilterCountConsole or betterReverbDownsampleConsole first. // This can be changed at any time, but is best set when 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. +// If performance is desirable, it is recommended to change reverbFilterCountEmulator or betterReverbDownsampleEmulator first. // This can be changed at any time, but is best set when calling audio_reset_session. u8 monoReverbEmulator = FALSE; @@ -109,36 +111,49 @@ s32 gReverbWetSignal = 0xE7; // Amount of reverb specific output in final signal // These values should never be changed unless in this declaration or during a call to audio_reset_session, as it could otherwise lead to a major memory leak or garbage audio. // None of the delay values should ever be smaller than 1 either; these are s32s purely to avoid typecasts. // These values are currently set by using delaysBaseline in the audio_reset_session function, so its behavior must be overridden to use dynamically (or at all). -s32 delays[NUM_ALLPASS] = { +s32 delaysL[NUM_ALLPASS] = { 1080, 1352, 1200, 1384, 1048, 1352, 1200, 1232, 1432, 928, 1504, 1512 }; +s32 delaysR[NUM_ALLPASS] = { + 1200, 1232, 1432, + 928, 1504, 1512, + 1080, 1352, 1200, + 1384, 1048, 1352 +}; // Like the delays array, but represents default max values that don't change (also probably somewhat redundant) // Change this array rather than the delays array to customize reverb delay times globally. // Similarly to delays, these should be kept within the memory constraints defined by BETTER_REVERB_SIZE. -const s32 delaysBaseline[NUM_ALLPASS] = { +const s32 delaysBaselineL[NUM_ALLPASS] = { 1080, 1352, 1200, 1384, 1048, 1352, 1200, 1232, 1432, 928, 1504, 1512 }; +const s32 delaysBaselineR[NUM_ALLPASS] = { + 1200, 1432, 1232, + 928, 1512, 1504, + 1080, 1200, 1352, + 1384, 1352, 1048 +}; // These values affect reverb decay depending on the filter index; can be messed with at any time -s32 reverbMultsL[NUM_ALLPASS / 3] = {0xD2, 0x6E, 0x36, 0x1F}; -s32 reverbMultsR[NUM_ALLPASS / 3] = {0x26, 0xCF, 0x71, 0x38}; +s32 reverbMultsL[NUM_ALLPASS / 3] = {0xD5, 0x6E, 0x36, 0x1F}; +s32 reverbMultsR[NUM_ALLPASS / 3] = {0xCF, 0x71, 0x38, 0x26}; /* -----------------------------------------------------------------------END REVERB PARAMETERS----------------------------------------------------------------------- */ // Do not touch these values manually, unless you want potential for problems. u32 reverbFilterCount = NUM_ALLPASS; -u32 reverbFilterRightChIdx = 6; +u32 reverbFilterCountm1 = NUM_ALLPASS - 1; u8 monoReverb = FALSE; u8 toggleBetterReverb = TRUE; -s32 allpassIdx[NUM_ALLPASS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +s32 allpassIdxL[NUM_ALLPASS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +s32 allpassIdxR[NUM_ALLPASS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; s32 tmpBufL[NUM_ALLPASS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; s32 tmpBufR[NUM_ALLPASS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; s32 **delayBufsL; @@ -195,37 +210,35 @@ inline void reverb_samples(s16 *outSampleL, s16 *outSampleR, s32 inSampleL, s32 u8 k = 0; s32 outTmpL = 0; s32 outTmpR = 0; - s32 tmpCarryoverL = ((tmpBufL[reverbFilterCount-1] * gReverbRevIndex) / 256) + inSampleL; // Unique to left channel - s32 tmpCarryoverR = ((tmpBufR[reverbFilterCount-1] * gReverbRevIndex) / 256); + s32 tmpCarryoverL = ((tmpBufL[reverbFilterCountm1] * gReverbRevIndex) / 256) + inSampleL; + s32 tmpCarryoverR = ((tmpBufR[reverbFilterCountm1] * gReverbRevIndex) / 256) + inSampleR; for (; i < reverbFilterCount; ++i, ++j) { - tmpBufL[i] = delayBufsL[i][allpassIdx[i]]; - tmpBufR[i] = delayBufsR[i][allpassIdx[i]]; + tmpBufL[i] = delayBufsL[i][allpassIdxL[i]]; + tmpBufR[i] = delayBufsR[i][allpassIdxR[i]]; if (j == 2) { j = -1; outTmpL += (tmpBufL[i] * reverbMultsL[k]) / 256; outTmpR += (tmpBufR[i] * reverbMultsR[k++]) / 256; - delayBufsL[i][allpassIdx[i]] = tmpCarryoverL; - delayBufsR[i][allpassIdx[i]] = tmpCarryoverR; - if (i != reverbFilterCount - 1) { + delayBufsL[i][allpassIdxL[i]] = tmpCarryoverL; + delayBufsR[i][allpassIdxR[i]] = tmpCarryoverR; + if (i != reverbFilterCountm1) { tmpCarryoverL = (tmpBufL[i] * gReverbRevIndex) / 256; tmpCarryoverR = (tmpBufR[i] * gReverbRevIndex) / 256; } } else { - delayBufsL[i][allpassIdx[i]] = (tmpBufL[i] * (-gReverbGainIndex)) / 256 + tmpCarryoverL; - delayBufsR[i][allpassIdx[i]] = (tmpBufR[i] * (-gReverbGainIndex)) / 256 + tmpCarryoverR; - - if (i == reverbFilterRightChIdx) - delayBufsR[i][allpassIdx[i]] += inSampleR; // Unique to right channel - - tmpCarryoverL = (delayBufsL[i][allpassIdx[i]] * gReverbGainIndex) / 256 + tmpBufL[i]; - tmpCarryoverR = (delayBufsR[i][allpassIdx[i]] * gReverbGainIndex) / 256 + tmpBufR[i]; + delayBufsL[i][allpassIdxL[i]] = (tmpBufL[i] * (-gReverbGainIndex)) / 256 + tmpCarryoverL; + delayBufsR[i][allpassIdxR[i]] = (tmpBufR[i] * (-gReverbGainIndex)) / 256 + tmpCarryoverR; + tmpCarryoverL = (delayBufsL[i][allpassIdxL[i]] * gReverbGainIndex) / 256 + tmpBufL[i]; + tmpCarryoverR = (delayBufsR[i][allpassIdxR[i]] * gReverbGainIndex) / 256 + tmpBufR[i]; } - if (++allpassIdx[i] == delays[i]) - allpassIdx[i] = 0; + if (++allpassIdxL[i] == delaysL[i]) + allpassIdxL[i] = 0; + if (++allpassIdxR[i] == delaysR[i]) + allpassIdxR[i] = 0; } *outSampleL = clamp16((outTmpL * gReverbWetSignal/* + inSampleL * gReverbDrySignal*/) / 256); @@ -237,25 +250,25 @@ inline void reverb_mono_sample(s16 *outSample, s32 inSample) { s32 j = 0; u8 k = 0; s32 outTmp = 0; - s32 tmpCarryover = ((tmpBufL[reverbFilterCount-1] * gReverbRevIndex) / 256) + inSample; + s32 tmpCarryover = ((tmpBufL[reverbFilterCountm1] * gReverbRevIndex) / 256) + inSample; for (; i < reverbFilterCount; ++i, ++j) { - tmpBufL[i] = delayBufsL[i][allpassIdx[i]]; + tmpBufL[i] = delayBufsL[i][allpassIdxL[i]]; if (j == 2) { j = -1; outTmp += (tmpBufL[i] * reverbMultsL[k++]) / 256; - delayBufsL[i][allpassIdx[i]] = tmpCarryover; - if (i != reverbFilterCount - 1) + delayBufsL[i][allpassIdxL[i]] = tmpCarryover; + if (i != reverbFilterCountm1) tmpCarryover = (tmpBufL[i] * gReverbRevIndex) / 256; } else { - delayBufsL[i][allpassIdx[i]] = (tmpBufL[i] * (-gReverbGainIndex)) / 256 + tmpCarryover; - tmpCarryover = (delayBufsL[i][allpassIdx[i]] * gReverbGainIndex) / 256 + tmpBufL[i]; + delayBufsL[i][allpassIdxL[i]] = (tmpBufL[i] * (-gReverbGainIndex)) / 256 + tmpCarryover; + tmpCarryover = (delayBufsL[i][allpassIdxL[i]] * gReverbGainIndex) / 256 + tmpBufL[i]; } - if (++allpassIdx[i] == delays[i]) - allpassIdx[i] = 0; + if (++allpassIdxL[i] == delaysL[i]) + allpassIdxL[i] = 0; } *outSample = clamp16((outTmp * gReverbWetSignal/* + inSample * gReverbDrySignal*/) / 256); @@ -578,10 +591,11 @@ u64 *synthesis_execute(u64 *cmdBuf, s32 *writtenCmds, s16 *aiBuf, s32 bufLen) { reverbFilterCount = reverbFilterCountEmulator; monoReverb = monoReverbEmulator; } - if (reverbFilterCount >= 6) - reverbFilterRightChIdx = 6; - else - reverbFilterRightChIdx = 3; + if (reverbFilterCount > NUM_ALLPASS) + reverbFilterCount = NUM_ALLPASS; + reverbFilterCountm1 = reverbFilterCount - 1; + if (reverbFilterCount < 3) + reverbFilterCountm1 = 0; #endif for (i = gAudioUpdatesPerFrame; i > 0; i--) { diff --git a/src/audio/synthesis.h b/src/audio/synthesis.h index 83440eaf..ee1d96ce 100644 --- a/src/audio/synthesis.h +++ b/src/audio/synthesis.h @@ -18,8 +18,8 @@ #endif #ifdef BETTER_REVERB - // Size determined by ((all delaysBaseline values * 16) / (2 ^ Minimum Downsample Factor)) + array pointers. - // The default value can be increased or decreased in conjunction with the values in delaysBaseline + // Size determined by ((all delaysBaselineL/R values * 8) / (2 ^ Minimum Downsample Factor)) + array pointers. + // The default value can be increased or decreased in conjunction with the values in delaysBaselineL/R #define BETTER_REVERB_SIZE 0xF200 // #define BETTER_REVERB_SIZE 0x7A00 // Default for use only with a downsampling value of 3 (i.e. double the emulator default) @@ -39,8 +39,10 @@ extern s32 gReverbGainIndex; extern s32 gReverbWetSigna; // extern s32 gReverbDrySignal; -extern const s32 delaysBaseline[NUM_ALLPASS]; -extern s32 delays[NUM_ALLPASS]; +extern const s32 delaysBaselineL[NUM_ALLPASS]; +extern const 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; From f816c1ebd739588f10009df221bf4e32333b91a9 Mon Sep 17 00:00:00 2001 From: gheskett Date: Thu, 5 Aug 2021 01:19:55 -0500 Subject: [PATCH 07/11] Update reverb documentation --- src/audio/synthesis.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index dc870ff9..fcf4d6c6 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -106,8 +106,8 @@ s32 gReverbWetSignal = 0xE7; // Amount of reverb specific output in final signal /* ---------------------------------------------------------------------ADVANCED REVERB PARAMETERS-------------------------------------------------------------------- */ -// These values affect filter delays. Bigger values will result in fatter echo (and more memory); must be cumulatively smaller than BETTER_REVERB_SIZE/4. -// If setting a reverb downsample value to 1, this must be smaller than BETTER_REVERB_SIZE/8. +// These values affect filter delays. Bigger values will result in fatter echo (and more memory); must be cumulatively smaller than BETTER_REVERB_SIZE/2. +// If setting a reverb downsample value to 1, these must be cumulatively smaller than BETTER_REVERB_SIZE/4. // These values should never be changed unless in this declaration or during a call to audio_reset_session, as it could otherwise lead to a major memory leak or garbage audio. // None of the delay values should ever be smaller than 1 either; these are s32s purely to avoid typecasts. // These values are currently set by using delaysBaseline in the audio_reset_session function, so its behavior must be overridden to use dynamically (or at all). @@ -126,7 +126,7 @@ s32 delaysR[NUM_ALLPASS] = { // Like the delays array, but represents default max values that don't change (also probably somewhat redundant) // Change this array rather than the delays array to customize reverb delay times globally. -// Similarly to delays, these should be kept within the memory constraints defined by BETTER_REVERB_SIZE. +// Similarly to delaysL/R, these should be kept within the memory constraints defined by BETTER_REVERB_SIZE. const s32 delaysBaselineL[NUM_ALLPASS] = { 1080, 1352, 1200, 1384, 1048, 1352, From 50f472bd40a49c7bae4805550ba773d61e022fac Mon Sep 17 00:00:00 2001 From: gheskett Date: Thu, 5 Aug 2021 01:43:25 -0500 Subject: [PATCH 08/11] Fix -Os compilation flag with reverb use --- src/audio/synthesis.c | 6 +++--- src/game/behaviors/snow_mound.inc.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index fcf4d6c6..f1d1627d 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -195,7 +195,7 @@ u8 sAudioSynthesisPad[0x20]; #endif #ifdef BETTER_REVERB -inline s16 clamp16(s32 x) { +static inline s16 clamp16(s32 x) { if (x >= 32767) return 32767; if (x <= -32768) @@ -204,7 +204,7 @@ inline s16 clamp16(s32 x) { return (s16) x; } -inline void reverb_samples(s16 *outSampleL, s16 *outSampleR, s32 inSampleL, s32 inSampleR) { +static inline void reverb_samples(s16 *outSampleL, s16 *outSampleR, s32 inSampleL, s32 inSampleR) { u32 i = 0; s32 j = 0; u8 k = 0; @@ -245,7 +245,7 @@ inline void reverb_samples(s16 *outSampleL, s16 *outSampleR, s32 inSampleL, s32 *outSampleR = clamp16((outTmpR * gReverbWetSignal/* + inSampleR * gReverbDrySignal*/) / 256); } -inline void reverb_mono_sample(s16 *outSample, s32 inSample) { +static inline void reverb_mono_sample(s16 *outSample, s32 inSample) { u32 i = 0; s32 j = 0; u8 k = 0; diff --git a/src/game/behaviors/snow_mound.inc.c b/src/game/behaviors/snow_mound.inc.c index f8fcc539..b9bfb978 100644 --- a/src/game/behaviors/snow_mound.inc.c +++ b/src/game/behaviors/snow_mound.inc.c @@ -33,7 +33,7 @@ void bhv_snow_mound_spawn_loop(void) { if (o->oTimer == 64 || o->oTimer == 128 || o->oTimer == 192 || o->oTimer == 224 || o->oTimer == 256) sp1C = spawn_object(o, MODEL_SL_SNOW_TRIANGLE, bhvSlidingSnowMound); - if (o->oTimer == 256) { + if (sp1C && o->oTimer == 256) { sp1C->header.gfx.scale[0] = 2.0f; sp1C->header.gfx.scale[1] = 2.0f; } From bf4cbc304e5bf3bb852d97c58a058890651c9a3f Mon Sep 17 00:00:00 2001 From: gheskett Date: Thu, 5 Aug 2021 02:17:01 -0500 Subject: [PATCH 09/11] Balance reverb L/R channels --- src/audio/synthesis.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index f1d1627d..47578b41 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -51,6 +51,11 @@ * To use with M64 sequences, set the Effect parameter for each channel accordingly (CC 91 for MIDI files). * * Most parameter configuration is to be done here, though BETTER_REVERB_SIZE can be adjusted in audio/synthesis.h. + * + * If after changing the parameters, you hear increasing noise followed by a sudden disappearance of reverb and/or scratchy audio, this indicates an s16 overflow. + * If this happens, stop immediately and reduce the parameters at fault. This becomes a ticking time bomb, and may eventually result in very loud noise if it reaches the point of s32 overflow. + * Checks to prevent this have not been implemented to maximize performance potential, so choose your parameters wisely. + * Generally speaking, a sound that doesn't seem to be fading is a parameter red flag (also known as feedback). */ @@ -113,15 +118,15 @@ s32 gReverbWetSignal = 0xE7; // Amount of reverb specific output in final signal // These values are currently set by using delaysBaseline in the audio_reset_session function, so its behavior must be overridden to use dynamically (or at all). s32 delaysL[NUM_ALLPASS] = { 1080, 1352, 1200, - 1384, 1048, 1352, 1200, 1232, 1432, + 1384, 1048, 1352, 928, 1504, 1512 }; s32 delaysR[NUM_ALLPASS] = { - 1200, 1232, 1432, - 928, 1504, 1512, - 1080, 1352, 1200, - 1384, 1048, 1352 + 1384, 1352, 1048, + 928, 1512, 1504, + 1080, 1200, 1352, + 1200, 1432, 1232 }; // Like the delays array, but represents default max values that don't change (also probably somewhat redundant) @@ -129,20 +134,20 @@ s32 delaysR[NUM_ALLPASS] = { // Similarly to delaysL/R, these should be kept within the memory constraints defined by BETTER_REVERB_SIZE. const s32 delaysBaselineL[NUM_ALLPASS] = { 1080, 1352, 1200, - 1384, 1048, 1352, 1200, 1232, 1432, + 1384, 1048, 1352, 928, 1504, 1512 }; const s32 delaysBaselineR[NUM_ALLPASS] = { - 1200, 1432, 1232, + 1384, 1352, 1048, 928, 1512, 1504, 1080, 1200, 1352, - 1384, 1352, 1048 + 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] = {0xD5, 0x6E, 0x36, 0x1F}; -s32 reverbMultsR[NUM_ALLPASS / 3] = {0xCF, 0x71, 0x38, 0x26}; +s32 reverbMultsL[NUM_ALLPASS / 3] = {0xD7, 0x6F, 0x36, 0x22}; +s32 reverbMultsR[NUM_ALLPASS / 3] = {0xCF, 0x73, 0x38, 0x1F}; /* -----------------------------------------------------------------------END REVERB PARAMETERS----------------------------------------------------------------------- */ From e9d75c55ba1eaf42ed8a02b27d33ffd272766412 Mon Sep 17 00:00:00 2001 From: gheskett Date: Thu, 5 Aug 2021 02:36:28 -0500 Subject: [PATCH 10/11] Reverb documentation typo --- src/audio/synthesis.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index 47578b41..c88237c6 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -74,13 +74,13 @@ 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 this to 3 will only generate reverb in one channel (using mono is recommended here), and setting it to anything less 0 will disable reverb outright. +// Setting it to anything less 3 will disable reverb outright. // This can be changed at any time, but is best set when calling audio_reset_session. u32 reverbFilterCountConsole = NUM_ALLPASS - 3; // 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. -// Ssetting it to anything less 3 will disable reverb outright. +// Setting it to anything less 3 will disable reverb outright. // This can be changed at any time, but is best set when calling audio_reset_session. u32 reverbFilterCountEmulator = NUM_ALLPASS; From 24253c7598ec0c1e170640cfdccf26e76e3bf166 Mon Sep 17 00:00:00 2001 From: gheskett Date: Thu, 5 Aug 2021 02:45:45 -0500 Subject: [PATCH 11/11] Reverb documentation clarification --- src/audio/synthesis.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index c88237c6..38f242f1 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -105,7 +105,7 @@ s32 gReverbRevIndex = 0x5F; // Affects decay time mostly (large values can cause s32 gReverbGainIndex = 0x9F; // Affects signal immediately retransmitted back into buffers (mid-high values yield the strongest effect); can be messed with at any time s32 gReverbWetSignal = 0xE7; // Amount of reverb specific output in final signal (also affects decay); can be messed with at any time, also very easy to control -// s32 gReverbDrySignal = 0x00; // Amount of original input in final signal (large values can cause terrible feedback!); disabled by default to improve compiler optimization +// s32 gReverbDrySignal = 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 /* ---------------------------------------------------------------------ADVANCED REVERB PARAMETERS-------------------------------------------------------------------- */