From a98d089de12e82c73d91f5a0dee4747c28bcc24d Mon Sep 17 00:00:00 2001 From: Otto Andelin Date: Thu, 28 May 2026 15:05:24 +0300 Subject: [PATCH 1/4] update changelog --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5b371a..66f2fc3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,10 @@ All notable changes to this project will be documented in this file. * Ranges finetuned for usability and musicality, subject to change * Insert effect selector * Allows user to switch between chorus and lofi effect in the feedback loop - + +### Fixed + * Delay time offset bug where offset never truly returned to zero once modified + ## \[0.2.0] - 2026-05-18 ### Added From 6b8172a04628d95b5af65df942b71010cfbcdd2a Mon Sep 17 00:00:00 2001 From: Otto Andelin Date: Fri, 29 May 2026 08:37:08 +0300 Subject: [PATCH 2/4] add feedback kill functionality, triggered from gui or from daw with 2 stop signals --- Source/Chorus.cpp | 6 +++++ Source/Chorus.h | 2 ++ Source/ChorusEngine.cpp | 11 +++++++++ Source/ChorusEngine.h | 1 + Source/DelayEngine.cpp | 43 ++++++++++++++++++++++++++++++--- Source/DelayEngine.h | 11 ++++++++- Source/FilterEngine.cpp | 6 +++++ Source/FilterEngine.h | 2 +- Source/InsertEffect.h | 1 + Source/InsertEffectSelector.cpp | 6 +++++ Source/InsertEffectSelector.h | 1 + Source/KillButton.cpp | 40 ++++++++++++++++++++++++++++++ Source/KillButton.h | 21 ++++++++++++++++ Source/LofiEngine.cpp | 10 ++++++++ Source/LofiEngine.h | 1 + Source/Parameters.cpp | 7 ++++++ Source/Parameters.h | 5 +++- Source/PluginEditor.cpp | 3 +++ Source/PluginEditor.h | 3 +++ Source/PluginProcessor.cpp | 10 ++++++-- Source/PluginProcessor.h | 1 + Source/StereoDelay.cpp | 7 ++++++ Source/StereoDelay.h | 2 +- 23 files changed, 190 insertions(+), 10 deletions(-) create mode 100644 Source/KillButton.cpp create mode 100644 Source/KillButton.h diff --git a/Source/Chorus.cpp b/Source/Chorus.cpp index b2cba50..bcdb7cc 100644 --- a/Source/Chorus.cpp +++ b/Source/Chorus.cpp @@ -29,6 +29,12 @@ void Chorus::reset() noexcept m_modDepthMs = -1.0f; } +void Chorus::kill() +{ + m_delayLine.reset(); + m_lpState = 0.0f; +} + void Chorus::setBaseDelay(float delayInMs) { if (!juce::approximatelyEqual(delayInMs, m_baseDelayMs)) { diff --git a/Source/Chorus.h b/Source/Chorus.h index 6831446..a3722ce 100644 --- a/Source/Chorus.h +++ b/Source/Chorus.h @@ -15,6 +15,8 @@ class Chorus void prepareToPlay(double sampleRate, int samplesPerBlock) noexcept; void reset() noexcept; void update(); + void kill(); + float processSample(const float& in); void setBaseDelay(float delayInMs); void setModRate(float rate) { m_lfo.setFrequency(rate); } diff --git a/Source/ChorusEngine.cpp b/Source/ChorusEngine.cpp index 18acc02..c81ac75 100644 --- a/Source/ChorusEngine.cpp +++ b/Source/ChorusEngine.cpp @@ -36,6 +36,17 @@ void ChorusEngine::reset() noexcept m_modDepth = -1.0f; } +void ChorusEngine::kill() +{ + m_chorus1L.kill(); + m_chorus1R.kill(); + m_chorus2L.kill(); + m_chorus2R.kill(); + m_highpass.reset(); + m_dcBlockStateL = 0.0f; + m_dcBlockStateR = 0.0f; +} + void ChorusEngine::setModRate(float newRate) { if (!juce::approximatelyEqual(newRate, m_modRate)) { diff --git a/Source/ChorusEngine.h b/Source/ChorusEngine.h index 2e37169..c4d6106 100644 --- a/Source/ChorusEngine.h +++ b/Source/ChorusEngine.h @@ -18,6 +18,7 @@ class ChorusEngine : public InsertEffect void prepareToPlay(double sampleRate, int samplesPerBlock) noexcept override; void reset() noexcept override; + void kill() override; void processSample(const float& inL, const float& inR, float& outL, float& outR) override; void update(const Parameters& params) override; diff --git a/Source/DelayEngine.cpp b/Source/DelayEngine.cpp index 049659c..4d69811 100644 --- a/Source/DelayEngine.cpp +++ b/Source/DelayEngine.cpp @@ -22,7 +22,8 @@ void DelayEngine::prepareToPlay(double sampleRate, int samplesPerBlock) noexcept m_filterEngine.prepareToPlay(sampleRate, samplesPerBlock); m_feedbackHighpass.prepare(spec); m_stereoDelay.prepareToPlay(sampleRate, samplesPerBlock); - m_coeff = onePoleLowpassCoeff(100.0f, static_cast(sampleRate)); + m_coeff = onePoleLowpassCoeff(100.0f, m_sampleRate); + m_killCoeff = onePoleLowpassCoeff(10.0f, m_sampleRate); } void DelayEngine::reset() noexcept @@ -82,20 +83,53 @@ void DelayEngine::update(const Parameters& params) setWidthLevel(params.stereo()); setOffsetMs(params.offset()); setDelayTimes(params.delayTimeL(), params.delayTimeR()); + setKill(params.feedbackKill()); m_filterEngine.update(params); m_insertEffectSelector.update(params); } +void DelayEngine::setKill(bool shouldKill) +{ + bool risingEdge = shouldKill && !m_killButtonHeld; + m_killButtonHeld = shouldKill; + if (risingEdge) + triggerKill(); +} + +void DelayEngine::triggerKill() +{ + m_isKilling = true; +} + +void DelayEngine::resetAfterKill() +{ + m_stereoDelay.kill(); + m_insertEffectSelector.kill(); + m_filterEngine.kill(); + m_feedbackHighpass.reset(); + m_feedbackCompressor.reset(); + m_feedbackL = 0.0f; + m_feedbackR = 0.0f; + m_killGain = 1.0f; + m_isKilling = false; +} + void DelayEngine::updateSyncedModulators(const double ppqPosition) { m_filterEngine.updateLFOs(ppqPosition); } -void DelayEngine::processSample(const float& inL, const float& inR, float& outL, float& outR, const Parameters& params) +void DelayEngine::processSample(const float& inL, const float& inR, float& outL, float& outR) { float dryL = inL; float dryR = inR; + if (m_isKilling) { + m_killGain = onePoleLowpass(0.0f, m_killGain, m_killCoeff); + if (m_killGain < 0.001f) + resetAfterKill(); + } + float mono = (dryL + dryR) * .5f; float delayInL = 0.0f, delayInR = 0.0f, wetL = 0.0f, wetR = 0.0f; switch (m_delayMode) { @@ -107,7 +141,7 @@ void DelayEngine::processSample(const float& inL, const float& inR, float& outL, delayInL = m_feedbackR; delayInR = mono + m_feedbackL; break; - case DelayMode::Cross: + case DelayMode::Cross: delayInL = dryL + m_feedbackR; delayInR = dryR + m_feedbackL; break; @@ -122,7 +156,8 @@ void DelayEngine::processSample(const float& inL, const float& inR, float& outL, m_stereoDelay.processSample(delayInL, wetL, Channel::Left); m_stereoDelay.processSample(delayInR, wetR, Channel::Right); - + wetL *= m_killGain; + wetR *= m_killGain; m_insertEffectSelector.processSample(wetL, wetR, wetL, wetR); m_filterEngine.processSample(wetL, wetR, wetL, wetR); diff --git a/Source/DelayEngine.h b/Source/DelayEngine.h index 80a20a9..dfbd947 100644 --- a/Source/DelayEngine.h +++ b/Source/DelayEngine.h @@ -21,7 +21,7 @@ class DelayEngine void reset() noexcept; void update(const Parameters& params); - void processSample(const float& inL, const float& inR, float& outL, float& outR, const Parameters& params); + void processSample(const float& inL, const float& inR, float& outL, float& outR); void setDelayTimes(const float delayInMsL, const float delayInMsR); void setFeedbackLevel(const float value) { m_feedbackLevel = value; } void setMixLevel(const float value) { m_mixLevel = value; } @@ -30,6 +30,8 @@ class DelayEngine void setOffsetMs(const float value) { m_offsetMs = value * .5f; } void setDelayMode(const int modeIndex); void setInsertEffect(const int effectIndex); + void setKill(bool shouldKill); + void triggerKill(); void updateSyncedModulators(const double ppqPosition); std::atomic& getLowCutLfoValue() { return m_filterEngine.getLowCutLfoValue(); } @@ -52,9 +54,12 @@ class DelayEngine float m_coeff = 0.0f; + float m_killCoeff = 0.0f; float m_feedbackL = 0.0f; float m_feedbackR = 0.0f; + void resetAfterKill(); + float m_baseDelayTimeMs = -1.0f; float m_delayTimeMsL = -1.0f; float m_delayTimeMsR = -1.0f; @@ -64,6 +69,10 @@ class DelayEngine float m_gainLevel = 1.0f; float m_widthLevel = 1.0f; float m_offsetMs = 0.0f; + float m_killGain = 1.0f; + + bool m_isKilling = false; + bool m_killButtonHeld = false; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(DelayEngine) }; \ No newline at end of file diff --git a/Source/FilterEngine.cpp b/Source/FilterEngine.cpp index ad29844..edb4928 100644 --- a/Source/FilterEngine.cpp +++ b/Source/FilterEngine.cpp @@ -38,6 +38,12 @@ void FilterEngine::reset() noexcept m_highCutSmoothed[0] = m_highCutSmoothed[1] = Parameters::maxFilterFreq; } +void FilterEngine::kill() +{ + m_lowCut.reset(); + m_highCut.reset(); +} + float FilterEngine::modulatedCutoff(float base, float lfoValue, float modDepth) { return juce::jlimit(Parameters::minFilterFreq, Parameters::maxFilterFreq, base * std::pow(2.0f, lfoValue * modDepth)); diff --git a/Source/FilterEngine.h b/Source/FilterEngine.h index 7597fcc..6c88e76 100644 --- a/Source/FilterEngine.h +++ b/Source/FilterEngine.h @@ -20,7 +20,7 @@ class FilterEngine void update(const Parameters& params); void processSample(const float& inL, const float& inR, float& outL, float& outR); - + void kill(); void updateLFOs(const double ppqPosition) noexcept; std::atomic& getLowCutLfoValue() { return m_lowCutLfoValue; } diff --git a/Source/InsertEffect.h b/Source/InsertEffect.h index f6c2330..a9819a9 100644 --- a/Source/InsertEffect.h +++ b/Source/InsertEffect.h @@ -12,4 +12,5 @@ class InsertEffect virtual void reset() noexcept = 0; virtual void processSample(const float& inL, const float& inR, float& outL, float& outR) = 0; virtual void update(const Parameters& params) = 0; + virtual void kill() = 0; }; \ No newline at end of file diff --git a/Source/InsertEffectSelector.cpp b/Source/InsertEffectSelector.cpp index aaaf710..0d6834a 100644 --- a/Source/InsertEffectSelector.cpp +++ b/Source/InsertEffectSelector.cpp @@ -29,6 +29,12 @@ void InsertEffectSelector::update(const Parameters& params) m_active->update(params); } +void InsertEffectSelector::kill() +{ + if (m_active) + m_active->kill(); +} + void InsertEffectSelector::select(InsertEffectType type) { switch(type) { diff --git a/Source/InsertEffectSelector.h b/Source/InsertEffectSelector.h index fa4e987..6fd1d0f 100644 --- a/Source/InsertEffectSelector.h +++ b/Source/InsertEffectSelector.h @@ -15,6 +15,7 @@ class InsertEffectSelector void processSample(const float& inL, const float& inR, float& outL, float& outR); void update(const Parameters& params); void select(InsertEffectType type); + void kill(); private: ChorusEngine m_chorus; diff --git a/Source/KillButton.cpp b/Source/KillButton.cpp new file mode 100644 index 0000000..862a10a --- /dev/null +++ b/Source/KillButton.cpp @@ -0,0 +1,40 @@ +#include +#include "KillButton.h" + +KillButton::KillButton(const juce::String& text, juce::AudioProcessorValueTreeState& apvts, const juce::ParameterID& parameterID, int width, int height) + : m_apvts(apvts), m_paramID(parameterID.getParamID()) +{ + m_button.setButtonText(text); + m_button.setClickingTogglesState(false); + m_button.setColour(juce::TextButton::ColourIds::buttonColourId, juce::Colours::transparentWhite); + m_button.setColour(juce::TextButton::ColourIds::buttonOnColourId, juce::Colours::orange); + m_button.setColour(juce::TextButton::ColourIds::textColourOnId, juce::Colours::black); + m_button.setBounds(0, 0, width, height); + m_button.addMouseListener(this, false); + addAndMakeVisible(m_button); + setSize(width, height); +} + +KillButton::~KillButton() +{ + m_button.removeMouseListener(this); +} + +void KillButton::resized() +{ + m_button.setTopLeftPosition(0, 0); +} + +void KillButton::mouseDown(const juce::MouseEvent&) +{ + m_button.setToggleState(true, juce::dontSendNotification); + if (auto* param = m_apvts.getParameter(m_paramID)) + param->setValueNotifyingHost(1.0f); +} + +void KillButton::mouseUp(const juce::MouseEvent&) +{ + m_button.setToggleState(false, juce::dontSendNotification); + if (auto* param = m_apvts.getParameter(m_paramID)) + param->setValueNotifyingHost(0.0f); +} diff --git a/Source/KillButton.h b/Source/KillButton.h new file mode 100644 index 0000000..b1b14d2 --- /dev/null +++ b/Source/KillButton.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +class KillButton : public juce::Component +{ +public: + KillButton(const juce::String& text, juce::AudioProcessorValueTreeState& apvts, const juce::ParameterID& parameterID, int width = 60, int height = 25); + ~KillButton() override; + + void resized() override; + void mouseDown(const juce::MouseEvent& e) override; + void mouseUp(const juce::MouseEvent& e) override; + +private: + juce::TextButton m_button; + juce::AudioProcessorValueTreeState& m_apvts; + juce::String m_paramID; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(KillButton) +}; diff --git a/Source/LofiEngine.cpp b/Source/LofiEngine.cpp index 14b3b7b..78f1589 100644 --- a/Source/LofiEngine.cpp +++ b/Source/LofiEngine.cpp @@ -32,6 +32,16 @@ void LofiEngine::reset() noexcept m_envR = 0.0f; } +void LofiEngine::kill() +{ + m_bitCrusherL.reset(); + m_bitCrusherR.reset(); + m_highpass.reset(); + m_lowpass.reset(); + m_envL = 0.0f; + m_envR = 0.0f; +} + void LofiEngine::update(const Parameters& params) { setResampleFreq(params.lofiResampleFreq()); diff --git a/Source/LofiEngine.h b/Source/LofiEngine.h index dc0b053..372de9d 100644 --- a/Source/LofiEngine.h +++ b/Source/LofiEngine.h @@ -17,6 +17,7 @@ class LofiEngine : public InsertEffect void reset() noexcept override; void processSample(const float& inL, const float& inR, float& outL, float& outR) override; void update(const Parameters& params) override; + void kill () override; private: BitCrusher m_bitCrusherL{ 8.0f, 22050.0f }; diff --git a/Source/Parameters.cpp b/Source/Parameters.cpp index a3b8d6d..50cfab4 100644 --- a/Source/Parameters.cpp +++ b/Source/Parameters.cpp @@ -153,6 +153,7 @@ Parameters::Parameters(juce::AudioProcessorValueTreeState& apvts) castParameter(apvts, delayModeParamID, m_delayModeParam); castParameter(apvts, mixParamID, m_mixParam); castParameter(apvts, feedbackParamID, m_feedbackParam); + castParameter(apvts, feedbackKillParamID, m_feedbackKillParam); castParameter(apvts, stereoParamID, m_stereoParam); castParameter(apvts, offsetParamID, m_offsetParam); castParameter(apvts, insertEffectTypeParamID, m_insertEffectTypeParam); @@ -444,6 +445,11 @@ juce::AudioProcessorValueTreeState::ParameterLayout Parameters::createParameterL "Lofi Noise", true )); + layout.add(std::make_unique( + feedbackKillParamID.getParamID(), + "Feedback Kill", + false + )); return layout; } @@ -544,6 +550,7 @@ void Parameters::update(const Tempo& tempo) noexcept m_lofiMixLevelSmoother.setTargetValue(m_lofiMixLevelParam->get() * 0.01f); m_lofiDampenFreqSmoother.setTargetValue(m_lofiDampenFreqParam->get()); m_lofiNoiseEnabled = m_lofiNoiseEnabledParam->get(); + m_feedbackKill = m_feedbackKillParam->get(); m_tempoSyncL = m_tempoSyncLParam->get(); m_tempoSyncR = m_tempoSyncRParam->get(); m_delayNoteL = m_delayNoteLParam->getIndex(); diff --git a/Source/Parameters.h b/Source/Parameters.h index 3b29452..e789fa4 100644 --- a/Source/Parameters.h +++ b/Source/Parameters.h @@ -12,6 +12,7 @@ const juce::ParameterID tempoSyncRParamID{ "tempoSyncR", 1 }; const juce::ParameterID delayModeParamID{ "delayMode", 1 }; const juce::ParameterID mixParamID{ "mix", 1 }; const juce::ParameterID feedbackParamID{ "feedback", 1 }; +const juce::ParameterID feedbackKillParamID{ "feedbackKill", 1 }; const juce::ParameterID stereoParamID{ "stereo", 1 }; const juce::ParameterID offsetParamID{ "offset", 1 }; const juce::ParameterID lowCutFreqParamID{ "lowCutFreq", 1 }; @@ -109,6 +110,7 @@ class Parameters { float lofiMixLevel() const { return m_lofiMixLevel; } float lofiDampenFreq() const { return m_lofiDampenFreq; } bool lofiNoiseEnabled() const { return m_lofiNoiseEnabled; } + bool feedbackKill() const { return m_feedbackKill; } private: juce::AudioParameterFloat* m_gainParam; @@ -121,6 +123,7 @@ class Parameters { juce::AudioParameterChoice* m_delayModeParam; juce::AudioParameterFloat* m_mixParam; juce::AudioParameterFloat* m_feedbackParam; + juce::AudioParameterBool* m_feedbackKillParam; juce::AudioParameterFloat* m_stereoParam; juce::AudioParameterFloat* m_offsetParam; juce::AudioParameterChoice* m_insertEffectTypeParam; @@ -170,7 +173,6 @@ class Parameters { juce::LinearSmoothedValue m_lofiResampleFreqSmoother; juce::LinearSmoothedValue m_lofiMixLevelSmoother; juce::LinearSmoothedValue m_lofiDampenFreqSmoother; - ; // for tempo-synced offset modulation float m_gain = 0.0f; float m_delayTimeL = 0.0f; @@ -182,6 +184,7 @@ class Parameters { int m_delayMode = 0; float m_mix = 0.5f; float m_feedback = 0.0f; + bool m_feedbackKill = false; float m_stereo = 0.0f; float m_offset = 0.0f; float m_lowCutFreq = 20.0f; diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 96c3f8f..44a40c8 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -72,6 +72,7 @@ ViiveAudioProcessorEditor::ViiveAudioProcessorEditor(ViiveAudioProcessor& p) // addAndMakeVisible(m_lofiGroup); addAndMakeVisible(m_insertEffectTypeSelector); + addAndMakeVisible(m_feedbackKillButton); addAndMakeVisible(m_meter); #if JUCE_DEBUG @@ -190,6 +191,8 @@ void ViiveAudioProcessorEditor::resized() m_lofiDampenFreqKnob.setTopLeftPosition(m_lofiResampleFreqKnob.getRight() + 20, 20); m_lofiNoiseEnabledButton.setCentrePosition(m_lofiDampenFreqKnob.getRight() - 35, m_lofiDampenFreqKnob.getBottom() + 15); + m_feedbackKillButton.setCentrePosition(m_outputGroup.getX() + m_outputGroup.getWidth() / 2, + m_outputGroup.getBottom() + 20); m_meter.setBounds(m_outputGroup.getRight() + 15, y + 45, 35, height - 45); #if JUCE_DEBUG diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index 8d7a2a4..2e84c01 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -5,6 +5,7 @@ #include "Parameters.h" #include "RotaryKnob.h" #include "SyncButton.h" +#include "KillButton.h" #include "Selector.h" #include "LevelMeter.h" #include "LFOIndicator.h" @@ -78,6 +79,8 @@ class ViiveAudioProcessorEditor : public juce::AudioProcessorEditor, LFOIndicator m_highCutLfoIndicator { m_audioProcessor.getHighCutLfoValue() }; SyncButton m_highCutModInvertButton{ juce::String::fromUTF8("\xC3\x98"), m_audioProcessor.apvts, highCutModInvertParamID.getParamID(), 20}; + KillButton m_feedbackKillButton{ "Kill", m_audioProcessor.apvts, feedbackKillParamID }; + juce::Label m_modulationLabel; juce::HashMap m_linkedParams; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index bd880a7..26469c8 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -51,7 +51,7 @@ bool ViiveAudioProcessor::isMidiEffect() const double ViiveAudioProcessor::getTailLengthSeconds() const { - return 0.0; + return 5.0; } int ViiveAudioProcessor::getNumPrograms() @@ -115,6 +115,12 @@ void ViiveAudioProcessor::processBlock(juce::AudioBuffer& buffer, juce::M buffer.clear(i, 0, buffer.getNumSamples()); m_tempo.update(getPlayHead()); + + double ppqPos = m_tempo.getPpqPosition(); + if (!m_tempo.getIsPlaying() && ppqPos < m_lastPpqPosition - 0.01) + m_delayEngine.triggerKill(); + m_lastPpqPosition = ppqPos; + m_params.update(m_tempo); if (m_tempo.getIsPlaying()) m_delayEngine.updateSyncedModulators(m_tempo.getPpqPosition()); @@ -136,7 +142,7 @@ void ViiveAudioProcessor::processBlock(juce::AudioBuffer& buffer, juce::M m_params.smoothen(); m_delayEngine.update(m_params); m_delayEngine.processSample(inputDataL[sample], inputDataR[sample], - outL, outR, m_params); + outL, outR); outputDataL[sample] = outL; outputDataR[sample] = outR; diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index b0b35e0..796d7d3 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -70,6 +70,7 @@ class ViiveAudioProcessor : public juce::AudioProcessor Tempo m_tempo; std::atomic m_outputLevelL, m_outputLevelR; + double m_lastPpqPosition = 0.0; //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ViiveAudioProcessor) }; diff --git a/Source/StereoDelay.cpp b/Source/StereoDelay.cpp index 1e14888..3882507 100644 --- a/Source/StereoDelay.cpp +++ b/Source/StereoDelay.cpp @@ -38,6 +38,13 @@ void StereoDelay::setDelayTime(float ms, Channel channel) } } +void StereoDelay::kill() +{ + for (int i = 0; i < 2; i++) { + m_delayLines[i].reset(); + } +} + void StereoDelay::processSample(const float& in, float& out, Channel channel) { m_delayLines[channel].pushSample(0, in); diff --git a/Source/StereoDelay.h b/Source/StereoDelay.h index a59e0f7..94741e7 100644 --- a/Source/StereoDelay.h +++ b/Source/StereoDelay.h @@ -10,7 +10,7 @@ class StereoDelay { void prepareToPlay(double sampleRate, int samplesPerBlock) noexcept; void reset() noexcept; void processSample(const float &in, float &out, Channel channel); - + void kill(); void setDelayTime(float ms, Channel channel); private: From e15d2aec0cfad30d1e68a5fcfa208018986c08cb Mon Sep 17 00:00:00 2001 From: Otto Andelin Date: Fri, 29 May 2026 14:06:13 +0300 Subject: [PATCH 3/4] update changelog and cmakelists --- CHANGELOG.md | 5 +++++ CMakeLists.txt | 1 + 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66f2fc3..fd0384f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,11 @@ All notable changes to this project will be documented in this file. * Ranges finetuned for usability and musicality, subject to change * Insert effect selector * Allows user to switch between chorus and lofi effect in the feedback loop +* Feedback loop kill + * fades wet signal out and clears delay line buffers, feedback and any audio processor states + * Automatable param + * Button in GUI + * Triggered with position rewind when playback stop (ie pressing stop twice during playback) ### Fixed * Delay time offset bug where offset never truly returned to zero once modified diff --git a/CMakeLists.txt b/CMakeLists.txt index a8e0b43..38dcc7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,6 +74,7 @@ target_sources(viive PRIVATE Source/FilterEngine.cpp Source/Tempo.cpp Source/Selector.cpp + Source/KillButton.cpp Source/SyncButton.cpp Source/StereoDelay.cpp Source/StereoFilter.cpp From 2753d50e771c8473766c9b14da4e160ab38bc352 Mon Sep 17 00:00:00 2001 From: Otto Andelin Date: Fri, 29 May 2026 14:14:52 +0300 Subject: [PATCH 4/4] refactor: move resetAfterKill declaration to end of private section --- Source/DelayEngine.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/DelayEngine.h b/Source/DelayEngine.h index dfbd947..9ff2204 100644 --- a/Source/DelayEngine.h +++ b/Source/DelayEngine.h @@ -58,8 +58,6 @@ class DelayEngine float m_feedbackL = 0.0f; float m_feedbackR = 0.0f; - void resetAfterKill(); - float m_baseDelayTimeMs = -1.0f; float m_delayTimeMsL = -1.0f; float m_delayTimeMsR = -1.0f; @@ -74,5 +72,7 @@ class DelayEngine bool m_isKilling = false; bool m_killButtonHeld = false; + void resetAfterKill(); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(DelayEngine) }; \ No newline at end of file