Add lightweight configuration settings to BETTER_REVERB (#602)

* Add light configuration settings to BETTER_REVERB

This can reduce runtime demand down to ~75% of the standard demand, at the cost of the configurability of generally more advanced parameters

* Reformat the BETTER_REVERB preset entries to be easier to look at

* :peterVOID:

* haha lol formatting change definitely related to reverb yes this isn't unwarranted at all lmao hehe hoho
This commit is contained in:
Gregory Heskett
2023-03-15 10:52:05 -04:00
committed by GitHub
parent c09103d456
commit 851ba192f6
6 changed files with 254 additions and 70 deletions

View File

@@ -48,19 +48,19 @@ u32 delaysArr[][NUM_ALLPASS] = {
4, 4, 4,
4, 4, 4,
4, 4, 4,
4, 4, 4
4, 4, 4,
},
{ /* 1 */
1080, 1352, 1200,
1200, 1232, 1432,
1384, 1048, 1352,
928, 1504, 1512
928, 1504, 1512,
},
{ /* 2 */
1384, 1352, 1048,
928, 1512, 1504,
928, 1512, 1504,
1080, 1200, 1352,
1200, 1432, 1232
1200, 1432, 1232,
},
};
@@ -74,34 +74,72 @@ s32 reverbMultsArr[][NUM_ALLPASS / 3] = {
/**
* Format:
* - downsampleRate (Higher values exponentially reduce the number of input samples to process, improving perfomance at cost of quality)
* - isMono (Only process reverb on the left channel and share it with the right channel, improving performance at cost of quality)
* - filterCount (Number of filters to process data with; in general, more filters means higher quality at the cost of performance demand)
* - windowSize (Size of circular reverb buffer; higher values work better for a more open soundscape, lower is better for a more compact sound)
* - gain (Amount of audio retransmitted into the circular reverb buffer, emulating decay; higher values represent a lengthier decay period)
* - gainIndex (Advanced parameter used to tune the outputs of every first two of three filters)
* - reverbIndex (Advanced parameter used to tune the incoming output of every third filter)
* - useLightweightSettings (Reduce some runtime configurability options in favor of a slight speed boost during processing; Light configurability settings are found in synthesis.h)
* - downsampleRate (Higher values exponentially reduce the number of input samples to process, improving perfomance at cost of quality)
* - isMono (Only process reverb on the left channel and share it with the right channel, improving performance at cost of quality)
* - filterCount (Number of filters to process data with; in general, more filters means higher quality at the cost of performance demand; always 3 with light settings)
*
* - *delaysL (Array of variable audio buffer sizes / delays for each respective filter [left channel])
* - *delaysR (Array of variable audio buffer sizes / delays for each respective filter [right channel])
* - *reverbMultsL (Array of multipliers applied to the final output of each group of 3 filters [left channel])
* - *reverbMultsR (Array of multipliers applied to the final output of each group of 3 filters [right channel])
* - windowSize (Size of circular reverb buffer; higher values work better for a more open soundscape, lower is better for a more compact sound)
* - gain (Amount of audio retransmitted into the circular reverb buffer, emulating decay; higher values represent a lengthier decay period)
* - gainIndex (Advanced parameter; used to tune the outputs of every first two of three filters; overridden when using light settings)
* - reverbIndex (Advanced parameter; used to tune the incoming output of every third filter; overridden when using light settings)
*
* NOTE: First entry will always be used by default when not using the level commands to specify a preset.
* - *delaysL (Advanced parameter; array of variable audio buffer sizes / delays for each respective filter [left channel]; overridden when using light settings)
* - *delaysR (Advanced parameter; array of variable audio buffer sizes / delays for each respective filter [right channel]; overridden when using light settings)
* - *reverbMultsL (Advanced parameter; array of multipliers applied to the final output of each group of 3 filters [left channel])
* - *reverbMultsR (Advanced parameter; array of multipliers applied to the final output of each group of 3 filters [right channel])
*
* NOTE: The first entry will always be used by default when not using the level commands to specify a preset.
* Please reference the HackerSM64 Wiki for more descriptive documentation of these parameters and usage of BETTER_REVERB in general.
*/
struct BetterReverbSettings gBetterReverbSettings[] = {
{ /* 0 */
-1, FALSE, NUM_ALLPASS, -1, -1, 0x00, 0x00, // Vanilla Reverb
delaysArr[0], delaysArr[0], reverbMultsArr[0], reverbMultsArr[0]
{ /* Preset 0 - Vanilla Reverb [Default Preset] */
.useLightweightSettings = FALSE, // Ignored with vanilla reverb
.downsampleRate = -1, // Signifies use of vanilla reverb
.isMono = FALSE, // Ignored with vanilla reverb
.filterCount = NUM_ALLPASS, // Ignored with vanilla reverb
.windowSize = -1, // Use vanilla preset window size
.gain = -1, // Use vanilla preset gain value
.gainIndex = 0x00, // Ignored with vanilla reverb
.reverbIndex = 0x00, // Ignored with vanilla reverb
.delaysL = delaysArr[0], // Ignored with vanilla reverb
.delaysR = delaysArr[0], // Ignored with vanilla reverb
.reverbMultsL = reverbMultsArr[0], // Ignored with vanilla reverb
.reverbMultsR = reverbMultsArr[0], // Ignored with vanilla reverb
},
{ /* 1 */
2, FALSE, (NUM_ALLPASS - 9), 0xE00, 0x43FF, 0xA0, 0x30, // Default Console
delaysArr[1], delaysArr[2], reverbMultsArr[1], reverbMultsArr[2]
{ /* Preset 1 - Sample Console Configuration */
.useLightweightSettings = TRUE,
.downsampleRate = 2,
.isMono = FALSE,
.filterCount = (NUM_ALLPASS - 9), // Ignored with lightweight settings
.windowSize = 0x0E00,
.gain = 0x43FF,
.gainIndex = 0xA0, // Ignored with lightweight settings
.reverbIndex = 0x30, // Ignored with lightweight settings
.delaysL = delaysArr[1],
.delaysR = delaysArr[2],
.reverbMultsL = reverbMultsArr[1], // Ignored with lightweight settings
.reverbMultsR = reverbMultsArr[2], // Ignored with lightweight settings
},
{ /* 2 */
1, FALSE, NUM_ALLPASS, 0xE00, 0x28FF, 0xA0, 0x60, // Default Emulator (RCVI Hack only)
delaysArr[1], delaysArr[2], reverbMultsArr[1], reverbMultsArr[2]
{ /* Preset 2 - Sample Emulator Configuration (RCVI Hack Only) */
.useLightweightSettings = FALSE,
.downsampleRate = 1,
.isMono = FALSE,
.filterCount = NUM_ALLPASS,
.windowSize = 0x0E00,
.gain = 0x28FF,
.gainIndex = 0xA0,
.reverbIndex = 0x60,
.delaysL = delaysArr[1],
.delaysR = delaysArr[2],
.reverbMultsL = reverbMultsArr[1],
.reverbMultsR = reverbMultsArr[2],
},
};
#endif

View File

@@ -1051,6 +1051,7 @@ void init_reverb_us(s32 presetId) {
// This will likely crash if given an invalid preset value. Adding a safety check here isn't worth the usability interference.
struct BetterReverbSettings *betterReverbPreset = &gBetterReverbSettings[gBetterReverbPreset];
betterReverbLightweight = betterReverbPreset->useLightweightSettings;
betterReverbDownsampleRate = betterReverbPreset->downsampleRate;
monoReverb = betterReverbPreset->isMono;
reverbFilterCount = betterReverbPreset->filterCount;

View File

@@ -732,6 +732,7 @@ struct NoteSynthesisBuffers {
#ifdef BETTER_REVERB
struct BetterReverbSettings {
u8 useLightweightSettings;
s8 downsampleRate;
u8 isMono;
u8 filterCount;

View File

@@ -42,6 +42,7 @@
// Do not touch these values manually, unless you want potential for problems.
u8 gBetterReverbPreset = 0;
u8 toggleBetterReverb = FALSE;
u8 betterReverbLightweight = FALSE;
u8 monoReverb;
s8 betterReverbDownsampleRate;
static u8 reverbMultsL[NUM_ALLPASS / 3] = {0};
@@ -52,6 +53,8 @@ static s32 delaysL[NUM_ALLPASS] = {0};
static s32 delaysR[NUM_ALLPASS] = {0};
static s32 **delayBufsL;
static s32 **delayBufsR;
static s32 lastDelayLightL;
static s32 lastDelayLightR;
s32 reverbLastFilterIndex;
s32 reverbFilterCount;
s32 betterReverbWindowsSize;
@@ -99,13 +102,13 @@ static void reverb_samples(s16 *outSampleL, s16 *outSampleR, s32 inSampleL, s32
s32 *curDelaySampleR;
s32 historySampleL;
s32 historySampleR;
s32 i = 0;
s32 j = 0;
s32 k = 0;
s32 outTmpL = 0;
s32 outTmpR = 0;
s32 tmpCarryoverL = ((delayBufsL[reverbLastFilterIndex][allpassIdxL[reverbLastFilterIndex]] * betterReverbRevIndex) >> 8) + inSampleL;
s32 tmpCarryoverR = ((delayBufsR[reverbLastFilterIndex][allpassIdxR[reverbLastFilterIndex]] * betterReverbRevIndex) >> 8) + inSampleR;
s32 i = 0;
s32 j = 0;
s32 k = 0;
for (; i <= reverbLastFilterIndex; ++i, ++j) {
curDelaySampleL = &delayBufsL[i][allpassIdxL[i]];
@@ -141,11 +144,11 @@ static void reverb_samples(s16 *outSampleL, s16 *outSampleR, s32 inSampleL, s32
static void reverb_mono_sample(s16 *outSample, s32 inSample) {
s32 *curDelaySample;
s32 historySample;
s32 outTmp = 0;
s32 tmpCarryover = ((delayBufsL[reverbLastFilterIndex][allpassIdxL[reverbLastFilterIndex]] * betterReverbRevIndex) >> 8) + inSample;
s32 i = 0;
s32 j = 0;
s32 k = 0;
s32 outTmp = 0;
s32 tmpCarryover = ((delayBufsL[reverbLastFilterIndex][allpassIdxL[reverbLastFilterIndex]] * betterReverbRevIndex) >> 8) + inSample;
for (; i <= reverbLastFilterIndex; ++i, ++j) {
curDelaySample = &delayBufsL[i][allpassIdxL[i]];
@@ -168,6 +171,72 @@ static void reverb_mono_sample(s16 *outSample, s32 inSample) {
*outSample = CLAMP_S16(outTmp);
}
// Light reverb processing functions!
#define FILTERS_MINUS_1 (BETTER_REVERB_FILTER_COUNT_LIGHT - 1)
static void reverb_samples_light(s16 *outSampleL, s16 *outSampleR, s32 inSampleL, s32 inSampleR) {
s32 *curDelaySampleL;
s32 *curDelaySampleR;
s32 historySampleL;
s32 historySampleR;
s32 tmpCarryoverL = (((delayBufsL[FILTERS_MINUS_1][allpassIdxL[FILTERS_MINUS_1]] * BETTER_REVERB_REVERB_INDEX_LIGHT) >> 8) + inSampleL);
s32 tmpCarryoverR = (((delayBufsR[FILTERS_MINUS_1][allpassIdxR[FILTERS_MINUS_1]] * BETTER_REVERB_REVERB_INDEX_LIGHT) >> 8) + inSampleR);
s32 i = 0;
for (; i < FILTERS_MINUS_1; ++i) {
curDelaySampleL = &delayBufsL[i][allpassIdxL[i]];
curDelaySampleR = &delayBufsR[i][allpassIdxR[i]];
historySampleL = *curDelaySampleL;
historySampleR = *curDelaySampleR;
*curDelaySampleL = (((historySampleL * (-BETTER_REVERB_GAIN_INDEX_LIGHT)) >> 8) + tmpCarryoverL);
*curDelaySampleR = (((historySampleR * (-BETTER_REVERB_GAIN_INDEX_LIGHT)) >> 8) + tmpCarryoverR);
tmpCarryoverL = (((*curDelaySampleL * BETTER_REVERB_GAIN_INDEX_LIGHT) >> 8) + historySampleL);
tmpCarryoverR = (((*curDelaySampleR * BETTER_REVERB_GAIN_INDEX_LIGHT) >> 8) + historySampleR);
if (++allpassIdxL[i] == delaysL[i]) allpassIdxL[i] = 0;
if (++allpassIdxR[i] == delaysR[i]) allpassIdxR[i] = 0;
}
curDelaySampleL = &delayBufsL[FILTERS_MINUS_1][allpassIdxL[FILTERS_MINUS_1]];
curDelaySampleR = &delayBufsR[FILTERS_MINUS_1][allpassIdxR[FILTERS_MINUS_1]];
historySampleL = ((*curDelaySampleL * BETTER_REVERB_MULTIPLE_LIGHT) >> 8); // outTmpL variable not needed, as there is no sample addition happening here. Not really a history sample though.
historySampleR = ((*curDelaySampleR * BETTER_REVERB_MULTIPLE_LIGHT) >> 8); // outTmpR variable not needed, as there is no sample addition happening here. Not really a history sample though.
*curDelaySampleL = tmpCarryoverL;
*curDelaySampleR = tmpCarryoverR;
if (++allpassIdxL[FILTERS_MINUS_1] == lastDelayLightL) allpassIdxL[FILTERS_MINUS_1] = 0;
if (++allpassIdxR[FILTERS_MINUS_1] == lastDelayLightR) allpassIdxR[FILTERS_MINUS_1] = 0;
*outSampleL = CLAMP_S16(historySampleL);
*outSampleR = CLAMP_S16(historySampleR);
}
static void reverb_mono_sample_light(s16 *outSample, s32 inSample) {
s32 *curDelaySample;
s32 historySample;
s32 tmpCarryover = (((delayBufsL[FILTERS_MINUS_1][allpassIdxL[FILTERS_MINUS_1]] * BETTER_REVERB_REVERB_INDEX_LIGHT) >> 8) + inSample);
s32 i = 0;
for (; i < FILTERS_MINUS_1; ++i) {
curDelaySample = &delayBufsL[i][allpassIdxL[i]];
historySample = *curDelaySample;
*curDelaySample = (((historySample * (-BETTER_REVERB_GAIN_INDEX_LIGHT)) >> 8) + tmpCarryover);
tmpCarryover = (((*curDelaySample * BETTER_REVERB_GAIN_INDEX_LIGHT) >> 8) + historySample);
if (++allpassIdxL[i] == delaysL[i]) allpassIdxL[i] = 0;
}
curDelaySample = &delayBufsL[FILTERS_MINUS_1][allpassIdxL[FILTERS_MINUS_1]];
historySample = ((*curDelaySample * BETTER_REVERB_MULTIPLE_LIGHT) >> 8); // outTmp variable not needed, as there is no sample addition happening here. Not really a history sample though.
*curDelaySample = tmpCarryover;
if (++allpassIdxL[FILTERS_MINUS_1] == lastDelayLightL) allpassIdxL[FILTERS_MINUS_1] = 0;
*outSample = CLAMP_S16(historySample);
}
#undef FILTERS_MINUS_1
void initialize_better_reverb_buffers(void) {
delayBufsL = (s32**) soundAlloc(&gBetterReverbPool, BETTER_REVERB_PTR_SIZE);
delayBufsR = &delayBufsL[NUM_ALLPASS];
@@ -176,6 +245,10 @@ void initialize_better_reverb_buffers(void) {
void set_better_reverb_buffers(u32 *inputDelaysL, u32 *inputDelaysR) {
s32 bufOffset = 0;
s32 i;
s32 filterCount = reverbFilterCount;
if (betterReverbLightweight)
filterCount = BETTER_REVERB_FILTER_COUNT_LIGHT;
gBetterReverbPool.cur = gBetterReverbPool.start + ALIGN16(BETTER_REVERB_PTR_SIZE); // Reset reverb data pool
@@ -183,9 +256,9 @@ void set_better_reverb_buffers(u32 *inputDelaysL, u32 *inputDelaysR) {
if (!toggleBetterReverb)
return;
// NOTE: Using reverbFilterCount over NUM_ALLPASS will report less memory usage with fewer filters, but poses an additional
// NOTE: Using filterCount over NUM_ALLPASS will report less memory usage with fewer filters, but poses an additional
// risk to anybody testing on console with performance compromises, as emulator can be easily overlooked.
for (i = 0; i < reverbFilterCount; ++i) {
for (i = 0; i < filterCount; ++i) {
delaysL[i] = (s32) (inputDelaysL[i] / gReverbDownsampleRate);
delaysR[i] = (s32) (inputDelaysR[i] / gReverbDownsampleRate);
delayBufsL[i] = soundAlloc(&gBetterReverbPool, delaysL[i] * sizeof(s32));
@@ -196,6 +269,9 @@ void set_better_reverb_buffers(u32 *inputDelaysL, u32 *inputDelaysR) {
aggress(bufOffset * sizeof(s32) <= BETTER_REVERB_SIZE - ALIGN16(BETTER_REVERB_PTR_SIZE), "BETTER_REVERB_SIZE is too small for this preset!");
lastDelayLightL = delaysL[filterCount-1];
lastDelayLightR = delaysR[filterCount-1];
bzero(allpassIdxL, sizeof(allpassIdxL));
bzero(allpassIdxR, sizeof(allpassIdxR));
}
@@ -300,40 +376,82 @@ void prepare_reverb_ring_buffer(s32 chunkLen, u32 updateIndex) {
}
#ifdef BETTER_REVERB
else if (toggleBetterReverb) {
s32 firstLoopMax;
s32 secondLoopMax;
item = &gSynthesisReverb.items[gSynthesisReverb.curFrame][updateIndex];
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++) {
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++) {
reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) item->toDownsampleLeft[srcPos] + (s32) item->toDownsampleRight[srcPos]) / 2);
gSynthesisReverb.ringBuffer.right[dstPos] = gSynthesisReverb.ringBuffer.left[dstPos];
dstPos = item->startPos;
firstLoopMax = ((item->lengthA / 2) + item->startPos);
secondLoopMax = (item->lengthB / 2);
// This block could be simplified probably with function pointers or rewriting reverb_mono_sample to support L and R individually. Another idea would be to process more than one sample at a time
// for each function call. In practice with HackerSM64, these ideas either aren't super trivial to implement efficiently or don't benefit performance at all, so I'm leaving this as it is for now.
if (betterReverbLightweight) {
if (gSoundMode == SOUND_MODE_MONO || monoReverb) {
if (gReverbDownsampleRate != 1) {
osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH);
for (srcPos = 0; dstPos < firstLoopMax; srcPos += gReverbDownsampleRate, dstPos++)
reverb_mono_sample_light(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) item->toDownsampleLeft[srcPos] + (s32) item->toDownsampleRight[srcPos]) / 2);
bcopy(&gSynthesisReverb.ringBuffer.left[item->startPos], &gSynthesisReverb.ringBuffer.right[item->startPos], (dstPos - item->startPos) * sizeof(s16));
for (dstPos = 0; dstPos < secondLoopMax; srcPos += gReverbDownsampleRate, dstPos++)
reverb_mono_sample_light(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) item->toDownsampleLeft[srcPos] + (s32) item->toDownsampleRight[srcPos]) / 2);
bcopy(gSynthesisReverb.ringBuffer.left, gSynthesisReverb.ringBuffer.right, dstPos * sizeof(s16));
} else {
for (; dstPos < firstLoopMax; dstPos++)
reverb_mono_sample_light(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) gSynthesisReverb.ringBuffer.left[dstPos] + (s32) gSynthesisReverb.ringBuffer.right[dstPos]) / 2);
bcopy(&gSynthesisReverb.ringBuffer.left[item->startPos], &gSynthesisReverb.ringBuffer.right[item->startPos], (dstPos - item->startPos) * sizeof(s16));
for (dstPos = 0; dstPos < secondLoopMax; dstPos++)
reverb_mono_sample_light(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) gSynthesisReverb.ringBuffer.left[dstPos] + (s32) gSynthesisReverb.ringBuffer.right[dstPos]) / 2);
bcopy(gSynthesisReverb.ringBuffer.left, gSynthesisReverb.ringBuffer.right, dstPos * sizeof(s16));
}
} else {
for (dstPos = item->startPos; dstPos < ((item->lengthA / 2) + item->startPos); dstPos++) {
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++) {
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];
if (gReverbDownsampleRate != 1) {
osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH);
for (srcPos = 0; dstPos < firstLoopMax; srcPos += gReverbDownsampleRate, dstPos++)
reverb_samples_light(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], item->toDownsampleLeft[srcPos], item->toDownsampleRight[srcPos]);
for (dstPos = 0; dstPos < secondLoopMax; srcPos += gReverbDownsampleRate, dstPos++)
reverb_samples_light(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], item->toDownsampleLeft[srcPos], item->toDownsampleRight[srcPos]);
} else {
for (; dstPos < firstLoopMax; dstPos++)
reverb_samples_light(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], gSynthesisReverb.ringBuffer.left[dstPos], gSynthesisReverb.ringBuffer.right[dstPos]);
for (dstPos = 0; dstPos < secondLoopMax; dstPos++)
reverb_samples_light(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], gSynthesisReverb.ringBuffer.left[dstPos], gSynthesisReverb.ringBuffer.right[dstPos]);
}
}
} 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]);
if (gSoundMode == SOUND_MODE_MONO || monoReverb) {
if (gReverbDownsampleRate != 1) {
osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH);
for (srcPos = 0; dstPos < firstLoopMax; srcPos += gReverbDownsampleRate, dstPos++)
reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) item->toDownsampleLeft[srcPos] + (s32) item->toDownsampleRight[srcPos]) / 2);
bcopy(&gSynthesisReverb.ringBuffer.left[item->startPos], &gSynthesisReverb.ringBuffer.right[item->startPos], (dstPos - item->startPos) * sizeof(s16));
for (dstPos = 0; dstPos < secondLoopMax; srcPos += gReverbDownsampleRate, dstPos++)
reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) item->toDownsampleLeft[srcPos] + (s32) item->toDownsampleRight[srcPos]) / 2);
bcopy(gSynthesisReverb.ringBuffer.left, gSynthesisReverb.ringBuffer.right, dstPos * sizeof(s16));
} else {
for (; dstPos < firstLoopMax; dstPos++)
reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) gSynthesisReverb.ringBuffer.left[dstPos] + (s32) gSynthesisReverb.ringBuffer.right[dstPos]) / 2);
bcopy(&gSynthesisReverb.ringBuffer.left[item->startPos], &gSynthesisReverb.ringBuffer.right[item->startPos], (dstPos - item->startPos) * sizeof(s16));
for (dstPos = 0; dstPos < secondLoopMax; dstPos++)
reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) gSynthesisReverb.ringBuffer.left[dstPos] + (s32) gSynthesisReverb.ringBuffer.right[dstPos]) / 2);
bcopy(gSynthesisReverb.ringBuffer.left, gSynthesisReverb.ringBuffer.right, dstPos * sizeof(s16));
}
} else {
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]);
if (gReverbDownsampleRate != 1) {
osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH);
for (srcPos = 0; dstPos < firstLoopMax; srcPos += gReverbDownsampleRate, dstPos++)
reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], item->toDownsampleLeft[srcPos], item->toDownsampleRight[srcPos]);
for (dstPos = 0; dstPos < secondLoopMax; srcPos += gReverbDownsampleRate, dstPos++)
reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], item->toDownsampleLeft[srcPos], item->toDownsampleRight[srcPos]);
} else {
for (; dstPos < firstLoopMax; dstPos++)
reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], gSynthesisReverb.ringBuffer.left[dstPos], gSynthesisReverb.ringBuffer.right[dstPos]);
for (dstPos = 0; dstPos < secondLoopMax; dstPos++)
reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], gSynthesisReverb.ringBuffer.left[dstPos], gSynthesisReverb.ringBuffer.right[dstPos]);
}
}
}
}

View File

@@ -19,14 +19,37 @@
#ifdef BETTER_REVERB
#define REVERB_WINDOW_SIZE_MAX 0x2000
/* ------------ BETTER REVERB GENERAL PARAMETERS ------------ */
#define NUM_ALLPASS 12 // Maximum number of delay filters to use with better reverb; do not change this value if you don't know what you're doing.
#define BETTER_REVERB_PTR_SIZE ALIGN16(NUM_ALLPASS * sizeof(s32*) * 2) // Allocation space consumed by dynamically allocated pointers
// Size determined by (all delaysL/R values * 8) / (2 ^ Minimum Downsample Factor).
// The default value can be increased or decreased in conjunction with the values in delaysL/R
#define BETTER_REVERB_SIZE ALIGN16(0x1E000 + BETTER_REVERB_PTR_SIZE) // This can be significantly decreased if a downsample rate of 1 is not being used.
// The default value can be increased or decreased in conjunction with the values in delaysL/R.
// This can be significantly decreased if a downsample rate of 1 is not being used or if filter count is less than NUM_ALLPASS,
// as this default is configured to handle the emulator RCVI settings.
#define BETTER_REVERB_SIZE ALIGN16(0x1E000 + BETTER_REVERB_PTR_SIZE)
/* ------ BETTER REVERB LIGHTWEIGHT PARAMETER OVERRIDES ------ */
// Filter count works differently than normal when used with light settings and can support numbers that are not multiples of 3, though 3 is generally recommended.
// This can be reduced to 2 to save a third of runtime overhead, but substantially reduces reverb saturation.
// Similarly this can be increased from 3, but likely won't have beneficial outcomes worth the runtime expense compared to the modification of other parameters without using light settings.
#define BETTER_REVERB_FILTER_COUNT_LIGHT 3
#define BETTER_REVERB_GAIN_INDEX_LIGHT 0xA0 // Advanced parameter; used to tune the outputs of every filter except for the final one
#define BETTER_REVERB_REVERB_INDEX_LIGHT 0x30 // Advanced parameter; used to tune the incoming output of the final filter
#define BETTER_REVERB_MULTIPLE_LIGHT 0xD0 // Advanced parameter; multiplier applied to the final output signal for both the left and right channels (divided by 256)
/* ------------ BETTER REVERB EXTERNED VARIABLES ------------ */
extern u8 toggleBetterReverb;
extern u8 gBetterReverbPreset;
extern u8 betterReverbLightweight;
extern s8 betterReverbDownsampleRate;
extern u8 monoReverb;
extern s32 reverbFilterCount;
@@ -36,10 +59,18 @@ extern s32 betterReverbGainIndex;
extern s32 *gReverbMultsL;
extern s32 *gReverbMultsR;
extern u8 toggleBetterReverb;
#define REVERB_WINDOW_SIZE_MAX 0x2000
/* ------------ BETTER REVERB EXTERNED FUNCTIONS ------------ */
void initialize_better_reverb_buffers(void);
void set_better_reverb_buffers(u32 *inputDelaysL, u32 *inputDelaysR);
/* -------------- BETTER REVERB STATIC ASSERTS -------------- */
STATIC_ASSERT(NUM_ALLPASS % 3 == 0, "NUM_ALLPASS must be a multiple of 3!");
STATIC_ASSERT(BETTER_REVERB_FILTER_COUNT_LIGHT >= 2, "BETTER_REVERB_FILTER_COUNT_LIGHT should be no less than 2!");
STATIC_ASSERT(BETTER_REVERB_FILTER_COUNT_LIGHT <= NUM_ALLPASS, "BETTER_REVERB_FILTER_COUNT_LIGHT cannot be larger than NUM_ALLPASS!");
#else
@@ -137,11 +168,6 @@ extern struct SynthesisReverb gSynthesisReverb;
extern s16 D_SH_803479B4;
#endif
#ifdef BETTER_REVERB
void initialize_better_reverb_buffers(void);
void set_better_reverb_buffers(u32 *inputDelaysL, u32 *inputDelaysR);
#endif
u64 *synthesis_execute(u64 *cmdBuf, s32 *writtenCmds, s16 *aiBuf, s32 bufLen);
#if defined(VERSION_JP) || defined(VERSION_US)
void note_init_volume(struct Note *note);

View File

@@ -289,7 +289,7 @@ static const char *audioPoolNames[NUM_AUDIO_POOLS] = {
"Temporary Sequence Pool:\t ",
"Temporary Bank Pool:\t\t ",
#ifdef BETTER_REVERB
"Better Reverb Pool",
"Better Reverb Pool:\t\t ",
#endif
};
@@ -1424,7 +1424,7 @@ void get_char_from_byte(s32 *textX, s32 *textPos, u8 letter, u8 *wideX, u8 *spac
}
}
// Because we're using bcopy when both reading and writing to the text buffer, this doesn't care about alignment with the multi-bit types.
// Because we're using bcopy when both reading and writing to the text buffer, this doesn't care about alignment with the multi-byte types.
struct PuppyprintDeferredBufferHeader {
u16 x;
u16 y;