Improve BETTER_REVERB runtime by about 15-20% overall (#619)

Refactored everything to be more memory efficient and execute fewer function calls than prior

Also accidentally discovered a significant bug with the audio modes always being forced to stereo so this is definitely 2.1 scope lmao
This commit is contained in:
Gregory Heskett
2023-06-05 18:09:03 -04:00
committed by GitHub
parent 580fd45079
commit 81c0a8ae61
7 changed files with 152 additions and 233 deletions

View File

@@ -65,8 +65,7 @@ u32 delaysArr[][NUM_ALLPASS] = {
};
// Each entry represents an array of multipliers applied to the final output of each group of 3 filters.
// These values are u8s in spirit, but are set as s32 values to slightly increase performance during calculations.
s32 reverbMultsArr[][NUM_ALLPASS / 3] = {
u8 reverbMultsArr[][NUM_ALLPASS / 3] = {
/* 0 */ {0x00, 0x00, 0x00, 0x00},
/* 1 */ {0xD7, 0x6F, 0x36, 0x22},
/* 2 */ {0xCF, 0x73, 0x38, 0x1F},
@@ -125,7 +124,7 @@ struct BetterReverbSettings gBetterReverbSettings[] = {
.reverbMultsL = reverbMultsArr[1], // Ignored with lightweight settings
.reverbMultsR = reverbMultsArr[2], // Ignored with lightweight settings
},
{ /* Preset 2 - Sample Emulator Configuration (RCVI Hack Only) */
{ /* Preset 2 - Sample Emulator Configuration (RCVI Hack or Emulator CPU Overclocking Required!) */
.useLightweightSettings = FALSE,
.downsampleRate = 1,
.isMono = FALSE,

View File

@@ -1985,7 +1985,6 @@ void sound_init(void) {
sLowerBackgroundMusicVolume = FALSE;
sSoundBanksThatLowerBackgroundMusic = 0;
sCurrentBackgroundMusicSeqId = 0xff;
gSoundMode = SOUND_MODE_STEREO;
sBackgroundMusicQueueSize = 0;
sBackgroundMusicMaxTargetVolume = TARGET_VOLUME_UNSET;
D_80332120 = 0;

View File

@@ -1058,8 +1058,8 @@ void init_reverb_us(s32 presetId) {
betterReverbWindowsSize = betterReverbPreset->windowSize;
betterReverbRevIndex = betterReverbPreset->reverbIndex;
betterReverbGainIndex = betterReverbPreset->gainIndex;
gReverbMultsL = betterReverbPreset->reverbMultsL;
gReverbMultsR = betterReverbPreset->reverbMultsR;
gReverbMults[SYNTH_CHANNEL_LEFT] = betterReverbPreset->reverbMultsL;
gReverbMults[SYNTH_CHANNEL_RIGHT] = betterReverbPreset->reverbMultsR;
if (betterReverbDownsampleRate <= 0) {
toggleBetterReverb = FALSE;

View File

@@ -744,8 +744,8 @@ struct BetterReverbSettings {
u8 reverbIndex;
u32 *delaysL;
u32 *delaysR;
s32 *reverbMultsL;
s32 *reverbMultsR;
u8 *reverbMultsL;
u8 *reverbMultsR;
};
#endif

View File

@@ -86,7 +86,7 @@ s16 gTempoInternalToExternal;
s8 gAudioUpdatesPerFrame;
#endif
s8 gSoundMode;
s8 gSoundMode = SOUND_MODE_STEREO;
#if defined(VERSION_EU)
s8 gAudioUpdatesPerFrame;
@@ -855,7 +855,6 @@ void audio_init() {
gAudioFrameCount = 0;
gAudioTaskIndex = 0;
gCurrAiBufferIndex = 0;
gSoundMode = 0;
gAudioTask = NULL;
gAudioTasks[0].task.t.data_size = 0;
gAudioTasks[1].task.t.data_size = 0;

View File

@@ -45,23 +45,17 @@ u8 toggleBetterReverb = FALSE;
u8 betterReverbLightweight = FALSE;
u8 monoReverb;
s8 betterReverbDownsampleRate;
static u8 reverbMultsL[NUM_ALLPASS / 3] = {0};
static u8 reverbMultsR[NUM_ALLPASS / 3] = {0};
static s32 allpassIdxL[NUM_ALLPASS] = {0};
static s32 allpassIdxR[NUM_ALLPASS] = {0};
static s32 delaysL[NUM_ALLPASS] = {0};
static s32 delaysR[NUM_ALLPASS] = {0};
static s32 **delayBufsL;
static s32 **delayBufsR;
static s32 lastDelayLightL;
static s32 lastDelayLightR;
static s32 reverbMults[SYNTH_CHANNEL_STEREO_COUNT][NUM_ALLPASS / 3] = {0};
static s32 allpassIdx[SYNTH_CHANNEL_STEREO_COUNT][NUM_ALLPASS] = {0};
static s32 betterReverbDelays[SYNTH_CHANNEL_STEREO_COUNT][NUM_ALLPASS] = {0};
static s32 **delayBufs[SYNTH_CHANNEL_STEREO_COUNT];
static s32 lastDelayLight[SYNTH_CHANNEL_STEREO_COUNT];
u8 *gReverbMults[SYNTH_CHANNEL_STEREO_COUNT];
s32 reverbLastFilterIndex;
s32 reverbFilterCount;
s32 betterReverbWindowsSize;
s32 betterReverbRevIndex; // This one is okay to adjust whenever
s32 betterReverbGainIndex; // This one is okay to adjust whenever
s32 *gReverbMultsL;
s32 *gReverbMultsR;
#endif
@@ -97,149 +91,97 @@ u8 sAudioSynthesisPad[0x20];
#endif
#ifdef BETTER_REVERB
static void reverb_samples(s16 *outSampleL, s16 *outSampleR, s32 inSampleL, s32 inSampleR) {
s32 *curDelaySampleL;
s32 *curDelaySampleR;
s32 historySampleL;
s32 historySampleR;
s32 outTmpL = 0;
s32 outTmpR = 0;
s32 tmpCarryoverL = ((delayBufsL[reverbLastFilterIndex][allpassIdxL[reverbLastFilterIndex]] * betterReverbRevIndex) >> 8) + inSampleL;
s32 tmpCarryoverR = ((delayBufsR[reverbLastFilterIndex][allpassIdxR[reverbLastFilterIndex]] * betterReverbRevIndex) >> 8) + inSampleR;
s32 i = 0;
s32 j = 0;
s32 k = 0;
static void reverb_samples(s16 *start, s16 *end, s16 *downsampleBuffer, s32 channel) {
s32 *curDelaySample;
s32 historySample;
s32 tmpCarryover;
s32 outSampleTotal;
s32 i;
s32 j;
s32 k;
for (; i <= reverbLastFilterIndex; ++i, ++j) {
curDelaySampleL = &delayBufsL[i][allpassIdxL[i]];
curDelaySampleR = &delayBufsR[i][allpassIdxR[i]];
historySampleL = *curDelaySampleL;
historySampleR = *curDelaySampleR;
s32 downsampleIncrement = gReverbDownsampleRate;
s32 **delayBufsLocal = delayBufs[channel];
s32 *delaysLocal = betterReverbDelays[channel];
s32 *reverbMultsLocal = reverbMults[channel];
s32 *allpassIdxLocal = allpassIdx[channel];
if (j == 2) {
j = -1;
outTmpL += (historySampleL * reverbMultsL[k ]) >> 8;
outTmpR += (historySampleR * reverbMultsR[k++]) >> 8;
*curDelaySampleL = tmpCarryoverL;
*curDelaySampleR = tmpCarryoverR;
if (i != reverbLastFilterIndex) {
tmpCarryoverL = ((historySampleL * betterReverbRevIndex) >> 8)/* + inSampleL*/;
tmpCarryoverR = ((historySampleR * betterReverbRevIndex) >> 8)/* + inSampleR*/;
s32 lastFilterIndex = reverbLastFilterIndex;
s32 revIndex = betterReverbRevIndex;
s32 gainIndex = betterReverbGainIndex;
// Set j outside of the loop only. Because we're forcing filter count to always be a multiple of 3, we can count on j always being 0 when exiting the second for loop.
j = 0;
for (; start < end; start++, downsampleBuffer += downsampleIncrement) {
tmpCarryover = ((delayBufsLocal[lastFilterIndex][allpassIdxLocal[lastFilterIndex]] * revIndex) >> 8) + *downsampleBuffer;
outSampleTotal = 0;
i = 0;
k = 0;
for (; i <= lastFilterIndex; ++i, ++j) {
curDelaySample = &delayBufsLocal[i][allpassIdxLocal[i]];
historySample = *curDelaySample;
if (j == 2) {
j = -1;
outSampleTotal += ((historySample * reverbMultsLocal[k++]) >> 8);
*curDelaySample = tmpCarryover;
if (i != lastFilterIndex)
tmpCarryover = ((historySample * revIndex) >> 8)/* + *downsampleBuffer*/;
} else {
*curDelaySample = ((historySample * (-gainIndex)) >> 8) + tmpCarryover;
tmpCarryover = ((*curDelaySample * gainIndex) >> 8) + historySample;
}
} else {
*curDelaySampleL = ((historySampleL * (-betterReverbGainIndex)) >> 8) + tmpCarryoverL;
*curDelaySampleR = ((historySampleR * (-betterReverbGainIndex)) >> 8) + tmpCarryoverR;
tmpCarryoverL = ((*curDelaySampleL * betterReverbGainIndex) >> 8) + historySampleL;
tmpCarryoverR = ((*curDelaySampleR * betterReverbGainIndex) >> 8) + historySampleR;
if (++allpassIdxLocal[i] == delaysLocal[i]) allpassIdxLocal[i] = 0;
}
if (++allpassIdxL[i] == delaysL[i]) allpassIdxL[i] = 0;
if (++allpassIdxR[i] == delaysR[i]) allpassIdxR[i] = 0;
*start = CLAMP_S16(outSampleTotal);
}
*outSampleL = CLAMP_S16(outTmpL);
*outSampleR = CLAMP_S16(outTmpR);
}
static void reverb_mono_sample(s16 *outSample, s32 inSample) {
s32 *curDelaySample;
s32 historySample;
s32 outTmp = 0;
s32 tmpCarryover = ((delayBufsL[reverbLastFilterIndex][allpassIdxL[reverbLastFilterIndex]] * betterReverbRevIndex) >> 8) + inSample;
s32 i = 0;
s32 j = 0;
s32 k = 0;
for (; i <= reverbLastFilterIndex; ++i, ++j) {
curDelaySample = &delayBufsL[i][allpassIdxL[i]];
historySample = *curDelaySample;
if (j == 2) {
j = -1;
outTmp += ((historySample * reverbMultsL[k++]) >> 8);
*curDelaySample = tmpCarryover;
if (i != reverbLastFilterIndex)
tmpCarryover = ((historySample * betterReverbRevIndex) >> 8)/* + inSample*/;
} else {
*curDelaySample = ((historySample * (-betterReverbGainIndex)) >> 8) + tmpCarryover;
tmpCarryover = ((*curDelaySample * betterReverbGainIndex) >> 8) + historySample;
}
if (++allpassIdxL[i] == delaysL[i]) allpassIdxL[i] = 0;
}
*outSample = CLAMP_S16(outTmp);
}
// Light reverb processing functions!
#define FILTERS_MINUS_1 (BETTER_REVERB_FILTER_COUNT_LIGHT - 1)
static void reverb_samples_light(s16 *outSampleL, s16 *outSampleR, s32 inSampleL, s32 inSampleR) {
s32 *curDelaySampleL;
s32 *curDelaySampleR;
s32 historySampleL;
s32 historySampleR;
s32 tmpCarryoverL = (((delayBufsL[FILTERS_MINUS_1][allpassIdxL[FILTERS_MINUS_1]] * BETTER_REVERB_REVERB_INDEX_LIGHT) >> 8) + inSampleL);
s32 tmpCarryoverR = (((delayBufsR[FILTERS_MINUS_1][allpassIdxR[FILTERS_MINUS_1]] * BETTER_REVERB_REVERB_INDEX_LIGHT) >> 8) + inSampleR);
s32 i = 0;
for (; i < FILTERS_MINUS_1; ++i) {
curDelaySampleL = &delayBufsL[i][allpassIdxL[i]];
curDelaySampleR = &delayBufsR[i][allpassIdxR[i]];
historySampleL = *curDelaySampleL;
historySampleR = *curDelaySampleR;
*curDelaySampleL = (((historySampleL * (-BETTER_REVERB_GAIN_INDEX_LIGHT)) >> 8) + tmpCarryoverL);
*curDelaySampleR = (((historySampleR * (-BETTER_REVERB_GAIN_INDEX_LIGHT)) >> 8) + tmpCarryoverR);
tmpCarryoverL = (((*curDelaySampleL * BETTER_REVERB_GAIN_INDEX_LIGHT) >> 8) + historySampleL);
tmpCarryoverR = (((*curDelaySampleR * BETTER_REVERB_GAIN_INDEX_LIGHT) >> 8) + historySampleR);
if (++allpassIdxL[i] == delaysL[i]) allpassIdxL[i] = 0;
if (++allpassIdxR[i] == delaysR[i]) allpassIdxR[i] = 0;
}
curDelaySampleL = &delayBufsL[FILTERS_MINUS_1][allpassIdxL[FILTERS_MINUS_1]];
curDelaySampleR = &delayBufsR[FILTERS_MINUS_1][allpassIdxR[FILTERS_MINUS_1]];
historySampleL = ((*curDelaySampleL * BETTER_REVERB_MULTIPLE_LIGHT) >> 8); // outTmpL variable not needed, as there is no sample addition happening here. Not really a history sample though.
historySampleR = ((*curDelaySampleR * BETTER_REVERB_MULTIPLE_LIGHT) >> 8); // outTmpR variable not needed, as there is no sample addition happening here. Not really a history sample though.
*curDelaySampleL = tmpCarryoverL;
*curDelaySampleR = tmpCarryoverR;
if (++allpassIdxL[FILTERS_MINUS_1] == lastDelayLightL) allpassIdxL[FILTERS_MINUS_1] = 0;
if (++allpassIdxR[FILTERS_MINUS_1] == lastDelayLightR) allpassIdxR[FILTERS_MINUS_1] = 0;
*outSampleL = CLAMP_S16(historySampleL);
*outSampleR = CLAMP_S16(historySampleR);
}
static void reverb_mono_sample_light(s16 *outSample, s32 inSample) {
static void reverb_samples_light(s16 *start, s16 *end, s16 *downsampleBuffer, s32 channel) {
s32 *curDelaySample;
s32 historySample;
s32 tmpCarryover = (((delayBufsL[FILTERS_MINUS_1][allpassIdxL[FILTERS_MINUS_1]] * BETTER_REVERB_REVERB_INDEX_LIGHT) >> 8) + inSample);
s32 i = 0;
s32 tmpCarryover;
s32 i;
for (; i < FILTERS_MINUS_1; ++i) {
curDelaySample = &delayBufsL[i][allpassIdxL[i]];
historySample = *curDelaySample;
s32 downsampleIncrement = gReverbDownsampleRate;
s32 **delayBufsLocal = delayBufs[channel];
s32 *delaysLocal = betterReverbDelays[channel];
s32 *allpassIdxLocal = allpassIdx[channel];
s32 lastDelayLightLocal = lastDelayLight[channel];
*curDelaySample = (((historySample * (-BETTER_REVERB_GAIN_INDEX_LIGHT)) >> 8) + tmpCarryover);
tmpCarryover = (((*curDelaySample * BETTER_REVERB_GAIN_INDEX_LIGHT) >> 8) + historySample);
for (; start < end; start++, downsampleBuffer += downsampleIncrement) {
tmpCarryover = (((delayBufsLocal[FILTERS_MINUS_1][allpassIdxLocal[FILTERS_MINUS_1]] * BETTER_REVERB_REVERB_INDEX_LIGHT) >> 8) + *downsampleBuffer);
i = 0;
if (++allpassIdxL[i] == delaysL[i]) allpassIdxL[i] = 0;
for (; i < FILTERS_MINUS_1; ++i) {
curDelaySample = &delayBufsLocal[i][allpassIdxLocal[i]];
historySample = *curDelaySample;
*curDelaySample = (((historySample * (-BETTER_REVERB_GAIN_INDEX_LIGHT)) >> 8) + tmpCarryover);
tmpCarryover = (((*curDelaySample * BETTER_REVERB_GAIN_INDEX_LIGHT) >> 8) + historySample);
if (++allpassIdxLocal[i] == delaysLocal[i]) allpassIdxLocal[i] = 0;
}
curDelaySample = &delayBufsLocal[FILTERS_MINUS_1][allpassIdxLocal[FILTERS_MINUS_1]];
historySample = ((*curDelaySample * BETTER_REVERB_MULTIPLE_LIGHT) >> 8); // outSampleTotal variable not needed, as there is no sample addition happening here. Not really a history sample though.
*curDelaySample = tmpCarryover;
if (++allpassIdxLocal[FILTERS_MINUS_1] == lastDelayLightLocal) allpassIdxLocal[FILTERS_MINUS_1] = 0;
*start = CLAMP_S16(historySample);
}
curDelaySample = &delayBufsL[FILTERS_MINUS_1][allpassIdxL[FILTERS_MINUS_1]];
historySample = ((*curDelaySample * BETTER_REVERB_MULTIPLE_LIGHT) >> 8); // outTmp variable not needed, as there is no sample addition happening here. Not really a history sample though.
*curDelaySample = tmpCarryover;
if (++allpassIdxL[FILTERS_MINUS_1] == lastDelayLightL) allpassIdxL[FILTERS_MINUS_1] = 0;
*outSample = CLAMP_S16(historySample);
}
#undef FILTERS_MINUS_1
void initialize_better_reverb_buffers(void) {
delayBufsL = (s32**) soundAlloc(&gBetterReverbPool, BETTER_REVERB_PTR_SIZE);
delayBufsR = &delayBufsL[NUM_ALLPASS];
delayBufs[SYNTH_CHANNEL_LEFT] = (s32**) soundAlloc(&gBetterReverbPool, BETTER_REVERB_PTR_SIZE);
delayBufs[SYNTH_CHANNEL_RIGHT] = &delayBufs[SYNTH_CHANNEL_LEFT][NUM_ALLPASS];
}
void set_better_reverb_buffers(u32 *inputDelaysL, u32 *inputDelaysR) {
@@ -259,21 +201,20 @@ void set_better_reverb_buffers(u32 *inputDelaysL, u32 *inputDelaysR) {
// NOTE: Using filterCount over NUM_ALLPASS will report less memory usage with fewer filters, but poses an additional
// risk to anybody testing on console with performance compromises, as emulator can be easily overlooked.
for (i = 0; i < filterCount; ++i) {
delaysL[i] = (s32) (inputDelaysL[i] / gReverbDownsampleRate);
delaysR[i] = (s32) (inputDelaysR[i] / gReverbDownsampleRate);
delayBufsL[i] = soundAlloc(&gBetterReverbPool, delaysL[i] * sizeof(s32));
bufOffset += delaysL[i];
delayBufsR[i] = soundAlloc(&gBetterReverbPool, delaysR[i] * sizeof(s32));
bufOffset += delaysR[i];
betterReverbDelays[SYNTH_CHANNEL_LEFT][i] = (s32) (inputDelaysL[i] / gReverbDownsampleRate);
betterReverbDelays[SYNTH_CHANNEL_RIGHT][i] = (s32) (inputDelaysR[i] / gReverbDownsampleRate);
delayBufs[SYNTH_CHANNEL_LEFT][i] = soundAlloc(&gBetterReverbPool, betterReverbDelays[SYNTH_CHANNEL_LEFT][i] * sizeof(s32));
bufOffset += betterReverbDelays[SYNTH_CHANNEL_LEFT][i];
delayBufs[SYNTH_CHANNEL_RIGHT][i] = soundAlloc(&gBetterReverbPool, betterReverbDelays[SYNTH_CHANNEL_RIGHT][i] * sizeof(s32));
bufOffset += betterReverbDelays[SYNTH_CHANNEL_RIGHT][i];
}
aggress(bufOffset * sizeof(s32) <= BETTER_REVERB_SIZE - ALIGN16(BETTER_REVERB_PTR_SIZE), "BETTER_REVERB_SIZE is too small for this preset!");
lastDelayLightL = delaysL[filterCount-1];
lastDelayLightR = delaysR[filterCount-1];
lastDelayLight[SYNTH_CHANNEL_LEFT] = betterReverbDelays[SYNTH_CHANNEL_LEFT][filterCount-1];
lastDelayLight[SYNTH_CHANNEL_RIGHT] = betterReverbDelays[SYNTH_CHANNEL_RIGHT][filterCount-1];
bzero(allpassIdxL, sizeof(allpassIdxL));
bzero(allpassIdxR, sizeof(allpassIdxR));
bzero(allpassIdx, sizeof(allpassIdx));
}
#endif
@@ -376,81 +317,57 @@ void prepare_reverb_ring_buffer(s32 chunkLen, u32 updateIndex) {
}
#ifdef BETTER_REVERB
else if (toggleBetterReverb) {
s32 firstLoopMax;
s32 secondLoopMax;
s32 loopCounts[2];
s16 *betterReverbDownampleBuffers[SYNTH_CHANNEL_STEREO_COUNT][ARRAY_COUNT(loopCounts)]; // StartA and StartB for both channels
s16 *betterReverbSampleBuffers[SYNTH_CHANNEL_STEREO_COUNT][ARRAY_COUNT(loopCounts)]; // Output reverb buffers
void (*reverbFunc)(s16*, s16*, s16*, s32) = betterReverbLightweight ? reverb_samples_light : reverb_samples; // Function pointers for both heavy and lightweight reverb functions
item = &gSynthesisReverb.items[gSynthesisReverb.curFrame][updateIndex];
dstPos = item->startPos;
firstLoopMax = ((item->lengthA / 2) + item->startPos);
secondLoopMax = (item->lengthB / 2);
loopCounts[0] = item->lengthA / 2;
loopCounts[1] = item->lengthB / 2;
// This block could be simplified probably with function pointers or rewriting reverb_mono_sample to support L and R individually. Another idea would be to process more than one sample at a time
// for each function call. In practice with HackerSM64, these ideas either aren't super trivial to implement efficiently or don't benefit performance at all, so I'm leaving this as it is for now.
if (betterReverbLightweight) {
if (gSoundMode == SOUND_MODE_MONO || monoReverb) {
if (gReverbDownsampleRate != 1) {
osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH);
for (srcPos = 0; dstPos < firstLoopMax; srcPos += gReverbDownsampleRate, dstPos++)
reverb_mono_sample_light(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) item->toDownsampleLeft[srcPos] + (s32) item->toDownsampleRight[srcPos]) / 2);
bcopy(&gSynthesisReverb.ringBuffer.left[item->startPos], &gSynthesisReverb.ringBuffer.right[item->startPos], (dstPos - item->startPos) * sizeof(s16));
if (gReverbDownsampleRate != 1) {
// NOTE: / HACKERSM64 TODO: Commenting this check seems to improve runtime by about 100 microseconds (per 30fps frame),
// but idk enough about why it was added here in vanilla to comfortably remove it. Is it supposed to act as an
// optimization (that isn't actually an optimization) or is it a safety measure since it's loaded from the RSP?
osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH);
}
betterReverbSampleBuffers[SYNTH_CHANNEL_LEFT][0] = &gSynthesisReverb.ringBuffer.left[item->startPos];
betterReverbSampleBuffers[SYNTH_CHANNEL_LEFT][1] = &gSynthesisReverb.ringBuffer.left[0];
betterReverbSampleBuffers[SYNTH_CHANNEL_RIGHT][0] = &gSynthesisReverb.ringBuffer.right[item->startPos];
betterReverbSampleBuffers[SYNTH_CHANNEL_RIGHT][1] = &gSynthesisReverb.ringBuffer.right[0];
if (gReverbDownsampleRate > 1) {
betterReverbDownampleBuffers[SYNTH_CHANNEL_LEFT][0] = &item->toDownsampleLeft[0];
betterReverbDownampleBuffers[SYNTH_CHANNEL_LEFT][1] = &item->toDownsampleLeft[loopCounts[0] * gReverbDownsampleRate];
betterReverbDownampleBuffers[SYNTH_CHANNEL_RIGHT][0] = &item->toDownsampleRight[0];
betterReverbDownampleBuffers[SYNTH_CHANNEL_RIGHT][1] = &item->toDownsampleRight[loopCounts[0] * gReverbDownsampleRate];
} else {
betterReverbDownampleBuffers[SYNTH_CHANNEL_LEFT][0] = betterReverbSampleBuffers[SYNTH_CHANNEL_LEFT][0];
betterReverbDownampleBuffers[SYNTH_CHANNEL_LEFT][1] = betterReverbSampleBuffers[SYNTH_CHANNEL_LEFT][1];
betterReverbDownampleBuffers[SYNTH_CHANNEL_RIGHT][0] = betterReverbSampleBuffers[SYNTH_CHANNEL_RIGHT][0];
betterReverbDownampleBuffers[SYNTH_CHANNEL_RIGHT][1] = betterReverbSampleBuffers[SYNTH_CHANNEL_RIGHT][1];
}
for (dstPos = 0; dstPos < secondLoopMax; srcPos += gReverbDownsampleRate, dstPos++)
reverb_mono_sample_light(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) item->toDownsampleLeft[srcPos] + (s32) item->toDownsampleRight[srcPos]) / 2);
bcopy(gSynthesisReverb.ringBuffer.left, gSynthesisReverb.ringBuffer.right, dstPos * sizeof(s16));
} else {
for (; dstPos < firstLoopMax; dstPos++)
reverb_mono_sample_light(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) gSynthesisReverb.ringBuffer.left[dstPos] + (s32) gSynthesisReverb.ringBuffer.right[dstPos]) / 2);
bcopy(&gSynthesisReverb.ringBuffer.left[item->startPos], &gSynthesisReverb.ringBuffer.right[item->startPos], (dstPos - item->startPos) * sizeof(s16));
for (dstPos = 0; dstPos < secondLoopMax; dstPos++)
reverb_mono_sample_light(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) gSynthesisReverb.ringBuffer.left[dstPos] + (s32) gSynthesisReverb.ringBuffer.right[dstPos]) / 2);
bcopy(gSynthesisReverb.ringBuffer.left, gSynthesisReverb.ringBuffer.right, dstPos * sizeof(s16));
}
} else {
if (gReverbDownsampleRate != 1) {
osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH);
for (srcPos = 0; dstPos < firstLoopMax; srcPos += gReverbDownsampleRate, dstPos++)
reverb_samples_light(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], item->toDownsampleLeft[srcPos], item->toDownsampleRight[srcPos]);
for (dstPos = 0; dstPos < secondLoopMax; srcPos += gReverbDownsampleRate, dstPos++)
reverb_samples_light(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], item->toDownsampleLeft[srcPos], item->toDownsampleRight[srcPos]);
} else {
for (; dstPos < firstLoopMax; dstPos++)
reverb_samples_light(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], gSynthesisReverb.ringBuffer.left[dstPos], gSynthesisReverb.ringBuffer.right[dstPos]);
for (dstPos = 0; dstPos < secondLoopMax; dstPos++)
reverb_samples_light(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], gSynthesisReverb.ringBuffer.left[dstPos], gSynthesisReverb.ringBuffer.right[dstPos]);
if (gSoundMode == SOUND_MODE_MONO || monoReverb) {
for (srcPos = 0; srcPos < ARRAY_COUNT(loopCounts); srcPos++) { // LengthA and LengthB processing
s16 *downsampleBufferL = betterReverbDownampleBuffers[SYNTH_CHANNEL_LEFT][srcPos];
s16 *downsampleBufferR = betterReverbDownampleBuffers[SYNTH_CHANNEL_RIGHT][srcPos];
for (dstPos = 0; dstPos < loopCounts[srcPos]; dstPos += gReverbDownsampleRate) { // Individual sample processing
downsampleBufferL[dstPos] = ((s32) downsampleBufferL[dstPos] + (s32) downsampleBufferR[dstPos]) >> 1; // Merge stereo samples into left channel
}
}
for (srcPos = 0; srcPos < ARRAY_COUNT(loopCounts); srcPos++) { // LengthA and LengthB processing
// Call core reverb processing function, either reverb_samples() or reverb_samples_light()
(*reverbFunc)(betterReverbSampleBuffers[SYNTH_CHANNEL_LEFT][srcPos], betterReverbSampleBuffers[SYNTH_CHANNEL_LEFT][srcPos] + loopCounts[srcPos], betterReverbDownampleBuffers[SYNTH_CHANNEL_LEFT][srcPos], SYNTH_CHANNEL_LEFT);
bcopy(betterReverbSampleBuffers[SYNTH_CHANNEL_LEFT][srcPos], betterReverbSampleBuffers[SYNTH_CHANNEL_RIGHT][srcPos], loopCounts[srcPos] * sizeof(s16));
}
} else {
if (gSoundMode == SOUND_MODE_MONO || monoReverb) {
if (gReverbDownsampleRate != 1) {
osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH);
for (srcPos = 0; dstPos < firstLoopMax; srcPos += gReverbDownsampleRate, dstPos++)
reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) item->toDownsampleLeft[srcPos] + (s32) item->toDownsampleRight[srcPos]) / 2);
bcopy(&gSynthesisReverb.ringBuffer.left[item->startPos], &gSynthesisReverb.ringBuffer.right[item->startPos], (dstPos - item->startPos) * sizeof(s16));
for (dstPos = 0; dstPos < secondLoopMax; srcPos += gReverbDownsampleRate, dstPos++)
reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) item->toDownsampleLeft[srcPos] + (s32) item->toDownsampleRight[srcPos]) / 2);
bcopy(gSynthesisReverb.ringBuffer.left, gSynthesisReverb.ringBuffer.right, dstPos * sizeof(s16));
} else {
for (; dstPos < firstLoopMax; dstPos++)
reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) gSynthesisReverb.ringBuffer.left[dstPos] + (s32) gSynthesisReverb.ringBuffer.right[dstPos]) / 2);
bcopy(&gSynthesisReverb.ringBuffer.left[item->startPos], &gSynthesisReverb.ringBuffer.right[item->startPos], (dstPos - item->startPos) * sizeof(s16));
for (dstPos = 0; dstPos < secondLoopMax; dstPos++)
reverb_mono_sample(&gSynthesisReverb.ringBuffer.left[dstPos], ((s32) gSynthesisReverb.ringBuffer.left[dstPos] + (s32) gSynthesisReverb.ringBuffer.right[dstPos]) / 2);
bcopy(gSynthesisReverb.ringBuffer.left, gSynthesisReverb.ringBuffer.right, dstPos * sizeof(s16));
}
} else {
if (gReverbDownsampleRate != 1) {
osInvalDCache(item->toDownsampleLeft, DEFAULT_LEN_2CH);
for (srcPos = 0; dstPos < firstLoopMax; srcPos += gReverbDownsampleRate, dstPos++)
reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], item->toDownsampleLeft[srcPos], item->toDownsampleRight[srcPos]);
for (dstPos = 0; dstPos < secondLoopMax; srcPos += gReverbDownsampleRate, dstPos++)
reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], item->toDownsampleLeft[srcPos], item->toDownsampleRight[srcPos]);
} else {
for (; dstPos < firstLoopMax; dstPos++)
reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], gSynthesisReverb.ringBuffer.left[dstPos], gSynthesisReverb.ringBuffer.right[dstPos]);
for (dstPos = 0; dstPos < secondLoopMax; dstPos++)
reverb_samples(&gSynthesisReverb.ringBuffer.left[dstPos], &gSynthesisReverb.ringBuffer.right[dstPos], gSynthesisReverb.ringBuffer.left[dstPos], gSynthesisReverb.ringBuffer.right[dstPos]);
for (dstPos = 0; dstPos < SYNTH_CHANNEL_STEREO_COUNT; dstPos++) { // left and right channels
for (srcPos = 0; srcPos < ARRAY_COUNT(loopCounts); srcPos++) { // LengthA and LengthB processing
// Call core reverb processing function, either reverb_samples() or reverb_samples_light()
(*reverbFunc)(betterReverbSampleBuffers[dstPos][srcPos], betterReverbSampleBuffers[dstPos][srcPos] + loopCounts[srcPos], betterReverbDownampleBuffers[dstPos][srcPos], dstPos);
}
}
}
@@ -601,18 +518,18 @@ u64 *synthesis_execute(u64 *cmdBuf, s32 *writtenCmds, s16 *aiBuf, s32 bufLen) {
reverbLastFilterIndex = reverbFilterCount - 1;
// Update reverbMultsL every audio frame just in case gReverbMults is ever to change.
if (gReverbMultsL != NULL && gReverbMultsR != NULL) {
// Update reverbMults every audio frame just in case gReverbMults is ever to change.
if (gReverbMults[SYNTH_CHANNEL_LEFT] != NULL && gReverbMults[SYNTH_CHANNEL_RIGHT] != NULL) {
for (i = 0; i < filterCountDiv3; ++i) {
reverbMultsL[i] = gReverbMultsL[i];
reverbMultsR[i] = gReverbMultsR[i];
reverbMults[SYNTH_CHANNEL_LEFT][i] = gReverbMults[SYNTH_CHANNEL_LEFT][i];
reverbMults[SYNTH_CHANNEL_RIGHT][i] = gReverbMults[SYNTH_CHANNEL_RIGHT][i];
}
}
// If there's only one reverb multiplier set, adjust these to match so one channel doesn't end up potentially overpowering the other.
if (filterCountDiv3 == 1) {
reverbMultsL[0] = (reverbMultsR[0] + reverbMultsL[0]) / 2;
reverbMultsR[0] = reverbMultsL[0];
reverbMults[SYNTH_CHANNEL_LEFT][0] = (reverbMults[SYNTH_CHANNEL_RIGHT][0] + reverbMults[SYNTH_CHANNEL_LEFT][0]) / 2;
reverbMults[SYNTH_CHANNEL_RIGHT][0] = reverbMults[SYNTH_CHANNEL_LEFT][0];
}
#endif

View File

@@ -17,6 +17,12 @@
#define MAX_UPDATES_PER_FRAME 4
#endif
enum ChannelIndexes {
SYNTH_CHANNEL_LEFT,
SYNTH_CHANNEL_RIGHT,
SYNTH_CHANNEL_STEREO_COUNT,
};
#ifdef BETTER_REVERB
#define REVERB_WINDOW_SIZE_MAX 0x2000
@@ -56,8 +62,7 @@ extern s32 reverbFilterCount;
extern s32 betterReverbWindowsSize;
extern s32 betterReverbRevIndex;
extern s32 betterReverbGainIndex;
extern s32 *gReverbMultsL;
extern s32 *gReverbMultsR;
extern u8 *gReverbMults[SYNTH_CHANNEL_STEREO_COUNT];
/* ------------ BETTER REVERB EXTERNED FUNCTIONS ------------ */