diff --git a/Resources/oscilloscope/oscilloscope.html b/Resources/oscilloscope/oscilloscope.html index ae76d74b..69138ccc 100644 --- a/Resources/oscilloscope/oscilloscope.html +++ b/Resources/oscilloscope/oscilloscope.html @@ -103,7 +103,8 @@ sweepMsDiv : 1, sweepTriggerValue : 0, mainGain : 0.0, - exposureStops : 0.0, + brightness : 0.0, + intensity: 0.02, hue : 125, invertXY : false, grid : true, diff --git a/Resources/oscilloscope/oscilloscope.js b/Resources/oscilloscope/oscilloscope.js index 5af39a69..baf4cc3f 100644 --- a/Resources/oscilloscope/oscilloscope.js +++ b/Resources/oscilloscope/oscilloscope.js @@ -297,7 +297,7 @@ var Render = this.activateTargetTexture(null); this.setShader(this.outputShader); - var brightness = Math.pow(2, controls.exposureStops-2.0); + var brightness = Math.pow(2, controls.brightness-2.0); //if (controls.disableFilter) brightness *= Filter.steps; gl.uniform1f(this.outputShader.uExposure, brightness); gl.uniform1f(this.outputShader.uResizeForCanvas, this.lineTexture.width/1024); @@ -433,7 +433,7 @@ var Render = if (controls.invertXY) gl.uniform1f(program.uInvert, -1.0); else gl.uniform1f(program.uInvert, 1.0); - var intensity = 0.02 * (41000 / externalSampleRate); + var intensity = controls.intensity * (41000 / externalSampleRate); if (controls.disableFilter) gl.uniform1f(program.uIntensity, intensity *(Filter.steps+1.5)); // +1.5 needed above for some reason for the brightness to match @@ -688,7 +688,8 @@ function doScriptProcessor(bufferBase64) { const getSettingsFn = Juce.getNativeFunction("getSettings"); getSettingsFn().then(settings => { - controls.exposureStops = settings.intensity; + controls.brightness = settings.brightness; + controls.intensity = settings.intensity; controls.persistence = settings.persistence; controls.hue = settings.hue; controls.disableFilter = !settings.upsampling; diff --git a/Source/MainComponent.cpp b/Source/MainComponent.cpp index 3f6cad46..f87d1821 100644 --- a/Source/MainComponent.cpp +++ b/Source/MainComponent.cpp @@ -10,7 +10,7 @@ MainComponent::MainComponent(OscirenderAudioProcessor& p, OscirenderAudioProcess fileButton.setButtonText("Choose File(s)"); fileButton.onClick = [this] { - chooser = std::make_unique("Open", audioProcessor.lastOpenedDirectory, "*.obj;*.svg;*.lua;*.txt;*.gpla;*.gif;*.png;*.jpg;*.jpeg"); + chooser = std::make_unique("Open", audioProcessor.lastOpenedDirectory, "*.obj;*.svg;*.lua;*.txt;*.gpla;*.gif;*.png;*.jpg;*.jpeg;*.wav;*.aiff"); auto flags = juce::FileBrowserComponent::openMode | juce::FileBrowserComponent::canSelectMultipleItems | juce::FileBrowserComponent::canSelectFiles; diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 7f1769bf..d77af7e4 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -102,6 +102,22 @@ OscirenderAudioProcessorEditor::OscirenderAudioProcessorEditor(OscirenderAudioPr addAndMakeVisible(lua); addAndMakeVisible(luaResizerBar); addAndMakeVisible(visualiser); + + visualiser.openSettings = [this] { + visualiserSettingsWindow.setVisible(true); + visualiserSettingsWindow.toFront(true); + }; + + visualiser.closeSettings = [this] { + visualiserSettingsWindow.setVisible(false); + }; + + visualiserSettingsWindow.setResizable(false, false); + visualiserSettingsWindow.setUsingNativeTitleBar(true); + visualiserSettings.setLookAndFeel(&getLookAndFeel()); + visualiserSettings.setSize(550, 260); + visualiserSettingsWindow.setContentNonOwned(&visualiserSettings, true); + visualiserSettingsWindow.centreWithSize(550, 260); tooltipDropShadow.setOwner(&tooltipWindow); } @@ -121,7 +137,7 @@ OscirenderAudioProcessorEditor::~OscirenderAudioProcessorEditor() { } bool OscirenderAudioProcessorEditor::isBinaryFile(juce::String name) { - return name.endsWith(".gpla") || name.endsWith(".gif") || name.endsWith(".png") || name.endsWith(".jpg") || name.endsWith(".jpeg"); + return name.endsWith(".gpla") || name.endsWith(".gif") || name.endsWith(".png") || name.endsWith(".jpg") || name.endsWith(".jpeg") || name.endsWith(".wav") || name.endsWith(".aiff"); } // parsersLock must be held diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index 8f367e6f..0e5ad9bb 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -9,6 +9,7 @@ #include "LookAndFeel.h" #include "components/ErrorCodeEditorComponent.h" #include "components/LuaConsole.h" +#include "components/VisualiserSettings.h" class OscirenderAudioProcessorEditor : public juce::AudioProcessorEditor, private juce::CodeDocument::Listener, public juce::AsyncUpdater, public juce::ChangeListener { public: @@ -48,7 +49,10 @@ class OscirenderAudioProcessorEditor : public juce::AudioProcessorEditor, privat std::atomic editingCustomFunction = false; - VisualiserComponent visualiser{audioProcessor, nullptr, audioProcessor.legacyVisualiserEnabled->getBoolValue()}; + VisualiserSettings visualiserSettings = VisualiserSettings(audioProcessor); + SettingsWindow visualiserSettingsWindow = SettingsWindow("Visualiser Settings"); + VisualiserComponent visualiser{audioProcessor, visualiserSettings, nullptr, audioProcessor.legacyVisualiserEnabled->getBoolValue()}; + SettingsComponent settings{audioProcessor, *this}; juce::ComponentAnimator codeEditorAnimator; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 6e44d0aa..7bb6e5d6 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -149,6 +149,7 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() permanentEffects.push_back(thresholdEffect); permanentEffects.push_back(imageThreshold); permanentEffects.push_back(imageStride); + permanentEffects.push_back(brightnessEffect); permanentEffects.push_back(intensityEffect); permanentEffects.push_back(persistenceEffect); permanentEffects.push_back(hueEffect); diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index 3cbe9caa..b7f198a2 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -180,10 +180,18 @@ class OscirenderAudioProcessor : public juce::AudioProcessor, juce::AudioProces VERSION_HINT, 125, 0, 359, 1 ) ); + std::shared_ptr brightnessEffect = std::make_shared( + new EffectParameter( + "Brightness", + "Controls how bright the light glows for on the oscilloscope display.", + "brightness", + VERSION_HINT, 3.0, 0.0, 10.0 + ) + ); std::shared_ptr intensityEffect = std::make_shared( new EffectParameter( "Intensity", - "Controls how bright the light glows for on the oscilloscope display.", + "Controls how bright the electron beam of the oscilloscope is.", "intensity", VERSION_HINT, 3.0, 0.0, 10.0 ) diff --git a/Source/components/VisualiserComponent.cpp b/Source/components/VisualiserComponent.cpp index ee49ff15..fb107f2d 100644 --- a/Source/components/VisualiserComponent.cpp +++ b/Source/components/VisualiserComponent.cpp @@ -1,20 +1,13 @@ #include "../LookAndFeel.h" #include "VisualiserComponent.h" -VisualiserComponent::VisualiserComponent(OscirenderAudioProcessor& p, VisualiserComponent* parent, bool useOldVisualiser) : backgroundColour(juce::Colours::black), waveformColour(juce::Colour(0xff00ff00)), audioProcessor(p), oldVisualiser(useOldVisualiser), juce::Thread("VisualiserComponent"), parent(parent) { +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); resetBuffer(); startTimerHz(60); startThread(); - settingsWindow.setResizable(false, false); - settingsWindow.setUsingNativeTitleBar(true); - settings.setLookAndFeel(&getLookAndFeel()); - settings.setSize(550, 230); - settingsWindow.setContentNonOwned(&settings, true); - settingsWindow.centreWithSize(550, 230); - setMouseCursor(juce::MouseCursor::PointingHandCursor); setWantsKeyboardFocus(true); @@ -228,6 +221,9 @@ void VisualiserComponent::setVisualiserType(bool oldVisualiser) { } if (oldVisualiser) { browser.reset(); + if (closeSettings != nullptr) { + closeSettings(); + } } else { initialiseBrowser(); } @@ -349,8 +345,10 @@ void VisualiserComponent::childChanged() { } void VisualiserComponent::popoutWindow() { - auto visualiser = new VisualiserComponent(audioProcessor, this, oldVisualiser); + auto visualiser = new VisualiserComponent(audioProcessor, settings, this, oldVisualiser); visualiser->settings.setLookAndFeel(&getLookAndFeel()); + visualiser->openSettings = openSettings; + visualiser->closeSettings = closeSettings; child = visualiser; childChanged(); popOutButton.setVisible(false); @@ -365,8 +363,3 @@ void VisualiserComponent::popoutWindow() { resized(); popOutButton.setVisible(false); } - -void VisualiserComponent::openSettings() { - settingsWindow.setVisible(true); - settingsWindow.toFront(true); -} diff --git a/Source/components/VisualiserComponent.h b/Source/components/VisualiserComponent.h index 85e9b7c9..547d90af 100644 --- a/Source/components/VisualiserComponent.h +++ b/Source/components/VisualiserComponent.h @@ -18,10 +18,12 @@ enum class FullScreenMode { class VisualiserWindow; class VisualiserComponent : public juce::Component, public juce::Timer, public juce::Thread, public juce::MouseListener, public juce::SettableTooltipClient { public: - VisualiserComponent(OscirenderAudioProcessor& p, VisualiserComponent* parent = nullptr, bool useOldVisualiser = false); + VisualiserComponent(OscirenderAudioProcessor& p, VisualiserSettings& settings, VisualiserComponent* parent = nullptr, bool useOldVisualiser = false); ~VisualiserComponent() override; - void openSettings(); + std::function openSettings; + std::function closeSettings; + void childChanged(); void enableFullScreen(); void setFullScreenCallback(std::function callback); @@ -76,8 +78,8 @@ class VisualiserComponent : public juce::Component, public juce::Timer, public j std::shared_ptr consumer; std::function fullScreenCallback; - VisualiserSettings settings = VisualiserSettings(audioProcessor, *this); - SettingsWindow settingsWindow = SettingsWindow("Visualiser Settings"); + + VisualiserSettings& settings; juce::WebBrowserComponent::ResourceProvider provider = [this](const juce::String& path) { juce::String mimeType; diff --git a/Source/components/VisualiserSettings.cpp b/Source/components/VisualiserSettings.cpp index af78ce7a..b83b5900 100644 --- a/Source/components/VisualiserSettings.cpp +++ b/Source/components/VisualiserSettings.cpp @@ -2,14 +2,17 @@ #include "VisualiserComponent.h" #include "../PluginEditor.h" -VisualiserSettings::VisualiserSettings(OscirenderAudioProcessor& p, VisualiserComponent& visualiser) : audioProcessor(p), visualiser(visualiser) { - addAndMakeVisible(intensity); + +VisualiserSettings::VisualiserSettings(OscirenderAudioProcessor& p) : audioProcessor(p) { + addAndMakeVisible(brightness); + addAndMakeVisible(intensity); addAndMakeVisible(persistence); addAndMakeVisible(hue); addAndMakeVisible(graticuleToggle); addAndMakeVisible(smudgeToggle); addAndMakeVisible(upsamplingToggle); + brightness.setSliderOnValueChange(); intensity.setSliderOnValueChange(); persistence.setSliderOnValueChange(); hue.setSliderOnValueChange(); @@ -20,6 +23,7 @@ VisualiserSettings::~VisualiserSettings() {} void VisualiserSettings::resized() { auto area = getLocalBounds().reduced(20); double rowHeight = 30; + brightness.setBounds(area.removeFromTop(rowHeight)); intensity.setBounds(area.removeFromTop(rowHeight)); persistence.setBounds(area.removeFromTop(rowHeight)); hue.setBounds(area.removeFromTop(rowHeight)); @@ -30,7 +34,8 @@ void VisualiserSettings::resized() { juce::var VisualiserSettings::getSettings() { auto settings = new juce::DynamicObject(); - settings->setProperty("intensity", audioProcessor.intensityEffect->getActualValue() - 2); + settings->setProperty("brightness", audioProcessor.brightnessEffect->getActualValue() - 2); + settings->setProperty("intensity", audioProcessor.intensityEffect->getActualValue() / 100); settings->setProperty("persistence", audioProcessor.persistenceEffect->getActualValue() - 1.33); settings->setProperty("hue", audioProcessor.hueEffect->getActualValue()); settings->setProperty("graticule", audioProcessor.graticuleEnabled->getBoolValue()); diff --git a/Source/components/VisualiserSettings.h b/Source/components/VisualiserSettings.h index 5ea48f6b..5be228dd 100644 --- a/Source/components/VisualiserSettings.h +++ b/Source/components/VisualiserSettings.h @@ -6,18 +6,17 @@ #include "../LookAndFeel.h" #include "SwitchButton.h" -class VisualiserComponent; class VisualiserSettings : public juce::Component { public: - VisualiserSettings(OscirenderAudioProcessor&, VisualiserComponent&); + VisualiserSettings(OscirenderAudioProcessor&); ~VisualiserSettings(); void resized() override; juce::var getSettings(); private: OscirenderAudioProcessor& audioProcessor; - VisualiserComponent& visualiser; + EffectComponent brightness{audioProcessor, *audioProcessor.brightnessEffect}; EffectComponent intensity{audioProcessor, *audioProcessor.intensityEffect}; EffectComponent persistence{audioProcessor, *audioProcessor.persistenceEffect}; EffectComponent hue{audioProcessor, *audioProcessor.hueEffect}; diff --git a/Source/parser/FileParser.cpp b/Source/parser/FileParser.cpp index 6d8e7e84..568b3236 100644 --- a/Source/parser/FileParser.cpp +++ b/Source/parser/FileParser.cpp @@ -18,6 +18,7 @@ void FileParser::parse(juce::String fileId, juce::String extension, std::unique_ gpla = nullptr; lua = nullptr; img = nullptr; + wav = nullptr; if (extension == ".obj") { object = std::make_shared(stream->readEntireStreamAsString().toStdString()); @@ -33,10 +34,12 @@ void FileParser::parse(juce::String fileId, juce::String extension, std::unique_ juce::MemoryBlock buffer{}; int bytesRead = stream->readIntoMemoryBlock(buffer); img = std::make_shared(audioProcessor, extension, buffer); + } else if (extension == ".wav" || extension == ".aiff") { + wav = std::make_shared(audioProcessor, std::move(stream)); } isAnimatable = gpla != nullptr || (img != nullptr && extension == ".gif"); - sampleSource = lua != nullptr || img != nullptr; + sampleSource = lua != nullptr || img != nullptr || wav != nullptr; } std::vector> FileParser::nextFrame() { @@ -72,7 +75,9 @@ Point FileParser::nextSample(lua_State*& L, LuaVariables& vars) { } } else if (img != nullptr) { return img->getSample(); - } + } else if (wav != nullptr) { + return wav->getSample(); + } return Point(); } @@ -122,3 +127,7 @@ std::shared_ptr FileParser::getLua() { std::shared_ptr FileParser::getImg() { return img; } + +std::shared_ptr FileParser::getWav() { + return wav; +} diff --git a/Source/parser/FileParser.h b/Source/parser/FileParser.h index 5750df93..5755fc7d 100644 --- a/Source/parser/FileParser.h +++ b/Source/parser/FileParser.h @@ -8,6 +8,7 @@ #include "../gpla/LineArtParser.h" #include "../lua/LuaParser.h" #include "../img/ImageParser.h" +#include "../wav/WavParser.h" class OscirenderAudioProcessor; class FileParser { @@ -29,6 +30,7 @@ class FileParser { std::shared_ptr getLineArt(); std::shared_ptr getLua(); std::shared_ptr getImg(); + std::shared_ptr getWav(); bool isAnimatable = false; @@ -46,6 +48,7 @@ class FileParser { std::shared_ptr gpla; std::shared_ptr lua; std::shared_ptr img; + std::shared_ptr wav; juce::String fallbackLuaScript = "return { 0.0, 0.0 }"; diff --git a/Source/wav/WavParser.cpp b/Source/wav/WavParser.cpp new file mode 100644 index 00000000..45b1fed2 --- /dev/null +++ b/Source/wav/WavParser.cpp @@ -0,0 +1,43 @@ +#include "WavParser.h" +#include "../PluginProcessor.h" + + +WavParser::WavParser(OscirenderAudioProcessor& p, std::unique_ptr stream) : audioProcessor(p) { + juce::AudioFormatManager formatManager; + formatManager.registerBasicFormats(); + juce::AudioFormatReader* reader = formatManager.createReaderFor(std::move(stream)); + auto* afSource = new juce::AudioFormatReaderSource(reader, true); + afSource->setLooping(true); + source = std::make_unique(afSource, true); + fileSampleRate = reader->sampleRate; + audioBuffer.setSize(reader->numChannels, 1); + setSampleRate(audioProcessor.currentSampleRate); + source->prepareToPlay(1, audioProcessor.currentSampleRate); +} + +WavParser::~WavParser() { +} + +void WavParser::setSampleRate(double sampleRate) { + double ratio = fileSampleRate / sampleRate; + source->setResamplingRatio(ratio); + source->prepareToPlay(1, sampleRate); + currentSampleRate = sampleRate; +} + +Point WavParser::getSample() { + if (currentSampleRate != audioProcessor.currentSampleRate) { + setSampleRate(audioProcessor.currentSampleRate); + } + if (source == nullptr) { + return Point(); + } + + source->getNextAudioBlock(juce::AudioSourceChannelInfo(audioBuffer)); + + if (audioBuffer.getNumChannels() == 1) { + return Point(audioBuffer.getSample(0, 0), audioBuffer.getSample(0, 0)); + } else { + return Point(audioBuffer.getSample(0, 0), audioBuffer.getSample(1, 0)); + } +} diff --git a/Source/wav/WavParser.h b/Source/wav/WavParser.h new file mode 100644 index 00000000..7504508a --- /dev/null +++ b/Source/wav/WavParser.h @@ -0,0 +1,22 @@ +#pragma once +#include "../shape/Point.h" +#include + +class OscirenderAudioProcessor; +class WavParser { +public: + WavParser(OscirenderAudioProcessor& p, std::unique_ptr stream); + ~WavParser(); + + Point getSample(); + +private: + void setSampleRate(double sampleRate); + + std::unique_ptr source; + juce::AudioBuffer audioBuffer; + int currentSample = 0; + int fileSampleRate; + int currentSampleRate; + OscirenderAudioProcessor& audioProcessor; +}; \ No newline at end of file diff --git a/osci-render.jucer b/osci-render.jucer index 59e19f7c..a2eebc57 100644 --- a/osci-render.jucer +++ b/osci-render.jucer @@ -584,6 +584,10 @@ + + + +