From 6194b039f353e10f862788f7231b82ac594ffb75 Mon Sep 17 00:00:00 2001 From: James H Ball Date: Mon, 26 Aug 2024 18:09:29 +0100 Subject: [PATCH] Fix several threading bugs, and crash when changing sample rate --- Source/MainComponent.cpp | 6 --- Source/MainComponent.h | 2 - Source/PluginProcessor.cpp | 16 +++--- Source/PluginProcessor.h | 16 +++--- Source/SettingsComponent.cpp | 2 +- Source/audio/BitCrushEffect.cpp | 2 +- Source/audio/BitCrushEffect.h | 2 +- Source/audio/BulgeEffect.cpp | 2 +- Source/audio/BulgeEffect.h | 4 +- Source/audio/CustomEffect.cpp | 4 +- Source/audio/CustomEffect.h | 2 +- Source/audio/DashedLineEffect.cpp | 12 ++--- Source/audio/DashedLineEffect.h | 4 +- Source/audio/DelayEffect.cpp | 2 +- Source/audio/DelayEffect.h | 4 +- Source/audio/DistortEffect.cpp | 6 +-- Source/audio/DistortEffect.h | 4 +- Source/audio/Effect.cpp | 10 ++-- Source/audio/Effect.h | 4 +- Source/audio/EffectApplication.h | 4 +- Source/audio/PerspectiveEffect.cpp | 6 +-- Source/audio/PerspectiveEffect.h | 2 +- Source/audio/PitchDetector.cpp | 10 +++- Source/audio/PitchDetector.h | 1 + Source/audio/SmoothEffect.cpp | 4 +- Source/audio/SmoothEffect.h | 4 +- Source/audio/VectorCancellingEffect.cpp | 2 +- Source/audio/VectorCancellingEffect.h | 4 +- Source/audio/WobbleEffect.cpp | 2 +- Source/audio/WobbleEffect.h | 4 +- Source/components/EffectsListComponent.cpp | 4 -- Source/components/SwitchButton.h | 4 +- Source/components/VisualiserComponent.cpp | 62 ++++++++++++++++------ Source/components/VisualiserComponent.h | 14 +++-- Source/components/VolumeComponent.cpp | 10 +++- Source/components/VolumeComponent.h | 1 + osci-render.jucer | 2 +- 37 files changed, 139 insertions(+), 105 deletions(-) diff --git a/Source/MainComponent.cpp b/Source/MainComponent.cpp index f87d182..a51bf97 100644 --- a/Source/MainComponent.cpp +++ b/Source/MainComponent.cpp @@ -143,12 +143,6 @@ MainComponent::MainComponent(OscirenderAudioProcessor& p, OscirenderAudioProcess resized(); repaint(); }); - addAndMakeVisible(openOscilloscope); - - openOscilloscope.onClick = [this] { - // TODO: Log if this fails - juce::URL("https://james.ball.sh/oscilloscope").launchInDefaultBrowser(); - }; addAndMakeVisible(frequencyLabel); diff --git a/Source/MainComponent.h b/Source/MainComponent.h index 76fca31..d6c9561 100644 --- a/Source/MainComponent.h +++ b/Source/MainComponent.h @@ -38,8 +38,6 @@ private: juce::ComboBox fileType; juce::TextButton createFile{"Create File"}; - juce::TextButton openOscilloscope{"Open Oscilloscope"}; - juce::Label frequencyLabel; int callbackIndex = -1; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 7bb6e5d..d2613b7 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -45,7 +45,7 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() new EffectParameter("Vector Cancelling", "Inverts the audio and image every few samples to 'cancel out' the audio, making the audio quiet, and distorting the image.", "vectorCancelling", VERSION_HINT, 0.1111111, 0.0, 1.0) )); toggleableEffects.push_back(std::make_shared( - [this](int index, Point input, const std::vector& values, double sampleRate) { + [this](int index, Point input, const std::vector>& values, double sampleRate) { return input * Point(values[0], values[1], values[2]); }, std::vector{ new EffectParameter("Scale X", "Scales the object in the horizontal direction.", "scaleX", VERSION_HINT, 1.0, -5.0, 5.0), @@ -54,7 +54,7 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() } )); toggleableEffects.push_back(std::make_shared( - [this](int index, Point input, const std::vector& values, double sampleRate) { + [this](int index, Point input, const std::vector>& values, double sampleRate) { int flip = index % 2 == 0 ? 1 : -1; Point jitter = Point(flip * values[0], flip * values[1], flip * values[2]); return input + jitter; @@ -65,7 +65,7 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() } )); auto rippleEffect = std::make_shared( - [this](int index, Point input, const std::vector& values, double sampleRate) { + [this](int index, Point input, const std::vector>& values, double sampleRate) { double phase = values[1] * std::numbers::pi; double distance = 100 * values[2] * (input.x * input.x + input.y * input.y); input.z += values[0] * std::sin(phase + distance); @@ -79,7 +79,7 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() rippleEffect->getParameter("ripplePhase")->lfo->setUnnormalisedValueNotifyingHost((int) LfoType::Sawtooth); toggleableEffects.push_back(rippleEffect); auto rotateEffect = std::make_shared( - [this](int index, Point input, const std::vector& values, double sampleRate) { + [this](int index, Point input, const std::vector>& values, double sampleRate) { input.rotate(values[0] * std::numbers::pi, values[1] * std::numbers::pi, values[2] * std::numbers::pi); return input; }, std::vector{ @@ -92,7 +92,7 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() rotateEffect->getParameter("rotateY")->lfoRate->setUnnormalisedValueNotifyingHost(0.2); toggleableEffects.push_back(rotateEffect); toggleableEffects.push_back(std::make_shared( - [this](int index, Point input, const std::vector& values, double sampleRate) { + [this](int index, Point input, const std::vector>& values, double sampleRate) { return input + Point(values[0], values[1], values[2]); }, std::vector{ new EffectParameter("Translate X", "Moves the object horizontally.", "translateX", VERSION_HINT, 0.0, -1.0, 1.0), @@ -101,7 +101,7 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() } )); toggleableEffects.push_back(std::make_shared( - [this](int index, Point input, const std::vector& values, double sampleRate) { + [this](int index, Point input, const std::vector>& values, double sampleRate) { double length = 10 * values[0] * input.magnitude(); double newX = input.x * std::cos(length) - input.y * std::sin(length); double newY = input.x * std::sin(length) + input.y * std::cos(length); @@ -331,7 +331,7 @@ void OscirenderAudioProcessor::addLuaSlider() { } luaEffects.push_back(std::make_shared( - [this, sliderIndex](int index, Point input, const std::vector& values, double sampleRate) { + [this, sliderIndex](int index, Point input, const std::vector>& values, double sampleRate) { luaValues[sliderIndex] = values[0]; return input; }, new EffectParameter( @@ -668,6 +668,8 @@ void OscirenderAudioProcessor::processBlock(juce::AudioBuffer& buffer, ju animationTime = playTimeSeconds; } + juce::SpinLock::ScopedLockType lock1(parsersLock); + juce::SpinLock::ScopedLockType lock2(effectsLock); if (currentFile >= 0 && sounds[currentFile]->parser->isAnimatable) { int animFrame = (int)(animationTime * animationRate->getValueUnnormalised() + animationOffset->getValueUnnormalised()); auto lineArt = sounds[currentFile]->parser->getLineArt(); diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index b7f198a..0e8d005 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -85,8 +85,8 @@ public: double luaValues[26] = { 0.0 }; std::shared_ptr frequencyEffect = std::make_shared( - [this](int index, Point input, const std::vector& values, double sampleRate) { - frequency = values[0]; + [this](int index, Point input, const std::vector>& values, double sampleRate) { + frequency = values[0].load(); return input; }, new EffectParameter( "Frequency", @@ -97,8 +97,8 @@ public: ); std::shared_ptr volumeEffect = std::make_shared( - [this](int index, Point input, const std::vector& values, double sampleRate) { - volume = values[0]; + [this](int index, Point input, const std::vector>& values, double sampleRate) { + volume = values[0].load(); return input; }, new EffectParameter( "Volume", @@ -109,8 +109,8 @@ public: ); std::shared_ptr thresholdEffect = std::make_shared( - [this](int index, Point input, const std::vector& values, double sampleRate) { - threshold = values[0]; + [this](int index, Point input, const std::vector>& values, double sampleRate) { + threshold = values[0].load(); return input; }, new EffectParameter( "Threshold", @@ -243,7 +243,7 @@ public: BooleanParameter* invertImage = new BooleanParameter("Invert Image", "invertImage", VERSION_HINT, false, "Inverts the image so that dark pixels become light, and vice versa."); std::shared_ptr imageThreshold = std::make_shared( - [this](int index, Point input, const std::vector& values, double sampleRate) { + [this](int index, Point input, const std::vector>& values, double sampleRate) { return input; }, new EffectParameter( "Image Threshold", @@ -253,7 +253,7 @@ public: ) ); std::shared_ptr imageStride = std::make_shared( - [this](int index, Point input, const std::vector& values, double sampleRate) { + [this](int index, Point input, const std::vector>& values, double sampleRate) { return input; }, new EffectParameter( "Image Stride", diff --git a/Source/SettingsComponent.cpp b/Source/SettingsComponent.cpp index a087c6f..a49d800 100644 --- a/Source/SettingsComponent.cpp +++ b/Source/SettingsComponent.cpp @@ -88,7 +88,7 @@ void SettingsComponent::update() { } void SettingsComponent::mouseMove(const juce::MouseEvent& event) { - for (int i = 0; i < 2; i++) { + for (int i = 0; i < 1; i++) { if (toggleComponents[i]->getBounds().removeFromTop(pluginEditor.CLOSED_PREF_SIZE).contains(event.getPosition())) { setMouseCursor(juce::MouseCursor::PointingHandCursor); return; diff --git a/Source/audio/BitCrushEffect.cpp b/Source/audio/BitCrushEffect.cpp index 8336a85..090f2e2 100644 --- a/Source/audio/BitCrushEffect.cpp +++ b/Source/audio/BitCrushEffect.cpp @@ -3,7 +3,7 @@ BitCrushEffect::BitCrushEffect() {} // algorithm from https://www.kvraudio.com/forum/viewtopic.php?t=163880 -Point BitCrushEffect::apply(int index, Point input, const std::vector& values, double sampleRate) { +Point BitCrushEffect::apply(int index, Point input, const std::vector>& values, double sampleRate) { double value = values[0]; // change rage of value from 0-1 to 0.0-0.78 double rangedValue = value * 0.78; diff --git a/Source/audio/BitCrushEffect.h b/Source/audio/BitCrushEffect.h index 34f5c75..9e061ef 100644 --- a/Source/audio/BitCrushEffect.h +++ b/Source/audio/BitCrushEffect.h @@ -6,5 +6,5 @@ class BitCrushEffect : public EffectApplication { public: BitCrushEffect(); - Point apply(int index, Point input, const std::vector& values, double sampleRate) override; + Point apply(int index, Point input, const std::vector>& values, double sampleRate) override; }; diff --git a/Source/audio/BulgeEffect.cpp b/Source/audio/BulgeEffect.cpp index 965fc42..9aeffba 100644 --- a/Source/audio/BulgeEffect.cpp +++ b/Source/audio/BulgeEffect.cpp @@ -4,7 +4,7 @@ BulgeEffect::BulgeEffect() {} BulgeEffect::~BulgeEffect() {} -Point BulgeEffect::apply(int index, Point input, const std::vector& values, double sampleRate) { +Point BulgeEffect::apply(int index, Point input, const std::vector>& values, double sampleRate) { double value = values[0]; double translatedBulge = -value + 1; diff --git a/Source/audio/BulgeEffect.h b/Source/audio/BulgeEffect.h index 2bdd86b..f9ab3cb 100644 --- a/Source/audio/BulgeEffect.h +++ b/Source/audio/BulgeEffect.h @@ -7,5 +7,5 @@ public: BulgeEffect(); ~BulgeEffect(); - Point apply(int index, Point input, const std::vector&, double sampleRate) override; -}; \ No newline at end of file + Point apply(int index, Point input, const std::vector>&, double sampleRate) override; +}; diff --git a/Source/audio/CustomEffect.cpp b/Source/audio/CustomEffect.cpp index 639b487..0d1c4c7 100644 --- a/Source/audio/CustomEffect.cpp +++ b/Source/audio/CustomEffect.cpp @@ -13,8 +13,8 @@ CustomEffect::~CustomEffect() { parser->close(L); } -Point CustomEffect::apply(int index, Point input, const std::vector& values, double sampleRate) { - auto effectScale = values[0]; +Point CustomEffect::apply(int index, Point input, const std::vector>& values, double sampleRate) { + auto effectScale = values[0].load(); auto x = input.x; auto y = input.y; diff --git a/Source/audio/CustomEffect.h b/Source/audio/CustomEffect.h index f78aeb4..4f370c4 100644 --- a/Source/audio/CustomEffect.h +++ b/Source/audio/CustomEffect.h @@ -13,7 +13,7 @@ public: static const juce::String UNIQUE_ID; static const juce::String FILE_NAME; - Point apply(int index, Point input, const std::vector& values, double sampleRate) override; + Point apply(int index, Point input, const std::vector>& values, double sampleRate) override; void updateCode(const juce::String& newCode); juce::String getCode(); diff --git a/Source/audio/DashedLineEffect.cpp b/Source/audio/DashedLineEffect.cpp index ac586a2..ac4e2f0 100644 --- a/Source/audio/DashedLineEffect.cpp +++ b/Source/audio/DashedLineEffect.cpp @@ -4,15 +4,15 @@ DashedLineEffect::DashedLineEffect() {} DashedLineEffect::~DashedLineEffect() {} -Point DashedLineEffect::apply(int index, Point vector, const std::vector& values, double sampleRate) { +Point DashedLineEffect::apply(int index, Point vector, const std::vector>& values, double sampleRate) { // dash length in seconds double dashLength = values[0] / 400; int dashLengthSamples = (int)(dashLength * sampleRate); dashLengthSamples = juce::jmin(dashLengthSamples, MAX_BUFFER); - if (dashIndex >= dashLengthSamples) { - dashIndex = 0; - bufferIndex = 0; + if (dashIndex >= dashLengthSamples) { + dashIndex = 0; + bufferIndex = 0; } buffer[bufferIndex] = vector; @@ -20,8 +20,8 @@ Point DashedLineEffect::apply(int index, Point vector, const std::vector vector = buffer[dashIndex]; - if (index % 2 == 0) { - dashIndex++; + if (index % 2 == 0) { + dashIndex++; } return vector; diff --git a/Source/audio/DashedLineEffect.h b/Source/audio/DashedLineEffect.h index c6351e1..a532160 100644 --- a/Source/audio/DashedLineEffect.h +++ b/Source/audio/DashedLineEffect.h @@ -7,11 +7,11 @@ public: DashedLineEffect(); ~DashedLineEffect(); - Point apply(int index, Point input, const std::vector& values, double sampleRate) override; + Point apply(int index, Point input, const std::vector>& values, double sampleRate) override; private: const static int MAX_BUFFER = 192000; std::vector buffer = std::vector(MAX_BUFFER); int dashIndex = 0; int bufferIndex = 0; -}; \ No newline at end of file +}; diff --git a/Source/audio/DelayEffect.cpp b/Source/audio/DelayEffect.cpp index cdd1634..dd7dc58 100644 --- a/Source/audio/DelayEffect.cpp +++ b/Source/audio/DelayEffect.cpp @@ -4,7 +4,7 @@ DelayEffect::DelayEffect() {} DelayEffect::~DelayEffect() {} -Point DelayEffect::apply(int index, Point vector, const std::vector& values, double sampleRate) { +Point DelayEffect::apply(int index, Point vector, const std::vector>& values, double sampleRate) { double decay = values[0]; double decayLength = values[1]; int delayBufferLength = (int)(sampleRate * decayLength); diff --git a/Source/audio/DelayEffect.h b/Source/audio/DelayEffect.h index 5c77726..1285d43 100644 --- a/Source/audio/DelayEffect.h +++ b/Source/audio/DelayEffect.h @@ -7,7 +7,7 @@ public: DelayEffect(); ~DelayEffect(); - Point apply(int index, Point input, const std::vector& values, double sampleRate) override; + Point apply(int index, Point input, const std::vector>& values, double sampleRate) override; private: const static int MAX_DELAY = 192000 * 10; @@ -15,4 +15,4 @@ private: int head = 0; int position = 0; int samplesSinceLastDelay = 0; -}; \ No newline at end of file +}; diff --git a/Source/audio/DistortEffect.cpp b/Source/audio/DistortEffect.cpp index 8a0f9c7..6ebcbdf 100644 --- a/Source/audio/DistortEffect.cpp +++ b/Source/audio/DistortEffect.cpp @@ -4,13 +4,13 @@ DistortEffect::DistortEffect(bool vertical) : vertical(vertical) {} DistortEffect::~DistortEffect() {} -Point DistortEffect::apply(int index, Point input, const std::vector& values, double sampleRate) { +Point DistortEffect::apply(int index, Point input, const std::vector>& values, double sampleRate) { double value = values[0]; int vertical = (int)this->vertical; if (index % 2 == 0) { input.translate((1 - vertical) * value, vertical * value, 0); - } else { - input.translate((1 - vertical) * -value, vertical * -value, 0); + } else { + input.translate((1 - vertical) * -value, vertical * -value, 0); } return input; } diff --git a/Source/audio/DistortEffect.h b/Source/audio/DistortEffect.h index edcbd4d..bb5ec93 100644 --- a/Source/audio/DistortEffect.h +++ b/Source/audio/DistortEffect.h @@ -7,7 +7,7 @@ public: DistortEffect(bool vertical); ~DistortEffect(); - Point apply(int index, Point input, const std::vector& values, double sampleRate) override; + Point apply(int index, Point input, const std::vector>& values, double sampleRate) override; private: bool vertical; -}; \ No newline at end of file +}; diff --git a/Source/audio/Effect.cpp b/Source/audio/Effect.cpp index 7db1db8..8b2c9f0 100644 --- a/Source/audio/Effect.cpp +++ b/Source/audio/Effect.cpp @@ -5,7 +5,7 @@ Effect::Effect(std::shared_ptr effectApplication, const std:: effectApplication(effectApplication), parameters(parameters), enabled(nullptr), - actualValues(std::vector(parameters.size(), 0.0)) {} + actualValues(std::vector>(parameters.size())) {} Effect::Effect(std::shared_ptr effectApplication, EffectParameter* parameter) : Effect(effectApplication, std::vector{parameter}) {} @@ -13,13 +13,13 @@ Effect::Effect(EffectApplicationType application, const std::vector(parameters.size(), 0.0)) {} + actualValues(std::vector>(parameters.size())) {} Effect::Effect(EffectApplicationType application, EffectParameter* parameter) : Effect(application, std::vector{parameter}) {} -Effect::Effect(const std::vector& parameters) : Effect([](int index, Point input, const std::vector& values, double sampleRate) {return input;}, parameters) {} +Effect::Effect(const std::vector& parameters) : Effect([](int index, Point input, const std::vector>& values, double sampleRate) {return input;}, parameters) {} -Effect::Effect(EffectParameter* parameter) : Effect([](int index, Point input, const std::vector& values, double sampleRate) {return input;}, parameter) {} +Effect::Effect(EffectParameter* parameter) : Effect([](int index, Point input, const std::vector>& values, double sampleRate) {return input;}, parameter) {} Point Effect::apply(int index, Point input, double volume) { animateValues(volume); @@ -105,12 +105,10 @@ double Effect::getValue() { return getValue(0); } -// Not thread safe! Should only be called from the audio thread double Effect::getActualValue(int index) { return actualValues[index]; } -// Not thread safe! Should only be called from the audio thread double Effect::getActualValue() { return actualValues[0]; } diff --git a/Source/audio/Effect.h b/Source/audio/Effect.h index 27a916e..2d8cf5c 100644 --- a/Source/audio/Effect.h +++ b/Source/audio/Effect.h @@ -5,7 +5,7 @@ #include "EffectParameter.h" #include "BooleanParameter.h" -typedef std::function& values, double sampleRate)> EffectApplicationType; +typedef std::function>& values, double sampleRate)> EffectApplicationType; class Effect { public: @@ -43,7 +43,7 @@ public: private: juce::SpinLock listenerLock; - std::vector actualValues; + std::vector> actualValues; int precedence = -1; int sampleRate = 192000; EffectApplicationType application; diff --git a/Source/audio/EffectApplication.h b/Source/audio/EffectApplication.h index ac944ca..6069137 100644 --- a/Source/audio/EffectApplication.h +++ b/Source/audio/EffectApplication.h @@ -6,10 +6,10 @@ class EffectApplication { public: EffectApplication() {}; - virtual Point apply(int index, Point input, const std::vector& values, double sampleRate) = 0; + virtual Point apply(int index, Point input, const std::vector>& values, double sampleRate) = 0; void resetPhase(); double nextPhase(double frequency, double sampleRate); private: double phase = 0.0; -}; \ No newline at end of file +}; diff --git a/Source/audio/PerspectiveEffect.cpp b/Source/audio/PerspectiveEffect.cpp index 32ed99e..9820641 100644 --- a/Source/audio/PerspectiveEffect.cpp +++ b/Source/audio/PerspectiveEffect.cpp @@ -7,9 +7,9 @@ PerspectiveEffect::PerspectiveEffect() {} PerspectiveEffect::~PerspectiveEffect() {} -Point PerspectiveEffect::apply(int index, Point input, const std::vector& values, double sampleRate) { - auto effectScale = values[0]; - auto focalLength = juce::jmax(values[1], 0.001); +Point PerspectiveEffect::apply(int index, Point input, const std::vector>& values, double sampleRate) { + auto effectScale = values[0].load(); + auto focalLength = juce::jmax(values[1].load(), 0.001); Vec3 origin = Vec3(0, 0, -focalLength); camera.setPosition(origin); diff --git a/Source/audio/PerspectiveEffect.h b/Source/audio/PerspectiveEffect.h index 97ce0c5..3126c22 100644 --- a/Source/audio/PerspectiveEffect.h +++ b/Source/audio/PerspectiveEffect.h @@ -9,7 +9,7 @@ public: PerspectiveEffect(); ~PerspectiveEffect(); - Point apply(int index, Point input, const std::vector& values, double sampleRate) override; + Point apply(int index, Point input, const std::vector>& values, double sampleRate) override; private: diff --git a/Source/audio/PitchDetector.cpp b/Source/audio/PitchDetector.cpp index c836ee9..8d9767e 100644 --- a/Source/audio/PitchDetector.cpp +++ b/Source/audio/PitchDetector.cpp @@ -6,13 +6,19 @@ PitchDetector::PitchDetector(OscirenderAudioProcessor& audioProcessor) : juce::T } PitchDetector::~PitchDetector() { - audioProcessor.consumerStop(consumer); + { + juce::CriticalSection::ScopedLockType scope(consumerLock); + audioProcessor.consumerStop(consumer); + } stopThread(1000); } void PitchDetector::run() { while (!threadShouldExit()) { - consumer = audioProcessor.consumerRegister(buffer); + { + juce::CriticalSection::ScopedLockType scope(consumerLock); + consumer = audioProcessor.consumerRegister(buffer); + } audioProcessor.consumerRead(consumer); // buffer is for 2 channels, so we need to only use one diff --git a/Source/audio/PitchDetector.h b/Source/audio/PitchDetector.h index 94f683e..c3b7259 100644 --- a/Source/audio/PitchDetector.h +++ b/Source/audio/PitchDetector.h @@ -20,6 +20,7 @@ private: static constexpr int fftOrder = 15; static constexpr int fftSize = 1 << fftOrder; + juce::CriticalSection consumerLock; std::shared_ptr consumer; std::vector buffer = std::vector(2 * fftSize); juce::dsp::FFT forwardFFT{fftOrder}; diff --git a/Source/audio/SmoothEffect.cpp b/Source/audio/SmoothEffect.cpp index 066998a..f81ab13 100644 --- a/Source/audio/SmoothEffect.cpp +++ b/Source/audio/SmoothEffect.cpp @@ -4,8 +4,8 @@ SmoothEffect::SmoothEffect() {} SmoothEffect::~SmoothEffect() {} -Point SmoothEffect::apply(int index, Point input, const std::vector& values, double sampleRate) { - double weight = juce::jmax(values[0], 0.00001); +Point SmoothEffect::apply(int index, Point input, const std::vector>& values, double sampleRate) { + double weight = juce::jmax(values[0].load(), 0.00001); weight *= 0.95; double strength = 10; weight = std::log(strength * weight + 1) / std::log(strength + 1); diff --git a/Source/audio/SmoothEffect.h b/Source/audio/SmoothEffect.h index 7067b65..723946d 100644 --- a/Source/audio/SmoothEffect.h +++ b/Source/audio/SmoothEffect.h @@ -7,7 +7,7 @@ public: SmoothEffect(); ~SmoothEffect(); - Point apply(int index, Point input, const std::vector& values, double sampleRate) override; + Point apply(int index, Point input, const std::vector>& values, double sampleRate) override; private: Point avg; -}; \ No newline at end of file +}; diff --git a/Source/audio/VectorCancellingEffect.cpp b/Source/audio/VectorCancellingEffect.cpp index ead3ad0..c41b68c 100644 --- a/Source/audio/VectorCancellingEffect.cpp +++ b/Source/audio/VectorCancellingEffect.cpp @@ -4,7 +4,7 @@ VectorCancellingEffect::VectorCancellingEffect() {} VectorCancellingEffect::~VectorCancellingEffect() {} -Point VectorCancellingEffect::apply(int index, Point input, const std::vector& values, double sampleRate) { +Point VectorCancellingEffect::apply(int index, Point input, const std::vector>& values, double sampleRate) { double value = values[0]; if (value < 0.001) { return input; diff --git a/Source/audio/VectorCancellingEffect.h b/Source/audio/VectorCancellingEffect.h index 38aac06..a0ef195 100644 --- a/Source/audio/VectorCancellingEffect.h +++ b/Source/audio/VectorCancellingEffect.h @@ -7,8 +7,8 @@ public: VectorCancellingEffect(); ~VectorCancellingEffect(); - Point apply(int index, Point input, const std::vector& values, double sampleRate) override; + Point apply(int index, Point input, const std::vector>& values, double sampleRate) override; private: int lastIndex = 0; double nextInvert = 0; -}; \ No newline at end of file +}; diff --git a/Source/audio/WobbleEffect.cpp b/Source/audio/WobbleEffect.cpp index f7619d6..0067b2c 100644 --- a/Source/audio/WobbleEffect.cpp +++ b/Source/audio/WobbleEffect.cpp @@ -4,7 +4,7 @@ WobbleEffect::WobbleEffect(PitchDetector& pitchDetector) : pitchDetector(pitchDe WobbleEffect::~WobbleEffect() {} -Point WobbleEffect::apply(int index, Point input, const std::vector& values, double sampleRate) { +Point WobbleEffect::apply(int index, Point input, const std::vector>& values, double sampleRate) { // TODO: this doesn't consider sample rate smoothedFrequency = smoothedFrequency * 0.99995 + pitchDetector.frequency * 0.00005; double theta = nextPhase(smoothedFrequency, sampleRate); diff --git a/Source/audio/WobbleEffect.h b/Source/audio/WobbleEffect.h index 2d30ca6..c33086b 100644 --- a/Source/audio/WobbleEffect.h +++ b/Source/audio/WobbleEffect.h @@ -8,9 +8,9 @@ public: WobbleEffect(PitchDetector& pitchDetector); ~WobbleEffect(); - Point apply(int index, Point input, const std::vector& values, double sampleRate) override; + Point apply(int index, Point input, const std::vector>& values, double sampleRate) override; private: PitchDetector& pitchDetector; double smoothedFrequency = 0; -}; \ No newline at end of file +}; diff --git a/Source/components/EffectsListComponent.cpp b/Source/components/EffectsListComponent.cpp index 6be859e..bfa1e5d 100644 --- a/Source/components/EffectsListComponent.cpp +++ b/Source/components/EffectsListComponent.cpp @@ -5,10 +5,6 @@ EffectsListComponent::EffectsListComponent(DraggableListBox& lb, AudioEffectListBoxItemData& data, int rn, Effect& effect) : DraggableListBoxItem(lb, data, rn), effect(effect), audioProcessor(data.audioProcessor), editor(data.editor) { - if (effect.enabled == nullptr) { - DBG("Effect enabled is null"); - } - auto parameters = effect.parameters; for (int i = 0; i < parameters.size(); i++) { std::shared_ptr effectComponent = std::make_shared(audioProcessor, effect, i); diff --git a/Source/components/SwitchButton.h b/Source/components/SwitchButton.h index b3743e3..4a1c947 100644 --- a/Source/components/SwitchButton.h +++ b/Source/components/SwitchButton.h @@ -78,9 +78,9 @@ public: void parameterValueChanged(int parameterIndex, float newValue) override { juce::WeakReference weakThis = this; - juce::MessageManager::callAsync([weakThis, this]() { + juce::MessageManager::callAsync([weakThis]() { if (weakThis != nullptr) { - setToggleState(parameter->getBoolValue(), juce::NotificationType::dontSendNotification); + weakThis->setToggleState(weakThis->parameter->getBoolValue(), juce::NotificationType::dontSendNotification); } }); } diff --git a/Source/components/VisualiserComponent.cpp b/Source/components/VisualiserComponent.cpp index fb107f2..e774355 100644 --- a/Source/components/VisualiserComponent.cpp +++ b/Source/components/VisualiserComponent.cpp @@ -1,10 +1,11 @@ #include "../LookAndFeel.h" #include "VisualiserComponent.h" -VisualiserComponent::VisualiserComponent(OscirenderAudioProcessor& p, VisualiserSettings& settings, VisualiserComponent* parent, bool useOldVisualiser) : settings(settings), backgroundColour(juce::Colours::black), waveformColour(juce::Colour(0xff00ff00)), audioProcessor(p), oldVisualiser(useOldVisualiser), juce::Thread("VisualiserComponent"), parent(parent) { - setVisualiserType(oldVisualiser); - +VisualiserComponent::VisualiserComponent(OscirenderAudioProcessor& p, VisualiserSettings& settings, VisualiserComponent* parent, bool useOldVisualiser) : settings(settings), backgroundColour(juce::Colours::black), waveformColour(juce::Colour(0xff00ff00)), audioProcessor(p), oldVisualiser(useOldVisualiser), juce::Thread("VisualiserComponent"), parent(parent) { resetBuffer(); + if (!oldVisualiser) { + initialiseBrowser(); + } startTimerHz(60); startThread(); @@ -49,7 +50,10 @@ VisualiserComponent::VisualiserComponent(OscirenderAudioProcessor& p, Visualiser } VisualiserComponent::~VisualiserComponent() { - audioProcessor.consumerStop(consumer); + { + juce::CriticalSection::ScopedLockType scope(consumerLock); + audioProcessor.consumerStop(consumer); + } stopThread(1000); masterReference.clear(); } @@ -127,16 +131,16 @@ void VisualiserComponent::run() { resetBuffer(); } - consumer = audioProcessor.consumerRegister(tempBuffer); + { + juce::CriticalSection::ScopedLockType scope(consumerLock); + consumer = audioProcessor.consumerRegister(tempBuffer); + } audioProcessor.consumerRead(consumer); + setBuffer(tempBuffer); - juce::WeakReference visualiser(this); if (!oldVisualiser) { - juce::MessageManager::callAsync([this, visualiser] () { - if (visualiser != nullptr && browser != nullptr) { - browser->emitEventIfBrowserIsVisible("audioUpdated", juce::Base64::toBase64(buffer.data(), buffer.size() * sizeof(float))); - } - }); + audioUpdated = true; + triggerAsyncUpdate(); } } } @@ -147,7 +151,10 @@ void VisualiserComponent::setPaused(bool paused) { startTimerHz(60); startThread(); } else { - audioProcessor.consumerStop(consumer); + { + juce::CriticalSection::ScopedLockType scope(consumerLock); + audioProcessor.consumerStop(consumer); + } stopTimer(); stopThread(1000); } @@ -220,7 +227,11 @@ void VisualiserComponent::setVisualiserType(bool oldVisualiser) { child->setVisualiserType(oldVisualiser); } if (oldVisualiser) { - browser.reset(); + oldBrowser = std::move(browser); + if (oldBrowser != nullptr) { + removeChildComponent(oldBrowser.get()); + oldBrowser->goToURL("about:blank"); + } if (closeSettings != nullptr) { closeSettings(); } @@ -260,6 +271,12 @@ void VisualiserComponent::paintXY(juce::Graphics& g, juce::Rectangle area } void VisualiserComponent::initialiseBrowser() { + oldBrowser = std::move(browser); + if (oldBrowser != nullptr) { + removeChildComponent(oldBrowser.get()); + oldBrowser->goToURL("about:blank"); + } + browser = std::make_unique( juce::WebBrowserComponent::Options() .withNativeIntegrationEnabled() @@ -315,9 +332,20 @@ void VisualiserComponent::resetBuffer() { sampleRate = (int) audioProcessor.currentSampleRate; tempBuffer = std::vector(2 * sampleRate * BUFFER_LENGTH_SECS); if (!oldVisualiser && isShowing()) { - juce::MessageManager::callAsync([this] () { - initialiseBrowser(); - }); + restartBrowser = true; + triggerAsyncUpdate(); + } +} + +void VisualiserComponent::handleAsyncUpdate() { + if (restartBrowser) { + initialiseBrowser(); + restartBrowser = false; + } + if (audioUpdated && browser != nullptr) { + juce::CriticalSection::ScopedLockType scope(lock); + browser->emitEventIfBrowserIsVisible("audioUpdated", juce::Base64::toBase64(buffer.data(), buffer.size() * sizeof(float))); + audioUpdated = false; } } @@ -339,7 +367,7 @@ void VisualiserComponent::resized() { } void VisualiserComponent::childChanged() { - if (!oldVisualiser) { + if (!oldVisualiser && browser != nullptr) { browser->emitEventIfBrowserIsVisible("childPresent", child != nullptr); } } diff --git a/Source/components/VisualiserComponent.h b/Source/components/VisualiserComponent.h index 547d90a..cbaa616 100644 --- a/Source/components/VisualiserComponent.h +++ b/Source/components/VisualiserComponent.h @@ -16,7 +16,7 @@ enum class FullScreenMode { }; class VisualiserWindow; -class VisualiserComponent : public juce::Component, public juce::Timer, public juce::Thread, public juce::MouseListener, public juce::SettableTooltipClient { +class VisualiserComponent : public juce::Component, public juce::Timer, public juce::Thread, public juce::MouseListener, public juce::SettableTooltipClient, public juce::AsyncUpdater { public: VisualiserComponent(OscirenderAudioProcessor& p, VisualiserSettings& settings, VisualiserComponent* parent = nullptr, bool useOldVisualiser = false); ~VisualiserComponent() override; @@ -41,6 +41,7 @@ public: bool keyPressed(const juce::KeyPress& key) override; void setFullScreen(bool fullScreen); void setVisualiserType(bool oldVisualiser); + void handleAsyncUpdate() override; VisualiserComponent* parent = nullptr; VisualiserComponent* child = nullptr; @@ -52,12 +53,13 @@ private: const double BUFFER_LENGTH_SECS = 0.02; const double DEFAULT_SAMPLE_RATE = 192000.0; - + std::atomic restartBrowser = false; + std::atomic audioUpdated = false; std::atomic timerId; std::atomic lastMouseX; std::atomic lastMouseY; - bool oldVisualiser; + std::atomic oldVisualiser; juce::CriticalSection lock; std::vector buffer; @@ -75,6 +77,7 @@ private: std::vector tempBuffer; int precision = 4; + juce::CriticalSection consumerLock; std::shared_ptr consumer; std::function fullScreenCallback; @@ -110,14 +113,15 @@ private: }; std::unique_ptr browser = nullptr; + // keeping this around for memory management reasons + std::unique_ptr oldBrowser = nullptr; void initialiseBrowser(); void resetBuffer(); void popoutWindow(); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(VisualiserComponent) - juce::WeakReference::Master masterReference; - friend class juce::WeakReference; + JUCE_DECLARE_WEAK_REFERENCEABLE(VisualiserComponent) }; class VisualiserWindow : public juce::DocumentWindow { diff --git a/Source/components/VolumeComponent.cpp b/Source/components/VolumeComponent.cpp index 1fd8349..9323146 100644 --- a/Source/components/VolumeComponent.cpp +++ b/Source/components/VolumeComponent.cpp @@ -49,7 +49,10 @@ VolumeComponent::VolumeComponent(OscirenderAudioProcessor& p) : audioProcessor(p } VolumeComponent::~VolumeComponent() { - audioProcessor.consumerStop(consumer); + { + juce::CriticalSection::ScopedLockType lock(consumerLock); + audioProcessor.consumerStop(consumer); + } stopThread(1000); } @@ -103,7 +106,10 @@ void VolumeComponent::run() { continue; } - consumer = audioProcessor.consumerRegister(buffer); + { + juce::CriticalSection::ScopedLockType lock(consumerLock); + consumer = audioProcessor.consumerRegister(buffer); + } audioProcessor.consumerRead(consumer); float leftVolume = 0; diff --git a/Source/components/VolumeComponent.h b/Source/components/VolumeComponent.h index 3724d2a..5b7c233 100644 --- a/Source/components/VolumeComponent.h +++ b/Source/components/VolumeComponent.h @@ -91,6 +91,7 @@ private: std::unique_ptr volumeIcon; std::unique_ptr thresholdIcon; + juce::CriticalSection consumerLock; std::shared_ptr consumer; void resetBuffer(); diff --git a/osci-render.jucer b/osci-render.jucer index ac3c0d1..87e2f80 100644 --- a/osci-render.jucer +++ b/osci-render.jucer @@ -684,7 +684,7 @@ bigIcon="pSc1mq" applicationCategory="public.app-category.music"> - +