From ef718570446060a753852320452e5131cc877852 Mon Sep 17 00:00:00 2001 From: Otto Andelin <112075238+trsctr@users.noreply.github.com> Date: Wed, 6 May 2026 10:40:13 +0300 Subject: [PATCH 1/4] 0.2.0-beta.1: Filter modulation (#5) * add filterengine and stereofilter classes * update cmakelists, wire filterengine to delayengine, create types.h for channel enum * add parameters for filter modulation * add mod param update to filterengine, smoothen cutoff with onepolelp, rename params * switch to octave based filter modulation, fix lfo phase bug * add lfoscope component + buffer for debug purposes, add it to editor only present in debug build * finetune filter mod range and value display * add square to lfo shapes * flip lfo phase for high cut * clean up old code from delay engine * add tempo lfo tempo sync + parameters * add size options and custom look and feel for small rotary knob * add filter mod depth controls to gui * display phase in degrees * add saw up/down shapes to lfo * add parameter for highcut mod invert toggle * add params for setting filter lfo shape * sync tempo synced lfo to song position * add sample & hold modulation * redefine lfo tempo sync note values, set max mod rate to 8hz * set max filter q to 1.5 * update changelog * add selector component and lfo waveform gui selector element * fix tempo sync bug in s&h lfo * add lfo value indicators to filter mod section * add high cut lfo invert toggle to gui * move delay mode selector label to left side of menu * update changelog, readme, cmakelists version number * code cleanup * fix version number string in cmakelists, hardcode beta version number to editor * remove unused variables from Parameters.h --- CHANGELOG.md | 23 +++++ CMakeLists.txt | 9 +- README.md | 8 +- Source/DelayEngine.cpp | 62 +++--------- Source/DelayEngine.h | 24 ++--- Source/FilterEngine.cpp | 170 +++++++++++++++++++++++++++++++++ Source/FilterEngine.h | 85 +++++++++++++++++ Source/LFO.cpp | 33 +++++++ Source/LFO.h | 16 +++- Source/LFOIndicator.h | 35 +++++++ Source/LFOScope.cpp | 71 ++++++++++++++ Source/LFOScope.h | 23 +++++ Source/LFOScopeBuffer.h | 19 ++++ Source/Parameters.cpp | 190 ++++++++++++++++++++++++++++++++++++- Source/Parameters.h | 71 ++++++++++++-- Source/PluginEditor.cpp | 120 +++++++++++++++++------ Source/PluginEditor.h | 35 ++++++- Source/PluginProcessor.cpp | 2 + Source/PluginProcessor.h | 9 ++ Source/RotaryKnob.cpp | 63 +++++++++++- Source/RotaryKnob.h | 13 ++- Source/Selector.cpp | 55 +++++++++++ Source/Selector.h | 41 ++++++++ Source/StereoDelay.h | 3 +- Source/StereoFilter.cpp | 40 ++++++++ Source/StereoFilter.h | 25 +++++ Source/Sync.h | 47 +++++++++ Source/Tempo.cpp | 33 +++---- Source/Tempo.h | 5 + Source/Types.h | 5 + viive.png | Bin 32184 -> 37335 bytes 31 files changed, 1196 insertions(+), 139 deletions(-) create mode 100644 Source/FilterEngine.cpp create mode 100644 Source/FilterEngine.h create mode 100644 Source/LFOIndicator.h create mode 100644 Source/LFOScope.cpp create mode 100644 Source/LFOScope.h create mode 100644 Source/LFOScopeBuffer.h create mode 100644 Source/Selector.cpp create mode 100644 Source/Selector.h create mode 100644 Source/StereoFilter.cpp create mode 100644 Source/StereoFilter.h create mode 100644 Source/Sync.h create mode 100644 Source/Types.h diff --git a/CHANGELOG.md b/CHANGELOG.md index a32176c..11e5d37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,29 @@ All notable changes to this project will be documented in this file. +## \[0.2.0-beta.1] - 2026-05-06 + +### Added + +* Filter modulation for High and Low cuts with independent LFOs + * LFO rate control + * LFO Tempo sync linked to song position during playback + * LFO phase offset per filter + * LFO Shape selection + * Filter modulation depth control + * Phase invert switch for high cut lfo (on by default) +* Square, Saw Up/Down and Sample&Hold LFO Shapes +* Indicator lights for both LFOs to get visual idea of its current value + +### Changed + +* Refactored filters to StereoFilter primitive and FilterEngine class +* Set maximum resonance for low and high cut filters to 1.5 + +### Known limitations + +* Tempo synced LFOs might sync incorrectly to song playback positions in DAW on extreme settings (very high or very low BPMs) + ## \[0.1.0] - 2026-04-17 ### Added diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e81347..0aa96b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.22) -project(viive VERSION 0.1.0) +project(viive VERSION 0.2.0) ### Dependency versions ### @@ -41,6 +41,7 @@ set(VST3_DEV_COPY_DIR "" CACHE PATH "If set, copies the Debug VST3 build here af ### Plugin Project Setup ### juce_add_plugin(viive + VERSION 0.2.0 FORMATS VST3 Standalone PRODUCT_NAME "viive" COMPANY_NAME "trsctr" @@ -67,9 +68,12 @@ get_property(project_targets DIRECTORY "${PROJECT_SOURCE_DIR}" PROPERTY BUILDSYS set_target_properties(${project_targets} PROPERTIES CXX_EXTENSIONS OFF) target_sources(viive PRIVATE + Source/FilterEngine.cpp Source/Tempo.cpp + Source/Selector.cpp Source/SyncButton.cpp Source/StereoDelay.cpp + Source/StereoFilter.cpp Source/ChorusEngine.cpp Source/LFO.cpp Source/Chorus.cpp @@ -78,7 +82,8 @@ target_sources(viive PRIVATE Source/DelayEngine.cpp Source/Parameters.cpp Source/PluginProcessor.cpp - Source/PluginEditor.cpp) + Source/PluginEditor.cpp + Source/LFOScope.cpp) juce_generate_juce_header(viive) diff --git a/README.md b/README.md index 60ac2d1..7537da0 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Currently developed and tested on Windows (JUCE AudioPluginHost, Ableton Live Su - Mix and feedback controls with feedback stabilization (0-140%) - Stereo width control with mid/side processing - Sculpting low/high-cut filters with adjustable resonance on delayed signal / feedback loop +- Filter modulation section with separate LFOs for high and low cut filters with tempo sync, adjustable rate, modulation depth, stereo phase offset and selectable shapes: Sine, Triangle, Square, Saw Up/Down, Sample & Hold - Dual-voice chorus effect in feedback loop with intensity, modulation rate, and modulation depth controls - Feedback loop protection: HPF, compressor, soft clipping @@ -31,8 +32,11 @@ Download the zip for your platform, extract it, and copy `viive.vst3` to your VS Then rescan plugins in your DAW. +NOTE: MacOS build is untested and unsigned. May be blocked by Gatekeeper on +newer macOS versions and cannot be guaranteed to work without +code signing and notarization. + ## Potential future features -- LFO/modulation for general parameters (such as filter cutoffs) - Additional effects in feedback loop (bitcrush, phaser) - Preset manager - Single/dual delay mode switch @@ -57,6 +61,8 @@ cmake --build Build --config Release JUCE 8.0.11 is downloaded automatically during configuration. +NOTE: Linux builds are reportedly possible but untested and undocumented. + ### Output The VST3 will be produced under `Build/viive_artefacts/Release/VST3/`. diff --git a/Source/DelayEngine.cpp b/Source/DelayEngine.cpp index ac537ff..d8b4a32 100644 --- a/Source/DelayEngine.cpp +++ b/Source/DelayEngine.cpp @@ -1,9 +1,9 @@ #include "DelayEngine.h" #include "DSP.h" +#include "Types.h" + DelayEngine::DelayEngine() { - m_lowCutFilter.setType(juce::dsp::StateVariableTPTFilterType::highpass); - m_highCutFilter.setType(juce::dsp::StateVariableTPTFilterType::lowpass); m_feedbackHighpass.setType(juce::dsp::StateVariableTPTFilterType::highpass); m_feedbackCompressor.setAttack(.1f); m_feedbackCompressor.setRelease(50.f); @@ -19,8 +19,7 @@ void DelayEngine::prepareToPlay(double sampleRate, int samplesPerBlock) noexcept spec.numChannels = 2; m_sampleRate = static_cast(sampleRate); m_feedbackCompressor.prepare(spec); - m_lowCutFilter.prepare(spec); - m_highCutFilter.prepare(spec); + m_filterEngine.prepareToPlay(sampleRate, samplesPerBlock); m_feedbackHighpass.prepare(spec); m_chorusEngine.prepareToPlay(sampleRate, samplesPerBlock); m_stereoDelay.prepareToPlay(sampleRate, samplesPerBlock); @@ -31,19 +30,14 @@ void DelayEngine::prepareToPlay(double sampleRate, int samplesPerBlock) noexcept void DelayEngine::reset() noexcept { m_stereoDelay.reset(); - m_lowCutFilter.reset(); - m_highCutFilter.reset(); m_chorusEngine.reset(); + m_filterEngine.reset(); m_feedbackHighpass.reset(); m_feedbackHighpass.setCutoffFrequency(60.0f); // just to remove rumble from fb loop m_feedbackHighpass.setResonance(Parameters::defaultFilterQ); m_feedbackCompressor.reset(); m_feedbackL = 0.0f; m_feedbackR = 0.0f; - m_lowCutFreq = -1.0f; - m_lowCutQ = -1.0f; - m_highCutFreq = -1.0f; - m_highCutQ = -1.0f; m_baseDelayTimeMs = -1.0f; m_mixLevel = 0.5f; m_feedbackLevel = 0.0f; @@ -58,31 +52,6 @@ void DelayEngine::setDelayMode(const int modeIndex) } } -void DelayEngine::setLowCut(const Parameters& params) { - setFilterFreq(params.lowCutFreq(), m_lowCutFreq, m_lowCutFilter); - setFilterQ(params.lowCutQ(), m_lowCutQ, m_lowCutFilter); -} - -void DelayEngine::setHighCut(const Parameters& params) { - setFilterFreq(params.highCutFreq(), m_highCutFreq, m_highCutFilter); - setFilterQ(params.highCutQ(), m_highCutQ, m_highCutFilter); -} - -void DelayEngine::setFilterFreq(const float freq, float& currentFreq, Filter& filter) { - if (!juce::approximatelyEqual(freq, currentFreq)) { - filter.setCutoffFrequency(freq); - currentFreq = freq; - } -} - -void DelayEngine::setFilterQ(const float q, float& currentQ, Filter& filter) { - float newQ = std::min(q, Parameters::maxFilterQ); - if (!juce::approximatelyEqual(newQ, currentQ)) { - filter.setResonance(newQ); - currentQ = newQ; - } -} - void DelayEngine::setDelayTimes(const float targetL, const float targetR) { float targetLeftMs = targetL - m_offsetMs; float targetRightMs = targetR + m_offsetMs; @@ -98,14 +67,18 @@ void DelayEngine::setDelayTimes(const float targetL, const float targetR) { void DelayEngine::update(const Parameters& params) { - setLowCut(params); - setHighCut(params); setMixLevel(params.mix()); setGainLevel(params.gain()); setFeedbackLevel(params.feedback()); setWidthLevel(params.stereo()); setOffsetMs(params.offset()); setDelayTimes(params.delayTimeL(), params.delayTimeR()); + m_filterEngine.update(params); +} + +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) @@ -114,7 +87,7 @@ void DelayEngine::processSample(const float& inL, const float& inR, float& outL, float dryR = inR; float mono = (dryL + dryR) * .5f; - float delayInL = 0.0f, delayInR = 0.0f, popL = 0.0f, popR = 0.0f, wetL = 0.0f, wetR = 0.0f; + float delayInL = 0.0f, delayInR = 0.0f, wetL = 0.0f, wetR = 0.0f; switch (m_delayMode) { case DelayMode::PingPongLR: delayInL = mono + m_feedbackR; @@ -137,17 +110,12 @@ void DelayEngine::processSample(const float& inL, const float& inR, float& outL, delayInR = dryR; } - m_stereoDelay.processSample(delayInL, popL, Channel::Left); - m_stereoDelay.processSample(delayInR, popR, Channel::Right); + m_stereoDelay.processSample(delayInL, wetL, Channel::Left); + m_stereoDelay.processSample(delayInR, wetR, Channel::Right); - m_chorusEngine.processSample(popL, popR, wetL, wetR, params); - - // sculpting filters, first left and then right channel - wetL = m_lowCutFilter.processSample(0, wetL); - wetL = m_highCutFilter.processSample(0, wetL); + m_chorusEngine.processSample(wetL, wetR, wetL, wetR, params); - wetR = m_lowCutFilter.processSample(1, wetR); - wetR = m_highCutFilter.processSample(1, wetR); + m_filterEngine.processSample(wetL, wetR, wetL, wetR); // this removes rumble generated by low cut filter on higher q values wetL = m_feedbackHighpass.processSample(0, wetL); diff --git a/Source/DelayEngine.h b/Source/DelayEngine.h index f7f4004..f360c71 100644 --- a/Source/DelayEngine.h +++ b/Source/DelayEngine.h @@ -3,6 +3,10 @@ #include "Parameters.h" #include "ChorusEngine.h" #include "StereoDelay.h" +#include "FilterEngine.h" +#if JUCE_DEBUG +#include "LFOScopeBuffer.h" +#endif using Filter = juce::dsp::StateVariableTPTFilter; @@ -19,10 +23,6 @@ class DelayEngine void update(const Parameters& params); void processSample(const float& inL, const float& inR, float& outL, float& outR, const Parameters& params); - void setLowCut(const Parameters& params); - void setHighCut(const Parameters& params); - void setFilterFreq(const float freq, float& currentFreq, Filter& filter); - void setFilterQ(const float q, float& currentQ, Filter& filter); 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,13 +30,19 @@ class DelayEngine void setWidthLevel(const float value) { m_widthLevel = value; } void setOffsetMs(const float value) { m_offsetMs = value * .5f; } void setDelayMode(const int modeIndex); + void updateSyncedModulators(const double ppqPosition); + + std::atomic& getLowCutLfoValue() { return m_filterEngine.getLowCutLfoValue(); } + std::atomic& getHighCutLfoValue() { return m_filterEngine.getHighCutLfoValue(); } + +#if JUCE_DEBUG + LFOScopeBuffer& getLFOScopeBuffer() { return m_filterEngine.m_scopeBuffer; } +#endif private: - //juce::dsp::DelayLine m_delayLine; StereoDelay m_stereoDelay; DelayMode m_delayMode = DelayMode::Stereo; - Filter m_lowCutFilter; - Filter m_highCutFilter; + FilterEngine m_filterEngine; Filter m_feedbackHighpass; juce::dsp::Compressor m_feedbackCompressor; ChorusEngine m_chorusEngine; @@ -57,10 +63,6 @@ class DelayEngine float m_gainLevel = 1.0f; float m_widthLevel = 1.0f; float m_offsetMs = 0.0f; - float m_lowCutFreq = -1.0f; - float m_lowCutQ = -1.0f; - float m_highCutFreq = -1.0f; - float m_highCutQ = -1.0f; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(DelayEngine) }; \ No newline at end of file diff --git a/Source/FilterEngine.cpp b/Source/FilterEngine.cpp new file mode 100644 index 0000000..ad29844 --- /dev/null +++ b/Source/FilterEngine.cpp @@ -0,0 +1,170 @@ +#include "FilterEngine.h" +#include "Sync.h" + +FilterEngine::FilterEngine() +{ + int lowCutSeed = juce::Random::getSystemRandom().nextInt(); + int highCutSeed = juce::Random::getSystemRandom().nextInt(); + for (int i = 0; i < 2; i++) { + m_lowCutLfos[i].setSeed(lowCutSeed); + m_highCutLfos[i].setSeed(highCutSeed); + } +} + +void FilterEngine::prepareToPlay(double sampleRate, int samplesPerBlock) noexcept +{ + m_lowCut.prepareToPlay(sampleRate, samplesPerBlock); + m_highCut.prepareToPlay(sampleRate, samplesPerBlock); + for (auto& lfo : m_lowCutLfos) + lfo.prepareToPlay(static_cast(sampleRate)); + for (auto& lfo : m_highCutLfos) + lfo.prepareToPlay(static_cast(sampleRate)); + m_cutoffSmoothCoeff = onePoleLowpassCoeff(5.0f, static_cast(sampleRate)); +} + +void FilterEngine::reset() noexcept +{ + m_lowCut.reset(); + m_highCut.reset(); + for (auto& lfo : m_lowCutLfos) + lfo.reset(); + for (auto& lfo : m_highCutLfos) + lfo.reset(); + m_lowCutBase = -1.0f; + m_lowCutQ = -1.0f; + m_highCutBase = -1.0f; + m_highCutQ = -1.0f; + m_lowCutSmoothed[0] = m_lowCutSmoothed[1] = Parameters::minFilterFreq; + m_highCutSmoothed[0] = m_highCutSmoothed[1] = Parameters::maxFilterFreq; +} + +float FilterEngine::modulatedCutoff(float base, float lfoValue, float modDepth) +{ + return juce::jlimit(Parameters::minFilterFreq, Parameters::maxFilterFreq, base * std::pow(2.0f, lfoValue * modDepth)); +} + +void FilterEngine::setFilterFreq(float freq, float& current) +{ + if (!juce::approximatelyEqual(freq, current)) + current = freq; +} + +void FilterEngine::setFilterQ(float q, float& current, StereoFilter& filter) +{ + float newQ = std::min(q, Parameters::maxFilterQ); + if (!juce::approximatelyEqual(newQ, current)) { + current = newQ; + filter.setQ(current); + } +} + +void FilterEngine::setFilterModDepth(float depth, float& current) +{ + if (!juce::approximatelyEqual(depth, current)) + current = depth; +} + +void FilterEngine::setFilterModRate(float rate, float& current, LFO* lfos) +{ + if (!juce::approximatelyEqual(rate, current)) { + current = rate; + for (int i = 0; i < 2; i++) + lfos[i].setFrequency(current); + } +} + +void FilterEngine::setFilterModPhase(float phase, float& current, LFO* lfos) +{ + if (!juce::approximatelyEqual(phase, current)) { + current = phase; + float normalized = std::fmod(std::fmod(phase, 360.0f) + 360.0f, 360.0f) / 360.0f; + lfos[Channel::Right].setOffset(normalized); + } +} + +void FilterEngine::setFilterModShape(int shapeIndex, LFOShape& current, LFO* lfos) +{ + LFOShape newShape = static_cast(shapeIndex); + if (newShape != current) { + current = newShape; + for (int i = 0; i < 2; i++) + lfos[i].setShape(current); + } +} + +void FilterEngine::setLowCutFilter(const Parameters& params) +{ + setFilterFreq(params.lowCutFreq(), m_lowCutBase); + setFilterQ(params.lowCutQ(), m_lowCutQ, m_lowCut); + setFilterModRate(params.lowCutModRate(), m_lowCutModRate, m_lowCutLfos); + setFilterModDepth(params.lowCutModDepth(), m_lowCutModDepth); + setFilterModPhase(params.lowCutModPhase(), m_lowCutModPhase, m_lowCutLfos); + setFilterModShape(params.lowCutModShape(), m_lowCutModShape, m_lowCutLfos); + m_lowCutModTempoSync = params.lowCutModTempoSync(); + m_lowCutModNote = params.lowCutModNote(); +} + +void FilterEngine::setHighCutFilter(const Parameters& params) +{ + setFilterFreq(params.highCutFreq(), m_highCutBase); + setFilterQ(params.highCutQ(), m_highCutQ, m_highCut); + setFilterModRate(params.highCutModRate(), m_highCutModRate, m_highCutLfos); + setFilterModDepth(params.highCutModDepth(), m_highCutModDepth); + setFilterModPhase(params.highCutModPhase(), m_highCutModPhase, m_highCutLfos); + setFilterModShape(params.highCutModShape(), m_highCutModShape, m_highCutLfos); + m_highCutModTempoSync = params.highCutModTempoSync(); + m_highCutModNote = params.highCutModNote(); +} + +void FilterEngine::update(const Parameters& params) +{ + setLowCutFilter(params); + setHighCutFilter(params); + m_highCutModInvert = params.highCutModInvert(); +} + +void FilterEngine::updateLFOs(const double ppqPosition) noexcept +{ + if (m_lowCutModTempoSync) + { + float phase = getSyncedPhase(m_lowCutModNote, ppqPosition); + m_lowCutLfos[Channel::Left].setPhase(phase); + m_lowCutLfos[Channel::Right].setPhase(phase); + } + if (m_highCutModTempoSync) + { + float phase = getSyncedPhase(m_highCutModNote, ppqPosition); + m_highCutLfos[Channel::Left].setPhase(phase); + m_highCutLfos[Channel::Right].setPhase(phase); + } +} + +void FilterEngine::processSample(const float& inL, const float& inR, float& outL, float& outR) +{ + float lcL = m_lowCutLfos[Channel::Left].process(); + float lcR = m_lowCutLfos[Channel::Right].process(); + float hcL = m_highCutLfos[Channel::Left].process(); + float hcR = m_highCutLfos[Channel::Right].process(); + + hcL = m_highCutModInvert ? -hcL : hcL; + hcR = m_highCutModInvert ? -hcR : hcR; + + m_lowCutLfoValue.store(lcL, std::memory_order_relaxed); + m_highCutLfoValue.store(hcL, std::memory_order_relaxed); + +#if JUCE_DEBUG + if (++m_scopeCounter >= scopeDownsample) { + m_scopeCounter = 0; + m_scopeBuffer.push(lcL, lcR, hcL, hcR); + } +#endif + + m_lowCut.setCutoff(onePoleLowpass(modulatedCutoff(m_lowCutBase, lcL, m_lowCutModDepth), m_lowCutSmoothed[Channel::Left], m_cutoffSmoothCoeff), Channel::Left); + m_lowCut.setCutoff(onePoleLowpass(modulatedCutoff(m_lowCutBase, lcR, m_lowCutModDepth), m_lowCutSmoothed[Channel::Right], m_cutoffSmoothCoeff), Channel::Right); + m_highCut.setCutoff(onePoleLowpass(modulatedCutoff(m_highCutBase, hcL, m_highCutModDepth), m_highCutSmoothed[Channel::Left], m_cutoffSmoothCoeff), Channel::Left); + m_highCut.setCutoff(onePoleLowpass(modulatedCutoff(m_highCutBase, hcR, m_highCutModDepth), m_highCutSmoothed[Channel::Right], m_cutoffSmoothCoeff), Channel::Right); + + float lowL, lowR; + m_lowCut.processSample(inL, inR, lowL, lowR); + m_highCut.processSample(lowL, lowR, outL, outR); +} diff --git a/Source/FilterEngine.h b/Source/FilterEngine.h new file mode 100644 index 0000000..7597fcc --- /dev/null +++ b/Source/FilterEngine.h @@ -0,0 +1,85 @@ +#pragma once +#include +#include "Parameters.h" +#include "LFO.h" +#include "StereoFilter.h" +#include "Types.h" +#include "DSP.h" +#if JUCE_DEBUG +#include "LFOScopeBuffer.h" +#endif + +class FilterEngine +{ +public: + FilterEngine(); + ~FilterEngine() = default; + + void prepareToPlay(double sampleRate, int samplesPerBlock) noexcept; + void reset() noexcept; + + void update(const Parameters& params); + void processSample(const float& inL, const float& inR, float& outL, float& outR); + + void updateLFOs(const double ppqPosition) noexcept; + + std::atomic& getLowCutLfoValue() { return m_lowCutLfoValue; } + std::atomic& getHighCutLfoValue() { return m_highCutLfoValue; } + +#if JUCE_DEBUG + LFOScopeBuffer m_scopeBuffer; +#endif + +private: + StereoFilter m_lowCut{ FilterType::highpass }; + StereoFilter m_highCut{ FilterType::lowpass }; + juce::Random m_random; + + LFO m_lowCutLfos[2]; + LFO m_highCutLfos[2]; + + float m_lowCutBase = -1.0f; + float m_lowCutQ = -1.0f; + float m_lowCutModDepth = 0.0f; + float m_lowCutModRate = 0.0f; + float m_lowCutModPhase = 0.0f; + LFOShape m_lowCutModShape = LFOShape::Sine; + bool m_lowCutModTempoSync = false; + int m_lowCutModNote = 0; + + float m_highCutBase = -1.0f; + float m_highCutQ = -1.0f; + float m_highCutModDepth = 0.0f; + float m_highCutModRate = 0.0f; + float m_highCutModPhase = 0.0f; + LFOShape m_highCutModShape = LFOShape::Sine; + bool m_highCutModTempoSync = false; + int m_highCutModNote = 0; + + bool m_highCutModInvert = false; + + std::atomic m_lowCutLfoValue { 0.0f }; + std::atomic m_highCutLfoValue { 0.0f }; + + float m_lowCutSmoothed[2] = { Parameters::minFilterFreq, Parameters::minFilterFreq }; + float m_highCutSmoothed[2] = { Parameters::maxFilterFreq, Parameters::maxFilterFreq }; + float m_cutoffSmoothCoeff = 0.0f; + +#if JUCE_DEBUG + static constexpr int scopeDownsample = 64; + int m_scopeCounter = 0; +#endif + + void setLowCutFilter(const Parameters& params); + void setHighCutFilter(const Parameters& params); + + float modulatedCutoff(float base, float lfoValue, float modDepth); + void setFilterFreq(float freq, float& current); + void setFilterQ(float q, float& current, StereoFilter& filter); + void setFilterModDepth(float depth, float& current); + void setFilterModRate(float rate, float& current, LFO* lfos); + void setFilterModPhase(float phase, float& current, LFO* lfos); + void setFilterModShape(int shapeIndex, LFOShape& current, LFO* lfos); + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(FilterEngine) +}; diff --git a/Source/LFO.cpp b/Source/LFO.cpp index d63d220..e577899 100644 --- a/Source/LFO.cpp +++ b/Source/LFO.cpp @@ -29,6 +29,11 @@ void LFO::setShape(LFOShape shape) { m_shape = shape; } +void LFO::setPhase(float phase) { + m_phase = std::fmod(phase, 1.0f); + m_lastOffsetPhase = std::fmod(m_phase + m_offset, 1.0f); +} + void LFO::setIncrement() { if (m_sampleRate > 0.0f) { m_increment = m_frequency / m_sampleRate; @@ -40,12 +45,22 @@ void LFO::setIncrement() { float LFO::process() { m_phase += m_increment; m_phase = std::fmod(m_phase, 1.0f); + m_nextSample = (m_phase < m_lastPhase) ? m_random.nextFloat() * 2.0f - 1.0f : m_nextSample; + m_lastPhase = m_phase; float offsetPhase = std::fmod(m_phase + m_offset, 1.0f); switch (m_shape) { case (LFOShape::Sine): return processSine(offsetPhase); case (LFOShape::Triangle): return processTriangle(offsetPhase); + case (LFOShape::Square): + return processSquare(offsetPhase); + case (LFOShape::SawDown): + return processSawDown(offsetPhase); + case (LFOShape::SawUp): + return processSawUp(offsetPhase); + case (LFOShape::SampleAndHold): + return processSampleAndHold(offsetPhase); default: return 0.0f; } @@ -57,4 +72,22 @@ float LFO::processSine(float phase) { float LFO::processTriangle(float phase) { return 1.0f - 4.0f * std::abs(phase - 0.5f); +} + +float LFO::processSquare(float phase) { + return (phase < 0.5f) ? 1.0f : -1.0f; +} + +float LFO::processSawUp(float phase) { + return phase * 2.0f - 1.0f; +} + +float LFO::processSawDown(float phase) { + return phase * -2.0f + 1.0f; +} + +float LFO::processSampleAndHold(float phase) { + float out = (phase < m_lastOffsetPhase) ? m_currentSample = m_nextSample : m_currentSample; + m_lastOffsetPhase = phase; + return out; } \ No newline at end of file diff --git a/Source/LFO.h b/Source/LFO.h index 9e91150..e827f5c 100644 --- a/Source/LFO.h +++ b/Source/LFO.h @@ -1,7 +1,7 @@ #pragma once #include +#include "Types.h" -enum class LFOShape { Sine, Triangle }; class LFO { public: LFO(float frequency = 1.0f, float offset = 0.0f, LFOShape shape = LFOShape::Sine); @@ -12,10 +12,13 @@ class LFO { void setFrequency(float frequency); void setOffset(float offset); void setShape(LFOShape shape); + void setPhase(float phase); + void setSeed(int seed) { m_random.setSeed(seed); } float process(); private: - LFOShape m_shape = LFOShape::Sine; + LFOShape m_shape; + juce::Random m_random; float m_sampleRate = 0.0f; @@ -23,8 +26,17 @@ class LFO { float m_phase = 0.0f; float m_offset = 0.0f; float m_increment = 0.0f; + float m_lastPhase = 0.0f; + float m_lastOffsetPhase = 0.0f; + float m_currentSample = 0.0f; + float m_nextSample = 0.0f; + void setIncrement(); float processSine(float phase); float processTriangle(float phase); + float processSquare(float phase); + float processSawUp(float phase); + float processSawDown(float phase); + float processSampleAndHold(float phase); }; \ No newline at end of file diff --git a/Source/LFOIndicator.h b/Source/LFOIndicator.h new file mode 100644 index 0000000..c84a6fd --- /dev/null +++ b/Source/LFOIndicator.h @@ -0,0 +1,35 @@ +#pragma once +#include + +class LFOIndicator : public juce::Component, private juce::Timer +{ +public: + LFOIndicator(std::atomic& value) : m_value(value) + { + startTimerHz(60); + } + + ~LFOIndicator() override { stopTimer(); } + + void paint(juce::Graphics& g) override + { + auto bounds = getLocalBounds().toFloat().reduced(1.0f); + float v = m_value.load(); + juce::Colour fill; + if (v >= 0.0f) + fill = juce::Colours::black.interpolatedWith(juce::Colours::green, v); + else + fill = juce::Colours::black.interpolatedWith(juce::Colours::red, -v); + + g.setColour(juce::Colours::darkgrey.darker(0.5f)); + g.drawEllipse(bounds, 1.0f); + g.setColour(fill); + g.fillEllipse(bounds.reduced(1.0f)); + } + +private: + std::atomic& m_value; + void timerCallback() override { repaint(); } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(LFOIndicator) +}; diff --git a/Source/LFOScope.cpp b/Source/LFOScope.cpp new file mode 100644 index 0000000..24c472e --- /dev/null +++ b/Source/LFOScope.cpp @@ -0,0 +1,71 @@ +#include "LFOScope.h" + +LFOScope::LFOScope(LFOScopeBuffer& buffer) : m_buffer(buffer) +{ + startTimerHz(s_refreshRate); +} + +LFOScope::~LFOScope() +{ + stopTimer(); +} + +void LFOScope::resized() {} + +void LFOScope::paint(juce::Graphics& g) +{ + auto bounds = getLocalBounds(); + int w = bounds.getWidth(); + int h = bounds.getHeight(); + + g.fillAll(juce::Colour(0xff1a1a1a)); + + g.setColour(juce::Colour(0xff333333)); + g.drawHorizontalLine(h / 2, 0.0f, (float)w); + g.drawHorizontalLine(h / 4, 0.0f, (float)w); + g.drawHorizontalLine(3 * h / 4, 0.0f, (float)w); + g.drawRect(bounds); + + int numSamples = std::min(w, LFOScopeBuffer::size); + int writeIdx = m_buffer.writeIndex.load(std::memory_order_acquire); + + const juce::Colour colours[LFOScopeBuffer::numChannels] = { + juce::Colour(0xff4fc3f7), // lowcut L — light blue + juce::Colour(0xff0288d1), // lowcut R — dark blue + juce::Colour(0xffffb74d), // highcut L — amber + juce::Colour(0xfff57c00), // highcut R — dark amber + }; + + const char* labels[LFOScopeBuffer::numChannels] = { + "LC L", "LC R", "HC L", "HC R" + }; + + for (int ch = 0; ch < LFOScopeBuffer::numChannels; ++ch) + drawChannel(g, ch, colours[ch], writeIdx, numSamples); + + g.setFont(11.0f); + for (int ch = 0; ch < LFOScopeBuffer::numChannels; ++ch) { + g.setColour(colours[ch]); + g.drawText(labels[ch], 4 + ch * 48, 4, 44, 14, juce::Justification::left); + } +} + +void LFOScope::drawChannel(juce::Graphics& g, int channel, juce::Colour colour, int writeIdx, int numSamples) +{ + int h = getHeight(); + juce::Path path; + + for (int x = 0; x < numSamples; ++x) { + int bufIdx = (writeIdx - numSamples + x + LFOScopeBuffer::size) & (LFOScopeBuffer::size - 1); + float value = m_buffer.data[channel][bufIdx]; + float y = juce::jmap(value, -1.0f, 1.0f, (float)(h - 1), 0.0f); + + if (x == 0) + path.startNewSubPath(0.0f, y); + else + path.lineTo((float)x, y); + } + + g.setColour(colour); + g.strokePath(path, juce::PathStrokeType(1.5f)); +} diff --git a/Source/LFOScope.h b/Source/LFOScope.h new file mode 100644 index 0000000..bc239e7 --- /dev/null +++ b/Source/LFOScope.h @@ -0,0 +1,23 @@ +#pragma once +#include +#include "LFOScopeBuffer.h" + +class LFOScope : public juce::Component, private juce::Timer +{ +public: + LFOScope(LFOScopeBuffer& buffer); + ~LFOScope() override; + + void paint(juce::Graphics&) override; + void resized() override; + +private: + LFOScopeBuffer& m_buffer; + + static constexpr int s_refreshRate = 30; + + void timerCallback() override { repaint(); } + void drawChannel(juce::Graphics& g, int channel, juce::Colour colour, int writeIdx, int numSamples); + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(LFOScope) +}; diff --git a/Source/LFOScopeBuffer.h b/Source/LFOScopeBuffer.h new file mode 100644 index 0000000..3002e03 --- /dev/null +++ b/Source/LFOScopeBuffer.h @@ -0,0 +1,19 @@ +#pragma once +#include + +struct LFOScopeBuffer { + static constexpr int numChannels = 4; + static constexpr int size = 4096; + + float data[numChannels][size] = {}; + std::atomic writeIndex { 0 }; + + void push(float lcL, float lcR, float hcL, float hcR) noexcept { + int idx = writeIndex.load(std::memory_order_relaxed); + data[0][idx] = lcL; + data[1][idx] = lcR; + data[2][idx] = hcL; + data[3][idx] = hcR; + writeIndex.store((idx + 1) & (size - 1), std::memory_order_release); + } +}; diff --git a/Source/Parameters.cpp b/Source/Parameters.cpp index 3f4c9e9..984b92a 100644 --- a/Source/Parameters.cpp +++ b/Source/Parameters.cpp @@ -33,7 +33,7 @@ static juce::String stringFromPercent(float value, int) static juce::String stringFromHertz(float value, int) { - if (value < 10) { + if (value < 20) { return juce::String(value, 2) + " Hz"; } if (value < 1000.0f) { @@ -47,6 +47,11 @@ static juce::String stringFromHertz(float value, int) } } +static juce::String stringFromDegrees(float value, int) +{ + return juce::String(int(value)) + "°"; +} + static float millisecondsFromString(const juce::String& text) { float value = text.getFloatValue(); @@ -68,6 +73,11 @@ static float hzFromString(const juce::String& text) return value; } +static float degreesFromString(const juce::String& text) +{ + return text.getFloatValue(); +} + const juce::StringArray Parameters::delayModes = { "Stereo", "Cross Fb", @@ -75,7 +85,7 @@ const juce::StringArray Parameters::delayModes = { "Ping Pong RL", }; -const juce::StringArray Parameters::noteLengths = { +const juce::StringArray Parameters::delayNoteLengths = { "1/32", "1/16 trip", "1/32 dot", @@ -94,6 +104,33 @@ const juce::StringArray Parameters::noteLengths = { "1/1", }; +const juce::StringArray Parameters::modNoteLengths = { + "1/8", + "1/8 dot", + "1/4", + "1/4 dot", + "1/2", + "1/2 dot", + "1/1", + "1/1 dot", + "2/1", + "3/1", + "4/1", + "5/1", + "6/1", + "7/1", + "8/1", +}; + +const juce::StringArray Parameters::lfoShapes = { + "SIN", + "TRI", + "SQR", + "SW+", + "SW-", + "S&H", +}; + Parameters::Parameters(juce::AudioProcessorValueTreeState& apvts) { @@ -112,8 +149,21 @@ Parameters::Parameters(juce::AudioProcessorValueTreeState& apvts) castParameter(apvts, chorusIntensityParamID, m_chorusIntensityParam); castParameter(apvts, lowCutFreqParamID, m_lowCutFreqParam); castParameter(apvts, lowCutQParamID, m_lowCutQParam); + castParameter(apvts, lowCutModRateParamID, m_lowCutModRateParam); + castParameter(apvts, lowCutModDepthParamID, m_lowCutModDepthParam); + castParameter(apvts, lowCutModPhaseParamID, m_lowCutModPhaseParam); + castParameter(apvts, lowCutModTempoSyncParamID, m_lowCutModTempoSyncParam); + castParameter(apvts, lowCutModNoteParamID, m_lowCutModNoteParam); + castParameter(apvts, lowCutModShapeParamID, m_lowCutModShapeParam); castParameter(apvts, highCutFreqParamID, m_highCutFreqParam); castParameter(apvts, highCutQParamID, m_highCutQParam); + castParameter(apvts, highCutModRateParamID, m_highCutModRateParam); + castParameter(apvts, highCutModDepthParamID, m_highCutModDepthParam); + castParameter(apvts, highCutModPhaseParamID, m_highCutModPhaseParam); + castParameter(apvts, highCutModTempoSyncParamID, m_highCutModTempoSyncParam); + castParameter(apvts, highCutModNoteParamID, m_highCutModNoteParam); + castParameter(apvts, highCutModShapeParamID, m_highCutModShapeParam); + castParameter(apvts, highCutModInvertParamID, m_highCutModInvertParam); castParameter(apvts, chorusModRateParamID, m_chorusModRateParam); castParameter(apvts, chorusModDepthParamID, m_chorusModDepthParam); } @@ -148,13 +198,13 @@ juce::AudioProcessorValueTreeState::ParameterLayout Parameters::createParameterL layout.add(std::make_unique( delayNoteLParamID, "Delay Note L", - noteLengths, + delayNoteLengths, 9 )); layout.add(std::make_unique( delayNoteRParamID, "Delay Note R", - noteLengths, + delayNoteLengths, 9 )); layout.add(std::make_unique( @@ -223,6 +273,47 @@ juce::AudioProcessorValueTreeState::ParameterLayout Parameters::createParameterL juce::NormalisableRange(minFilterQ, maxFilterQ, 0.01f), defaultFilterQ )); + layout.add(std::make_unique( + lowCutModRateParamID.getParamID(), + "Low Cut Mod Rate", + juce::NormalisableRange(0.01f, 8.0f, 0.001f, 0.3f), + 0.05f, + juce::AudioParameterFloatAttributes() + .withStringFromValueFunction(stringFromHertz) + .withValueFromStringFunction(hzFromString) + )); + layout.add(std::make_unique( + lowCutModNoteParamID, + "Low Cut Mod Note", + modNoteLengths, + 6 + )); + layout.add(std::make_unique( + lowCutModTempoSyncParamID.getParamID(), + "Low Cut Mod Tempo Sync", + false + )); + layout.add(std::make_unique( + lowCutModDepthParamID.getParamID(), + "Low Cut Mod Depth", + juce::NormalisableRange(0.0f, 2.0f, 0.01f), + 0.0f + )); + layout.add(std::make_unique( + lowCutModPhaseParamID.getParamID(), + "Low Cut Mod Phase", + juce::NormalisableRange(0.0f, 360.0f, 0.1f), + 0.0f, + juce::AudioParameterFloatAttributes() + .withStringFromValueFunction(stringFromDegrees) + .withValueFromStringFunction(degreesFromString) + )); + layout.add(std::make_unique( + lowCutModShapeParamID, + "Low Cut Mod Shape", + lfoShapes, + 0 + )); layout.add(std::make_unique( highCutFreqParamID.getParamID(), "High Cut Frequency", @@ -238,6 +329,52 @@ juce::AudioProcessorValueTreeState::ParameterLayout Parameters::createParameterL juce::NormalisableRange(minFilterQ, maxFilterQ, 0.01f), defaultFilterQ )); + layout.add(std::make_unique( + highCutModRateParamID.getParamID(), + "High Cut Mod Rate", + juce::NormalisableRange(0.01f, 8.0f, 0.001f, 0.3f), + 0.05f, + juce::AudioParameterFloatAttributes() + .withStringFromValueFunction(stringFromHertz) + .withValueFromStringFunction(hzFromString) + )); + layout.add(std::make_unique( + highCutModNoteParamID, + "High Cut Mod Note", + modNoteLengths, + 6 + )); + layout.add(std::make_unique( + highCutModShapeParamID, + "High Cut Mod Shape", + lfoShapes, + 0 + )); + layout.add(std::make_unique( + highCutModTempoSyncParamID.getParamID(), + "High Cut Mod Tempo Sync", + false + )); + layout.add(std::make_unique( + highCutModDepthParamID.getParamID(), + "High Cut Mod Depth", + juce::NormalisableRange(0.0f, 2.0f, 0.01f), + 0.0f + )); + layout.add(std::make_unique( + highCutModPhaseParamID.getParamID(), + "High Cut Mod Phase", + juce::NormalisableRange(0.0f, 360.0f, 0.1f), + 0.0f, + juce::AudioParameterFloatAttributes() + .withStringFromValueFunction(stringFromDegrees) + .withValueFromStringFunction(degreesFromString) + )); + layout.add(std::make_unique( + highCutModInvertParamID.getParamID(), + "High Cut Mod Invert", + true + )); layout.add(std::make_unique( chorusIntensityParamID.getParamID(), "Chorus Intensity", @@ -270,8 +407,14 @@ void Parameters::prepareToPlay(double sampleRate) noexcept m_chorusIntensitySmoother.reset(sampleRate, duration); m_lowCutFreqSmoother.reset(sampleRate, duration); m_lowCutQSmoother.reset(sampleRate, duration); + m_lowCutModRateSmoother.reset(sampleRate, duration); + m_lowCutModDepthSmoother.reset(sampleRate, duration); + m_lowCutModPhaseSmoother.reset(sampleRate, duration); m_highCutFreqSmoother.reset(sampleRate, duration); m_highCutQSmoother.reset(sampleRate, duration); + m_highCutModRateSmoother.reset(sampleRate, duration); + m_highCutModDepthSmoother.reset(sampleRate, duration); + m_highCutModPhaseSmoother.reset(sampleRate, duration); m_chorusModRateSmoother.reset(sampleRate, duration); m_chorusModDepthSmoother.reset(sampleRate, duration); } @@ -288,8 +431,14 @@ void Parameters::reset() noexcept m_chorusIntensity = 0.0f; m_lowCutFreq = 20.0f; m_lowCutQ = defaultFilterQ; + m_lowCutModRate = 0.0f; + m_lowCutModDepth = 0.0f; + m_lowCutModPhase = 0.0f; m_highCutFreq = 20000.0f; m_highCutQ = defaultFilterQ; + m_highCutModRate = 0.0f; + m_highCutModDepth = 0.0f; + m_highCutModPhase = 0.0f; m_chorusModRate = defaultChorusModRate; m_chorusModDepth = defaultChorusModDepth; m_gainSmoother.setCurrentAndTargetValue(juce::Decibels::decibelsToGain(m_gainParam->get())); @@ -299,8 +448,14 @@ void Parameters::reset() noexcept m_chorusIntensitySmoother.setCurrentAndTargetValue(m_chorusIntensityParam->get()); m_lowCutFreqSmoother.setCurrentAndTargetValue(m_lowCutFreqParam->get()); m_lowCutQSmoother.setCurrentAndTargetValue(m_lowCutQParam->get()); + m_lowCutModRateSmoother.setCurrentAndTargetValue(m_lowCutModRateParam->get()); + m_lowCutModDepthSmoother.setCurrentAndTargetValue(m_lowCutModDepthParam->get()); + m_lowCutModPhaseSmoother.setCurrentAndTargetValue(m_lowCutModPhaseParam->get()); m_highCutFreqSmoother.setCurrentAndTargetValue(m_highCutFreqParam->get()); m_highCutQSmoother.setCurrentAndTargetValue(m_highCutQParam->get()); + m_highCutModRateSmoother.setCurrentAndTargetValue(m_highCutModRateParam->get()); + m_highCutModDepthSmoother.setCurrentAndTargetValue(m_highCutModDepthParam->get()); + m_highCutModPhaseSmoother.setCurrentAndTargetValue(m_highCutModPhaseParam->get()); m_chorusModRateSmoother.setCurrentAndTargetValue(m_chorusModRateParam->get()); m_chorusModDepthSmoother.setCurrentAndTargetValue(m_chorusModDepthParam->get()); } @@ -315,8 +470,14 @@ void Parameters::update(const Tempo& tempo) noexcept m_chorusIntensitySmoother.setTargetValue(m_chorusIntensityParam->get()); m_lowCutFreqSmoother.setTargetValue(m_lowCutFreqParam->get()); m_lowCutQSmoother.setTargetValue(m_lowCutQParam->get()); + m_lowCutModRateSmoother.setTargetValue(m_lowCutModRateParam->get()); + m_lowCutModDepthSmoother.setTargetValue(m_lowCutModDepthParam->get()); + m_lowCutModPhaseSmoother.setTargetValue(m_lowCutModPhaseParam->get()); m_highCutFreqSmoother.setTargetValue(m_highCutFreqParam->get()); m_highCutQSmoother.setTargetValue(m_highCutQParam->get()); + m_highCutModRateSmoother.setTargetValue(m_highCutModRateParam->get()); + m_highCutModDepthSmoother.setTargetValue(m_highCutModDepthParam->get()); + m_highCutModPhaseSmoother.setTargetValue(m_highCutModPhaseParam->get()); m_chorusModRateSmoother.setTargetValue(m_chorusModRateParam->get()); m_chorusModDepthSmoother.setTargetValue(m_chorusModDepthParam->get()); m_tempoSyncL = m_tempoSyncLParam->get(); @@ -324,6 +485,21 @@ void Parameters::update(const Tempo& tempo) noexcept m_delayNoteL = m_delayNoteLParam->getIndex(); m_delayNoteR = m_delayNoteRParam->getIndex(); m_delayMode = m_delayModeParam->getIndex(); + m_lowCutModTempoSync = m_lowCutModTempoSyncParam->get(); + m_lowCutModNote = m_lowCutModNoteParam->getIndex(); + m_highCutModTempoSync = m_highCutModTempoSyncParam->get(); + m_highCutModNote = m_highCutModNoteParam->getIndex(); + m_highCutModInvert = m_highCutModInvertParam->get(); + m_lowCutModShape = m_lowCutModShapeParam->getIndex(); + m_highCutModShape = m_highCutModShapeParam->getIndex(); + if (m_lowCutModTempoSync) { + float syncedHz = float(tempo.noteLengthToHz(m_lowCutModNote)); + m_lowCutModRateParam->setValueNotifyingHost(m_lowCutModRateParam->convertTo0to1(syncedHz)); + } + if (m_highCutModTempoSync) { + float syncedHz = float(tempo.noteLengthToHz(m_highCutModNote)); + m_highCutModRateParam->setValueNotifyingHost(m_highCutModRateParam->convertTo0to1(syncedHz)); + } if (m_tempoSyncL) { float syncedMs = juce::jmin(float(tempo.noteLengthToMs(m_delayNoteL)), maxDelayTime); m_delayTimeLParam->setValueNotifyingHost(m_delayTimeLParam->convertTo0to1(syncedMs)); @@ -346,8 +522,14 @@ void Parameters::smoothen() noexcept m_chorusIntensity = m_chorusIntensitySmoother.getNextValue(); m_lowCutFreq = m_lowCutFreqSmoother.getNextValue(); m_lowCutQ = m_lowCutQSmoother.getNextValue(); + m_lowCutModRate = m_lowCutModRateSmoother.getNextValue(); + m_lowCutModDepth = m_lowCutModDepthSmoother.getNextValue(); + m_lowCutModPhase = m_lowCutModPhaseSmoother.getNextValue(); m_highCutFreq = m_highCutFreqSmoother.getNextValue(); m_highCutQ = m_highCutQSmoother.getNextValue(); + m_highCutModRate = m_highCutModRateSmoother.getNextValue(); + m_highCutModDepth = m_highCutModDepthSmoother.getNextValue(); + m_highCutModPhase = m_highCutModPhaseSmoother.getNextValue(); m_chorusModRate = m_chorusModRateSmoother.getNextValue(); m_chorusModDepth = m_chorusModDepthSmoother.getNextValue(); } \ No newline at end of file diff --git a/Source/Parameters.h b/Source/Parameters.h index b0b40e1..f9dfbc4 100644 --- a/Source/Parameters.h +++ b/Source/Parameters.h @@ -16,8 +16,21 @@ const juce::ParameterID stereoParamID{ "stereo", 1 }; const juce::ParameterID offsetParamID{ "offset", 1 }; const juce::ParameterID lowCutFreqParamID{ "lowCutFreq", 1 }; const juce::ParameterID lowCutQParamID{ "lowCutQ", 1 }; +const juce::ParameterID lowCutModRateParamID{ "lowCutModRate", 1 }; +const juce::ParameterID lowCutModDepthParamID{ "lowCutModDepth", 1 }; +const juce::ParameterID lowCutModPhaseParamID{ "lowCutModPhase", 1 }; +const juce::ParameterID lowCutModTempoSyncParamID{ "lowCutModTempoSync", 1 }; +const juce::ParameterID lowCutModNoteParamID{ "lowCutModNote", 1 }; +const juce::ParameterID lowCutModShapeParamID{ "lowCutModShape", 1 }; const juce::ParameterID highCutFreqParamID{ "highCutFreq", 1 }; const juce::ParameterID highCutQParamID{ "highCutQ", 1 }; +const juce::ParameterID highCutModRateParamID{ "highCutModRate", 1 }; +const juce::ParameterID highCutModDepthParamID{ "highCutModDepth", 1 }; +const juce::ParameterID highCutModPhaseParamID{ "highCutModPhase", 1 }; +const juce::ParameterID highCutModTempoSyncParamID{ "highCutModTempoSync", 1 }; +const juce::ParameterID highCutModNoteParamID{ "highCutModNote", 1 }; +const juce::ParameterID highCutModShapeParamID{ "highCutModShape", 1 }; +const juce::ParameterID highCutModInvertParamID{ "highCutModInvert", 1 }; const juce::ParameterID chorusIntensityParamID{ "chorusIntensity", 1 }; const juce::ParameterID chorusModRateParamID{ "chorusModRate", 1 }; const juce::ParameterID chorusModDepthParamID{ "chorusModDepth", 1 }; @@ -30,14 +43,18 @@ class Parameters { static constexpr float minDelayTime = 5.0f; static constexpr float maxDelayTime = 5000.0f; static constexpr float defaultDelayTime = 100.0f; - static constexpr float maxFilterQ = 2.0f; + static constexpr float minFilterFreq = 20.0f; + static constexpr float maxFilterFreq = 20000.0f; + static constexpr float maxFilterQ = 1.5f; static constexpr float minFilterQ = 0.1f; static constexpr float defaultFilterQ = 0.707f; static constexpr float defaultChorusModRate = 0.2f; static constexpr float defaultChorusModRate2 = 0.45f; // this is not a passed param but default value for 2nd chorus static constexpr float defaultChorusModDepth = 4.0f; static const juce::StringArray delayModes; - static const juce::StringArray noteLengths; + static const juce::StringArray delayNoteLengths; + static const juce::StringArray modNoteLengths; + static const juce::StringArray lfoShapes; void prepareToPlay(double sampleRate) noexcept; void update(const Tempo& tempo) noexcept; @@ -58,8 +75,21 @@ class Parameters { float offset() const { return m_offset; } float lowCutFreq() const { return m_lowCutFreq; } float lowCutQ() const { return m_lowCutQ; } + float lowCutModRate() const { return m_lowCutModRate; } + float lowCutModDepth() const { return m_lowCutModDepth; } + float lowCutModPhase() const { return m_lowCutModPhase; } + bool lowCutModTempoSync() const { return m_lowCutModTempoSync; } + int lowCutModNote() const { return m_lowCutModNote; } + int lowCutModShape() const { return m_lowCutModShape; } float highCutFreq() const { return m_highCutFreq; } float highCutQ() const { return m_highCutQ; } + float highCutModRate() const { return m_highCutModRate; } + float highCutModDepth() const { return m_highCutModDepth; } + float highCutModPhase() const { return m_highCutModPhase; } + bool highCutModTempoSync() const { return m_highCutModTempoSync; } + int highCutModNote() const { return m_highCutModNote; } + int highCutModShape() const { return m_highCutModShape; } + bool highCutModInvert() const { return m_highCutModInvert; } float chorusIntensity() const { return m_chorusIntensity; } float chorusModRate() const { return m_chorusModRate; } float chorusModDepth() const { return m_chorusModDepth; } @@ -79,8 +109,21 @@ class Parameters { juce::AudioParameterFloat* m_offsetParam; juce::AudioParameterFloat* m_lowCutFreqParam; juce::AudioParameterFloat* m_lowCutQParam; + juce::AudioParameterFloat* m_lowCutModRateParam; + juce::AudioParameterFloat* m_lowCutModDepthParam; + juce::AudioParameterFloat* m_lowCutModPhaseParam; + juce::AudioParameterBool* m_lowCutModTempoSyncParam; + juce::AudioParameterChoice* m_lowCutModNoteParam; + juce::AudioParameterChoice* m_lowCutModShapeParam; juce::AudioParameterFloat* m_highCutFreqParam; juce::AudioParameterFloat* m_highCutQParam; + juce::AudioParameterFloat* m_highCutModRateParam; + juce::AudioParameterFloat* m_highCutModDepthParam; + juce::AudioParameterFloat* m_highCutModPhaseParam; + juce::AudioParameterBool* m_highCutModTempoSyncParam; + juce::AudioParameterChoice* m_highCutModNoteParam; + juce::AudioParameterChoice* m_highCutModShapeParam; + juce::AudioParameterBool* m_highCutModInvertParam; juce::AudioParameterFloat* m_chorusIntensityParam; juce::AudioParameterFloat* m_chorusModRateParam; juce::AudioParameterFloat* m_chorusModDepthParam; @@ -92,17 +135,18 @@ class Parameters { juce::LinearSmoothedValue m_offsetSmoother; juce::LinearSmoothedValue m_lowCutFreqSmoother; juce::LinearSmoothedValue m_lowCutQSmoother; + juce::LinearSmoothedValue m_lowCutModRateSmoother; + juce::LinearSmoothedValue m_lowCutModDepthSmoother; + juce::LinearSmoothedValue m_lowCutModPhaseSmoother; juce::LinearSmoothedValue m_highCutFreqSmoother; juce::LinearSmoothedValue m_highCutQSmoother; + juce::LinearSmoothedValue m_highCutModRateSmoother; + juce::LinearSmoothedValue m_highCutModDepthSmoother; + juce::LinearSmoothedValue m_highCutModPhaseSmoother; juce::LinearSmoothedValue m_chorusIntensitySmoother; juce::LinearSmoothedValue m_chorusModRateSmoother; juce::LinearSmoothedValue m_chorusModDepthSmoother; - float m_targetDelayTimeL = 0.0f; - float m_targetDelayTimeR = 0.0f; - - float m_coeff = 0.0f; - float m_gain = 0.0f; float m_delayTimeL = 0.0f; float m_delayTimeR = 0.0f; @@ -117,8 +161,21 @@ class Parameters { float m_offset = 0.0f; float m_lowCutFreq = 20.0f; float m_lowCutQ = defaultFilterQ; + float m_lowCutModRate = 0.0f; + float m_lowCutModDepth = 0.0f; + float m_lowCutModPhase = 0.0f; + bool m_lowCutModTempoSync = false; + int m_lowCutModNote = 0; + int m_lowCutModShape = 0; float m_highCutFreq = 20000.0f; float m_highCutQ = defaultFilterQ; + float m_highCutModRate = 0.0f; + float m_highCutModDepth = 0.0f; + float m_highCutModPhase = 0.0f; + bool m_highCutModTempoSync = false; + int m_highCutModNote = 0; + int m_highCutModShape = 0; + bool m_highCutModInvert = false; float m_chorusIntensity = 0.0f; float m_chorusModRate = defaultChorusModRate; float m_chorusModDepth = defaultChorusModDepth; diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index a8bd991..3991b74 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -17,21 +17,31 @@ ViiveAudioProcessorEditor::ViiveAudioProcessorEditor(ViiveAudioProcessor& p) m_delayGroup.addAndMakeVisible(m_offsetKnob); m_delayGroup.addAndMakeVisible(m_tempoSyncLButton); m_delayGroup.addAndMakeVisible(m_tempoSyncRButton); - addAndMakeVisible(m_delayModeLabel); - m_delayModeLabel.setText("Delay Mode:", juce::NotificationType::dontSendNotification); - m_delayModeLabel.setEditable(false); - m_delayModeLabel.setSize(80, 25); addAndMakeVisible(m_modeSelector); - m_modeSelector.addItemList(Parameters::delayModes, 1); - m_modeSelector.setEditableText(false); - m_modeSelector.setSize(130, 25); - m_modeSelector.onChange = [this] { modeSelectorChanged();}; - auto* param = dynamic_cast(m_audioProcessor.apvts.getParameter(delayModeParamID.getParamID())); - m_modeSelector.setSelectedId(param->getIndex() + 1, juce::dontSendNotification); addAndMakeVisible(m_delayGroup); - m_filterGroup.setText("Filter"); + m_filterModGroup.setText("Modulation"); + m_filterModGroup.setTextLabelPosition(juce::Justification::horizontallyCentred); + m_filterModGroup.setColour(juce::GroupComponent::outlineColourId, juce::Colours::transparentBlack); + m_filterModGroup.addAndMakeVisible(m_lowCutModRateKnob); + m_filterModGroup.addChildComponent(m_lowCutModNoteKnob); + m_filterModGroup.addAndMakeVisible(m_lowCutModDepthKnob); + m_filterModGroup.addAndMakeVisible(m_lowCutModPhaseKnob); + m_filterModGroup.addAndMakeVisible(m_highCutModRateKnob); + m_filterModGroup.addChildComponent(m_highCutModNoteKnob); + m_filterModGroup.addAndMakeVisible(m_highCutModDepthKnob); + m_filterModGroup.addAndMakeVisible(m_highCutModPhaseKnob); + m_filterModGroup.addAndMakeVisible(m_lowCutModTempoSyncButton); + m_filterModGroup.addAndMakeVisible(m_highCutModTempoSyncButton); + m_filterModGroup.addAndMakeVisible(m_lowCutModShapeSelector); + m_filterModGroup.addAndMakeVisible(m_highCutModShapeSelector); + m_filterModGroup.addAndMakeVisible(m_lowCutLfoIndicator); + m_filterModGroup.addAndMakeVisible(m_highCutLfoIndicator); + m_filterModGroup.addAndMakeVisible(m_highCutModInvertButton); + + m_filterGroup.setText("Filter"); m_filterGroup.setTextLabelPosition(juce::Justification::horizontallyCentred); + m_filterGroup.addAndMakeVisible(m_filterModGroup); m_filterGroup.addAndMakeVisible(m_lowCutFreqKnob); m_filterGroup.addAndMakeVisible(m_lowCutQKnob); m_filterGroup.addAndMakeVisible(m_highCutFreqKnob); @@ -54,20 +64,35 @@ ViiveAudioProcessorEditor::ViiveAudioProcessorEditor(ViiveAudioProcessor& p) addAndMakeVisible(m_meter); +#if JUCE_DEBUG + m_lfoScope = std::make_unique(p.getLFOScopeBuffer()); + addAndMakeVisible(*m_lfoScope); +#endif + bool syncL = m_audioProcessor.apvts.getParameter(tempoSyncLParamID.getParamID())->getValue() > 0.5f; bool syncR = m_audioProcessor.apvts.getParameter(tempoSyncRParamID.getParamID())->getValue() > 0.5f; + bool lowCutSync = m_audioProcessor.apvts.getParameter(lowCutModTempoSyncParamID.getParamID())->getValue() > 0.5f; + bool highCutSync = m_audioProcessor.apvts.getParameter(highCutModTempoSyncParamID.getParamID())->getValue() > 0.5f; - updateDelayKnobs(syncL, syncR); + updateSyncedKnobs(syncL, syncR, lowCutSync, highCutSync); m_audioProcessor.apvts.addParameterListener(tempoSyncLParamID.getParamID(), this); m_audioProcessor.apvts.addParameterListener(tempoSyncRParamID.getParamID(), this); - setSize (770, 370); + m_audioProcessor.apvts.addParameterListener(lowCutModTempoSyncParamID.getParamID(), this); + m_audioProcessor.apvts.addParameterListener(highCutModTempoSyncParamID.getParamID(), this); +#if JUCE_DEBUG + setSize(770, 670); +#else + setSize(770, 490); +#endif } ViiveAudioProcessorEditor::~ViiveAudioProcessorEditor() { m_audioProcessor.apvts.removeParameterListener(tempoSyncLParamID.getParamID(), this); m_audioProcessor.apvts.removeParameterListener(tempoSyncRParamID.getParamID(), this); + m_audioProcessor.apvts.removeParameterListener(lowCutModTempoSyncParamID.getParamID(), this); + m_audioProcessor.apvts.removeParameterListener(highCutModTempoSyncParamID.getParamID(), this); } //============================================================================== @@ -77,20 +102,26 @@ void ViiveAudioProcessorEditor::paint (juce::Graphics& g) g.fillAll(juce::Colours::darkgrey); g.setColour(juce::Colours::white); g.setFont(12.0f); - g.drawText(JucePlugin_VersionString, getLocalBounds().reduced(5), juce::Justification::topRight); + g.drawText("viive 0.2.beta-1", getLocalBounds().reduced(5), juce::Justification::topRight); + +// g.drawText("viive " JucePlugin_VersionString, getLocalBounds().reduced(5), juce::Justification::topRight); } void ViiveAudioProcessorEditor::resized() { auto bounds = getLocalBounds(); int y = 10; +#ifdef JUCE_DEBUG + int height = bounds.getHeight() - 30 - 180; +#else int height = bounds.getHeight() - 20; - - m_chorusGroup.setBounds(bounds.getWidth() - 360, y + 35, 300, height / 2 - 10); - m_delayGroup.setBounds(10, y + 35, m_chorusGroup.getX() - 20, height / 2 - 10); - m_outputGroup.setBounds(bounds.getWidth() - 360, height / 2 + 45, 300, height / 2 - 30); - m_filterGroup.setBounds(10, height / 2 + 45, m_outputGroup.getX() - 20, height / 2 - 30); - +#endif + m_chorusGroup.setBounds(bounds.getWidth() - 360, y + 35, 300, 165); + m_delayGroup.setBounds(10, y + 35, m_chorusGroup.getX() - 20, 165); + m_outputGroup.setBounds(bounds.getWidth() - 360, m_delayGroup.getBottom() + 10, 300, 155); + m_filterGroup.setBounds(10, m_delayGroup.getBottom() + 10, m_outputGroup.getX() - 20, 265); + m_filterModGroup.setBounds(0, m_lowCutFreqKnob.getBottom() + 30, m_filterGroup.getWidth(), 120); + m_delayTimeLKnob.setTopLeftPosition(20, 20); m_delayTimeRKnob.setTopLeftPosition(m_delayTimeLKnob.getRight() + 20, 20); m_delayNoteLKnob.setTopLeftPosition(m_delayTimeLKnob.getX(), m_delayTimeLKnob.getY()); @@ -99,14 +130,36 @@ void ViiveAudioProcessorEditor::resized() m_tempoSyncRButton.setCentrePosition(m_delayTimeRKnob.getRight() - 35, m_delayTimeRKnob.getBottom() + 15); m_feedbackKnob.setTopLeftPosition(m_delayTimeRKnob.getRight() + 20, 20); - m_delayModeLabel.setTopLeftPosition(10, y); - m_modeSelector.setTopLeftPosition(95, y); + m_modeSelector.setTopLeftPosition(10, y); m_offsetKnob.setTopLeftPosition(m_feedbackKnob.getRight() + 20, 20); m_lowCutFreqKnob.setTopLeftPosition(20, 20); m_lowCutQKnob.setTopLeftPosition(m_lowCutFreqKnob.getRight() + 20, 20); + m_lowCutModRateKnob.setTopLeftPosition(20, 20); + m_lowCutModNoteKnob.setTopLeftPosition(m_lowCutModRateKnob.getX(), m_lowCutModRateKnob.getY()); + m_lowCutModDepthKnob.setTopLeftPosition(m_lowCutModRateKnob.getRight() + 10, 20); + m_lowCutModPhaseKnob.setTopLeftPosition(m_lowCutModDepthKnob.getRight() + 10, 20); + m_lowCutModTempoSyncButton.setTopLeftPosition(20, m_lowCutModRateKnob.getBottom() + 5); + m_lowCutModShapeSelector.setTopLeftPosition(m_lowCutModDepthKnob.getX(), m_lowCutModTempoSyncButton.getY()); + + constexpr int indicatorSize = 8; + m_lowCutLfoIndicator.setSize(indicatorSize, indicatorSize); + m_lowCutLfoIndicator.setCentrePosition(m_lowCutModPhaseKnob.getBounds().getCentreX(), + m_lowCutModPhaseKnob.getBottom() + 15); m_highCutFreqKnob.setTopLeftPosition(m_lowCutQKnob.getRight() + 20, 20); m_highCutQKnob.setTopLeftPosition(m_highCutFreqKnob.getRight() + 20, 20); + m_highCutModPhaseKnob.setTopRightPosition(m_highCutQKnob.getRight(), 20); + m_highCutModDepthKnob.setTopRightPosition(m_highCutModPhaseKnob.getX() - 10, 20); + m_highCutModRateKnob.setTopRightPosition(m_highCutModDepthKnob.getX() - 10, 20); + m_highCutModNoteKnob.setTopLeftPosition(m_highCutModRateKnob.getX(), m_highCutModRateKnob.getY()); + m_highCutModTempoSyncButton.setTopLeftPosition(m_highCutModRateKnob.getX(), m_highCutModRateKnob.getBottom() + 5); + m_highCutModShapeSelector.setTopLeftPosition(m_highCutModDepthKnob.getX(), m_highCutModTempoSyncButton.getY()); + + m_highCutLfoIndicator.setSize(indicatorSize, indicatorSize); + m_highCutLfoIndicator.setCentrePosition(m_highCutModPhaseKnob.getBounds().getCentreX(), + m_highCutModPhaseKnob.getBottom() + 15); + m_highCutModInvertButton.setCentrePosition(m_highCutLfoIndicator.getBounds().getCentreX() + 25, + m_highCutLfoIndicator.getBounds().getCentreY()); m_stereoKnob.setTopLeftPosition(20, 20); m_mixKnob.setTopLeftPosition(m_stereoKnob.getRight() + 20, 20); m_gainKnob.setTopLeftPosition(m_mixKnob.getRight() + 20, 20); @@ -116,29 +169,34 @@ void ViiveAudioProcessorEditor::resized() m_meter.setBounds(m_outputGroup.getRight() + 15, y + 45, 35, height - 45); - +#if JUCE_DEBUG + m_lfoScope->setBounds(10, 490, bounds.getWidth() - 20, 170); +#endif } -void ViiveAudioProcessorEditor::updateDelayKnobs(bool syncLActive, bool syncRActive) +void ViiveAudioProcessorEditor::updateSyncedKnobs(bool syncLActive, bool syncRActive, bool lowCutSyncActive, bool highCutSyncActive) { m_delayTimeLKnob.setVisible(!syncLActive); m_delayNoteLKnob.setVisible(syncLActive); m_delayTimeRKnob.setVisible(!syncRActive); m_delayNoteRKnob.setVisible(syncRActive); + m_lowCutModRateKnob.setVisible(!lowCutSyncActive); + m_lowCutModNoteKnob.setVisible(lowCutSyncActive); + m_highCutModRateKnob.setVisible(!highCutSyncActive); + m_highCutModNoteKnob.setVisible(highCutSyncActive); + DBG("Updated synced knobs: " << (syncLActive ? "Sync L active, " : "Sync L inactive, ") << (syncRActive ? "Sync R active, " : "Sync R inactive, ") << (lowCutSyncActive ? "Low Cut Sync active, " : "Low Cut Sync inactive, ") << (highCutSyncActive ? "High Cut Sync active" : "High Cut Sync inactive")); + } void ViiveAudioProcessorEditor::parameterChanged(const juce::String& paramID, float newValue) { - if (paramID == tempoSyncLParamID.getParamID() || paramID == tempoSyncRParamID.getParamID()) { + if (paramID == tempoSyncLParamID.getParamID() || paramID == tempoSyncRParamID.getParamID() || paramID == lowCutModTempoSyncParamID.getParamID() || paramID == highCutModTempoSyncParamID.getParamID()){ juce::MessageManager::callAsync([this] { bool syncL = m_audioProcessor.apvts.getParameter(tempoSyncLParamID.getParamID())->getValue() > 0.5f; bool syncR = m_audioProcessor.apvts.getParameter(tempoSyncRParamID.getParamID())->getValue() > 0.5f; - updateDelayKnobs(syncL, syncR); + bool lowCutSync = m_audioProcessor.apvts.getParameter(lowCutModTempoSyncParamID.getParamID())->getValue() > 0.5f; + bool highCutSync = m_audioProcessor.apvts.getParameter(highCutModTempoSyncParamID.getParamID())->getValue() > 0.5f; + updateSyncedKnobs(syncL, syncR, lowCutSync, highCutSync); }); } } -void ViiveAudioProcessorEditor::modeSelectorChanged() { - int selectedID = m_modeSelector.getSelectedId(); - auto* param = dynamic_cast(m_audioProcessor.apvts.getParameter(delayModeParamID.getParamID())); - param->setValueNotifyingHost(param->convertTo0to1(selectedID - 1)); -} \ No newline at end of file diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index 508f533..8f83751 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -5,7 +5,12 @@ #include "Parameters.h" #include "RotaryKnob.h" #include "SyncButton.h" +#include "Selector.h" #include "LevelMeter.h" +#include "LFOIndicator.h" +#if JUCE_DEBUG +#include "LFOScope.h" +#endif //============================================================================== /** @@ -36,8 +41,16 @@ class ViiveAudioProcessorEditor : public juce::AudioProcessorEditor, RotaryKnob m_lowCutFreqKnob{ "Low Cut Freq", m_audioProcessor.apvts, lowCutFreqParamID.getParamID() }; RotaryKnob m_lowCutQKnob{ "Low Cut Q", m_audioProcessor.apvts, lowCutQParamID.getParamID() }; + RotaryKnob m_lowCutModRateKnob{ "Rate", m_audioProcessor.apvts, lowCutModRateParamID.getParamID(), RotaryKnob::Size::Small }; + RotaryKnob m_lowCutModNoteKnob{ "Note", m_audioProcessor.apvts, lowCutModNoteParamID.getParamID(), RotaryKnob::Size::Small }; + RotaryKnob m_lowCutModDepthKnob{ "Depth", m_audioProcessor.apvts, lowCutModDepthParamID.getParamID(), RotaryKnob::Size::Small }; + RotaryKnob m_lowCutModPhaseKnob{ "Phase", m_audioProcessor.apvts, lowCutModPhaseParamID.getParamID(), RotaryKnob::Size::Small }; RotaryKnob m_highCutFreqKnob{ "High Cut Freq", m_audioProcessor.apvts, highCutFreqParamID.getParamID() }; RotaryKnob m_highCutQKnob{ "High Cut Q", m_audioProcessor.apvts, highCutQParamID.getParamID() }; + RotaryKnob m_highCutModRateKnob{ "Rate", m_audioProcessor.apvts, highCutModRateParamID.getParamID(), RotaryKnob::Size::Small }; + RotaryKnob m_highCutModNoteKnob{ "Note", m_audioProcessor.apvts, highCutModNoteParamID.getParamID(), RotaryKnob::Size::Small }; + RotaryKnob m_highCutModDepthKnob{ "Depth", m_audioProcessor.apvts, highCutModDepthParamID.getParamID(), RotaryKnob::Size::Small }; + RotaryKnob m_highCutModPhaseKnob{ "Phase", m_audioProcessor.apvts, highCutModPhaseParamID.getParamID(), RotaryKnob::Size::Small }; RotaryKnob m_gainKnob{ "Gain", m_audioProcessor.apvts, gainParamID.getParamID() }; RotaryKnob m_mixKnob{ "Mix", m_audioProcessor.apvts, mixParamID.getParamID() }; @@ -48,17 +61,29 @@ class ViiveAudioProcessorEditor : public juce::AudioProcessorEditor, SyncButton m_tempoSyncLButton{ "Sync", m_audioProcessor.apvts, tempoSyncLParamID.getParamID() }; SyncButton m_tempoSyncRButton{ "Sync", m_audioProcessor.apvts, tempoSyncRParamID.getParamID() }; + SyncButton m_lowCutModTempoSyncButton{ "Sync", m_audioProcessor.apvts, lowCutModTempoSyncParamID.getParamID() }; + SyncButton m_highCutModTempoSyncButton{ "Sync", m_audioProcessor.apvts, highCutModTempoSyncParamID.getParamID() }; - juce::Label m_delayModeLabel; - juce::ComboBox m_modeSelector; + Selector m_modeSelector{ "Delay Mode:", m_audioProcessor.apvts, delayModeParamID.getParamID(), Parameters::delayModes, 215, 0.0f, 85, 25 }; + Selector m_lowCutModShapeSelector{ "", m_audioProcessor.apvts, lowCutModShapeParamID.getParamID(), Parameters::lfoShapes, 60, 12.0f }; + Selector m_highCutModShapeSelector{ "", m_audioProcessor.apvts, highCutModShapeParamID.getParamID(), Parameters::lfoShapes, 60, 12.0f }; + + LFOIndicator m_lowCutLfoIndicator { m_audioProcessor.getLowCutLfoValue() }; + LFOIndicator m_highCutLfoIndicator { m_audioProcessor.getHighCutLfoValue() }; + SyncButton m_highCutModInvertButton{ juce::String::fromUTF8("\xC3\x98"), m_audioProcessor.apvts, highCutModInvertParamID.getParamID(), 20}; + + juce::Label m_modulationLabel; juce::HashMap m_linkedParams; - juce::GroupComponent m_delayGroup, m_filterGroup, m_outputGroup, m_chorusGroup; + juce::GroupComponent m_delayGroup, m_filterGroup, m_outputGroup, m_chorusGroup, m_filterModGroup; + +#if JUCE_DEBUG + std::unique_ptr m_lfoScope; +#endif virtual void parameterChanged(const juce::String& paramID, float newValue) override; - void modeSelectorChanged(); - void updateDelayKnobs(bool syncLActive, bool syncRActive); + void updateSyncedKnobs(bool syncLActive, bool syncRActive, bool lowCutSyncActive, bool highCutSyncActive); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ViiveAudioProcessorEditor) }; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 9834a85..42022e9 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -116,6 +116,8 @@ void ViiveAudioProcessor::processBlock(juce::AudioBuffer& buffer, juce::M m_tempo.update(getPlayHead()); m_params.update(m_tempo); + if (m_tempo.getIsPlaying()) + m_delayEngine.updateSyncedModulators(m_tempo.getPpqPosition()); m_delayEngine.setDelayMode(m_params.delayMode()); //float syncedTimeL = static_cast(m_tempo.noteLengthToMs(m_params.delayNoteL())); diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index df8009a..b0b35e0 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -4,6 +4,9 @@ #include "Parameters.h" #include "DelayEngine.h" #include "Tempo.h" +#if JUCE_DEBUG +#include "LFOScopeBuffer.h" +#endif //============================================================================== /** @@ -43,6 +46,12 @@ class ViiveAudioProcessor : public juce::AudioProcessor std::atomic& getOutputLevelL() { return m_outputLevelL; } std::atomic& getOutputLevelR() { return m_outputLevelR; } + std::atomic& getLowCutLfoValue() { return m_delayEngine.getLowCutLfoValue(); } + std::atomic& getHighCutLfoValue() { return m_delayEngine.getHighCutLfoValue(); } + +#if JUCE_DEBUG + LFOScopeBuffer& getLFOScopeBuffer() { return m_delayEngine.getLFOScopeBuffer(); } +#endif //============================================================================== int getNumPrograms() override; diff --git a/Source/RotaryKnob.cpp b/Source/RotaryKnob.cpp index 6c2b3dd..819aafa 100644 --- a/Source/RotaryKnob.cpp +++ b/Source/RotaryKnob.cpp @@ -1,27 +1,80 @@ #include #include "RotaryKnob.h" +juce::Font RotaryKnob::KnobLookAndFeel::getLabelFont(juce::Label&) +{ + return juce::Font(juce::FontOptions().withHeight(12.0f)); +} + //============================================================================== -RotaryKnob::RotaryKnob(const juce::String& text, juce::AudioProcessorValueTreeState& apvts, const juce::ParameterID& parameterID) +void RotaryKnob::KnobLookAndFeel::drawRotarySlider(juce::Graphics& g, int x, int y, int width, int height, + float sliderPos, float rotaryStartAngle, float rotaryEndAngle, juce::Slider& slider) +{ + auto bounds = juce::Rectangle(x, y, width, height).toFloat().reduced(4); + auto radius = jmin(bounds.getWidth(), bounds.getHeight()) / 2.0f; + auto toAngle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle); + auto lineW = jmin(5.0f, radius * 0.3f); + auto arcRadius = radius - lineW * 0.5f; + + juce::Path backgroundArc; + backgroundArc.addCentredArc(bounds.getCentreX(), bounds.getCentreY(), + arcRadius, arcRadius, 0.0f, + rotaryStartAngle, rotaryEndAngle, true); + g.setColour(slider.findColour(juce::Slider::rotarySliderOutlineColourId)); + g.strokePath(backgroundArc, juce::PathStrokeType(lineW, juce::PathStrokeType::curved, juce::PathStrokeType::rounded)); + + if (slider.isEnabled()) + { + juce::Path valueArc; + valueArc.addCentredArc(bounds.getCentreX(), bounds.getCentreY(), + arcRadius, arcRadius, 0.0f, + rotaryStartAngle, toAngle, true); + g.setColour(slider.findColour(juce::Slider::rotarySliderFillColourId)); + g.strokePath(valueArc, juce::PathStrokeType(lineW, juce::PathStrokeType::curved, juce::PathStrokeType::rounded)); + } + + auto thumbWidth = lineW * 1.6f; // default is lineW * 2.0f + juce::Point thumb( + bounds.getCentreX() + arcRadius * std::cos(toAngle - juce::MathConstants::halfPi), + bounds.getCentreY() + arcRadius * std::sin(toAngle - juce::MathConstants::halfPi)); + g.setColour(slider.findColour(juce::Slider::thumbColourId)); + g.fillEllipse(juce::Rectangle(thumbWidth, thumbWidth).withCentre(thumb)); +} + +//============================================================================== +RotaryKnob::RotaryKnob(const juce::String& text, juce::AudioProcessorValueTreeState& apvts, const juce::ParameterID& parameterID, Size size) : m_attachment(apvts, parameterID.getParamID(), m_slider) { + const bool isSmall = size == Size::Small; + const int w = isSmall ? 45 : 70; + const int textBoxH = isSmall ? 12 : 16; + const int gap = isSmall ? 0 : 2; + const int labelH = isSmall ? 14 : 24; + + if (isSmall) + { + m_slider.setLookAndFeel(&m_laf); + m_slider.setColour(juce::Slider::textBoxOutlineColourId, juce::Colours::transparentBlack); + } m_slider.setSliderStyle(juce::Slider::SliderStyle::RotaryHorizontalVerticalDrag); - m_slider.setTextBoxStyle(juce::Slider::TextBoxBelow, false, 70, 16); - m_slider.setBounds(0, 0, 70, 86); + m_slider.setTextBoxStyle(juce::Slider::TextBoxBelow, false, w, textBoxH); + m_slider.setBounds(0, 0, w, w + textBoxH + gap); addAndMakeVisible(m_slider); m_label.setText(text, juce::NotificationType::dontSendNotification); + m_label.setFont(juce::Font(juce::FontOptions().withHeight(isSmall ? 13.0f : 14.0f))); m_label.setJustificationType(juce::Justification::horizontallyCentred); m_label.setBorderSize(juce::BorderSize(0)); m_label.attachToComponent(&m_slider, false); addAndMakeVisible(m_label); - setSize(70, 110); + setSize(w, w + textBoxH + gap + labelH); } RotaryKnob::~RotaryKnob() { + m_slider.setLookAndFeel(nullptr); } void RotaryKnob::resized() { - m_slider.setTopLeftPosition(0, 24); + m_slider.setTopLeftPosition(0, getHeight() - m_slider.getHeight()); } diff --git a/Source/RotaryKnob.h b/Source/RotaryKnob.h index a1ddc35..68ae0ad 100644 --- a/Source/RotaryKnob.h +++ b/Source/RotaryKnob.h @@ -8,7 +8,9 @@ class RotaryKnob : public juce::Component { public: - RotaryKnob(const juce::String& text, juce::AudioProcessorValueTreeState& apvts, const juce::ParameterID& parameterID); + enum class Size { Normal, Small }; + + RotaryKnob(const juce::String& text, juce::AudioProcessorValueTreeState& apvts, const juce::ParameterID& parameterID, Size size = Size::Normal); ~RotaryKnob() override; void resized() override; @@ -16,6 +18,15 @@ class RotaryKnob : public juce::Component juce::Label& getLabel() { return m_label; } private: + struct KnobLookAndFeel : public juce::LookAndFeel_V4 + { + void drawRotarySlider(juce::Graphics&, int x, int y, int width, int height, + float sliderPos, float rotaryStartAngle, float rotaryEndAngle, + juce::Slider&) override; + juce::Font getLabelFont(juce::Label&) override; + }; + + KnobLookAndFeel m_laf; juce::Slider m_slider; juce::Label m_label; diff --git a/Source/Selector.cpp b/Source/Selector.cpp new file mode 100644 index 0000000..0abccf4 --- /dev/null +++ b/Source/Selector.cpp @@ -0,0 +1,55 @@ +#include "Selector.h" + +Selector::Selector(const juce::String& labelText, + juce::AudioProcessorValueTreeState& apvts, + const juce::ParameterID& parameterID, + const juce::StringArray& items, + int width, + float fontSize, + int leftLabelWidth, + int height) + : m_hasLabel(labelText.isNotEmpty()), m_leftLabelWidth(leftLabelWidth) +{ + if (fontSize > 0.0f) + { + m_laf.fontSize = fontSize; + m_comboBox.setLookAndFeel(&m_laf); + } + + m_comboBox.addItemList(items, 1); + m_comboBox.setEditableText(false); + addAndMakeVisible(m_comboBox); + + m_attachment = std::make_unique( + apvts, parameterID.getParamID(), m_comboBox); + + if (m_hasLabel) + { + m_label.setText(labelText, juce::NotificationType::dontSendNotification); + addAndMakeVisible(m_label); + } + + const int autoH = (m_hasLabel && m_leftLabelWidth == 0 ? s_labelH : 0) + s_comboH; + setSize(width, height > 0 ? height : autoH); +} + +Selector::~Selector() +{ + m_comboBox.setLookAndFeel(nullptr); +} + +void Selector::resized() +{ + if (m_hasLabel && m_leftLabelWidth > 0) + { + m_label.setBounds(0, 0, m_leftLabelWidth, getHeight()); + m_comboBox.setBounds(m_leftLabelWidth, 0, getWidth() - m_leftLabelWidth, getHeight()); + } + else + { + const int comboY = m_hasLabel ? s_labelH : 0; + if (m_hasLabel) + m_label.setBounds(0, 0, getWidth(), s_labelH); + m_comboBox.setBounds(0, comboY, getWidth(), s_comboH); + } +} diff --git a/Source/Selector.h b/Source/Selector.h new file mode 100644 index 0000000..2116ce6 --- /dev/null +++ b/Source/Selector.h @@ -0,0 +1,41 @@ +#pragma once + +#include + +class Selector : public juce::Component +{ +public: + Selector(const juce::String& labelText, + juce::AudioProcessorValueTreeState& apvts, + const juce::ParameterID& parameterID, + const juce::StringArray& items, + int width = 60, + float fontSize = 0.0f, + int leftLabelWidth = 0, + int height = 0); + ~Selector() override; + + void resized() override; + +private: + static constexpr int s_labelH = 14; + static constexpr int s_comboH = 20; + + struct SelectorLookAndFeel : public juce::LookAndFeel_V4 + { + float fontSize = 0.0f; + juce::Font getComboBoxFont(juce::ComboBox&) override + { + return juce::Font(juce::FontOptions().withHeight(fontSize)); + } + }; + + SelectorLookAndFeel m_laf; + juce::Label m_label; + juce::ComboBox m_comboBox; + std::unique_ptr m_attachment; + bool m_hasLabel; + int m_leftLabelWidth = 0; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Selector) +}; diff --git a/Source/StereoDelay.h b/Source/StereoDelay.h index e5cb725..a59e0f7 100644 --- a/Source/StereoDelay.h +++ b/Source/StereoDelay.h @@ -1,7 +1,6 @@ #pragma once #include - -enum Channel { Left = 0, Right = 1 }; +#include "Types.h" class StereoDelay { public: diff --git a/Source/StereoFilter.cpp b/Source/StereoFilter.cpp new file mode 100644 index 0000000..f889824 --- /dev/null +++ b/Source/StereoFilter.cpp @@ -0,0 +1,40 @@ +#include "StereoFilter.h" + +StereoFilter::StereoFilter(FilterType type) +{ + for (auto& f : m_filters) + f.setType(type); +} + +void StereoFilter::prepareToPlay(double sampleRate, int samplesPerBlock) noexcept +{ + juce::dsp::ProcessSpec spec; + spec.sampleRate = sampleRate; + spec.maximumBlockSize = juce::uint32(samplesPerBlock); + spec.numChannels = 1; + for (auto& f : m_filters) + f.prepare(spec); +} + +void StereoFilter::reset() noexcept +{ + for (auto& f : m_filters) + f.reset(); +} + +void StereoFilter::setCutoff(float freq, Channel channel) +{ + m_filters[channel].setCutoffFrequency(freq); +} + +void StereoFilter::setQ(float q) +{ + for (auto& f : m_filters) + f.setResonance(q); +} + +void StereoFilter::processSample(const float& inL, const float& inR, float& outL, float& outR) +{ + outL = m_filters[Channel::Left].processSample(0, inL); + outR = m_filters[Channel::Right].processSample(0, inR); +} diff --git a/Source/StereoFilter.h b/Source/StereoFilter.h new file mode 100644 index 0000000..f38ee7e --- /dev/null +++ b/Source/StereoFilter.h @@ -0,0 +1,25 @@ +#pragma once +#include +#include "Types.h" + +using Filter = juce::dsp::StateVariableTPTFilter; +using FilterType = juce::dsp::StateVariableTPTFilterType; + +class StereoFilter +{ +public: + StereoFilter(FilterType type = FilterType::lowpass); + ~StereoFilter() = default; + + void prepareToPlay(double sampleRate, int samplesPerBlock) noexcept; + void reset() noexcept; + + void processSample(const float& inL, const float& inR, float& outL, float& outR); + void setCutoff(float freq, Channel channel); + void setQ(float q); + +private: + Filter m_filters[2]; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(StereoFilter) +}; diff --git a/Source/Sync.h b/Source/Sync.h new file mode 100644 index 0000000..2c46334 --- /dev/null +++ b/Source/Sync.h @@ -0,0 +1,47 @@ +#pragma once +#include + +inline static std::array delayNoteLengthMultipliers = +{ + 0.125, // 0 = 1/32 + 0.5 / 3.0, // 1 = 1/16 triplet + 0.1875, // 2 = 1/32 dotted + 0.25, // 3 = 1/16 + 1.0 / 3.0, // 4 = 1/8 triplet + 0.375, // 5 = 1/16 dotted + 0.5, // 6 = 1/8 + 2.0 / 3.0, // 7 = 1/4 triplet + 0.75, // 8 = 1/8 dotted + 1.0, // 9 = 1/4 + 4.0 / 3.0, // 10 = 1/2 triplet + 1.5, // 11 = 1/4 dotted + 2.0, // 12 = 1/2 + 8.0 / 3.0, // 13 = 1/1 triplet + 3.0, // 14 = 1/2 dotted + 4.0, // 15 = 1/1 +}; + +inline static std::array modNoteLengthMultipliers = +{ + 0.5, // 0 = 1/8 + 0.75, // 1 = 1/8 dotted + 1.0, // 2 = 1/4 + 1.5, // 3 = 1/4 dotted + 2.0, // 4 = 1/2 + 3.0, // 5 = 1/2 dotted + 4.0, // 6 = 1/1 + 6.0, // 7 = 1/1 dotted + 8.0, // 8 = 2/1 + 12.0, // 9 = 3/1 + 16.0, // 10 = 4/1 + 20.0, // 11 = 5/1 + 24.0, // 12 = 6/1 + 28.0, // 13 = 7/1 + 32.0, // 14 = 8/1 +}; + +inline float getSyncedPhase(int index, double ppqPosition) +{ + double cycleLengthInBeats = modNoteLengthMultipliers[size_t(index)]; + return static_cast(std::fmod(ppqPosition, cycleLengthInBeats) / cycleLengthInBeats); +} diff --git a/Source/Tempo.cpp b/Source/Tempo.cpp index 3d785d3..e3281e3 100644 --- a/Source/Tempo.cpp +++ b/Source/Tempo.cpp @@ -1,24 +1,5 @@ #include "Tempo.h" - -static std::array noteLengthMultipliers = -{ - 0.125, // 0 = 1/32 - 0.5 / 3.0, // 1 = 1/16 triplet - 0.1875, // 2 = 1/32 dotted - 0.25, // 3 = 1/16 - 1.0 / 3.0, // 4 = 1/8 triplet - 0.375, // 5 = 1/16 dotted - 0.5, // 6 = 1/8 - 2.0 / 3.0, // 7 = 1/4 triplet - 0.75, // 8 = 1/8 dotted - 1.0, // 9 = 1/4 - 4.0 / 3.0, // 10 = 1/2 triplet - 1.5, // 11 = 1/4 dotted - 2.0, // 12 = 1/2 - 8.0 / 3.0, // 13 = 1/1 triplet - 3.0, // 14 = 1/2 dotted - 4.0, // 15 = 1/1 -}; +#include "Sync.h" void Tempo::reset() noexcept { @@ -41,9 +22,19 @@ void Tempo::update(const juce::AudioPlayHead* playhead) noexcept if (pos.getBpm().hasValue()) m_bpm = *pos.getBpm(); + + m_isPlaying = pos.getIsPlaying(); + + m_ppqPosition = pos.getPpqPosition().hasValue() ? *pos.getPpqPosition() : 0.0; + } double Tempo::noteLengthToMs(int index) const noexcept { - return 60000.0 * noteLengthMultipliers[size_t(index)] / m_bpm; + return 60000.0 * delayNoteLengthMultipliers[size_t(index)] / m_bpm; +} + +double Tempo::noteLengthToHz(int index) const noexcept +{ + return m_bpm / (60.0 * modNoteLengthMultipliers[size_t(index)]); } \ No newline at end of file diff --git a/Source/Tempo.h b/Source/Tempo.h index 0b6425c..b488cf0 100644 --- a/Source/Tempo.h +++ b/Source/Tempo.h @@ -10,9 +10,14 @@ class Tempo void update(const juce::AudioPlayHead* playhead) noexcept; double noteLengthToMs(int index) const noexcept; + double noteLengthToHz(int index) const noexcept; double getTempo() const noexcept { return m_bpm; } + bool getIsPlaying() const noexcept { return m_isPlaying; } + double getPpqPosition() const noexcept { return m_ppqPosition; } private: double m_bpm = 120.0; + bool m_isPlaying = false; + double m_ppqPosition = 0.0; }; \ No newline at end of file diff --git a/Source/Types.h b/Source/Types.h new file mode 100644 index 0000000..e0374c0 --- /dev/null +++ b/Source/Types.h @@ -0,0 +1,5 @@ +#pragma once + +enum Channel { Left = 0, Right = 1 }; + +enum class LFOShape { Sine, Triangle, Square, SawUp, SawDown, SampleAndHold }; diff --git a/viive.png b/viive.png index a3a8a19ab936a55009afa6521f90b6b8621e4002..a811731a67f2cfcdab78b078bc9c8cd3ca3f4927 100644 GIT binary patch literal 37335 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�Sr|D&`;jmV*9f-9zr< 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 W`2FC;7cgxdB`>QYlP`Vy@&5urFq!87 From 798300d945f1c979803933c625befda42dbf7c2a Mon Sep 17 00:00:00 2001 From: Otto Andelin <112075238+trsctr@users.noreply.github.com> Date: Wed, 6 May 2026 13:04:07 +0300 Subject: [PATCH 2/4] fix: harden CI/CD pipeline security (#7) - Pin third-party actions to commit SHAs - Upgrade softprops/action-gh-release to v3 - Pin pluginval to v1.0.4 with SHA256 checksum verification - Run pluginval on a copy of the binary to prevent tampering with build artifacts --- .github/workflows/Validation.yml | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/.github/workflows/Validation.yml b/.github/workflows/Validation.yml index 63dd436..6f76f48 100644 --- a/.github/workflows/Validation.yml +++ b/.github/workflows/Validation.yml @@ -29,7 +29,8 @@ jobs: gen: 'make', cmake_config_flags: "-D CMAKE_C_COMPILER_LAUNCHER=ccache -D CMAKE_CXX_COMPILER_LAUNCHER=ccache -D CMAKE_OSX_ARCHITECTURES=arm64\\;x86_64 -D CMAKE_OSX_DEPLOYMENT_TARGET=10.15", cmake_build_flags: "--parallel 6", - pluginval_link: "https://github.com/Tracktion/pluginval/releases/latest/download/pluginval_macOS.zip", + pluginval_link: "https://github.com/Tracktion/pluginval/releases/download/v1.0.4/pluginval_macOS.zip", + pluginval_sha256: "3c4c533bda0c5059eea3ddaea752d757ee2025041f0f47e6bcb0e87f6082b29f", pluginval_path: "pluginval.app/Contents/MacOS/pluginval" } - { @@ -37,7 +38,8 @@ jobs: os: windows-latest, gen: 'MSVC', cmake_config_flags: "-D CMAKE_C_COMPILER_LAUNCHER=ccache -D CMAKE_CXX_COMPILER_LAUNCHER=ccache", - pluginval_link: "https://github.com/Tracktion/pluginval/releases/latest/download/pluginval_Windows.zip", + pluginval_link: "https://github.com/Tracktion/pluginval/releases/download/v1.0.4/pluginval_Windows.zip", + pluginval_sha256: "c08e61ce3b96db41636f8ec7e76f4c7e2c13ebdac7fa1b5a1f52b4f32ec715ab", pluginval_path: "./pluginval.exe" } @@ -46,7 +48,7 @@ jobs: uses: actions/checkout@v4 - name: Enable ccache - uses: hendrikmuhs/ccache-action@v1.2 + uses: hendrikmuhs/ccache-action@d62db5f07c26379fc4b4e0916f098a92573c3b03 # v1.2.23 with: key: ${{ runner.os }}-${{ env.BUILD_TYPE }} @@ -63,6 +65,13 @@ jobs: - name: Build run: cmake --build Build --config ${{ env.BUILD_TYPE }} ${{ matrix.config.cmake_build_flags }} + - name: Copy VST3 for validation + shell: bash + working-directory: Build + run: | + mkdir -p pluginval-input + cp -r *_artefacts/${{ env.BUILD_TYPE }}/VST3/*.vst3 pluginval-input/ + - name: Validate shell: bash working-directory: Build @@ -70,8 +79,13 @@ jobs: VALIDATE_IN_PROCESS: true run: | curl -LsS ${{ matrix.config.pluginval_link }} -o pluginval.zip + if [[ "$RUNNER_OS" == "macOS" ]]; then + echo "${{ matrix.config.pluginval_sha256 }} pluginval.zip" | shasum -a 256 -c || { echo "pluginval checksum verification failed"; exit 1; } + else + echo "${{ matrix.config.pluginval_sha256 }} pluginval.zip" | sha256sum -c || { echo "pluginval checksum verification failed"; exit 1; } + fi unzip -q pluginval - ${{ matrix.config.pluginval_path }} --validate *_artefacts/${{ env.BUILD_TYPE }}/VST3/*.vst3 + ${{ matrix.config.pluginval_path }} --validate pluginval-input/*.vst3 - name: Get safe ref name shell: bash @@ -109,7 +123,7 @@ jobs: done - name: Create Release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3 with: files: "**/*.zip" body_path: CHANGELOG.md From de236a4a25eb152a70813186c1af60845859c1e4 Mon Sep 17 00:00:00 2001 From: Otto Andelin <112075238+trsctr@users.noreply.github.com> Date: Mon, 18 May 2026 14:48:43 +0300 Subject: [PATCH 3/4] fix: set tempo synced lfo rate directly via smoother instead of apvts param (#8) --- Source/Parameters.cpp | 4 ++-- Source/PluginProcessor.cpp | 9 --------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/Source/Parameters.cpp b/Source/Parameters.cpp index 984b92a..66f181d 100644 --- a/Source/Parameters.cpp +++ b/Source/Parameters.cpp @@ -494,11 +494,11 @@ void Parameters::update(const Tempo& tempo) noexcept m_highCutModShape = m_highCutModShapeParam->getIndex(); if (m_lowCutModTempoSync) { float syncedHz = float(tempo.noteLengthToHz(m_lowCutModNote)); - m_lowCutModRateParam->setValueNotifyingHost(m_lowCutModRateParam->convertTo0to1(syncedHz)); + m_lowCutModRateSmoother.setTargetValue(syncedHz); } if (m_highCutModTempoSync) { float syncedHz = float(tempo.noteLengthToHz(m_highCutModNote)); - m_highCutModRateParam->setValueNotifyingHost(m_highCutModRateParam->convertTo0to1(syncedHz)); + m_highCutModRateSmoother.setTargetValue(syncedHz); } if (m_tempoSyncL) { float syncedMs = juce::jmin(float(tempo.noteLengthToMs(m_delayNoteL)), maxDelayTime); diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 42022e9..3789f73 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -120,15 +120,6 @@ void ViiveAudioProcessor::processBlock(juce::AudioBuffer& buffer, juce::M m_delayEngine.updateSyncedModulators(m_tempo.getPpqPosition()); m_delayEngine.setDelayMode(m_params.delayMode()); - //float syncedTimeL = static_cast(m_tempo.noteLengthToMs(m_params.delayNoteL())); - //float syncedTimeR = static_cast(m_tempo.noteLengthToMs(m_params.delayNoteR())); - //if (syncedTimeL > Parameters::maxDelayTime) - // syncedTimeL = Parameters::maxDelayTime; - //if (syncedTimeR > Parameters::maxDelayTime) - // syncedTimeR = Parameters::maxDelayTime; - - //float sampleRate = float(getSampleRate()); - auto mainInput = getBusBuffer(buffer, true, 0); const float* inputDataL = mainInput.getReadPointer(0); const float* inputDataR = mainInput.getReadPointer(1); From d36f02c99ef24b4200ab19305481b74abba35767 Mon Sep 17 00:00:00 2001 From: Otto Andelin Date: Mon, 18 May 2026 14:53:28 +0300 Subject: [PATCH 4/4] chore: update docs for release 0.2.0 --- CHANGELOG.md | 6 +----- README.md | 11 ++++++++--- Source/PluginEditor.cpp | 4 +--- viive.png | Bin 37335 -> 36054 bytes 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11e5d37..7e36365 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. -## \[0.2.0-beta.1] - 2026-05-06 +## \[0.2.0] - 2026-05-18 ### Added @@ -21,10 +21,6 @@ All notable changes to this project will be documented in this file. * Refactored filters to StereoFilter primitive and FilterEngine class * Set maximum resonance for low and high cut filters to 1.5 -### Known limitations - -* Tempo synced LFOs might sync incorrectly to song playback positions in DAW on extreme settings (very high or very low BPMs) - ## \[0.1.0] - 2026-04-17 ### Added diff --git a/README.md b/README.md index 7537da0..4543b48 100644 --- a/README.md +++ b/README.md @@ -32,9 +32,14 @@ Download the zip for your platform, extract it, and copy `viive.vst3` to your VS Then rescan plugins in your DAW. -NOTE: MacOS build is untested and unsigned. May be blocked by Gatekeeper on -newer macOS versions and cannot be guaranteed to work without -code signing and notarization. +NOTE: macOS build is unsigned and unnotarized. If Gatekeeper blocks the plugin, run the following commands in Terminal after copying it to your VST3 folder: + +```bash +xattr -dr com.apple.quarantine ~/Library/Audio/Plug-Ins/VST3/viive.vst3 +codesign --force --deep --sign - ~/Library/Audio/Plug-Ins/VST3/viive.vst3 +``` + +Then rescan plugins in your DAW. ## Potential future features - Additional effects in feedback loop (bitcrush, phaser) diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 3991b74..e03d99d 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -102,9 +102,7 @@ void ViiveAudioProcessorEditor::paint (juce::Graphics& g) g.fillAll(juce::Colours::darkgrey); g.setColour(juce::Colours::white); g.setFont(12.0f); - g.drawText("viive 0.2.beta-1", getLocalBounds().reduced(5), juce::Justification::topRight); - -// g.drawText("viive " JucePlugin_VersionString, getLocalBounds().reduced(5), juce::Justification::topRight); + g.drawText("viive " JucePlugin_VersionString, getLocalBounds().reduced(5), juce::Justification::topRight); } void ViiveAudioProcessorEditor::resized() diff --git a/viive.png b/viive.png index a811731a67f2cfcdab78b078bc9c8cd3ca3f4927..cce6370f41de76ac79320feaf5fdd39b932bc0ac 100644 GIT binary patch literal 36054 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�Sr|D&`;jmV*9f-9zr< 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