mirror of
https://codeberg.org/armin/horizont.git
synced 2026-09-01 04:10:46 +02:00
fix pitch feedback loop; tune analyzer UI and dry/wet reflection
This commit is contained in:
parent
b1b83104a7
commit
2359bfd579
5 changed files with 57 additions and 33 deletions
|
|
@ -182,13 +182,11 @@ void HorizontEngine::processSample (float inL, float inR, float& outL, float& ou
|
|||
|
||||
const float loopInvRt = invRt * (1.0f - 0.7f * fbGain);
|
||||
|
||||
const float gMax = std::exp (-6.907755f * baseLen[0] * sizeCur * loopInvRt);
|
||||
const float shiftOutL = shiftL.process (lastWetL, ratio);
|
||||
const float shiftOutR = shiftR.process (lastWetR, ratio);
|
||||
|
||||
const float fbL = fl + fbGain * 0.5f * (1.0f - gMax) * shiftL.process (lastWetL, ratio);
|
||||
const float fbR = fr + fbGain * 0.5f * (1.0f - gMax) * shiftR.process (lastWetR, ratio);
|
||||
|
||||
const float dL = diffL2.process (diffL1.process (fbL, diffG), diffG);
|
||||
const float dR = diffR2.process (diffR1.process (fbR, diffG), diffG);
|
||||
const float dL = diffL2.process (diffL1.process (fl, diffG), diffG);
|
||||
const float dR = diffR2.process (diffR1.process (fr, diffG), diffG);
|
||||
|
||||
float wetL = 0.0f;
|
||||
float wetR = 0.0f;
|
||||
|
|
@ -204,6 +202,14 @@ void HorizontEngine::processSample (float inL, float inR, float& outL, float& ou
|
|||
wetR += 0.25f * combR[(size_t) i].process (dR, lenR, dampC, gainR);
|
||||
}
|
||||
|
||||
// recirculate the shifted wet outside the comb bank: loop gain is exactly
|
||||
// fbGain (< 1) so it cannot self-oscillate; gated off at 0 semitones
|
||||
if (std::fabs (ratio - 1.0f) > 1e-4f)
|
||||
{
|
||||
wetL += fbGain * shiftOutL;
|
||||
wetR += fbGain * shiftOutR;
|
||||
}
|
||||
|
||||
wetL = dcL.process (wetL);
|
||||
wetR = dcR.process (wetR);
|
||||
|
||||
|
|
|
|||
|
|
@ -68,7 +68,10 @@ void Knob::resized()
|
|||
const int valueH = juce::roundToInt (16.0f * s);
|
||||
const int maxKnobSize = juce::roundToInt (84.0f * s);
|
||||
|
||||
bounds.removeFromTop (juce::roundToInt (15.0f * s));
|
||||
|
||||
auto nameArea = bounds.removeFromTop (nameH);
|
||||
bounds.removeFromBottom (juce::roundToInt (10.0f * s));
|
||||
auto valueArea = bounds.removeFromBottom (valueH);
|
||||
nameLabel.setBounds (nameArea);
|
||||
valueLabel.setBounds (valueArea);
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ namespace
|
|||
#define HORIZONT_BUILD_TIMESTAMP "unknown"
|
||||
#endif
|
||||
|
||||
juce::String text ("HORIZONT v" + juce::String (HORIZONT_VERSION));
|
||||
juce::String text ("HORIZONT");
|
||||
text << " · git " << HORIZONT_GIT_REVISION;
|
||||
text << " · build " << HORIZONT_BUILD_TIMESTAMP;
|
||||
return text;
|
||||
|
|
@ -248,7 +248,7 @@ void HorizontAudioProcessorEditor::resized()
|
|||
const int subtitleMarginV = juce::roundToInt (2.0f * s);
|
||||
const int rowsMarginH = juce::roundToInt (16.0f * s);
|
||||
const int rowsMarginV = juce::roundToInt (4.0f * s);
|
||||
const int gap = juce::roundToInt (10.0f * s);
|
||||
const int gap = juce::roundToInt (20.0f * s);
|
||||
|
||||
footerArea = area.removeFromBottom (footerH);
|
||||
footerLabel.setBounds (footerArea.reduced (rowsMarginH, 0));
|
||||
|
|
@ -263,7 +263,7 @@ void HorizontAudioProcessorEditor::resized()
|
|||
scaleBox.setBounds (scaleArea);
|
||||
presetBox.setBounds (comboArea);
|
||||
|
||||
auto titleArea = header.removeFromTop (titleH).reduced (titleMarginH, titleMarginV);
|
||||
auto titleArea = header.removeFromTop (titleH).reduced (titleMarginH, titleMarginV).translated (0, juce::roundToInt (10.0f * s));
|
||||
titleLabel.setBounds (titleArea);
|
||||
subtitleLabel.setBounds (header.reduced (subtitleMarginH, subtitleMarginV));
|
||||
|
||||
|
|
@ -271,13 +271,18 @@ void HorizontAudioProcessorEditor::resized()
|
|||
subtitleLabel.setFont (juce::Font (juce::FontOptions (juce::roundToInt (11.0f * s))));
|
||||
|
||||
auto modules = area.reduced (rowsMarginH, rowsMarginV);
|
||||
modules.removeFromBottom (juce::roundToInt (15.0f * s));
|
||||
const int panelH = (modules.getHeight() - 2 * gap) / 3;
|
||||
|
||||
auto displayPanel = modules.removeFromTop (panelH);
|
||||
display.setBounds (displayPanel);
|
||||
|
||||
knobRow1Area = modules.removeFromTop (panelH + gap);
|
||||
knobRow2Area = modules.removeFromTop (panelH + gap);
|
||||
modules.removeFromTop (gap);
|
||||
|
||||
knobRow1Area = modules.removeFromTop (panelH);
|
||||
modules.removeFromTop (gap);
|
||||
|
||||
knobRow2Area = modules.removeFromTop (panelH);
|
||||
|
||||
layoutKnobRow (knobRow1Area, 0, 5);
|
||||
layoutKnobRow (knobRow2Area, 5, 4);
|
||||
|
|
@ -317,8 +322,8 @@ void HorizontAudioProcessorEditor::paint (juce::Graphics& g)
|
|||
g.setGradientFill (shadowGrad);
|
||||
g.fillRoundedRectangle (shRect, 13.0f);
|
||||
|
||||
g.setGradientFill (juce::ColourGradient (juce::Colour (0xFF24323A), { 0.0f, r.getY() },
|
||||
juce::Colour (0xFF0B1216), { 0.0f, r.getBottom() },
|
||||
g.setGradientFill (juce::ColourGradient (juce::Colour (0x0024323A), { 0.0f, r.getY() },
|
||||
juce::Colour (0x000B1216), { 0.0f, r.getBottom() },
|
||||
false));
|
||||
g.fillRoundedRectangle (r, 12.0f);
|
||||
|
||||
|
|
@ -334,7 +339,7 @@ void HorizontAudioProcessorEditor::paint (juce::Graphics& g)
|
|||
|
||||
g.saveState();
|
||||
g.reduceClipRegion (r.toNearestInt().reduced (1));
|
||||
auto refl = juce::ColourGradient (juce::Colour (0x28FFFFFF), { 0.0f, r.getY() },
|
||||
auto refl = juce::ColourGradient (juce::Colour (0x00FFFFFF), { 0.0f, r.getY() },
|
||||
juce::Colour (0x00FFFFFF), { 0.0f, r.getY() + r.getHeight() * 0.45f },
|
||||
false);
|
||||
g.setGradientFill (refl);
|
||||
|
|
|
|||
|
|
@ -120,6 +120,8 @@ void HorizontAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juc
|
|||
const bool haveLeft = buffer.getNumChannels() > 0 && inputChannels > 0;
|
||||
const bool haveRight = buffer.getNumChannels() > 1 && inputChannels > 1;
|
||||
const bool canCaptureWet = wetScratch.size() >= (size_t) numSamples;
|
||||
const float wetVal = dryWetParam->load();
|
||||
const float dryGain = 1.0f - wetVal;
|
||||
|
||||
for (int s = 0; s < numSamples; ++s)
|
||||
{
|
||||
|
|
@ -136,8 +138,8 @@ void HorizontAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juc
|
|||
|
||||
if (canCaptureWet)
|
||||
{
|
||||
wetScratch[(size_t) s] = engine.getLastWetL();
|
||||
dryScratch[(size_t) s] = inL;
|
||||
wetScratch[(size_t) s] = engine.getLastWetL() * wetVal;
|
||||
dryScratch[(size_t) s] = inL * dryGain;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ namespace
|
|||
const juce::Colour wetFillCol (0xE63CE0C8);
|
||||
const juce::Colour wetPeakCol (0x883CE0C8);
|
||||
const juce::Colour dryLineCol (0xCCF0A03C);
|
||||
const juce::Colour dryFillCol (0x22F0A03C);
|
||||
const juce::Colour dryFillCol (0x66F0A03C);
|
||||
const juce::Colour dryPeakCol (0x55E89B3A);
|
||||
|
||||
juce::String formatFreq (float hz)
|
||||
|
|
@ -89,6 +89,19 @@ void SpectrumDisplay::timerCallback()
|
|||
computeLevels (wetBuf.getReadPointer (0), got, level);
|
||||
computeLevels (dryBuf.getReadPointer (0), fftSize, dryLevel);
|
||||
|
||||
float wetPeakDb = -200.0f;
|
||||
float dryPeakDb = -200.0f;
|
||||
for (int i = 0; i < numBins; ++i)
|
||||
{
|
||||
wetPeakDb = jmax (wetPeakDb, level[(size_t) i]);
|
||||
dryPeakDb = jmax (dryPeakDb, dryLevel[(size_t) i]);
|
||||
}
|
||||
|
||||
const float dryBoostDb = juce::jlimit (0.0f, 30.0f, wetPeakDb - dryPeakDb - 6.0f);
|
||||
if (dryBoostDb > 0.0f)
|
||||
for (int i = 0; i < numBins; ++i)
|
||||
dryLevel[(size_t) i] += dryBoostDb;
|
||||
|
||||
for (int i = 0; i < numBins; ++i)
|
||||
{
|
||||
const int lo = jmax (0, i - 1);
|
||||
|
|
@ -144,7 +157,7 @@ void SpectrumDisplay::paint (juce::Graphics& g)
|
|||
g.fillRoundedRectangle (bounds, 12.0f * s);
|
||||
|
||||
g.setColour (juce::Colour (0x3A3CE0C8));
|
||||
g.drawRoundedRectangle (bounds.expanded (1.0f * s), 13.0f * s, 1.0f * s);
|
||||
g.drawRoundedRectangle (bounds.expanded (1.0f * s), 16.0f * s, 1.0f * s);
|
||||
|
||||
g.setColour (juce::Colour (0x38FFFFFF));
|
||||
g.drawHorizontalLine (juce::roundToInt (bounds.getY() + 1.0f * s), juce::roundToInt (bounds.getX() + 3.0f * s), juce::roundToInt (bounds.getRight() - 3.0f * s));
|
||||
|
|
@ -155,7 +168,7 @@ void SpectrumDisplay::paint (juce::Graphics& g)
|
|||
|
||||
g.saveState();
|
||||
g.reduceClipRegion (bounds.toNearestInt().reduced (juce::roundToInt (1.0f * s)));
|
||||
auto refl = juce::ColourGradient (juce::Colour (0x28FFFFFF), { 0.0f, bounds.getY() },
|
||||
auto refl = juce::ColourGradient (juce::Colour (0x12FFFFFF), { 0.0f, bounds.getY() },
|
||||
juce::Colour (0x00FFFFFF), { 0.0f, bounds.getY() + bounds.getHeight() * 0.45f },
|
||||
false);
|
||||
g.setGradientFill (refl);
|
||||
|
|
@ -227,7 +240,7 @@ void SpectrumDisplay::paint (juce::Graphics& g)
|
|||
g.fillPath (wetFill);
|
||||
|
||||
auto dryGrad = juce::ColourGradient (dryFillCol, { 0.0f, graph.getY() },
|
||||
juce::Colour (0x00FFFFFF), { 0.0f, graph.getBottom() },
|
||||
juce::Colour (0x12F0A03C), { 0.0f, graph.getBottom() },
|
||||
false);
|
||||
g.setGradientFill (dryGrad);
|
||||
g.fillPath (dryFill);
|
||||
|
|
@ -235,6 +248,11 @@ void SpectrumDisplay::paint (juce::Graphics& g)
|
|||
g.setColour (wetPeakCol);
|
||||
g.strokePath (makeCurve (graph, peak), juce::PathStrokeType (1.0f * s));
|
||||
|
||||
g.setColour (wetLineCol);
|
||||
g.strokePath (curve, juce::PathStrokeType (1.4f * s,
|
||||
juce::PathStrokeType::JointStyle::curved,
|
||||
juce::PathStrokeType::EndCapStyle::rounded));
|
||||
|
||||
g.setColour (dryLineCol);
|
||||
g.strokePath (dry, juce::PathStrokeType (1.2f * s,
|
||||
juce::PathStrokeType::JointStyle::curved,
|
||||
|
|
@ -242,30 +260,20 @@ void SpectrumDisplay::paint (juce::Graphics& g)
|
|||
|
||||
g.setColour (dryPeakCol);
|
||||
g.strokePath (makeCurve (graph, dryPeak), juce::PathStrokeType (0.8f * s));
|
||||
|
||||
g.setColour (wetLineCol);
|
||||
g.strokePath (curve, juce::PathStrokeType (1.4f * s,
|
||||
juce::PathStrokeType::JointStyle::curved,
|
||||
juce::PathStrokeType::EndCapStyle::rounded));
|
||||
}
|
||||
|
||||
g.setColour (juce::Colour (0x66FFFFFF));
|
||||
g.setFont (juce::Font (juce::FontOptions (10.0f * s, juce::Font::bold)));
|
||||
g.drawText ("REVERB SPECTRUM", graph.withTop (graph.getY() - 2.0f * s).withBottom (graph.getY() + 14.0f * s),
|
||||
juce::Justification::bottomLeft, false);
|
||||
|
||||
const float legendY = graph.getY() + 4.0f * s;
|
||||
const float legendY = graph.getY() + 24.0f * s;
|
||||
const float legendX = graph.getRight() - 2.0f * s;
|
||||
const auto legendRow = juce::Rectangle<float> (legendX - 84.0f * s, legendY - 9.0f * s, 84.0f * s, 12.0f * s);
|
||||
|
||||
g.setFont (juce::Font (juce::FontOptions (9.0f * s)));
|
||||
g.setColour (wetPeakCol);
|
||||
g.setColour (wetLineCol);
|
||||
g.drawHorizontalLine (juce::roundToInt (legendRow.getY() + 8.0f * s), juce::roundToInt (legendRow.getX()), juce::roundToInt (legendRow.getX() + 11.0f * s));
|
||||
g.setColour (juce::Colour (0x88FFFFFF));
|
||||
g.drawText ("WET", legendRow.withX (legendRow.getX() + 14.0f * s).withWidth (28.0f * s),
|
||||
juce::Justification::left, false);
|
||||
|
||||
g.setColour (dryPeakCol);
|
||||
g.setColour (dryLineCol);
|
||||
g.drawHorizontalLine (juce::roundToInt (legendRow.getY() + 8.0f * s), juce::roundToInt (legendRow.getX() + 42.0f * s), juce::roundToInt (legendRow.getX() + 53.0f * s));
|
||||
g.setColour (juce::Colour (0x88FFFFFF));
|
||||
g.drawText ("DRY", legendRow.withX (legendRow.getX() + 56.0f * s).withWidth (28.0f * s),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue