Per-pad FX chain, animated toggles, GR meter, simplified master panel

- FX moved from master bus to per-pad processing:
  each pad has its own Filter, Distortion, EQ, Compressor, Reverb
  via DrumPad::applyPadFx() with temp buffer rendering
- FxPanel now edits the selected pad's FX parameters
- Animated toggle switches with smooth lerp transition and glow
- Per-pad compressor GR meter connected to FxPanel display
- Master panel simplified: Volume/Tune/Pan + Limiter toggle + VU meter
- Master bus chain: Vol/Pan → Output Limiter (0dB brickwall) → VU
- Pointer glow reduced to half intensity (4 layers, narrower spread)
- Smooth 8-layer arc glow with exponential opacity falloff

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hariel1985
2026-03-23 06:42:47 +01:00
szülő 20b9fe2674
commit a0e83fa0a4
11 fájl változott, egészen pontosan 500 új sor hozzáadva és 210 régi sor törölve

Fájl megtekintése

@@ -45,22 +45,7 @@ void InstaDrumsProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
for (int i = 0; i < numActivePads; ++i)
pads[i].prepareToPlay (sampleRate, samplesPerBlock);
// Master FX chain
juce::dsp::ProcessSpec spec { sampleRate, (juce::uint32) samplesPerBlock, 2 };
reverb.prepare (spec);
compressor.prepare (spec);
juce::dsp::ProcessSpec monoSpec { sampleRate, (juce::uint32) samplesPerBlock, 1 };
eqLoFilterL.prepare (monoSpec); eqLoFilterR.prepare (monoSpec);
eqMidFilterL.prepare (monoSpec); eqMidFilterR.prepare (monoSpec);
eqHiFilterL.prepare (monoSpec); eqHiFilterR.prepare (monoSpec);
reverb.reset();
compressor.reset();
eqLoFilterL.reset(); eqLoFilterR.reset();
eqMidFilterL.reset(); eqMidFilterR.reset();
eqHiFilterL.reset(); eqHiFilterR.reset();
// Per-pad FX is prepared in DrumPad::prepareToPlay()
}
void InstaDrumsProcessor::releaseResources()
@@ -115,85 +100,8 @@ void InstaDrumsProcessor::applyMasterFx (juce::AudioBuffer<float>& buffer)
{
const int numSamples = buffer.getNumSamples();
// --- Distortion (pre-EQ) ---
float drive = distDrive.load();
float dMix = distMix.load();
if (drive > 0.001f && dMix > 0.001f)
{
float driveGain = 1.0f + drive * 20.0f;
for (int ch = 0; ch < buffer.getNumChannels(); ++ch)
{
float* data = buffer.getWritePointer (ch);
for (int i = 0; i < numSamples; ++i)
{
float dry = data[i];
float wet = std::tanh (dry * driveGain) / std::tanh (driveGain);
data[i] = dry * (1.0f - dMix) + wet * dMix;
}
}
}
// --- 3-band EQ ---
float lo = eqLo.load();
float mid = eqMid.load();
float hi = eqHi.load();
if (std::abs (lo) > 0.1f || std::abs (mid) > 0.1f || std::abs (hi) > 0.1f)
{
auto loCoeffs = juce::dsp::IIR::Coefficients<float>::makeLowShelf (currentSampleRate, 200.0, 0.707f, juce::Decibels::decibelsToGain (lo));
auto midCoeffs = juce::dsp::IIR::Coefficients<float>::makePeakFilter (currentSampleRate, 1000.0, 1.0f, juce::Decibels::decibelsToGain (mid));
auto hiCoeffs = juce::dsp::IIR::Coefficients<float>::makeHighShelf (currentSampleRate, 5000.0, 0.707f, juce::Decibels::decibelsToGain (hi));
*eqLoFilterL.coefficients = *loCoeffs; *eqLoFilterR.coefficients = *loCoeffs;
*eqMidFilterL.coefficients = *midCoeffs; *eqMidFilterR.coefficients = *midCoeffs;
*eqHiFilterL.coefficients = *hiCoeffs; *eqHiFilterR.coefficients = *hiCoeffs;
if (buffer.getNumChannels() >= 2)
{
float* L = buffer.getWritePointer (0);
float* R = buffer.getWritePointer (1);
for (int i = 0; i < numSamples; ++i)
{
L[i] = eqLoFilterL.processSample (L[i]);
L[i] = eqMidFilterL.processSample (L[i]);
L[i] = eqHiFilterL.processSample (L[i]);
R[i] = eqLoFilterR.processSample (R[i]);
R[i] = eqMidFilterR.processSample (R[i]);
R[i] = eqHiFilterR.processSample (R[i]);
}
}
}
// --- Compressor ---
float thresh = compThreshold.load();
float ratio = compRatio.load();
compressor.setThreshold (thresh);
compressor.setRatio (ratio);
compressor.setAttack (10.0f);
compressor.setRelease (100.0f);
{
juce::dsp::AudioBlock<float> block (buffer);
juce::dsp::ProcessContextReplacing<float> ctx (block);
compressor.process (ctx);
}
// --- Reverb ---
float rSize = reverbSize.load();
float rDecay = reverbDecay.load();
if (rSize > 0.01f || rDecay > 0.01f)
{
juce::dsp::Reverb::Parameters rParams;
rParams.roomSize = rSize;
rParams.damping = 1.0f - rDecay;
rParams.wetLevel = rSize * 0.5f;
rParams.dryLevel = 1.0f;
rParams.width = 1.0f;
reverb.setParameters (rParams);
juce::dsp::AudioBlock<float> block (buffer);
juce::dsp::ProcessContextReplacing<float> ctx (block);
reverb.process (ctx);
}
// Per-pad FX is now in DrumPad::applyPadFx()
// Master chain: just Volume + Pan + Output Limiter
// --- Master Volume + Pan ---
float mVol = masterVolume.load();
@@ -212,6 +120,17 @@ void InstaDrumsProcessor::applyMasterFx (juce::AudioBuffer<float>& buffer)
buffer.applyGain (mVol);
}
// --- Output Limiter (brickwall at 0dB) ---
if (outputLimiterEnabled.load())
{
for (int ch = 0; ch < buffer.getNumChannels(); ++ch)
{
float* data = buffer.getWritePointer (ch);
for (int i = 0; i < numSamples; ++i)
data[i] = juce::jlimit (-1.0f, 1.0f, data[i]);
}
}
// --- VU Meter ---
float rmsL = 0.0f, rmsR = 0.0f;
if (buffer.getNumChannels() >= 1)