mirror of
https://codeberg.org/armin/horizont.git
synced 2026-09-01 12:20:47 +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 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 dL = diffL2.process (diffL1.process (fl, diffG), diffG);
|
||||||
const float fbR = fr + fbGain * 0.5f * (1.0f - gMax) * shiftR.process (lastWetR, ratio);
|
const float dR = diffR2.process (diffR1.process (fr, diffG), diffG);
|
||||||
|
|
||||||
const float dL = diffL2.process (diffL1.process (fbL, diffG), diffG);
|
|
||||||
const float dR = diffR2.process (diffR1.process (fbR, diffG), diffG);
|
|
||||||
|
|
||||||
float wetL = 0.0f;
|
float wetL = 0.0f;
|
||||||
float wetR = 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);
|
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);
|
wetL = dcL.process (wetL);
|
||||||
wetR = dcR.process (wetR);
|
wetR = dcR.process (wetR);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,10 @@ void Knob::resized()
|
||||||
const int valueH = juce::roundToInt (16.0f * s);
|
const int valueH = juce::roundToInt (16.0f * s);
|
||||||
const int maxKnobSize = juce::roundToInt (84.0f * s);
|
const int maxKnobSize = juce::roundToInt (84.0f * s);
|
||||||
|
|
||||||
|
bounds.removeFromTop (juce::roundToInt (15.0f * s));
|
||||||
|
|
||||||
auto nameArea = bounds.removeFromTop (nameH);
|
auto nameArea = bounds.removeFromTop (nameH);
|
||||||
|
bounds.removeFromBottom (juce::roundToInt (10.0f * s));
|
||||||
auto valueArea = bounds.removeFromBottom (valueH);
|
auto valueArea = bounds.removeFromBottom (valueH);
|
||||||
nameLabel.setBounds (nameArea);
|
nameLabel.setBounds (nameArea);
|
||||||
valueLabel.setBounds (valueArea);
|
valueLabel.setBounds (valueArea);
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ namespace
|
||||||
#define HORIZONT_BUILD_TIMESTAMP "unknown"
|
#define HORIZONT_BUILD_TIMESTAMP "unknown"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
juce::String text ("HORIZONT v" + juce::String (HORIZONT_VERSION));
|
juce::String text ("HORIZONT");
|
||||||
text << " · git " << HORIZONT_GIT_REVISION;
|
text << " · git " << HORIZONT_GIT_REVISION;
|
||||||
text << " · build " << HORIZONT_BUILD_TIMESTAMP;
|
text << " · build " << HORIZONT_BUILD_TIMESTAMP;
|
||||||
return text;
|
return text;
|
||||||
|
|
@ -248,7 +248,7 @@ void HorizontAudioProcessorEditor::resized()
|
||||||
const int subtitleMarginV = juce::roundToInt (2.0f * s);
|
const int subtitleMarginV = juce::roundToInt (2.0f * s);
|
||||||
const int rowsMarginH = juce::roundToInt (16.0f * s);
|
const int rowsMarginH = juce::roundToInt (16.0f * s);
|
||||||
const int rowsMarginV = juce::roundToInt (4.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);
|
footerArea = area.removeFromBottom (footerH);
|
||||||
footerLabel.setBounds (footerArea.reduced (rowsMarginH, 0));
|
footerLabel.setBounds (footerArea.reduced (rowsMarginH, 0));
|
||||||
|
|
@ -263,7 +263,7 @@ void HorizontAudioProcessorEditor::resized()
|
||||||
scaleBox.setBounds (scaleArea);
|
scaleBox.setBounds (scaleArea);
|
||||||
presetBox.setBounds (comboArea);
|
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);
|
titleLabel.setBounds (titleArea);
|
||||||
subtitleLabel.setBounds (header.reduced (subtitleMarginH, subtitleMarginV));
|
subtitleLabel.setBounds (header.reduced (subtitleMarginH, subtitleMarginV));
|
||||||
|
|
||||||
|
|
@ -271,13 +271,18 @@ void HorizontAudioProcessorEditor::resized()
|
||||||
subtitleLabel.setFont (juce::Font (juce::FontOptions (juce::roundToInt (11.0f * s))));
|
subtitleLabel.setFont (juce::Font (juce::FontOptions (juce::roundToInt (11.0f * s))));
|
||||||
|
|
||||||
auto modules = area.reduced (rowsMarginH, rowsMarginV);
|
auto modules = area.reduced (rowsMarginH, rowsMarginV);
|
||||||
|
modules.removeFromBottom (juce::roundToInt (15.0f * s));
|
||||||
const int panelH = (modules.getHeight() - 2 * gap) / 3;
|
const int panelH = (modules.getHeight() - 2 * gap) / 3;
|
||||||
|
|
||||||
auto displayPanel = modules.removeFromTop (panelH);
|
auto displayPanel = modules.removeFromTop (panelH);
|
||||||
display.setBounds (displayPanel);
|
display.setBounds (displayPanel);
|
||||||
|
|
||||||
knobRow1Area = modules.removeFromTop (panelH + gap);
|
modules.removeFromTop (gap);
|
||||||
knobRow2Area = modules.removeFromTop (panelH + gap);
|
|
||||||
|
knobRow1Area = modules.removeFromTop (panelH);
|
||||||
|
modules.removeFromTop (gap);
|
||||||
|
|
||||||
|
knobRow2Area = modules.removeFromTop (panelH);
|
||||||
|
|
||||||
layoutKnobRow (knobRow1Area, 0, 5);
|
layoutKnobRow (knobRow1Area, 0, 5);
|
||||||
layoutKnobRow (knobRow2Area, 5, 4);
|
layoutKnobRow (knobRow2Area, 5, 4);
|
||||||
|
|
@ -317,8 +322,8 @@ void HorizontAudioProcessorEditor::paint (juce::Graphics& g)
|
||||||
g.setGradientFill (shadowGrad);
|
g.setGradientFill (shadowGrad);
|
||||||
g.fillRoundedRectangle (shRect, 13.0f);
|
g.fillRoundedRectangle (shRect, 13.0f);
|
||||||
|
|
||||||
g.setGradientFill (juce::ColourGradient (juce::Colour (0xFF24323A), { 0.0f, r.getY() },
|
g.setGradientFill (juce::ColourGradient (juce::Colour (0x0024323A), { 0.0f, r.getY() },
|
||||||
juce::Colour (0xFF0B1216), { 0.0f, r.getBottom() },
|
juce::Colour (0x000B1216), { 0.0f, r.getBottom() },
|
||||||
false));
|
false));
|
||||||
g.fillRoundedRectangle (r, 12.0f);
|
g.fillRoundedRectangle (r, 12.0f);
|
||||||
|
|
||||||
|
|
@ -334,7 +339,7 @@ void HorizontAudioProcessorEditor::paint (juce::Graphics& g)
|
||||||
|
|
||||||
g.saveState();
|
g.saveState();
|
||||||
g.reduceClipRegion (r.toNearestInt().reduced (1));
|
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 },
|
juce::Colour (0x00FFFFFF), { 0.0f, r.getY() + r.getHeight() * 0.45f },
|
||||||
false);
|
false);
|
||||||
g.setGradientFill (refl);
|
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 haveLeft = buffer.getNumChannels() > 0 && inputChannels > 0;
|
||||||
const bool haveRight = buffer.getNumChannels() > 1 && inputChannels > 1;
|
const bool haveRight = buffer.getNumChannels() > 1 && inputChannels > 1;
|
||||||
const bool canCaptureWet = wetScratch.size() >= (size_t) numSamples;
|
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)
|
for (int s = 0; s < numSamples; ++s)
|
||||||
{
|
{
|
||||||
|
|
@ -136,8 +138,8 @@ void HorizontAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juc
|
||||||
|
|
||||||
if (canCaptureWet)
|
if (canCaptureWet)
|
||||||
{
|
{
|
||||||
wetScratch[(size_t) s] = engine.getLastWetL();
|
wetScratch[(size_t) s] = engine.getLastWetL() * wetVal;
|
||||||
dryScratch[(size_t) s] = inL;
|
dryScratch[(size_t) s] = inL * dryGain;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ namespace
|
||||||
const juce::Colour wetFillCol (0xE63CE0C8);
|
const juce::Colour wetFillCol (0xE63CE0C8);
|
||||||
const juce::Colour wetPeakCol (0x883CE0C8);
|
const juce::Colour wetPeakCol (0x883CE0C8);
|
||||||
const juce::Colour dryLineCol (0xCCF0A03C);
|
const juce::Colour dryLineCol (0xCCF0A03C);
|
||||||
const juce::Colour dryFillCol (0x22F0A03C);
|
const juce::Colour dryFillCol (0x66F0A03C);
|
||||||
const juce::Colour dryPeakCol (0x55E89B3A);
|
const juce::Colour dryPeakCol (0x55E89B3A);
|
||||||
|
|
||||||
juce::String formatFreq (float hz)
|
juce::String formatFreq (float hz)
|
||||||
|
|
@ -89,6 +89,19 @@ void SpectrumDisplay::timerCallback()
|
||||||
computeLevels (wetBuf.getReadPointer (0), got, level);
|
computeLevels (wetBuf.getReadPointer (0), got, level);
|
||||||
computeLevels (dryBuf.getReadPointer (0), fftSize, dryLevel);
|
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)
|
for (int i = 0; i < numBins; ++i)
|
||||||
{
|
{
|
||||||
const int lo = jmax (0, i - 1);
|
const int lo = jmax (0, i - 1);
|
||||||
|
|
@ -144,7 +157,7 @@ void SpectrumDisplay::paint (juce::Graphics& g)
|
||||||
g.fillRoundedRectangle (bounds, 12.0f * s);
|
g.fillRoundedRectangle (bounds, 12.0f * s);
|
||||||
|
|
||||||
g.setColour (juce::Colour (0x3A3CE0C8));
|
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.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));
|
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.saveState();
|
||||||
g.reduceClipRegion (bounds.toNearestInt().reduced (juce::roundToInt (1.0f * s)));
|
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 },
|
juce::Colour (0x00FFFFFF), { 0.0f, bounds.getY() + bounds.getHeight() * 0.45f },
|
||||||
false);
|
false);
|
||||||
g.setGradientFill (refl);
|
g.setGradientFill (refl);
|
||||||
|
|
@ -227,7 +240,7 @@ void SpectrumDisplay::paint (juce::Graphics& g)
|
||||||
g.fillPath (wetFill);
|
g.fillPath (wetFill);
|
||||||
|
|
||||||
auto dryGrad = juce::ColourGradient (dryFillCol, { 0.0f, graph.getY() },
|
auto dryGrad = juce::ColourGradient (dryFillCol, { 0.0f, graph.getY() },
|
||||||
juce::Colour (0x00FFFFFF), { 0.0f, graph.getBottom() },
|
juce::Colour (0x12F0A03C), { 0.0f, graph.getBottom() },
|
||||||
false);
|
false);
|
||||||
g.setGradientFill (dryGrad);
|
g.setGradientFill (dryGrad);
|
||||||
g.fillPath (dryFill);
|
g.fillPath (dryFill);
|
||||||
|
|
@ -235,6 +248,11 @@ void SpectrumDisplay::paint (juce::Graphics& g)
|
||||||
g.setColour (wetPeakCol);
|
g.setColour (wetPeakCol);
|
||||||
g.strokePath (makeCurve (graph, peak), juce::PathStrokeType (1.0f * s));
|
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.setColour (dryLineCol);
|
||||||
g.strokePath (dry, juce::PathStrokeType (1.2f * s,
|
g.strokePath (dry, juce::PathStrokeType (1.2f * s,
|
||||||
juce::PathStrokeType::JointStyle::curved,
|
juce::PathStrokeType::JointStyle::curved,
|
||||||
|
|
@ -242,30 +260,20 @@ void SpectrumDisplay::paint (juce::Graphics& g)
|
||||||
|
|
||||||
g.setColour (dryPeakCol);
|
g.setColour (dryPeakCol);
|
||||||
g.strokePath (makeCurve (graph, dryPeak), juce::PathStrokeType (0.8f * s));
|
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));
|
const float legendY = graph.getY() + 24.0f * s;
|
||||||
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 legendX = graph.getRight() - 2.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);
|
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.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.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.setColour (juce::Colour (0x88FFFFFF));
|
||||||
g.drawText ("WET", legendRow.withX (legendRow.getX() + 14.0f * s).withWidth (28.0f * s),
|
g.drawText ("WET", legendRow.withX (legendRow.getX() + 14.0f * s).withWidth (28.0f * s),
|
||||||
juce::Justification::left, false);
|
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.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.setColour (juce::Colour (0x88FFFFFF));
|
||||||
g.drawText ("DRY", legendRow.withX (legendRow.getX() + 56.0f * s).withWidth (28.0f * s),
|
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