diff --git a/Source/MainComponent.cpp b/Source/MainComponent.cpp index c783a2b..2917069 100644 --- a/Source/MainComponent.cpp +++ b/Source/MainComponent.cpp @@ -79,17 +79,10 @@ MainComponent::MainComponent(OscirenderAudioProcessor& p, OscirenderAudioProcess }; addAndMakeVisible(visualiser); - audioProcessor.audioProducer.registerConsumer(consumer); - visualiserProcessor.startThread(); - addAndMakeVisible(frequencyLabel); - pitchDetector.startThread(); } -MainComponent::~MainComponent() { - audioProcessor.audioProducer.unregisterConsumer(consumer); - visualiserProcessor.stopThread(1000); -} +MainComponent::~MainComponent() {} void MainComponent::updateFileLabel() { if (audioProcessor.getCurrentFileIndex() == -1) { diff --git a/Source/MainComponent.h b/Source/MainComponent.h index a05ce88..b9e29b5 100644 --- a/Source/MainComponent.h +++ b/Source/MainComponent.h @@ -29,8 +29,6 @@ private: juce::TextButton createFile{"Create File"}; VisualiserComponent visualiser{2, audioProcessor}; - std::shared_ptr consumer = std::make_shared(2048); - VisualiserProcessor visualiserProcessor{consumer, visualiser}; juce::Label frequencyLabel; PitchDetector pitchDetector{ diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 452b5f4..e8d3597 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -1,22 +1,14 @@ -/* - ============================================================================== - - This file contains the basic framework code for a JUCE plugin editor. - - ============================================================================== -*/ - #include "PluginProcessor.h" #include "PluginEditor.h" -//============================================================================== OscirenderAudioProcessorEditor::OscirenderAudioProcessorEditor(OscirenderAudioProcessor& p) - : AudioProcessorEditor(&p), audioProcessor(p), effects(p), main(p, *this), collapseButton("Collapse", juce::Colours::white, juce::Colours::white, juce::Colours::white), lua(p, *this), obj(p, *this) + : AudioProcessorEditor(&p), audioProcessor(p), collapseButton("Collapse", juce::Colours::white, juce::Colours::white, juce::Colours::white) { addAndMakeVisible(effects); addAndMakeVisible(main); addChildComponent(lua); addChildComponent(obj); + addAndMakeVisible(volume); addAndMakeVisible(collapseButton); collapseButton.onClick = [this] { @@ -54,8 +46,7 @@ OscirenderAudioProcessorEditor::OscirenderAudioProcessorEditor(OscirenderAudioPr OscirenderAudioProcessorEditor::~OscirenderAudioProcessorEditor() {} -//============================================================================== -void OscirenderAudioProcessorEditor::paint (juce::Graphics& g) +void OscirenderAudioProcessorEditor::paint(juce::Graphics& g) { g.fillAll(getLookAndFeel().findColour(juce::ResizableWindow::backgroundColourId)); @@ -65,6 +56,7 @@ void OscirenderAudioProcessorEditor::paint (juce::Graphics& g) void OscirenderAudioProcessorEditor::resized() { auto area = getLocalBounds(); + volume.setBounds(area.removeFromLeft(50)); auto sections = 2; int index = audioProcessor.getCurrentFileIndex(); if (index != -1) { diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index de9d5f3..cf9b973 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -6,14 +6,15 @@ #include "MainComponent.h" #include "LuaComponent.h" #include "ObjComponent.h" +#include "components/VolumeComponent.h" class OscirenderAudioProcessorEditor : public juce::AudioProcessorEditor, private juce::CodeDocument::Listener { public: - OscirenderAudioProcessorEditor (OscirenderAudioProcessor&); + OscirenderAudioProcessorEditor(OscirenderAudioProcessor&); ~OscirenderAudioProcessorEditor() override; - void paint (juce::Graphics&) override; + void paint(juce::Graphics&) override; void resized() override; void addCodeEditor(int index); @@ -22,10 +23,11 @@ public: private: OscirenderAudioProcessor& audioProcessor; - MainComponent main; - LuaComponent lua; - ObjComponent obj; - EffectsComponent effects; + MainComponent main{audioProcessor, *this}; + LuaComponent lua{audioProcessor, *this}; + ObjComponent obj{audioProcessor, *this}; + EffectsComponent effects{audioProcessor}; + VolumeComponent volume{audioProcessor}; std::vector> codeDocuments; std::vector> codeEditors; juce::LuaTokeniser luaTokeniser; diff --git a/Source/audio/PitchDetector.cpp b/Source/audio/PitchDetector.cpp index 5ebb9f8..8de3eb3 100644 --- a/Source/audio/PitchDetector.cpp +++ b/Source/audio/PitchDetector.cpp @@ -1,7 +1,9 @@ #include "PitchDetector.h" #include "PitchDetector.h" -PitchDetector::PitchDetector(OscirenderAudioProcessor& p, std::function frequencyCallback) : juce::Thread("PitchDetector"), audioProcessor(p), frequencyCallback(frequencyCallback) {} +PitchDetector::PitchDetector(OscirenderAudioProcessor& p, std::function frequencyCallback) : juce::Thread("PitchDetector"), audioProcessor(p), frequencyCallback(frequencyCallback) { + startThread(); +} PitchDetector::~PitchDetector() { audioProcessor.audioProducer.unregisterConsumer(consumer); diff --git a/Source/components/VisualiserComponent.cpp b/Source/components/VisualiserComponent.cpp index fba115f..f6d3924 100644 --- a/Source/components/VisualiserComponent.cpp +++ b/Source/components/VisualiserComponent.cpp @@ -1,11 +1,15 @@ #include "VisualiserComponent.h" -VisualiserComponent::VisualiserComponent(int numChannels, OscirenderAudioProcessor& p) : numChannels(numChannels), backgroundColour(juce::Colours::black), waveformColour(juce::Colour(0xff00ff00)), audioProcessor(p) { +VisualiserComponent::VisualiserComponent(int numChannels, OscirenderAudioProcessor& p) : numChannels(numChannels), backgroundColour(juce::Colours::black), waveformColour(juce::Colour(0xff00ff00)), audioProcessor(p), juce::Thread("VisualiserComponent") { setOpaque(true); startTimerHz(60); + startThread(); } -VisualiserComponent::~VisualiserComponent() {} +VisualiserComponent::~VisualiserComponent() { + audioProcessor.audioProducer.unregisterConsumer(consumer); + stopThread(1000); +} void VisualiserComponent::setBuffer(std::vector& newBuffer) { juce::SpinLock::ScopedLockType scope(lock); @@ -40,6 +44,16 @@ void VisualiserComponent::timerCallback() { repaint(); } +void VisualiserComponent::run() { + audioProcessor.audioProducer.registerConsumer(consumer); + + while (!threadShouldExit()) { + auto buffer = consumer->startProcessing(); + setBuffer(*buffer); + consumer->finishedProcessing(); + } +} + void VisualiserComponent::paintChannel(juce::Graphics& g, juce::Rectangle area, int channel) { juce::Path path; diff --git a/Source/components/VisualiserComponent.h b/Source/components/VisualiserComponent.h index 1d39a73..248770e 100644 --- a/Source/components/VisualiserComponent.h +++ b/Source/components/VisualiserComponent.h @@ -4,7 +4,7 @@ #include "../concurrency/BufferConsumer.h" #include "../PluginProcessor.h" -class VisualiserComponent : public juce::Component, public juce::Timer { +class VisualiserComponent : public juce::Component, public juce::Timer, public juce::Thread { public: VisualiserComponent(int numChannels, OscirenderAudioProcessor& p); ~VisualiserComponent() override; @@ -15,6 +15,7 @@ public: void paintXY(juce::Graphics&, juce::Rectangle bounds); void paint(juce::Graphics&) override; void timerCallback() override; + void run() override; private: juce::SpinLock lock; @@ -22,26 +23,8 @@ private: int numChannels = 2; juce::Colour backgroundColour, waveformColour; OscirenderAudioProcessor& audioProcessor; + std::shared_ptr consumer = std::make_shared(2048); int precision = 2; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(VisualiserComponent) -}; - -class VisualiserProcessor : public juce::Thread { -public: - VisualiserProcessor(std::shared_ptr consumer, VisualiserComponent& visualiser) : juce::Thread("VisualiserProcessor"), consumer(consumer), visualiser(visualiser) {} - ~VisualiserProcessor() override {} - - void run() override { - while (!threadShouldExit()) { - auto buffer = consumer->startProcessing(); - - visualiser.setBuffer(*buffer); - consumer->finishedProcessing(); - } - } - -private: - std::shared_ptr consumer; - VisualiserComponent& visualiser; }; \ No newline at end of file diff --git a/Source/components/VolumeComponent.cpp b/Source/components/VolumeComponent.cpp new file mode 100644 index 0000000..1d954e1 --- /dev/null +++ b/Source/components/VolumeComponent.cpp @@ -0,0 +1,95 @@ +#include "VolumeComponent.h" + +VolumeComponent::VolumeComponent(OscirenderAudioProcessor& p) : audioProcessor(p), juce::Thread("VolumeComponent") { + setOpaque(false); + startTimerHz(60); + startThread(); + + addAndMakeVisible(volumeSlider); + volumeSlider.setSliderStyle(juce::Slider::SliderStyle::LinearVertical); + volumeSlider.setTextBoxStyle(juce::Slider::NoTextBox, true, 0, 0); + volumeSlider.setColour(juce::Slider::ColourIds::backgroundColourId, juce::Colours::transparentWhite); + volumeSlider.setColour(juce::Slider::ColourIds::trackColourId, juce::Colours::transparentWhite); + volumeSlider.setOpaque(false); + volumeSlider.setRange(0, 1, 0.001); + + addAndMakeVisible(thresholdSlider); + thresholdSlider.setSliderStyle(juce::Slider::SliderStyle::LinearVertical); + thresholdSlider.setTextBoxStyle(juce::Slider::NoTextBox, true, 0, 0); + thresholdSlider.setColour(juce::Slider::ColourIds::backgroundColourId, juce::Colours::transparentWhite); + thresholdSlider.setColour(juce::Slider::ColourIds::trackColourId, juce::Colours::transparentWhite); + thresholdSlider.setOpaque(false); + thresholdSlider.setRange(0, 1, 0.001); +} + +VolumeComponent::~VolumeComponent() { + audioProcessor.audioProducer.unregisterConsumer(consumer); + stopThread(1000); +} + +void VolumeComponent::paint(juce::Graphics& g) { + auto r = getLocalBounds().toFloat(); + r.removeFromRight(r.getWidth() / 2); + + g.setColour(juce::Colours::white); + g.fillRect(r); + + auto channelHeight = r.getHeight(); + auto leftVolumeHeight = channelHeight * leftVolume; + auto rightVolumeHeight = channelHeight * rightVolume; + + auto leftRect = r.removeFromLeft(r.getWidth() / 2); + auto rightRect = r; + auto leftRegion = leftRect; + auto rightRegion = rightRect; + + g.setGradientFill(juce::ColourGradient(juce::Colours::green, 0, leftRect.getBottom(), juce::Colours::red, 0, leftRect.getY(), false)); + g.fillRect(leftRect.removeFromBottom(leftVolumeHeight)); + + g.setGradientFill(juce::ColourGradient(juce::Colours::green, 0, rightRect.getBottom(), juce::Colours::red, 0, rightRect.getY(), false)); + g.fillRect(rightRect.removeFromBottom(rightVolumeHeight)); + + // draw average volume as new rectangles on each channel 10 pixels tall + g.setColour(juce::Colours::black); + g.fillRect(leftRegion.getX(), leftRegion.getBottom() - (avgLeftVolume * channelHeight), leftRegion.getWidth(), 5.0f); + g.fillRect(rightRegion.getX(), rightRegion.getBottom() - (avgRightVolume * channelHeight), rightRegion.getWidth(), 5.0f); + +} + +void VolumeComponent::timerCallback() { + repaint(); +} + +void VolumeComponent::run() { + audioProcessor.audioProducer.registerConsumer(consumer); + + while (!threadShouldExit()) { + auto buffer = consumer->startProcessing(); + + float leftVolume = 0; + float rightVolume = 0; + + for (int i = 0; i < buffer->size(); i += 2) { + leftVolume += buffer->at(i) * buffer->at(i); + rightVolume += buffer->at(i + 1) * buffer->at(i + 1); + } + // RMS + leftVolume = std::sqrt(leftVolume / (buffer->size() / 2)); + rightVolume = std::sqrt(rightVolume / (buffer->size() / 2)); + + this->leftVolume = leftVolume; + this->rightVolume = rightVolume; + + avgLeftVolume = (avgLeftVolume * 0.95) + (leftVolume * 0.05); + avgRightVolume = (avgRightVolume * 0.95) + (rightVolume * 0.05); + + consumer->finishedProcessing(); + } +} + +void VolumeComponent::resized() { + auto r = getLocalBounds(); + + volumeSlider.setBounds(r.removeFromLeft(r.getWidth() / 2)); + thresholdSlider.setBounds(r); +} diff --git a/Source/components/VolumeComponent.h b/Source/components/VolumeComponent.h new file mode 100644 index 0000000..7dbdd4d --- /dev/null +++ b/Source/components/VolumeComponent.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include "../concurrency/BufferConsumer.h" +#include "../PluginProcessor.h" + +class VolumeComponent : public juce::Component, public juce::Timer, public juce::Thread { +public: + VolumeComponent(OscirenderAudioProcessor& p); + ~VolumeComponent() override; + + void paint(juce::Graphics&) override; + void timerCallback() override; + void run() override; + void resized() override; + +private: + OscirenderAudioProcessor& audioProcessor; + std::shared_ptr consumer = std::make_shared(1 << 11); + + std::atomic leftVolume = 0; + std::atomic rightVolume = 0; + std::atomic avgLeftVolume = 0; + std::atomic avgRightVolume = 0; + + juce::Slider volumeSlider; + juce::Slider thresholdSlider; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(VolumeComponent) +}; \ No newline at end of file diff --git a/osci-render.jucer b/osci-render.jucer index 4263097..9d67205 100644 --- a/osci-render.jucer +++ b/osci-render.jucer @@ -127,6 +127,10 @@ file="Source/components/VisualiserComponent.cpp"/> + +