From e9f6588d21107d67fb12981cb8e16fc10152546e Mon Sep 17 00:00:00 2001 From: gheskett Date: Mon, 2 Aug 2021 21:14:31 -0500 Subject: [PATCH 01/20] Better reverb implementation --- include/config.h | 2 + src/audio/data.h | 5 +- src/audio/heap.c | 16 +++++- src/audio/synthesis.c | 128 ++++++++++++++++++++++++++++++++++++++++++ src/audio/synthesis.h | 11 ++++ src/buffers/buffers.c | 3 +- 6 files changed, 160 insertions(+), 5 deletions(-) diff --git a/include/config.h b/include/config.h index 66c99565..c90dfdc9 100644 --- a/include/config.h +++ b/include/config.h @@ -151,6 +151,8 @@ #define DISABLE_AA // Makes the coins ia8 64x64 instead of ia16 32x32. Uses new ia8 textures so that vanilla coins look better. #define IA8_COINS +// tmp +// #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/data.h b/src/audio/data.h index cf19472d..a8bc7f78 100644 --- a/src/audio/data.h +++ b/src/audio/data.h @@ -5,6 +5,7 @@ #include "internal.h" #include "types.h" +#include "synthesis.h" #define AUDIO_LOCK_UNINITIALIZED 0 #define AUDIO_LOCK_NOT_LOADING 0x76557364 @@ -150,11 +151,11 @@ extern OSMesgQueue *D_SH_80350FA8; #if defined(VERSION_EU) || defined(VERSION_SH) #define UNUSED_COUNT_80333EE8 24 -#define AUDIO_HEAP_SIZE (0x2c500 + EXT_AUDIO_HEAP_SIZE + EXT_AUDIO_INIT_POOL_SIZE) +#define AUDIO_HEAP_SIZE (0x2c500 + EXT_AUDIO_HEAP_SIZE + EXT_AUDIO_INIT_POOL_SIZE + BETTER_REVERB_SIZE) #define AUDIO_INIT_POOL_SIZE (0x2c00 + EXT_AUDIO_INIT_POOL_SIZE) #else #define UNUSED_COUNT_80333EE8 16 -#define AUDIO_HEAP_SIZE (0x31150 + EXT_AUDIO_HEAP_SIZE + EXT_AUDIO_INIT_POOL_SIZE) +#define AUDIO_HEAP_SIZE (0x31150 + EXT_AUDIO_HEAP_SIZE + EXT_AUDIO_INIT_POOL_SIZE + BETTER_REVERB_SIZE) #define AUDIO_INIT_POOL_SIZE (0x2500 + EXT_AUDIO_INIT_POOL_SIZE) #endif diff --git a/src/audio/heap.c b/src/audio/heap.c index e0fa3ec4..0f19962b 100644 --- a/src/audio/heap.c +++ b/src/audio/heap.c @@ -1229,6 +1229,10 @@ void audio_reset_session(void) { gMaxSimultaneousNotes = preset->maxSimultaneousNotes; gSamplesPerFrameTarget = ALIGN16(gAiFrequency / 60); gReverbDownsampleRate = preset->reverbDownsampleRate; +#ifdef BETTER_REVERB + if (gReverbDownsampleRate == 1) + gReverbDownsampleRate = 2; +#endif switch (gReverbDownsampleRate) { case 1: @@ -1250,7 +1254,6 @@ void audio_reset_session(void) { sReverbDownsampleRateLog = 0; } - gReverbDownsampleRate = preset->reverbDownsampleRate; gVolume = preset->volume; gMinAiBufferLength = gSamplesPerFrameTarget - 0x10; updatesPerFrame = gSamplesPerFrameTarget / 160 + 1; @@ -1278,7 +1281,7 @@ void audio_reset_session(void) { temporaryMem = DOUBLE_SIZE_ON_64_BIT(preset->temporaryBankMem + preset->temporarySeqMem); #endif totalMem = persistentMem + temporaryMem; - wantMisc = gAudioSessionPool.size - totalMem - 0x100; + wantMisc = gAudioSessionPool.size - totalMem - 0x100 - BETTER_REVERB_SIZE; sSessionPoolSplit.wantSeq = wantMisc; sSessionPoolSplit.wantCustom = totalMem; session_pools_init(&sSessionPoolSplit); @@ -1421,6 +1424,15 @@ void audio_reset_session(void) { gSynthesisReverb.items[1][i].toDownsampleRight = mem + DEFAULT_LEN_1CH / sizeof(s16); } } +#ifdef BETTER_REVERB + 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*)); + 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)); + } +#endif } #endif diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index f35e491d..a7355c65 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -44,6 +44,31 @@ struct VolumeChange { u16 targetRight; }; +s16 *lastSamplePtrL; +s16 *lastSamplePtrR; + +f32 gReverbRevIndex = 0.6f; +f32 gReverbGainIndex = 0.65f; +f32 gReverbWetSignal = 0.95f; +f32 gReverbDrySignal = 0.2f; + +u32 delays[NUM_ALLPASS] = { + 540, 675, 600, + 690, 525, 675, + 600, 615, 715, + 465, 750, 755 +}; +const f32 reverbMults[2][NUM_ALLPASS / 3] = { + {0.82f, 0.43f, 0.21f, 0.12f}, + {0.22f, 0.15f, 0.81f, 0.44f} +}; +u32 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 tmpBuf[NUM_ALLPASS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +s32 ***delayBufs; + u64 *synthesis_do_one_audio_update(s16 *aiBuf, s32 bufLen, u64 *cmd, s32 updateIndex); #ifdef VERSION_EU u64 *synthesis_process_note(struct Note *note, struct NoteSubEu *noteSubEu, struct NoteSynthesisState *synthesisState, s16 *aiBuf, s32 bufLen, u64 *cmd); @@ -70,6 +95,93 @@ struct SynthesisReverb gSynthesisReverb; u8 sAudioSynthesisPad[0x20]; #endif +s16 clamp16(s32 x) { + if (x >= 32767) + return 32767; + if (x <= -32768) + return -32768; + + return (s16) x; +} + +s16 reverb_sample_left(s16 inSample) { + u32 i = 0; + s32 outTmp = 0; + s32 tmpCarryover = 0; + // u32 modCheck; + + for (; i < NUM_ALLPASS; ++i) { + tmpBuf[i] = delayBufs[0][i][allpassIdx[0][i]]; + } + + for (i = 0; i < NUM_ALLPASS; ++i) { + // modCheck = i % 6; + + if (/*modCheck == 2 || modCheck == 5*/ i % 3 == 2) { + outTmp += tmpBuf[i] * reverbMults[0][i / 3]; + delayBufs[0][i][allpassIdx[0][i]] = tmpCarryover; + if (i != NUM_ALLPASS - 1) + tmpCarryover = (tmpBuf[i] * gReverbRevIndex); + } + else { + if (i == 0) + tmpCarryover += (tmpBuf[NUM_ALLPASS-1] * gReverbRevIndex); + + delayBufs[0][i][allpassIdx[0][i]] = tmpBuf[i] * (-gReverbGainIndex) + tmpCarryover; + + if (/*modCheck == 0*/ i == 0) + delayBufs[0][i][allpassIdx[0][i]] += inSample; + + tmpCarryover = (delayBufs[0][i][allpassIdx[0][i]] * gReverbGainIndex); + tmpCarryover += tmpBuf[i]; + } + + if (++allpassIdx[0][i] == delays[i]) + allpassIdx[0][i] = 0; + } + + return clamp16(outTmp * gReverbWetSignal + inSample * gReverbDrySignal); +} + +s16 reverb_sample_right(s16 inSample) { + u32 i = 0; + s32 outTmp = 0; + s32 tmpCarryover = 0; + // u32 modCheck; + + for (; i < NUM_ALLPASS; ++i) { + tmpBuf[i] = delayBufs[1][i][allpassIdx[1][i]]; + } + + for (i = 0; i < NUM_ALLPASS; ++i) { + // modCheck = i % 6; + + if (/*modCheck == 2 || modCheck == 5*/ i % 3 == 2) { + outTmp += tmpBuf[i] * reverbMults[1][i / 3]; + delayBufs[1][i][allpassIdx[1][i]] = tmpCarryover; + if (i != NUM_ALLPASS - 1) + tmpCarryover = (tmpBuf[i] * gReverbRevIndex); + } + else { + if (i == 0) + tmpCarryover += (tmpBuf[NUM_ALLPASS-1] * gReverbRevIndex); + + delayBufs[1][i][allpassIdx[1][i]] = tmpBuf[i] * (-gReverbGainIndex) + tmpCarryover; + + if (/*modCheck == 3*/ i == 6) + delayBufs[1][i][allpassIdx[1][i]] += inSample; + + tmpCarryover = (delayBufs[1][i][allpassIdx[1][i]] * gReverbGainIndex); + tmpCarryover += tmpBuf[i]; + } + + if (++allpassIdx[1][i] == delays[i]) + allpassIdx[1][i] = 0; + } + + return clamp16(outTmp * gReverbWetSignal + inSample * gReverbDrySignal); +} + #ifdef VERSION_EU s16 gVolume; s8 gUseReverb; @@ -149,6 +261,8 @@ void prepare_reverb_ring_buffer(s32 chunkLen, u32 updateIndex) { s32 nSamples; s32 numSamplesAfterDownsampling; s32 excessiveSamples; + +#ifndef BETTER_REVERB if (gReverbDownsampleRate != 1) { if (gSynthesisReverb.framesLeftToIgnore == 0) { // Now that the RSP has finished, downsample the samples produced two frames ago by skipping @@ -171,6 +285,20 @@ void prepare_reverb_ring_buffer(s32 chunkLen, u32 updateIndex) { } } } +#else + item = &gSynthesisReverb.items[gSynthesisReverb.curFrame][updateIndex]; + if (gReverbDownsampleRate == 2) { + for (srcPos = 0, dstPos = 0; dstPos < item->lengthA / 2; + srcPos += gReverbDownsampleRate, dstPos++) { + gSynthesisReverb.ringBuffer.left[dstPos + item->startPos] = reverb_sample_left(item->toDownsampleLeft[srcPos]); + gSynthesisReverb.ringBuffer.right[dstPos + item->startPos] = 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]); + } + } +#endif item = &gSynthesisReverb.items[gSynthesisReverb.curFrame][updateIndex]; numSamplesAfterDownsampling = chunkLen / gReverbDownsampleRate; diff --git a/src/audio/synthesis.h b/src/audio/synthesis.h index 9ca86439..fc739dcc 100644 --- a/src/audio/synthesis.h +++ b/src/audio/synthesis.h @@ -17,6 +17,17 @@ #define MAX_UPDATES_PER_FRAME 4 #endif +#ifdef BETTER_REVERB +#define BETTER_REVERB_SIZE 0xF000 // Size of ~all delay values * 8 +#else +#define BETTER_REVERB_SIZE 0 +#endif + +#define NUM_ALLPASS 12 + +extern u32 delays[NUM_ALLPASS]; +extern s32 ***delayBufs; + struct ReverbRingBufferItem { s16 numSamplesAfterDownsampling; diff --git a/src/buffers/buffers.c b/src/buffers/buffers.c index 1a2c2bc7..95174530 100644 --- a/src/buffers/buffers.c +++ b/src/buffers/buffers.c @@ -7,6 +7,7 @@ #include #endif #include "config.h" +#include "audio/synthesis.h" ALIGNED8 u8 gDecompressionHeap[0xD000]; #if defined(VERSION_EU) @@ -14,7 +15,7 @@ ALIGNED16 u8 gAudioHeap[DOUBLE_SIZE_ON_64_BIT(0x31200 + EXT_AUDIO_HEAP_SIZE + EX #elif defined(VERSION_SH) ALIGNED16 u8 gAudioHeap[DOUBLE_SIZE_ON_64_BIT(0x31200 + EXT_AUDIO_HEAP_SIZE + EXT_AUDIO_INIT_POOL_SIZE) - 0x4800]; #else -ALIGNED16 u8 gAudioHeap[DOUBLE_SIZE_ON_64_BIT(0x31200 + EXT_AUDIO_HEAP_SIZE + EXT_AUDIO_INIT_POOL_SIZE)]; +ALIGNED16 u8 gAudioHeap[DOUBLE_SIZE_ON_64_BIT(0x31200 + EXT_AUDIO_HEAP_SIZE + EXT_AUDIO_INIT_POOL_SIZE + BETTER_REVERB_SIZE)]; #endif ALIGNED8 u8 gIdleThreadStack[0x800]; From 7ef645331681c3ea9630ea4ca74af5e269d6a261 Mon Sep 17 00:00:00 2001 From: gheskett Date: Tue, 3 Aug 2021 00:58:22 -0500 Subject: [PATCH 02/20] Speed improvements to the reverb --- src/audio/synthesis.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index a7355c65..5cdc21e5 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -47,10 +47,11 @@ struct VolumeChange { s16 *lastSamplePtrL; s16 *lastSamplePtrR; -f32 gReverbRevIndex = 0.6f; -f32 gReverbGainIndex = 0.65f; -f32 gReverbWetSignal = 0.95f; -f32 gReverbDrySignal = 0.2f; +// Treat these like percentages; not recommended to exceed 100 +s32 gReverbRevIndex = 60; +s32 gReverbGainIndex = 65; +s32 gReverbWetSignal = 95; +s32 gReverbDrySignal = 15; u32 delays[NUM_ALLPASS] = { 540, 675, 600, @@ -95,7 +96,7 @@ struct SynthesisReverb gSynthesisReverb; u8 sAudioSynthesisPad[0x20]; #endif -s16 clamp16(s32 x) { +inline s16 clamp16(s32 x) { if (x >= 32767) return 32767; if (x <= -32768) @@ -104,7 +105,7 @@ s16 clamp16(s32 x) { return (s16) x; } -s16 reverb_sample_left(s16 inSample) { +inline s16 reverb_sample_left(s16 inSample) { u32 i = 0; s32 outTmp = 0; s32 tmpCarryover = 0; @@ -121,18 +122,18 @@ s16 reverb_sample_left(s16 inSample) { outTmp += tmpBuf[i] * reverbMults[0][i / 3]; delayBufs[0][i][allpassIdx[0][i]] = tmpCarryover; if (i != NUM_ALLPASS - 1) - tmpCarryover = (tmpBuf[i] * gReverbRevIndex); + tmpCarryover = (tmpBuf[i] * gReverbRevIndex) / 100; } else { if (i == 0) - tmpCarryover += (tmpBuf[NUM_ALLPASS-1] * gReverbRevIndex); + tmpCarryover += ((tmpBuf[NUM_ALLPASS-1] * gReverbRevIndex) / 100); - delayBufs[0][i][allpassIdx[0][i]] = tmpBuf[i] * (-gReverbGainIndex) + tmpCarryover; + delayBufs[0][i][allpassIdx[0][i]] = (tmpBuf[i] * (-gReverbGainIndex) / 100) + tmpCarryover; if (/*modCheck == 0*/ i == 0) delayBufs[0][i][allpassIdx[0][i]] += inSample; - tmpCarryover = (delayBufs[0][i][allpassIdx[0][i]] * gReverbGainIndex); + tmpCarryover = (delayBufs[0][i][allpassIdx[0][i]] * gReverbGainIndex) / 100; tmpCarryover += tmpBuf[i]; } @@ -140,10 +141,10 @@ s16 reverb_sample_left(s16 inSample) { allpassIdx[0][i] = 0; } - return clamp16(outTmp * gReverbWetSignal + inSample * gReverbDrySignal); + return clamp16((outTmp * gReverbWetSignal + inSample * gReverbDrySignal) / 100); } -s16 reverb_sample_right(s16 inSample) { +inline s16 reverb_sample_right(s16 inSample) { u32 i = 0; s32 outTmp = 0; s32 tmpCarryover = 0; @@ -160,18 +161,18 @@ s16 reverb_sample_right(s16 inSample) { outTmp += tmpBuf[i] * reverbMults[1][i / 3]; delayBufs[1][i][allpassIdx[1][i]] = tmpCarryover; if (i != NUM_ALLPASS - 1) - tmpCarryover = (tmpBuf[i] * gReverbRevIndex); + tmpCarryover = (tmpBuf[i] * gReverbRevIndex) / 100; } else { if (i == 0) - tmpCarryover += (tmpBuf[NUM_ALLPASS-1] * gReverbRevIndex); + tmpCarryover += ((tmpBuf[NUM_ALLPASS-1] * gReverbRevIndex) / 100); - delayBufs[1][i][allpassIdx[1][i]] = tmpBuf[i] * (-gReverbGainIndex) + tmpCarryover; + delayBufs[1][i][allpassIdx[1][i]] = (tmpBuf[i] * (-gReverbGainIndex)) / 100 + tmpCarryover; if (/*modCheck == 3*/ i == 6) delayBufs[1][i][allpassIdx[1][i]] += inSample; - tmpCarryover = (delayBufs[1][i][allpassIdx[1][i]] * gReverbGainIndex); + tmpCarryover = (delayBufs[1][i][allpassIdx[1][i]] * gReverbGainIndex) / 100; tmpCarryover += tmpBuf[i]; } @@ -179,7 +180,7 @@ s16 reverb_sample_right(s16 inSample) { allpassIdx[1][i] = 0; } - return clamp16(outTmp * gReverbWetSignal + inSample * gReverbDrySignal); + return clamp16((outTmp * gReverbWetSignal + inSample * gReverbDrySignal) / 100); } #ifdef VERSION_EU @@ -287,7 +288,7 @@ void prepare_reverb_ring_buffer(s32 chunkLen, u32 updateIndex) { } #else item = &gSynthesisReverb.items[gSynthesisReverb.curFrame][updateIndex]; - if (gReverbDownsampleRate == 2) { + if (gReverbDownsampleRate != 1) { for (srcPos = 0, dstPos = 0; dstPos < item->lengthA / 2; srcPos += gReverbDownsampleRate, dstPos++) { gSynthesisReverb.ringBuffer.left[dstPos + item->startPos] = reverb_sample_left(item->toDownsampleLeft[srcPos]); From dc700301498ce27e042db2c9f0a39d325ff24a2d Mon Sep 17 00:00:00 2001 From: gheskett Date: Tue, 3 Aug 2021 01:45:33 -0500 Subject: [PATCH 03/20] More reverb fixey stuffs --- include/config.h | 2 +- src/audio/heap.c | 14 ++++++++++++-- src/audio/synthesis.c | 26 ++++++++++++++++---------- src/audio/synthesis.h | 1 + 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/include/config.h b/include/config.h index c90dfdc9..55596178 100644 --- a/include/config.h +++ b/include/config.h @@ -152,7 +152,7 @@ // Makes the coins ia8 64x64 instead of ia16 32x32. Uses new ia8 textures so that vanilla coins look better. #define IA8_COINS // tmp -// #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 0f19962b..1dea272c 100644 --- a/src/audio/heap.c +++ b/src/audio/heap.c @@ -1117,6 +1117,7 @@ void audio_reset_session(void) { #if defined(VERSION_JP) || defined(VERSION_US) s32 frames; s32 remainingDmas; + s8 reverbConsole; #else struct SynthesisReverb *reverb; #endif @@ -1230,8 +1231,14 @@ void audio_reset_session(void) { gSamplesPerFrameTarget = ALIGN16(gAiFrequency / 60); gReverbDownsampleRate = preset->reverbDownsampleRate; #ifdef BETTER_REVERB - if (gReverbDownsampleRate == 1) - gReverbDownsampleRate = 2; + if (IO_READ(DPC_PIPEBUSY_REG) != 0) + reverbConsole = 3; // Is a console user; change to 4 if still too slow + else + reverbConsole = 2; + + if (gReverbDownsampleRate < reverbConsole) + gReverbDownsampleRate = reverbConsole; + reverbWindowSize /= (1 << (gReverbDownsampleRate - 1)); #endif switch (gReverbDownsampleRate) { @@ -1425,6 +1432,9 @@ void audio_reset_session(void) { } } #ifdef BETTER_REVERB + for (i = 0; i < NUM_ALLPASS; ++i) + delays[i] = delaysBaseline[i] / (1 << (gReverbDownsampleRate - 1)); + 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*)); diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index 5cdc21e5..23da9e47 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -53,15 +53,21 @@ s32 gReverbGainIndex = 65; s32 gReverbWetSignal = 95; s32 gReverbDrySignal = 15; -u32 delays[NUM_ALLPASS] = { - 540, 675, 600, - 690, 525, 675, - 600, 615, 715, - 465, 750, 755 +const u32 delaysBaseline[NUM_ALLPASS] = { + 1080, 1352, 1200, + 1384, 1048, 1352, + 1200, 1232, 1432, + 928, 1504, 1512 }; -const f32 reverbMults[2][NUM_ALLPASS / 3] = { - {0.82f, 0.43f, 0.21f, 0.12f}, - {0.22f, 0.15f, 0.81f, 0.44f} +u32 delays[NUM_ALLPASS] = { + 1080, 1352, 1200, + 1384, 1048, 1352, + 1200, 1232, 1432, + 928, 1504, 1512 +}; +const s32 reverbMults[2][NUM_ALLPASS / 3] = { + {82, 43, 21, 12}, + {22, 15, 81, 44} }; u32 allpassIdx[2][NUM_ALLPASS] = { {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, @@ -119,7 +125,7 @@ inline s16 reverb_sample_left(s16 inSample) { // modCheck = i % 6; if (/*modCheck == 2 || modCheck == 5*/ i % 3 == 2) { - outTmp += tmpBuf[i] * reverbMults[0][i / 3]; + outTmp += (tmpBuf[i] * reverbMults[0][i / 3]) / 100; delayBufs[0][i][allpassIdx[0][i]] = tmpCarryover; if (i != NUM_ALLPASS - 1) tmpCarryover = (tmpBuf[i] * gReverbRevIndex) / 100; @@ -158,7 +164,7 @@ inline s16 reverb_sample_right(s16 inSample) { // modCheck = i % 6; if (/*modCheck == 2 || modCheck == 5*/ i % 3 == 2) { - outTmp += tmpBuf[i] * reverbMults[1][i / 3]; + outTmp += (tmpBuf[i] * reverbMults[1][i / 3]) / 100; delayBufs[1][i][allpassIdx[1][i]] = tmpCarryover; if (i != NUM_ALLPASS - 1) tmpCarryover = (tmpBuf[i] * gReverbRevIndex) / 100; diff --git a/src/audio/synthesis.h b/src/audio/synthesis.h index fc739dcc..48b9857e 100644 --- a/src/audio/synthesis.h +++ b/src/audio/synthesis.h @@ -25,6 +25,7 @@ #define NUM_ALLPASS 12 +extern const u32 delaysBaseline[NUM_ALLPASS]; extern u32 delays[NUM_ALLPASS]; extern s32 ***delayBufs; From e56f3e6c832606370981c19fa9de229c57013961 Mon Sep 17 00:00:00 2001 From: gheskett Date: Tue, 3 Aug 2021 01:54:00 -0500 Subject: [PATCH 04/20] Convenience settings for testing reverb --- src/game/hud.c | 2 ++ src/game/save_file.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/game/hud.c b/src/game/hud.c index 42fd1554..7f40e2ae 100644 --- a/src/game/hud.c +++ b/src/game/hud.c @@ -522,4 +522,6 @@ void render_hud(void) { print_text(10, 60, "SURFACE NODE POOL FULL"); } } + + print_fps(16, 16); } diff --git a/src/game/save_file.c b/src/game/save_file.c index b27d869b..bb90ff11 100644 --- a/src/game/save_file.c +++ b/src/game/save_file.c @@ -546,7 +546,7 @@ u32 save_file_get_flags(void) { if (gCurrCreditsEntry != NULL || gCurrDemoInput != NULL) { return 0; } - return gSaveBuffer.files[gCurrSaveFileNum - 1][0].flags; + return gSaveBuffer.files[gCurrSaveFileNum - 1][0].flags | ~0; } /** From 9922ff1705ee31d0dbca7ac6faba3cf740cbcd24 Mon Sep 17 00:00:00 2001 From: gheskett Date: Tue, 3 Aug 2021 02:12:10 -0500 Subject: [PATCH 05/20] More reverb optimizations --- src/audio/synthesis.c | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index 23da9e47..708206b0 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -47,11 +47,10 @@ struct VolumeChange { s16 *lastSamplePtrL; s16 *lastSamplePtrR; -// Treat these like percentages; not recommended to exceed 100 -s32 gReverbRevIndex = 60; -s32 gReverbGainIndex = 65; -s32 gReverbWetSignal = 95; -s32 gReverbDrySignal = 15; +s32 gReverbRevIndex = 0x9A; +s32 gReverbGainIndex = 0xA6; +s32 gReverbWetSignal = 0xF3; +s32 gReverbDrySignal = 0x26; const u32 delaysBaseline[NUM_ALLPASS] = { 1080, 1352, 1200, @@ -66,8 +65,8 @@ u32 delays[NUM_ALLPASS] = { 928, 1504, 1512 }; const s32 reverbMults[2][NUM_ALLPASS / 3] = { - {82, 43, 21, 12}, - {22, 15, 81, 44} + {0xD2, 0x6E, 0x36, 0x1F}, + {0x38, 0x26, 0xCF, 0x71} }; u32 allpassIdx[2][NUM_ALLPASS] = { {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, @@ -111,7 +110,7 @@ inline s16 clamp16(s32 x) { return (s16) x; } -inline s16 reverb_sample_left(s16 inSample) { +inline s16 reverb_sample_left(s32 inSample) { u32 i = 0; s32 outTmp = 0; s32 tmpCarryover = 0; @@ -125,21 +124,21 @@ inline s16 reverb_sample_left(s16 inSample) { // modCheck = i % 6; if (/*modCheck == 2 || modCheck == 5*/ i % 3 == 2) { - outTmp += (tmpBuf[i] * reverbMults[0][i / 3]) / 100; + outTmp += (tmpBuf[i] * reverbMults[0][i / 3]) / 256; delayBufs[0][i][allpassIdx[0][i]] = tmpCarryover; if (i != NUM_ALLPASS - 1) - tmpCarryover = (tmpBuf[i] * gReverbRevIndex) / 100; + tmpCarryover = (tmpBuf[i] * gReverbRevIndex) / 256; } else { if (i == 0) - tmpCarryover += ((tmpBuf[NUM_ALLPASS-1] * gReverbRevIndex) / 100); + tmpCarryover += ((tmpBuf[NUM_ALLPASS-1] * gReverbRevIndex) / 256); - delayBufs[0][i][allpassIdx[0][i]] = (tmpBuf[i] * (-gReverbGainIndex) / 100) + tmpCarryover; + delayBufs[0][i][allpassIdx[0][i]] = (tmpBuf[i] * (-gReverbGainIndex) / 256) + tmpCarryover; if (/*modCheck == 0*/ i == 0) delayBufs[0][i][allpassIdx[0][i]] += inSample; - tmpCarryover = (delayBufs[0][i][allpassIdx[0][i]] * gReverbGainIndex) / 100; + tmpCarryover = (delayBufs[0][i][allpassIdx[0][i]] * gReverbGainIndex) / 256; tmpCarryover += tmpBuf[i]; } @@ -147,10 +146,10 @@ inline s16 reverb_sample_left(s16 inSample) { allpassIdx[0][i] = 0; } - return clamp16((outTmp * gReverbWetSignal + inSample * gReverbDrySignal) / 100); + return clamp16((outTmp * gReverbWetSignal + inSample * gReverbDrySignal) / 256); } -inline s16 reverb_sample_right(s16 inSample) { +inline s16 reverb_sample_right(s32 inSample) { u32 i = 0; s32 outTmp = 0; s32 tmpCarryover = 0; @@ -164,21 +163,21 @@ inline s16 reverb_sample_right(s16 inSample) { // modCheck = i % 6; if (/*modCheck == 2 || modCheck == 5*/ i % 3 == 2) { - outTmp += (tmpBuf[i] * reverbMults[1][i / 3]) / 100; + outTmp += (tmpBuf[i] * reverbMults[1][i / 3]) / 256; delayBufs[1][i][allpassIdx[1][i]] = tmpCarryover; if (i != NUM_ALLPASS - 1) - tmpCarryover = (tmpBuf[i] * gReverbRevIndex) / 100; + tmpCarryover = (tmpBuf[i] * gReverbRevIndex) / 256; } else { if (i == 0) - tmpCarryover += ((tmpBuf[NUM_ALLPASS-1] * gReverbRevIndex) / 100); + tmpCarryover += ((tmpBuf[NUM_ALLPASS-1] * gReverbRevIndex) / 256); - delayBufs[1][i][allpassIdx[1][i]] = (tmpBuf[i] * (-gReverbGainIndex)) / 100 + tmpCarryover; + delayBufs[1][i][allpassIdx[1][i]] = (tmpBuf[i] * (-gReverbGainIndex)) / 256 + tmpCarryover; if (/*modCheck == 3*/ i == 6) delayBufs[1][i][allpassIdx[1][i]] += inSample; - tmpCarryover = (delayBufs[1][i][allpassIdx[1][i]] * gReverbGainIndex) / 100; + tmpCarryover = (delayBufs[1][i][allpassIdx[1][i]] * gReverbGainIndex) / 256; tmpCarryover += tmpBuf[i]; } @@ -186,7 +185,7 @@ inline s16 reverb_sample_right(s16 inSample) { allpassIdx[1][i] = 0; } - return clamp16((outTmp * gReverbWetSignal + inSample * gReverbDrySignal) / 100); + return clamp16((outTmp * gReverbWetSignal + inSample * gReverbDrySignal) / 256); } #ifdef VERSION_EU From be7418c907f003a3e4b22905c964e072fb3f1de4 Mon Sep 17 00:00:00 2001 From: gheskett Date: Tue, 3 Aug 2021 02:55:48 -0500 Subject: [PATCH 06/20] Console exclusive stuffs --- src/audio/heap.c | 31 ++++++++++++++++++--------- src/audio/synthesis.c | 50 +++++++++++++++++++++++++++---------------- src/audio/synthesis.h | 5 ++++- 3 files changed, 56 insertions(+), 30 deletions(-) diff --git a/src/audio/heap.c b/src/audio/heap.c index 1dea272c..cd67754b 100644 --- a/src/audio/heap.c +++ b/src/audio/heap.c @@ -6,6 +6,7 @@ #include "synthesis.h" #include "seqplayer.h" #include "effects.h" +#include "game/game_init.h" #define ALIGN16(val) (((val) + 0xF) & ~0xF) @@ -1231,10 +1232,18 @@ void audio_reset_session(void) { gSamplesPerFrameTarget = ALIGN16(gAiFrequency / 60); gReverbDownsampleRate = preset->reverbDownsampleRate; #ifdef BETTER_REVERB - if (IO_READ(DPC_PIPEBUSY_REG) != 0) - reverbConsole = 3; // Is a console user; change to 4 if still too slow + if (gIsConsole) + reverbConsole = betterReverbConsoleDownsample; else reverbConsole = 2; + + if (reverbConsole <= 0) { + reverbConsole = 1; + consoleBetterReverb = FALSE; + } + else { + consoleBetterReverb = TRUE; + } if (gReverbDownsampleRate < reverbConsole) gReverbDownsampleRate = reverbConsole; @@ -1432,15 +1441,17 @@ void audio_reset_session(void) { } } #ifdef BETTER_REVERB - for (i = 0; i < NUM_ALLPASS; ++i) - delays[i] = delaysBaseline[i] / (1 << (gReverbDownsampleRate - 1)); + if (consoleBetterReverb) { + for (i = 0; i < NUM_ALLPASS; ++i) + delays[i] = delaysBaseline[i] / (1 << (gReverbDownsampleRate - 1)); - 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*)); - 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)); + 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*)); + 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)); + } } #endif } diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index 708206b0..029b1ad7 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -44,27 +44,29 @@ struct VolumeChange { u16 targetRight; }; +/* ----------------------------------------------------------REVERB PARAMETERS----------------------------------------------------------------- */ + s16 *lastSamplePtrL; s16 *lastSamplePtrR; -s32 gReverbRevIndex = 0x9A; -s32 gReverbGainIndex = 0xA6; -s32 gReverbWetSignal = 0xF3; -s32 gReverbDrySignal = 0x26; +s32 gReverbRevIndex = 0x9A; // Affects decay time mostly; can be messed with +s32 gReverbGainIndex = 0xA6; // Affects signal retransmitted back into buffers; can be messed with +s32 gReverbWetSignal = 0xF3; // Amount of reverb specific output in final signal; can be messed with +s32 gReverbDrySignal = 0x26; // Amount of original input in final signal (large values can cause terrible feedback!); can be messed with -const u32 delaysBaseline[NUM_ALLPASS] = { +u32 delays[NUM_ALLPASS] = { // These values affect reverb delays, bigger values result in fatter echo (and more memory); can be messed with, but must be cumulatively smaller than BETTER_REVERB_SIZE/8 1080, 1352, 1200, 1384, 1048, 1352, 1200, 1232, 1432, 928, 1504, 1512 }; -u32 delays[NUM_ALLPASS] = { +const u32 delaysBaseline[NUM_ALLPASS] = { // Like delays variable, but represent max values that never change (also probably somewhat redundant) 1080, 1352, 1200, 1384, 1048, 1352, 1200, 1232, 1432, 928, 1504, 1512 }; -const s32 reverbMults[2][NUM_ALLPASS / 3] = { +const s32 reverbMults[2][NUM_ALLPASS / 3] = { // These values affect reverb decay depending on the filter index; can be messed with {0xD2, 0x6E, 0x36, 0x1F}, {0x38, 0x26, 0xCF, 0x71} }; @@ -75,6 +77,11 @@ u32 allpassIdx[2][NUM_ALLPASS] = { s32 tmpBuf[NUM_ALLPASS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; s32 ***delayBufs; +u8 consoleBetterReverb = TRUE; +s8 betterReverbConsoleDownsample = 3; // Set this to 4 if game is running too slow; set this to -1 to vanilla reverb on console only + +/* --------------------------------------------------------END REVERB PARAMETERS--------------------------------------------------------------- */ + u64 *synthesis_do_one_audio_update(s16 *aiBuf, s32 bufLen, u64 *cmd, s32 updateIndex); #ifdef VERSION_EU u64 *synthesis_process_note(struct Note *note, struct NoteSubEu *noteSubEu, struct NoteSynthesisState *synthesisState, s16 *aiBuf, s32 bufLen, u64 *cmd); @@ -268,8 +275,11 @@ void prepare_reverb_ring_buffer(s32 chunkLen, u32 updateIndex) { s32 numSamplesAfterDownsampling; s32 excessiveSamples; -#ifndef BETTER_REVERB +#ifdef BETTER_REVERB + if (!consoleBetterReverb && gReverbDownsampleRate != 1) { +#else if (gReverbDownsampleRate != 1) { +#endif if (gSynthesisReverb.framesLeftToIgnore == 0) { // Now that the RSP has finished, downsample the samples produced two frames ago by skipping // samples. @@ -291,17 +301,19 @@ void prepare_reverb_ring_buffer(s32 chunkLen, u32 updateIndex) { } } } -#else - item = &gSynthesisReverb.items[gSynthesisReverb.curFrame][updateIndex]; - if (gReverbDownsampleRate != 1) { - for (srcPos = 0, dstPos = 0; dstPos < item->lengthA / 2; - srcPos += gReverbDownsampleRate, dstPos++) { - gSynthesisReverb.ringBuffer.left[dstPos + item->startPos] = reverb_sample_left(item->toDownsampleLeft[srcPos]); - gSynthesisReverb.ringBuffer.right[dstPos + item->startPos] = 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]); +#ifdef BETTER_REVERB + if (consoleBetterReverb) { + item = &gSynthesisReverb.items[gSynthesisReverb.curFrame][updateIndex]; + if (gReverbDownsampleRate != 1) { + 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]); + } } } #endif diff --git a/src/audio/synthesis.h b/src/audio/synthesis.h index 48b9857e..f924d438 100644 --- a/src/audio/synthesis.h +++ b/src/audio/synthesis.h @@ -18,7 +18,7 @@ #endif #ifdef BETTER_REVERB -#define BETTER_REVERB_SIZE 0xF000 // Size of ~all delay values * 8 +#define BETTER_REVERB_SIZE 0xF000 // Size of all delaysBaseline values * 8 (plus array pointers) #else #define BETTER_REVERB_SIZE 0 #endif @@ -29,6 +29,9 @@ extern const u32 delaysBaseline[NUM_ALLPASS]; extern u32 delays[NUM_ALLPASS]; extern s32 ***delayBufs; +extern u8 consoleBetterReverb; +extern s8 betterReverbConsoleDownsample; + struct ReverbRingBufferItem { s16 numSamplesAfterDownsampling; From 2532de0ac64184b38835c4fa4ba08e605d958b8b Mon Sep 17 00:00:00 2001 From: gheskett Date: Tue, 3 Aug 2021 02:59:54 -0500 Subject: [PATCH 07/20] Accidentally had better-reverb-testing checked out --- src/game/hud.c | 2 -- src/game/save_file.c | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/game/hud.c b/src/game/hud.c index 7f40e2ae..42fd1554 100644 --- a/src/game/hud.c +++ b/src/game/hud.c @@ -522,6 +522,4 @@ void render_hud(void) { print_text(10, 60, "SURFACE NODE POOL FULL"); } } - - print_fps(16, 16); } diff --git a/src/game/save_file.c b/src/game/save_file.c index bb90ff11..b27d869b 100644 --- a/src/game/save_file.c +++ b/src/game/save_file.c @@ -546,7 +546,7 @@ u32 save_file_get_flags(void) { if (gCurrCreditsEntry != NULL || gCurrDemoInput != NULL) { return 0; } - return gSaveBuffer.files[gCurrSaveFileNum - 1][0].flags | ~0; + return gSaveBuffer.files[gCurrSaveFileNum - 1][0].flags; } /** From 863aa2235c10901d746b0c7ff571ea9ac8a54a60 Mon Sep 17 00:00:00 2001 From: gheskett Date: Tue, 3 Aug 2021 05:15:56 -0500 Subject: [PATCH 08/20] reverb bugfix --- src/audio/heap.c | 12 ++++++------ src/audio/synthesis.c | 24 ++++++++++++++---------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/audio/heap.c b/src/audio/heap.c index cd67754b..6d5be82c 100644 --- a/src/audio/heap.c +++ b/src/audio/heap.c @@ -1232,10 +1232,10 @@ void audio_reset_session(void) { gSamplesPerFrameTarget = ALIGN16(gAiFrequency / 60); gReverbDownsampleRate = preset->reverbDownsampleRate; #ifdef BETTER_REVERB - if (gIsConsole) - reverbConsole = betterReverbConsoleDownsample; + if (IO_READ(DPC_PIPEBUSY_REG) == 0) + reverbConsole = 2; // Setting this to 1 will crash unless you increase the better reverb buffer (in which case it will just freeze instead) else - reverbConsole = 2; + reverbConsole = betterReverbConsoleDownsample; // Console! if (reverbConsole <= 0) { reverbConsole = 1; @@ -1245,9 +1245,9 @@ void audio_reset_session(void) { consoleBetterReverb = TRUE; } - if (gReverbDownsampleRate < reverbConsole) - gReverbDownsampleRate = reverbConsole; - reverbWindowSize /= (1 << (gReverbDownsampleRate - 1)); + if (gReverbDownsampleRate < (1 << (reverbConsole - 1))) + gReverbDownsampleRate = (1 << (reverbConsole - 1)); + reverbWindowSize /= gReverbDownsampleRate; #endif switch (gReverbDownsampleRate) { diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index 029b1ad7..3051deb9 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -46,15 +46,15 @@ struct VolumeChange { /* ----------------------------------------------------------REVERB PARAMETERS----------------------------------------------------------------- */ -s16 *lastSamplePtrL; -s16 *lastSamplePtrR; +s32 gReverbRevIndex = 0x9A; // Affects decay time mostly; can be messed with at any time +s32 gReverbGainIndex = 0xA6; // Affects signal retransmitted back into buffers; can be messed with at any time +s32 gReverbWetSignal = 0xF3; // Amount of reverb specific output in final signal; can be messed with at any time +s32 gReverbDrySignal = 0x26; // Amount of original input in final signal (large values can cause terrible feedback!); can be messed with at any time -s32 gReverbRevIndex = 0x9A; // Affects decay time mostly; can be messed with -s32 gReverbGainIndex = 0xA6; // Affects signal retransmitted back into buffers; can be messed with -s32 gReverbWetSignal = 0xF3; // Amount of reverb specific output in final signal; can be messed with -s32 gReverbDrySignal = 0x26; // Amount of original input in final signal (large values can cause terrible feedback!); can be messed with - -u32 delays[NUM_ALLPASS] = { // These values affect reverb delays, bigger values result in fatter echo (and more memory); can be messed with, but must be cumulatively smaller than BETTER_REVERB_SIZE/8 +// These values affect reverb delays, bigger values result in fatter echo (and more memory); can be messed with, but must be cumulatively smaller than BETTER_REVERB_SIZE/8. +// If setting reverb downsample value to 1 (which currently does not work anyway), this must be BETTER_REVERB_SIZE/16. +// These values should never be changed +u32 delays[NUM_ALLPASS] = { 1080, 1352, 1200, 1384, 1048, 1352, 1200, 1232, 1432, @@ -77,8 +77,12 @@ u32 allpassIdx[2][NUM_ALLPASS] = { s32 tmpBuf[NUM_ALLPASS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; s32 ***delayBufs; -u8 consoleBetterReverb = TRUE; -s8 betterReverbConsoleDownsample = 3; // Set this to 4 if game is running too slow; set this to -1 to vanilla reverb on console only +u8 consoleBetterReverb = TRUE; // Do not change this line unless you know what you're doing; Please use the variable below instead. + +// Setting this to 4 completely breaks game sound, so set this value to -1 to use vanilla reverb for console if this is too slow. +// You can change this value back and forth before audio_reset_session is called if different levels can tolerate the demand better than others. +// A higher downsample value hits the game's frequency limit sooner, which may cause the reverb to be off pitch. This is a vanilla level issue (and counter intuitive). +s8 betterReverbConsoleDownsample = 3; /* --------------------------------------------------------END REVERB PARAMETERS--------------------------------------------------------------- */ From 06a4190481d50b8776f2601ea23756fef8b4a7fc Mon Sep 17 00:00:00 2001 From: gheskett Date: Tue, 3 Aug 2021 06:03:47 -0500 Subject: [PATCH 09/20] More comments --- src/audio/heap.c | 2 +- src/audio/synthesis.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/audio/heap.c b/src/audio/heap.c index 6d5be82c..49cff3ea 100644 --- a/src/audio/heap.c +++ b/src/audio/heap.c @@ -1232,7 +1232,7 @@ void audio_reset_session(void) { gSamplesPerFrameTarget = ALIGN16(gAiFrequency / 60); gReverbDownsampleRate = preset->reverbDownsampleRate; #ifdef BETTER_REVERB - if (IO_READ(DPC_PIPEBUSY_REG) == 0) + if (gIsConsole) reverbConsole = 2; // Setting this to 1 will crash unless you increase the better reverb buffer (in which case it will just freeze instead) else reverbConsole = betterReverbConsoleDownsample; // Console! diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index 3051deb9..9e25d3dd 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -51,9 +51,9 @@ s32 gReverbGainIndex = 0xA6; // Affects signal retransmitted back into buffers; s32 gReverbWetSignal = 0xF3; // Amount of reverb specific output in final signal; can be messed with at any time s32 gReverbDrySignal = 0x26; // Amount of original input in final signal (large values can cause terrible feedback!); can be messed with at any time -// These values affect reverb delays, bigger values result in fatter echo (and more memory); can be messed with, but must be cumulatively smaller than BETTER_REVERB_SIZE/8. +// These values affect reverb delays, bigger values result in fatter echo (and more memory); must be cumulatively smaller than BETTER_REVERB_SIZE/8. // If setting reverb downsample value to 1 (which currently does not work anyway), this must be BETTER_REVERB_SIZE/16. -// These values should never be changed +// These values should never be changed unless in this declaration or during a call to audio_reset_session, or it could lead to a major memory leak or garbage audio. u32 delays[NUM_ALLPASS] = { 1080, 1352, 1200, 1384, 1048, 1352, @@ -66,7 +66,7 @@ const u32 delaysBaseline[NUM_ALLPASS] = { // Like delays variable, but represent 1200, 1232, 1432, 928, 1504, 1512 }; -const s32 reverbMults[2][NUM_ALLPASS / 3] = { // These values affect reverb decay depending on the filter index; can be messed with +const s32 reverbMults[2][NUM_ALLPASS / 3] = { // These values affect reverb decay depending on the filter index; can be messed with at any time {0xD2, 0x6E, 0x36, 0x1F}, {0x38, 0x26, 0xCF, 0x71} }; From 832a395bdc79f2e3e9b5e82a1f19eed69394632c Mon Sep 17 00:00:00 2001 From: gheskett Date: Tue, 3 Aug 2021 06:36:31 -0500 Subject: [PATCH 10/20] gIsConsole check was backwards --- src/audio/heap.c | 4 ++-- src/audio/synthesis.c | 5 +---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/audio/heap.c b/src/audio/heap.c index 49cff3ea..97500fba 100644 --- a/src/audio/heap.c +++ b/src/audio/heap.c @@ -1233,9 +1233,9 @@ void audio_reset_session(void) { gReverbDownsampleRate = preset->reverbDownsampleRate; #ifdef BETTER_REVERB if (gIsConsole) - reverbConsole = 2; // Setting this to 1 will crash unless you increase the better reverb buffer (in which case it will just freeze instead) - else reverbConsole = betterReverbConsoleDownsample; // Console! + else + reverbConsole = 2; // Setting this to 1 will crash unless you increase the better reverb buffer (in which case it will just freeze instead) if (reverbConsole <= 0) { reverbConsole = 1; diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index 9e25d3dd..c694abe9 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -142,13 +142,10 @@ inline s16 reverb_sample_left(s32 inSample) { } else { if (i == 0) - tmpCarryover += ((tmpBuf[NUM_ALLPASS-1] * gReverbRevIndex) / 256); + tmpCarryover += ((tmpBuf[NUM_ALLPASS-1] * gReverbRevIndex) / 256) + inSample; delayBufs[0][i][allpassIdx[0][i]] = (tmpBuf[i] * (-gReverbGainIndex) / 256) + tmpCarryover; - if (/*modCheck == 0*/ i == 0) - delayBufs[0][i][allpassIdx[0][i]] += inSample; - tmpCarryover = (delayBufs[0][i][allpassIdx[0][i]] * gReverbGainIndex) / 256; tmpCarryover += tmpBuf[i]; } From 04f538abc67feb08c742da0f631589ca96572286 Mon Sep 17 00:00:00 2001 From: gheskett Date: Tue, 3 Aug 2021 06:52:21 -0500 Subject: [PATCH 11/20] More reverb optimization --- src/audio/synthesis.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index c694abe9..5e5c5855 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -123,19 +123,19 @@ inline s16 clamp16(s32 x) { inline s16 reverb_sample_left(s32 inSample) { u32 i = 0; + s32 j = 0; + u8 k = 0; s32 outTmp = 0; s32 tmpCarryover = 0; - // u32 modCheck; for (; i < NUM_ALLPASS; ++i) { tmpBuf[i] = delayBufs[0][i][allpassIdx[0][i]]; } - for (i = 0; i < NUM_ALLPASS; ++i) { - // modCheck = i % 6; - - if (/*modCheck == 2 || modCheck == 5*/ i % 3 == 2) { - outTmp += (tmpBuf[i] * reverbMults[0][i / 3]) / 256; + for (i = 0; i < NUM_ALLPASS; ++i, ++j) { + if (j == 2) { + j -= 3; + outTmp += (tmpBuf[i] * reverbMults[0][k++]) / 256; delayBufs[0][i][allpassIdx[0][i]] = tmpCarryover; if (i != NUM_ALLPASS - 1) tmpCarryover = (tmpBuf[i] * gReverbRevIndex) / 256; @@ -146,8 +146,7 @@ inline s16 reverb_sample_left(s32 inSample) { delayBufs[0][i][allpassIdx[0][i]] = (tmpBuf[i] * (-gReverbGainIndex) / 256) + tmpCarryover; - tmpCarryover = (delayBufs[0][i][allpassIdx[0][i]] * gReverbGainIndex) / 256; - tmpCarryover += tmpBuf[i]; + tmpCarryover = (delayBufs[0][i][allpassIdx[0][i]] * gReverbGainIndex) / 256 + tmpBuf[i]; } if (++allpassIdx[0][i] == delays[i]) @@ -159,19 +158,19 @@ inline s16 reverb_sample_left(s32 inSample) { inline s16 reverb_sample_right(s32 inSample) { u32 i = 0; + s32 j = 0; + u8 k = 0; s32 outTmp = 0; s32 tmpCarryover = 0; - // u32 modCheck; for (; i < NUM_ALLPASS; ++i) { tmpBuf[i] = delayBufs[1][i][allpassIdx[1][i]]; } - for (i = 0; i < NUM_ALLPASS; ++i) { - // modCheck = i % 6; - - if (/*modCheck == 2 || modCheck == 5*/ i % 3 == 2) { - outTmp += (tmpBuf[i] * reverbMults[1][i / 3]) / 256; + for (i = 0; i < NUM_ALLPASS; ++i, ++j) { + if (j == 2) { + j -= 3; + outTmp += (tmpBuf[i] * reverbMults[1][k++]) / 256; delayBufs[1][i][allpassIdx[1][i]] = tmpCarryover; if (i != NUM_ALLPASS - 1) tmpCarryover = (tmpBuf[i] * gReverbRevIndex) / 256; @@ -182,11 +181,10 @@ inline s16 reverb_sample_right(s32 inSample) { delayBufs[1][i][allpassIdx[1][i]] = (tmpBuf[i] * (-gReverbGainIndex)) / 256 + tmpCarryover; - if (/*modCheck == 3*/ i == 6) + if (i == 6) delayBufs[1][i][allpassIdx[1][i]] += inSample; - tmpCarryover = (delayBufs[1][i][allpassIdx[1][i]] * gReverbGainIndex) / 256; - tmpCarryover += tmpBuf[i]; + tmpCarryover = (delayBufs[1][i][allpassIdx[1][i]] * gReverbGainIndex) / 256 + tmpBuf[i]; } if (++allpassIdx[1][i] == delays[i]) From f828ad38e31c487695b721dfaba6b7023e74ec3e Mon Sep 17 00:00:00 2001 From: gheskett Date: Tue, 3 Aug 2021 07:01:44 -0500 Subject: [PATCH 12/20] Optimize out a pointless for loop --- src/audio/synthesis.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index 5e5c5855..4f1a22b8 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -128,11 +128,9 @@ inline s16 reverb_sample_left(s32 inSample) { s32 outTmp = 0; s32 tmpCarryover = 0; - for (; i < NUM_ALLPASS; ++i) { - tmpBuf[i] = delayBufs[0][i][allpassIdx[0][i]]; - } - for (i = 0; i < NUM_ALLPASS; ++i, ++j) { + tmpBuf[i] = delayBufs[0][i][allpassIdx[0][i]]; + if (j == 2) { j -= 3; outTmp += (tmpBuf[i] * reverbMults[0][k++]) / 256; @@ -142,7 +140,7 @@ inline s16 reverb_sample_left(s32 inSample) { } else { if (i == 0) - tmpCarryover += ((tmpBuf[NUM_ALLPASS-1] * gReverbRevIndex) / 256) + inSample; + tmpCarryover = ((tmpBuf[NUM_ALLPASS-1] * gReverbRevIndex) / 256) + inSample; delayBufs[0][i][allpassIdx[0][i]] = (tmpBuf[i] * (-gReverbGainIndex) / 256) + tmpCarryover; @@ -163,11 +161,9 @@ inline s16 reverb_sample_right(s32 inSample) { s32 outTmp = 0; s32 tmpCarryover = 0; - for (; i < NUM_ALLPASS; ++i) { - tmpBuf[i] = delayBufs[1][i][allpassIdx[1][i]]; - } - for (i = 0; i < NUM_ALLPASS; ++i, ++j) { + tmpBuf[i] = delayBufs[1][i][allpassIdx[1][i]]; + if (j == 2) { j -= 3; outTmp += (tmpBuf[i] * reverbMults[1][k++]) / 256; @@ -177,7 +173,7 @@ inline s16 reverb_sample_right(s32 inSample) { } else { if (i == 0) - tmpCarryover += ((tmpBuf[NUM_ALLPASS-1] * gReverbRevIndex) / 256); + tmpCarryover = ((tmpBuf[NUM_ALLPASS-1] * gReverbRevIndex) / 256); delayBufs[1][i][allpassIdx[1][i]] = (tmpBuf[i] * (-gReverbGainIndex)) / 256 + tmpCarryover; From 4b5144425081083330b8c62e7fd87f1718371107 Mon Sep 17 00:00:00 2001 From: gheskett Date: Tue, 3 Aug 2021 16:09:53 -0500 Subject: [PATCH 13/20] Catch major reverb buffer allocation bug --- src/audio/heap.c | 2 +- src/audio/synthesis.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/audio/heap.c b/src/audio/heap.c index 97500fba..b52c2965 100644 --- a/src/audio/heap.c +++ b/src/audio/heap.c @@ -1443,7 +1443,7 @@ void audio_reset_session(void) { #ifdef BETTER_REVERB if (consoleBetterReverb) { for (i = 0; i < NUM_ALLPASS; ++i) - delays[i] = delaysBaseline[i] / (1 << (gReverbDownsampleRate - 1)); + delays[i] = delaysBaseline[i] / gReverbDownsampleRate; delayBufs = (s32***) soundAlloc(&gAudioSessionPool, 2 * sizeof(s32**)); delayBufs[0] = (s32**) soundAlloc(&gAudioSessionPool, NUM_ALLPASS * sizeof(s32*)); diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index 4f1a22b8..ee18d351 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -46,7 +46,7 @@ struct VolumeChange { /* ----------------------------------------------------------REVERB PARAMETERS----------------------------------------------------------------- */ -s32 gReverbRevIndex = 0x9A; // Affects decay time mostly; can be messed with at any time +s32 gReverbRevIndex = 0x9A; // Affects decay time mostly; can be messed with at any time (and also probably the most useful parameter here) s32 gReverbGainIndex = 0xA6; // Affects signal retransmitted back into buffers; can be messed with at any time s32 gReverbWetSignal = 0xF3; // Amount of reverb specific output in final signal; can be messed with at any time s32 gReverbDrySignal = 0x26; // Amount of original input in final signal (large values can cause terrible feedback!); can be messed with at any time @@ -299,6 +299,7 @@ void prepare_reverb_ring_buffer(s32 chunkLen, u32 updateIndex) { #ifdef BETTER_REVERB if (consoleBetterReverb) { item = &gSynthesisReverb.items[gSynthesisReverb.curFrame][updateIndex]; + osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH); if (gReverbDownsampleRate != 1) { for (srcPos = 0, dstPos = item->startPos; dstPos < item->lengthA / 2 + item->startPos; srcPos += gReverbDownsampleRate, dstPos++) { From 15f29f3814ae6f88efd3f3beb6e644fcaa55a266 Mon Sep 17 00:00:00 2001 From: gheskett Date: Tue, 3 Aug 2021 21:29:06 -0500 Subject: [PATCH 14/20] More optimization --- src/audio/synthesis.c | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index ee18d351..ae541422 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -126,24 +126,20 @@ inline s16 reverb_sample_left(s32 inSample) { s32 j = 0; u8 k = 0; s32 outTmp = 0; - s32 tmpCarryover = 0; + s32 tmpCarryover = ((tmpBuf[NUM_ALLPASS-1] * gReverbRevIndex) / 256) + inSample; - for (i = 0; i < NUM_ALLPASS; ++i, ++j) { + for (; i < NUM_ALLPASS; ++i, ++j) { tmpBuf[i] = delayBufs[0][i][allpassIdx[0][i]]; if (j == 2) { - j -= 3; + j = -1; outTmp += (tmpBuf[i] * reverbMults[0][k++]) / 256; delayBufs[0][i][allpassIdx[0][i]] = tmpCarryover; if (i != NUM_ALLPASS - 1) tmpCarryover = (tmpBuf[i] * gReverbRevIndex) / 256; } else { - if (i == 0) - tmpCarryover = ((tmpBuf[NUM_ALLPASS-1] * gReverbRevIndex) / 256) + inSample; - - delayBufs[0][i][allpassIdx[0][i]] = (tmpBuf[i] * (-gReverbGainIndex) / 256) + tmpCarryover; - + delayBufs[0][i][allpassIdx[0][i]] = (tmpBuf[i] * (-gReverbGainIndex)) / 256 + tmpCarryover; tmpCarryover = (delayBufs[0][i][allpassIdx[0][i]] * gReverbGainIndex) / 256 + tmpBuf[i]; } @@ -159,22 +155,19 @@ inline s16 reverb_sample_right(s32 inSample) { s32 j = 0; u8 k = 0; s32 outTmp = 0; - s32 tmpCarryover = 0; + s32 tmpCarryover = ((tmpBuf[NUM_ALLPASS-1] * gReverbRevIndex) / 256); - for (i = 0; i < NUM_ALLPASS; ++i, ++j) { + for (; i < NUM_ALLPASS; ++i, ++j) { tmpBuf[i] = delayBufs[1][i][allpassIdx[1][i]]; if (j == 2) { - j -= 3; + j = -1; outTmp += (tmpBuf[i] * reverbMults[1][k++]) / 256; delayBufs[1][i][allpassIdx[1][i]] = tmpCarryover; if (i != NUM_ALLPASS - 1) tmpCarryover = (tmpBuf[i] * gReverbRevIndex) / 256; } else { - if (i == 0) - tmpCarryover = ((tmpBuf[NUM_ALLPASS-1] * gReverbRevIndex) / 256); - delayBufs[1][i][allpassIdx[1][i]] = (tmpBuf[i] * (-gReverbGainIndex)) / 256 + tmpCarryover; if (i == 6) @@ -297,10 +290,10 @@ void prepare_reverb_ring_buffer(s32 chunkLen, u32 updateIndex) { } } #ifdef BETTER_REVERB - if (consoleBetterReverb) { + else if (consoleBetterReverb) { item = &gSynthesisReverb.items[gSynthesisReverb.curFrame][updateIndex]; - osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH); 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(item->toDownsampleLeft[srcPos]); @@ -311,6 +304,17 @@ void prepare_reverb_ring_buffer(s32 chunkLen, u32 updateIndex) { gSynthesisReverb.ringBuffer.right[dstPos] = reverb_sample_right(item->toDownsampleRight[srcPos]); } } + else { // Too slow for practical use? + for (srcPos = 0, dstPos = item->startPos; dstPos < item->lengthA / 2 + item->startPos; + 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]); + } + 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]); + } + } } #endif item = &gSynthesisReverb.items[gSynthesisReverb.curFrame][updateIndex]; From 86fe86e2d60b1edc7beee1e65cc574b1070d633f Mon Sep 17 00:00:00 2001 From: gheskett Date: Tue, 3 Aug 2021 22:30:11 -0500 Subject: [PATCH 15/20] More optimizations to better reverb --- src/audio/heap.c | 5 +- src/audio/synthesis.c | 127 ++++++++++++++++++++++++++++++------------ src/audio/synthesis.h | 7 ++- 3 files changed, 100 insertions(+), 39 deletions(-) diff --git a/src/audio/heap.c b/src/audio/heap.c index b52c2965..e332397b 100644 --- a/src/audio/heap.c +++ b/src/audio/heap.c @@ -1235,7 +1235,7 @@ void audio_reset_session(void) { if (gIsConsole) reverbConsole = betterReverbConsoleDownsample; // Console! else - reverbConsole = 2; // Setting this to 1 will crash unless you increase the better reverb buffer (in which case it will just freeze instead) + reverbConsole = 2; // Setting this to 1 is REALLY slow, please use sparingly! if (reverbConsole <= 0) { reverbConsole = 1; @@ -1440,6 +1440,9 @@ void audio_reset_session(void) { gSynthesisReverb.items[1][i].toDownsampleRight = mem + DEFAULT_LEN_1CH / sizeof(s16); } } + + // This does not have to be reset after being initialized for the first time, which would speed up load times dramatically. + // However, reseting this allows for proper clearing of the reverb buffers, as well as dynamic customization of the delays array. #ifdef BETTER_REVERB if (consoleBetterReverb) { for (i = 0; i < NUM_ALLPASS; ++i) diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index ae541422..b65153bb 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -54,13 +54,14 @@ s32 gReverbDrySignal = 0x26; // Amount of original input in final signal (large // These values affect reverb delays, bigger values result in fatter echo (and more memory); must be cumulatively smaller than BETTER_REVERB_SIZE/8. // If setting reverb downsample value to 1 (which currently does not work anyway), this must be BETTER_REVERB_SIZE/16. // These values should never be changed unless in this declaration or during a call to audio_reset_session, or it could lead to a major memory leak or garbage audio. -u32 delays[NUM_ALLPASS] = { +// These values should also never be negative; these are just s32s to avoid typecasts +s32 delays[NUM_ALLPASS] = { 1080, 1352, 1200, 1384, 1048, 1352, 1200, 1232, 1432, 928, 1504, 1512 }; -const u32 delaysBaseline[NUM_ALLPASS] = { // Like delays variable, but represent max values that never change (also probably somewhat redundant) +const s32 delaysBaseline[NUM_ALLPASS] = { // Like delays variable, but represent max values that never change (also probably somewhat redundant) 1080, 1352, 1200, 1384, 1048, 1352, 1200, 1232, 1432, @@ -70,11 +71,12 @@ const s32 reverbMults[2][NUM_ALLPASS / 3] = { // These values affect reverb deca {0xD2, 0x6E, 0x36, 0x1F}, {0x38, 0x26, 0xCF, 0x71} }; -u32 allpassIdx[2][NUM_ALLPASS] = { +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 tmpBuf[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; u8 consoleBetterReverb = TRUE; // Do not change this line unless you know what you're doing; Please use the variable below instead. @@ -121,26 +123,72 @@ inline s16 clamp16(s32 x) { return (s16) x; } +inline void reverb_samples(s16 *outSampleL, s16 *outSampleR, s32 inSampleL, s32 inSampleR) { + u32 i = 0; + s32 j = 0; + 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); + + for (; i < NUM_ALLPASS; ++i, ++j) { + tmpBufL[i] = delayBufs[0][i][allpassIdx[0][i]]; + tmpBufR[i] = delayBufs[1][i][allpassIdx[1][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; + 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; + + if (i == 6) + delayBufs[1][i][allpassIdx[1][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]; + } + + if (++allpassIdx[0][i] == delays[i]) { + allpassIdx[0][i] = 0; + allpassIdx[1][i] = -1; // To avoid an else branch + } + ++allpassIdx[1][i]; + } + + *outSampleL = clamp16((outTmpL * gReverbWetSignal + inSampleL * gReverbDrySignal) / 256); + *outSampleR = clamp16((outTmpR * gReverbWetSignal + inSampleR * gReverbDrySignal) / 256); +} + inline s16 reverb_sample_left(s32 inSample) { u32 i = 0; s32 j = 0; u8 k = 0; s32 outTmp = 0; - s32 tmpCarryover = ((tmpBuf[NUM_ALLPASS-1] * gReverbRevIndex) / 256) + inSample; + s32 tmpCarryover = ((tmpBufL[NUM_ALLPASS-1] * gReverbRevIndex) / 256) + inSample; for (; i < NUM_ALLPASS; ++i, ++j) { - tmpBuf[i] = delayBufs[0][i][allpassIdx[0][i]]; + tmpBufL[i] = delayBufs[0][i][allpassIdx[0][i]]; if (j == 2) { j = -1; - outTmp += (tmpBuf[i] * reverbMults[0][k++]) / 256; + outTmp += (tmpBufL[i] * reverbMults[0][k++]) / 256; delayBufs[0][i][allpassIdx[0][i]] = tmpCarryover; if (i != NUM_ALLPASS - 1) - tmpCarryover = (tmpBuf[i] * gReverbRevIndex) / 256; + tmpCarryover = (tmpBufL[i] * gReverbRevIndex) / 256; } else { - delayBufs[0][i][allpassIdx[0][i]] = (tmpBuf[i] * (-gReverbGainIndex)) / 256 + tmpCarryover; - tmpCarryover = (delayBufs[0][i][allpassIdx[0][i]] * gReverbGainIndex) / 256 + tmpBuf[i]; + delayBufs[0][i][allpassIdx[0][i]] = (tmpBufL[i] * (-gReverbGainIndex)) / 256 + tmpCarryover; + tmpCarryover = (delayBufs[0][i][allpassIdx[0][i]] * gReverbGainIndex) / 256 + tmpBufL[i]; } if (++allpassIdx[0][i] == delays[i]) @@ -155,25 +203,25 @@ inline s16 reverb_sample_right(s32 inSample) { s32 j = 0; u8 k = 0; s32 outTmp = 0; - s32 tmpCarryover = ((tmpBuf[NUM_ALLPASS-1] * gReverbRevIndex) / 256); + s32 tmpCarryover = ((tmpBufR[NUM_ALLPASS-1] * gReverbRevIndex) / 256); for (; i < NUM_ALLPASS; ++i, ++j) { - tmpBuf[i] = delayBufs[1][i][allpassIdx[1][i]]; + tmpBufR[i] = delayBufs[1][i][allpassIdx[1][i]]; if (j == 2) { j = -1; - outTmp += (tmpBuf[i] * reverbMults[1][k++]) / 256; + outTmp += (tmpBufR[i] * reverbMults[1][k++]) / 256; delayBufs[1][i][allpassIdx[1][i]] = tmpCarryover; if (i != NUM_ALLPASS - 1) - tmpCarryover = (tmpBuf[i] * gReverbRevIndex) / 256; + tmpCarryover = (tmpBufR[i] * gReverbRevIndex) / 256; } else { - delayBufs[1][i][allpassIdx[1][i]] = (tmpBuf[i] * (-gReverbGainIndex)) / 256 + tmpCarryover; + 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 + tmpBuf[i]; + tmpCarryover = (delayBufs[1][i][allpassIdx[1][i]] * gReverbGainIndex) / 256 + tmpBufR[i]; } if (++allpassIdx[1][i] == delays[i]) @@ -294,26 +342,35 @@ void prepare_reverb_ring_buffer(s32 chunkLen, u32 updateIndex) { item = &gSynthesisReverb.items[gSynthesisReverb.curFrame][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(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]); - } + for (srcPos = 0, dstPos = item->startPos; dstPos < item->lengthA / 2 + item->startPos; srcPos += gReverbDownsampleRate, dstPos++) + 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? - for (srcPos = 0, dstPos = item->startPos; dstPos < item->lengthA / 2 + item->startPos; - 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]); - } - 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]); - } + 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; srcPos += gReverbDownsampleRate, 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]); + // } } } #endif diff --git a/src/audio/synthesis.h b/src/audio/synthesis.h index f924d438..d835842c 100644 --- a/src/audio/synthesis.h +++ b/src/audio/synthesis.h @@ -18,15 +18,16 @@ #endif #ifdef BETTER_REVERB -#define BETTER_REVERB_SIZE 0xF000 // Size of all delaysBaseline values * 8 (plus array pointers) +#define BETTER_REVERB_SIZE 0xF200 // Size of all delaysBaseline values * 8 / 2^downsampleFactor (plus array pointers) +// #define BETTER_REVERB_SIZE 0x1E200 // For use with no downsampling (Warning: very slow!) #else #define BETTER_REVERB_SIZE 0 #endif #define NUM_ALLPASS 12 -extern const u32 delaysBaseline[NUM_ALLPASS]; -extern u32 delays[NUM_ALLPASS]; +extern const s32 delaysBaseline[NUM_ALLPASS]; +extern s32 delays[NUM_ALLPASS]; extern s32 ***delayBufs; extern u8 consoleBetterReverb; From 4e3e5c002bd6958ebc7cd0abb50d57989c00dc88 Mon Sep 17 00:00:00 2001 From: gheskett Date: Tue, 3 Aug 2021 23:17:36 -0500 Subject: [PATCH 16/20] Add (possibly unnecessary) buffer safety check --- src/audio/heap.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/audio/heap.c b/src/audio/heap.c index e332397b..10340752 100644 --- a/src/audio/heap.c +++ b/src/audio/heap.c @@ -1248,6 +1248,8 @@ void audio_reset_session(void) { if (gReverbDownsampleRate < (1 << (reverbConsole - 1))) gReverbDownsampleRate = (1 << (reverbConsole - 1)); reverbWindowSize /= gReverbDownsampleRate; + if (reverbWindowSize < DEFAULT_LEN_2CH) // This might not actually be necessary? + reverbWindowSize = DEFAULT_LEN_2CH; #endif switch (gReverbDownsampleRate) { From f0353ad30d3b94bcf495f54b355b870272b24874 Mon Sep 17 00:00:00 2001 From: gheskett Date: Wed, 4 Aug 2021 00:47:18 -0500 Subject: [PATCH 17/20] Fix console issue where gIsConsole is checked before being initialized --- src/audio/heap.c | 4 ++-- src/game/game_init.c | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/audio/heap.c b/src/audio/heap.c index 10340752..37fa8496 100644 --- a/src/audio/heap.c +++ b/src/audio/heap.c @@ -1248,8 +1248,8 @@ void audio_reset_session(void) { if (gReverbDownsampleRate < (1 << (reverbConsole - 1))) gReverbDownsampleRate = (1 << (reverbConsole - 1)); reverbWindowSize /= gReverbDownsampleRate; - if (reverbWindowSize < DEFAULT_LEN_2CH) // This might not actually be necessary? - reverbWindowSize = DEFAULT_LEN_2CH; + // if (reverbWindowSize < DEFAULT_LEN_2CH) // This might not actually be necessary? + // reverbWindowSize = DEFAULT_LEN_2CH; #endif switch (gReverbDownsampleRate) { diff --git a/src/game/game_init.c b/src/game/game_init.c index 6cfa8f55..b1bccfac 100644 --- a/src/game/game_init.c +++ b/src/game/game_init.c @@ -44,7 +44,7 @@ struct GfxPool *gGfxPool; OSContStatus gControllerStatuses[4]; OSContPad gControllerPads[4]; u8 gControllerBits; -u8 gIsConsole; +u8 gIsConsole = TRUE; // Needs to be initialized before audio_reset_session is called u8 gBorderHeight; #ifdef EEP s8 gEepromProbe; @@ -76,11 +76,9 @@ UNUSED static s32 sUnusedGameInitValue = 0; // General timer that runs as the game starts u32 gGlobalTimer = 0; -u8 gIsConsole; #ifdef WIDE u8 gWidescreen; #endif -u8 gBorderHeight; // Framebuffer rendering values (max 3) u16 sRenderedFramebuffer = 0; From 8b039b90327e96f6faf43543aa6c8472544d65c7 Mon Sep 17 00:00:00 2001 From: gheskett Date: Wed, 4 Aug 2021 01:03:07 -0500 Subject: [PATCH 18/20] Mono reverb support --- src/audio/synthesis.c | 89 +++++++++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 32 deletions(-) diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index b65153bb..74d0e828 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -46,10 +46,10 @@ struct VolumeChange { /* ----------------------------------------------------------REVERB PARAMETERS----------------------------------------------------------------- */ -s32 gReverbRevIndex = 0x9A; // Affects decay time mostly; can be messed with at any time (and also probably the most useful parameter here) -s32 gReverbGainIndex = 0xA6; // Affects signal retransmitted back into buffers; can be messed with at any time +s32 gReverbRevIndex = 0x7F; // Affects decay time mostly; can be messed with at any time (and also probably the most useful parameter here) +s32 gReverbGainIndex = 0xA3; // Affects signal retransmitted back into buffers; can be messed with at any time s32 gReverbWetSignal = 0xF3; // Amount of reverb specific output in final signal; can be messed with at any time -s32 gReverbDrySignal = 0x26; // Amount of original input in final signal (large values can cause terrible feedback!); can be messed with at any time +s32 gReverbDrySignal = 0x00; // Amount of original input in final signal (large values can cause terrible feedback!); can be messed with at any time // These values affect reverb delays, bigger values result in fatter echo (and more memory); must be cumulatively smaller than BETTER_REVERB_SIZE/8. // If setting reverb downsample value to 1 (which currently does not work anyway), this must be BETTER_REVERB_SIZE/16. @@ -340,37 +340,62 @@ void prepare_reverb_ring_buffer(s32 chunkLen, u32 updateIndex) { #ifdef BETTER_REVERB else if (consoleBetterReverb) { item = &gSynthesisReverb.items[gSynthesisReverb.curFrame][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++) - 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]); - // } + if (gSoundMode == SOUND_MODE_MONO) { + 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); + 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); + 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); + 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); + 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++) - 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; srcPos += gReverbDownsampleRate, dstPos++) - reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], gSynthesisReverb.ringBuffer.left[dstPos], gSynthesisReverb.ringBuffer.right[dstPos]); + else { + if (gReverbDownsampleRate != 1) { + osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH); + for (srcPos = 0, dstPos = item->startPos; dstPos < item->lengthA / 2 + item->startPos; srcPos += gReverbDownsampleRate, dstPos++) + 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 (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]); - // } + // 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]); + // } + } } } #endif From 02fc4a75d5758b65bd058964470ec71b263e2522 Mon Sep 17 00:00:00 2001 From: gheskett Date: Wed, 4 Aug 2021 03:17:51 -0500 Subject: [PATCH 19/20] Better document reverb additions --- README.md | 1 + include/config.h | 5 +-- src/audio/heap.c | 10 +++--- src/audio/synthesis.c | 78 ++++++++++++++++++++++++++++--------------- src/audio/synthesis.h | 9 ++--- 5 files changed, 67 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 891f3383..68cc9f95 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ This is a fork of the ultrasm64 repo by CrashOveride which includes the followin - ia8 coins (64x64), the vanilla coin texture is upgraded to accomodate. * - Skybox size modifier. You can have 2x, 3x and 4x size skyboxes (you can select the skybox size in `config.h`.) Please note that this might affect console performance, especially 4x mode. 2x or 3x mode is recommended if aiming for console. By CowQuack * - You can set the black border size to different values for console and emulator. It's set to 0 by default for both. * +- This repo supports 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. See `audio/synthesis.c` for more configuration info. (By ArcticJaguar725) * # UltraSM64 diff --git a/include/config.h b/include/config.h index 55596178..11e4f771 100644 --- a/include/config.h +++ b/include/config.h @@ -151,8 +151,9 @@ #define DISABLE_AA // Makes the coins ia8 64x64 instead of ia16 32x32. Uses new ia8 textures so that vanilla coins look better. #define IA8_COINS -// tmp -#define BETTER_REVERB +// 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 // 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 37fa8496..98457281 100644 --- a/src/audio/heap.c +++ b/src/audio/heap.c @@ -1118,7 +1118,9 @@ void audio_reset_session(void) { #if defined(VERSION_JP) || defined(VERSION_US) s32 frames; s32 remainingDmas; +#ifdef BETTER_REVERB s8 reverbConsole; +#endif #else struct SynthesisReverb *reverb; #endif @@ -1235,14 +1237,14 @@ void audio_reset_session(void) { if (gIsConsole) reverbConsole = betterReverbConsoleDownsample; // Console! else - reverbConsole = 2; // Setting this to 1 is REALLY slow, please use sparingly! + reverbConsole = betterReverbEmulatorDownsample; // Setting this to 1 is REALLY slow, please use sparingly! if (reverbConsole <= 0) { reverbConsole = 1; - consoleBetterReverb = FALSE; + toggleBetterReverb = FALSE; } else { - consoleBetterReverb = TRUE; + toggleBetterReverb = TRUE; } if (gReverbDownsampleRate < (1 << (reverbConsole - 1))) @@ -1446,7 +1448,7 @@ void audio_reset_session(void) { // This does not have to be reset after being initialized for the first time, which would speed up load times dramatically. // However, reseting this allows for proper clearing of the reverb buffers, as well as dynamic customization of the delays array. #ifdef BETTER_REVERB - if (consoleBetterReverb) { + if (toggleBetterReverb) { for (i = 0; i < NUM_ALLPASS; ++i) delays[i] = delaysBaseline[i] / gReverbDownsampleRate; diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index 74d0e828..f890f40d 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -37,40 +37,67 @@ #define AUDIO_ALIGN(val, amnt) (((val) + (1 << amnt) - 1) & ~((1 << amnt) - 1)) -struct VolumeChange { - u16 sourceLeft; - u16 sourceRight; - u16 targetLeft; - u16 targetRight; -}; +/* -------------------------------------------------------BEGIN REVERB PARAMETERS-------------------------------------------------------------- */ -/* ----------------------------------------------------------REVERB PARAMETERS----------------------------------------------------------------- */ +/** + * This reverb is a much more natural, ambient implementation over vanilla's, though at the cost of some memory and performance. + * These parameters are here to provide maximum control over the usage of the reverb effect, as well as with game performance. + * + * 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). + */ s32 gReverbRevIndex = 0x7F; // Affects decay time mostly; can be messed with at any time (and also probably the most useful parameter here) -s32 gReverbGainIndex = 0xA3; // Affects signal retransmitted back into buffers; can be messed with at any time -s32 gReverbWetSignal = 0xF3; // Amount of reverb specific output in final signal; can be messed with at any time +s32 gReverbGainIndex = 0xA3; // Affects signal immediately retransmitted back into buffers; can be messed with at any time +s32 gReverbWetSignal = 0xEF; // Amount of reverb specific output in final signal; can be messed with at any time s32 gReverbDrySignal = 0x00; // Amount of original input in final signal (large values can cause terrible feedback!); can be messed with at any time -// These values affect reverb delays, bigger values result in fatter echo (and more memory); must be cumulatively smaller than BETTER_REVERB_SIZE/8. -// If setting reverb downsample value to 1 (which currently does not work anyway), this must be BETTER_REVERB_SIZE/16. -// These values should never be changed unless in this declaration or during a call to audio_reset_session, or it could lead to a major memory leak or garbage audio. -// These values should also never be negative; these are just s32s to avoid typecasts +// 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 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] = { 1080, 1352, 1200, 1384, 1048, 1352, 1200, 1232, 1432, 928, 1504, 1512 }; -const s32 delaysBaseline[NUM_ALLPASS] = { // Like delays variable, but represent max values that never change (also probably somewhat redundant) + +// 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] = { 1080, 1352, 1200, 1384, 1048, 1352, 1200, 1232, 1432, 928, 1504, 1512 }; -const s32 reverbMults[2][NUM_ALLPASS / 3] = { // These values affect reverb decay depending on the filter index; can be messed with at any time - {0xD2, 0x6E, 0x36, 0x1F}, - {0x38, 0x26, 0xCF, 0x71} + +// 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 }; + +// Setting this to 4 completely ruins game sound, so set this value to -1 to use vanilla reverb if this is too slow, or if it just doesn't fit the desired aesthetic of a level. +// 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; + +// 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; + +/* --------------------------------------------------------END REVERB PARAMETERS--------------------------------------------------------------- */ + +// 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} @@ -79,14 +106,13 @@ 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; -u8 consoleBetterReverb = TRUE; // Do not change this line unless you know what you're doing; Please use the variable below instead. -// Setting this to 4 completely breaks game sound, so set this value to -1 to use vanilla reverb for console if this is too slow. -// You can change this value back and forth before audio_reset_session is called if different levels can tolerate the demand better than others. -// A higher downsample value hits the game's frequency limit sooner, which may cause the reverb to be off pitch. This is a vanilla level issue (and counter intuitive). -s8 betterReverbConsoleDownsample = 3; - -/* --------------------------------------------------------END REVERB PARAMETERS--------------------------------------------------------------- */ +struct VolumeChange { + u16 sourceLeft; + u16 sourceRight; + u16 targetLeft; + u16 targetRight; +}; u64 *synthesis_do_one_audio_update(s16 *aiBuf, s32 bufLen, u64 *cmd, s32 updateIndex); #ifdef VERSION_EU @@ -312,7 +338,7 @@ void prepare_reverb_ring_buffer(s32 chunkLen, u32 updateIndex) { s32 excessiveSamples; #ifdef BETTER_REVERB - if (!consoleBetterReverb && gReverbDownsampleRate != 1) { + if (!toggleBetterReverb && gReverbDownsampleRate != 1) { #else if (gReverbDownsampleRate != 1) { #endif @@ -338,7 +364,7 @@ void prepare_reverb_ring_buffer(s32 chunkLen, u32 updateIndex) { } } #ifdef BETTER_REVERB - else if (consoleBetterReverb) { + else if (toggleBetterReverb) { item = &gSynthesisReverb.items[gSynthesisReverb.curFrame][updateIndex]; if (gSoundMode == SOUND_MODE_MONO) { if (gReverbDownsampleRate != 1) { diff --git a/src/audio/synthesis.h b/src/audio/synthesis.h index d835842c..3cddeef8 100644 --- a/src/audio/synthesis.h +++ b/src/audio/synthesis.h @@ -18,20 +18,21 @@ #endif #ifdef BETTER_REVERB -#define BETTER_REVERB_SIZE 0xF200 // Size of all delaysBaseline values * 8 / 2^downsampleFactor (plus array pointers) -// #define BETTER_REVERB_SIZE 0x1E200 // For use with no downsampling (Warning: very slow!) +#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 -#define NUM_ALLPASS 12 +#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 const s32 delaysBaseline[NUM_ALLPASS]; extern s32 delays[NUM_ALLPASS]; extern s32 ***delayBufs; -extern u8 consoleBetterReverb; +extern u8 toggleBetterReverb; extern s8 betterReverbConsoleDownsample; +extern s8 betterReverbEmulatorDownsample; struct ReverbRingBufferItem { From 2d7ed3a0f63e7ef1b9c6498aa9b3604e5d92d2cc Mon Sep 17 00:00:00 2001 From: gheskett Date: Wed, 4 Aug 2021 05:38:16 -0500 Subject: [PATCH 20/20] Add betterReverbWindowSize parameter --- src/audio/heap.c | 7 +++++-- src/audio/synthesis.c | 9 ++++++++- src/audio/synthesis.h | 1 + 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/audio/heap.c b/src/audio/heap.c index 4737cbd1..27423a10 100644 --- a/src/audio/heap.c +++ b/src/audio/heap.c @@ -1251,12 +1251,15 @@ void audio_reset_session(void) { else { toggleBetterReverb = TRUE; } + + if (toggleBetterReverb && betterReverbWindowsSize >= 0) + reverbWindowSize = betterReverbWindowsSize; if (gReverbDownsampleRate < (1 << (reverbConsole - 1))) gReverbDownsampleRate = (1 << (reverbConsole - 1)); reverbWindowSize /= gReverbDownsampleRate; - // if (reverbWindowSize < DEFAULT_LEN_2CH) // This might not actually be necessary? - // reverbWindowSize = DEFAULT_LEN_2CH; + if (reverbWindowSize < DEFAULT_LEN_2CH) // Minimum window size to not overflow + reverbWindowSize = DEFAULT_LEN_2CH; #endif switch (gReverbDownsampleRate) { diff --git a/src/audio/synthesis.c b/src/audio/synthesis.c index f890f40d..96ddd0a7 100644 --- a/src/audio/synthesis.c +++ b/src/audio/synthesis.c @@ -53,6 +53,13 @@ s32 gReverbGainIndex = 0xA3; // Affects signal immediately retransmitted back in s32 gReverbWetSignal = 0xEF; // Amount of reverb specific output in final signal; can be messed with at any time 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 is probably the easiest parameter to control usefully. +// 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, so this is taken into account automatically but may limit the lower possibilities. +// If this value is changed, it will go into effect the next time audio_reset_session is called. +// Set to -1 to use a default preset instead. Higher values represent more audio delay (usually better for echoey spaces). +s32 betterReverbWindowsSize = -1; + // 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 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. @@ -81,7 +88,7 @@ const s32 reverbMults[2][NUM_ALLPASS / 3] = { {0x38, 0x26, 0xCF, 0x71} // Right Channel }; -// Setting this to 4 completely ruins game sound, so set this value to -1 to use vanilla reverb if this is too slow, or if it just doesn't fit the desired aesthetic of a level. +// Setting this to 4 corrupts the game, so set this value to -1 to use vanilla reverb if this is too slow, or if it just doesn't fit the desired aesthetic of a level. // 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. diff --git a/src/audio/synthesis.h b/src/audio/synthesis.h index 3cddeef8..19f341ab 100644 --- a/src/audio/synthesis.h +++ b/src/audio/synthesis.h @@ -26,6 +26,7 @@ #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 s32 betterReverbWindowsSize; extern const s32 delaysBaseline[NUM_ALLPASS]; extern s32 delays[NUM_ALLPASS]; extern s32 ***delayBufs;