Skip to content

Commit

Permalink
Merge pull request #251 from jameshball/wav-file-input
Browse files Browse the repository at this point in the history
Fix visualiser bugs, add support for .wav and .aiff files, and add brightness slider to visualiser
  • Loading branch information
jameshball authored Aug 23, 2024
2 parents 08a196f + f4d8903 commit 49af99e
Show file tree
Hide file tree
Showing 16 changed files with 145 additions and 34 deletions.
3 changes: 2 additions & 1 deletion Resources/oscilloscope/oscilloscope.html
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
7 changes: 4 additions & 3 deletions Resources/oscilloscope/oscilloscope.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion Source/MainComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ MainComponent::MainComponent(OscirenderAudioProcessor& p, OscirenderAudioProcess
fileButton.setButtonText("Choose File(s)");

fileButton.onClick = [this] {
chooser = std::make_unique<juce::FileChooser>("Open", audioProcessor.lastOpenedDirectory, "*.obj;*.svg;*.lua;*.txt;*.gpla;*.gif;*.png;*.jpg;*.jpeg");
chooser = std::make_unique<juce::FileChooser>("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;

Expand Down
18 changes: 17 additions & 1 deletion Source/PluginEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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
Expand Down
6 changes: 5 additions & 1 deletion Source/PluginEditor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -48,7 +49,10 @@ class OscirenderAudioProcessorEditor : public juce::AudioProcessorEditor, privat

std::atomic<bool> 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;
Expand Down
1 change: 1 addition & 0 deletions Source/PluginProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
10 changes: 9 additions & 1 deletion Source/PluginProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,18 @@ class OscirenderAudioProcessor : public juce::AudioProcessor, juce::AudioProces
VERSION_HINT, 125, 0, 359, 1
)
);
std::shared_ptr<Effect> brightnessEffect = std::make_shared<Effect>(
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<Effect> intensityEffect = std::make_shared<Effect>(
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
)
Expand Down
21 changes: 7 additions & 14 deletions Source/components/VisualiserComponent.cpp
Original file line number Diff line number Diff line change
@@ -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);

Expand Down Expand Up @@ -228,6 +221,9 @@ void VisualiserComponent::setVisualiserType(bool oldVisualiser) {
}
if (oldVisualiser) {
browser.reset();
if (closeSettings != nullptr) {
closeSettings();
}
} else {
initialiseBrowser();
}
Expand Down Expand Up @@ -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);
Expand All @@ -365,8 +363,3 @@ void VisualiserComponent::popoutWindow() {
resized();
popOutButton.setVisible(false);
}

void VisualiserComponent::openSettings() {
settingsWindow.setVisible(true);
settingsWindow.toFront(true);
}
10 changes: 6 additions & 4 deletions Source/components/VisualiserComponent.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<void()> openSettings;
std::function<void()> closeSettings;

void childChanged();
void enableFullScreen();
void setFullScreenCallback(std::function<void(FullScreenMode)> callback);
Expand Down Expand Up @@ -76,8 +78,8 @@ class VisualiserComponent : public juce::Component, public juce::Timer, public j
std::shared_ptr<BufferConsumer> consumer;

std::function<void(FullScreenMode)> 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;
Expand Down
11 changes: 8 additions & 3 deletions Source/components/VisualiserSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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));
Expand All @@ -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());
Expand Down
5 changes: 2 additions & 3 deletions Source/components/VisualiserSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
13 changes: 11 additions & 2 deletions Source/parser/FileParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<WorldObject>(stream->readEntireStreamAsString().toStdString());
Expand All @@ -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<ImageParser>(audioProcessor, extension, buffer);
} else if (extension == ".wav" || extension == ".aiff") {
wav = std::make_shared<WavParser>(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<std::unique_ptr<Shape>> FileParser::nextFrame() {
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -122,3 +127,7 @@ std::shared_ptr<LuaParser> FileParser::getLua() {
std::shared_ptr<ImageParser> FileParser::getImg() {
return img;
}

std::shared_ptr<WavParser> FileParser::getWav() {
return wav;
}
3 changes: 3 additions & 0 deletions Source/parser/FileParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "../gpla/LineArtParser.h"
#include "../lua/LuaParser.h"
#include "../img/ImageParser.h"
#include "../wav/WavParser.h"

class OscirenderAudioProcessor;
class FileParser {
Expand All @@ -29,6 +30,7 @@ class FileParser {
std::shared_ptr<LineArtParser> getLineArt();
std::shared_ptr<LuaParser> getLua();
std::shared_ptr<ImageParser> getImg();
std::shared_ptr<WavParser> getWav();

bool isAnimatable = false;

Expand All @@ -46,6 +48,7 @@ class FileParser {
std::shared_ptr<LineArtParser> gpla;
std::shared_ptr<LuaParser> lua;
std::shared_ptr<ImageParser> img;
std::shared_ptr<WavParser> wav;

juce::String fallbackLuaScript = "return { 0.0, 0.0 }";

Expand Down
43 changes: 43 additions & 0 deletions Source/wav/WavParser.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#include "WavParser.h"
#include "../PluginProcessor.h"


WavParser::WavParser(OscirenderAudioProcessor& p, std::unique_ptr<juce::InputStream> 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<juce::ResamplingAudioSource>(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));
}
}
22 changes: 22 additions & 0 deletions Source/wav/WavParser.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once
#include "../shape/Point.h"
#include <JuceHeader.h>

class OscirenderAudioProcessor;
class WavParser {
public:
WavParser(OscirenderAudioProcessor& p, std::unique_ptr<juce::InputStream> stream);
~WavParser();

Point getSample();

private:
void setSampleRate(double sampleRate);

std::unique_ptr<juce::ResamplingAudioSource> source;
juce::AudioBuffer<float> audioBuffer;
int currentSample = 0;
int fileSampleRate;
int currentSampleRate;
OscirenderAudioProcessor& audioProcessor;
};
Loading

0 comments on commit 49af99e

Please sign in to comment.