Add delay and reverb FX section with full DSP

Rename the DIST section to FX and expand it with tempo-synced delay
(Time/Strength/Sync) and Schroeder reverb (Roomsize/Strength/Diffusion).
New FxProcessor runs post-voice on the stereo bus; widen the editor to
1280px to fit the new section. Add a TestHost check that the FX tail
rings out after note release.
This commit is contained in:
Armin 2026-08-06 16:35:34 +02:00
commit 342d413563
8 changed files with 505 additions and 23 deletions

View file

@ -299,6 +299,84 @@ static int testProcessorRendering()
std::cout << "Master=0 silences output OK\n";
// delay + reverb FX: after the note releases, the FX tail must ring out
// (delay repeats / reverb decay) instead of going silent, with no NaN/blow-up.
*processor.getAPVTS().getRawParameterValue ("master") = 1.0f;
*processor.getAPVTS().getRawParameterValue ("release") = 0.01f;
*processor.getAPVTS().getRawParameterValue ("delayStrength") = 0.0f;
*processor.getAPVTS().getRawParameterValue ("reverbStrength") = 0.0f;
const int releaseBlock = numBlocks / 2;
const auto renderFxTail = [&] () -> double
{
processor.reset();
processor.prepareToPlay (44100.0, 512);
juce::AudioBuffer<float> fxBlock (2, blockSize);
juce::MidiBuffer fxMidi;
double tailSum = 0.0;
int tailSamples = 0;
bool finite = true;
for (int b = 0; b < numBlocks; ++b)
{
fxBlock.clear();
fxMidi.clear();
if (b == 0)
fxMidi.addEvent (juce::MidiMessage::noteOn (1, 60, 0.9f), 0);
else if (b == releaseBlock)
fxMidi.addEvent (juce::MidiMessage::noteOff (1, 60), 0);
processor.processBlock (fxBlock, fxMidi);
if (b > releaseBlock)
for (int c = 0; c < 2; ++c)
for (int i = 0; i < blockSize; ++i)
{
const float s = fxBlock.getSample (c, i);
if (! std::isfinite (s))
finite = false;
tailSum += (double) s * s;
++tailSamples;
}
}
if (! finite)
return -1.0;
return std::sqrt (tailSum / (double) std::max (1, tailSamples));
};
const double dryTail = renderFxTail();
if (dryTail > 1e-3)
{
std::cout << "FAILED: dry tail after release should be silent, got " << dryTail << "\n";
return 1;
}
*processor.getAPVTS().getRawParameterValue ("delayStrength") = 0.8f;
*processor.getAPVTS().getRawParameterValue ("delayTime") = 0.4f;
*processor.getAPVTS().getRawParameterValue ("delaySync") = 1.0f;
*processor.getAPVTS().getRawParameterValue ("reverbStrength") = 0.8f;
*processor.getAPVTS().getRawParameterValue ("reverbRoom") = 0.9f;
*processor.getAPVTS().getRawParameterValue ("reverbDiffusion") = 1.0f;
const double wetTail = renderFxTail();
if (wetTail < 0.0)
return 1;
if (wetTail < 1e-3)
{
std::cout << "FAILED: delay/reverb tail is inaudible (wet=" << wetTail << ")\n";
return 1;
}
std::cout << "Delay + reverb FX OK (dryTail=" << dryTail << " wetTail=" << wetTail << ")\n";
return 0;
}