Get standalone software oscilloscope to compile

pull/263/head
James H Ball 2024-09-12 23:09:54 +01:00
rodzic b6465abc43
commit 44b6dea7ba
28 zmienionych plików z 1015 dodań i 162 usunięć

Wyświetl plik

@ -27,7 +27,7 @@ private:
EffectsListBoxModel listBoxModel;
DraggableListBox listBox;
EffectComponent frequency = EffectComponent(audioProcessor, *audioProcessor.frequencyEffect, false);
EffectComponent frequency = EffectComponent(*audioProcessor.frequencyEffect, false);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(EffectsComponent)
};

Wyświetl plik

@ -34,8 +34,8 @@ private:
DoubleTextBox offsetBox{ audioProcessor.animationOffset->min, audioProcessor.animationRate->max };
jux::SwitchButton invertImage{audioProcessor.invertImage};
EffectComponent threshold{ audioProcessor, *audioProcessor.imageThreshold };
EffectComponent stride{ audioProcessor, *audioProcessor.imageStride };
EffectComponent threshold{*audioProcessor.imageThreshold};
EffectComponent stride{*audioProcessor.imageStride};
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(FrameSettingsComponent)
};

Wyświetl plik

@ -124,19 +124,21 @@ MainComponent::MainComponent(OscirenderAudioProcessor& p, OscirenderAudioProcess
createFile.triggerClick();
};
if (!audioProcessor.visualiserFullScreen->getBoolValue()) {
BooleanParameter* visualiserFullScreen = audioProcessor.visualiserParameters.visualiserFullScreen;
if (!visualiserFullScreen->getBoolValue()) {
addAndMakeVisible(pluginEditor.visualiser);
}
pluginEditor.visualiser.setFullScreenCallback([this](FullScreenMode mode) {
pluginEditor.visualiser.setFullScreenCallback([this, visualiserFullScreen](FullScreenMode mode) {
if (mode == FullScreenMode::TOGGLE) {
audioProcessor.visualiserFullScreen->setBoolValueNotifyingHost(!audioProcessor.visualiserFullScreen->getBoolValue());
visualiserFullScreen->setBoolValueNotifyingHost(!visualiserFullScreen->getBoolValue());
} else if (mode == FullScreenMode::FULL_SCREEN) {
audioProcessor.visualiserFullScreen->setBoolValueNotifyingHost(true);
visualiserFullScreen->setBoolValueNotifyingHost(true);
} else if (mode == FullScreenMode::MAIN_COMPONENT) {
audioProcessor.visualiserFullScreen->setBoolValueNotifyingHost(false);
visualiserFullScreen->setBoolValueNotifyingHost(false);
}
pluginEditor.visualiser.setFullScreen(audioProcessor.visualiserFullScreen->getBoolValue());
pluginEditor.visualiser.setFullScreen(visualiserFullScreen->getBoolValue());
pluginEditor.resized();
pluginEditor.repaint();
@ -228,7 +230,7 @@ void MainComponent::resized() {
frequencyLabel.setBounds(bounds.removeFromTop(20));
bounds.removeFromTop(padding);
if (!audioProcessor.visualiserFullScreen->getBoolValue()) {
if (!audioProcessor.visualiserParameters.visualiserFullScreen->getBoolValue()) {
auto minDim = juce::jmin(bounds.getWidth(), bounds.getHeight());
juce::Point<int> localTopLeft = {bounds.getX(), bounds.getY()};
juce::Point<int> topLeft = pluginEditor.getLocalPoint(this, localTopLeft);

Wyświetl plik

@ -16,8 +16,8 @@ private:
OscirenderAudioProcessor& audioProcessor;
OscirenderAudioProcessorEditor& pluginEditor;
EffectComponent perspective{audioProcessor, *audioProcessor.perspective, 0};
EffectComponent focalLength{audioProcessor, *audioProcessor.perspective, 1};
EffectComponent perspective{*audioProcessor.perspective, 0};
EffectComponent focalLength{*audioProcessor.perspective, 1};
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PerspectiveComponent)
};

Wyświetl plik

@ -164,7 +164,7 @@ void OscirenderAudioProcessorEditor::paint(juce::Graphics& g) {
void OscirenderAudioProcessorEditor::resized() {
auto area = getLocalBounds();
if (audioProcessor.visualiserFullScreen->getBoolValue()) {
if (audioProcessor.visualiserParameters.visualiserFullScreen->getBoolValue()) {
visualiser.setBounds(area);
return;
}

Wyświetl plik

@ -49,9 +49,9 @@ public:
std::atomic<bool> editingCustomFunction = false;
VisualiserSettings visualiserSettings = VisualiserSettings(audioProcessor);
VisualiserSettings visualiserSettings = VisualiserSettings(audioProcessor.visualiserParameters);
SettingsWindow visualiserSettingsWindow = SettingsWindow("Visualiser Settings");
VisualiserComponent visualiser{audioProcessor, visualiserSettings, nullptr, audioProcessor.legacyVisualiserEnabled->getBoolValue()};
VisualiserComponent visualiser{audioProcessor, audioProcessor, visualiserSettings, nullptr, audioProcessor.visualiserParameters.legacyVisualiserEnabled->getBoolValue()};
SettingsComponent settings{audioProcessor, *this};

Wyświetl plik

@ -149,10 +149,10 @@ 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);
permanentEffects.push_back(visualiserParameters.brightnessEffect);
permanentEffects.push_back(visualiserParameters.intensityEffect);
permanentEffects.push_back(visualiserParameters.persistenceEffect);
permanentEffects.push_back(visualiserParameters.hueEffect);
for (int i = 0; i < 26; i++) {
addLuaSlider();
@ -176,11 +176,11 @@ OscirenderAudioProcessor::OscirenderAudioProcessor()
booleanParameters.push_back(animateFrames);
booleanParameters.push_back(animationSyncBPM);
booleanParameters.push_back(invertImage);
booleanParameters.push_back(graticuleEnabled);
booleanParameters.push_back(smudgesEnabled);
booleanParameters.push_back(upsamplingEnabled);
booleanParameters.push_back(legacyVisualiserEnabled);
booleanParameters.push_back(visualiserFullScreen);
booleanParameters.push_back(visualiserParameters.graticuleEnabled);
booleanParameters.push_back(visualiserParameters.smudgesEnabled);
booleanParameters.push_back(visualiserParameters.upsamplingEnabled);
booleanParameters.push_back(visualiserParameters.legacyVisualiserEnabled);
booleanParameters.push_back(visualiserParameters.visualiserFullScreen);
for (auto parameter : booleanParameters) {
addParameter(parameter);
@ -212,11 +212,18 @@ OscirenderAudioProcessor::OscirenderAudioProcessor()
}
voices->addListener(this);
for (int i = 0; i < luaEffects.size(); i++) {
luaEffects[i]->parameters[0]->addListener(this);
}
synth.addSound(defaultSound);
}
OscirenderAudioProcessor::~OscirenderAudioProcessor() {
for (int i = luaEffects.size() - 1; i >= 0; i--) {
luaEffects[i]->parameters[0]->removeListener(this);
}
voices->removeListener(this);
}
@ -332,7 +339,7 @@ void OscirenderAudioProcessor::addLuaSlider() {
luaEffects.push_back(std::make_shared<Effect>(
[this, sliderIndex](int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
luaValues[sliderIndex] = values[0];
luaValues[sliderIndex].store(values[0]);
return input;
}, new EffectParameter(
"Lua Slider " + sliderName,
@ -822,8 +829,8 @@ void OscirenderAudioProcessor::getStateInformation(juce::MemoryBlock& destData)
xml->setAttribute("currentFile", currentFile);
auto visualiserXml = xml->createNewChildElement("visualiser");
visualiserXml->setAttribute("roughness", roughness);
visualiserXml->setAttribute("intensity", intensity);
visualiserXml->setAttribute("roughness", visualiserParameters.roughness);
visualiserXml->setAttribute("intensity", visualiserParameters.intensity);
copyXmlToBinary(*xml, destData);
}
@ -941,8 +948,8 @@ void OscirenderAudioProcessor::setStateInformation(const void* data, int sizeInB
auto visualiserXml = xml->getChildByName("visualiser");
if (visualiserXml != nullptr) {
roughness = visualiserXml->getIntAttribute("roughness");
intensity = visualiserXml->getDoubleAttribute("intensity");
visualiserParameters.roughness = visualiserXml->getIntAttribute("roughness");
visualiserParameters.intensity = visualiserXml->getDoubleAttribute("intensity");
}
broadcaster.sendChangeMessage();
@ -950,28 +957,12 @@ void OscirenderAudioProcessor::setStateInformation(const void* data, int sizeInB
}
}
std::shared_ptr<BufferConsumer> OscirenderAudioProcessor::consumerRegister(std::vector<float>& buffer) {
std::shared_ptr<BufferConsumer> consumer = std::make_shared<BufferConsumer>(buffer);
juce::SpinLock::ScopedLockType scope(consumerLock);
consumers.push_back(consumer);
return consumer;
}
void OscirenderAudioProcessor::consumerRead(std::shared_ptr<BufferConsumer> consumer) {
consumer->waitUntilFull();
juce::SpinLock::ScopedLockType scope(consumerLock);
consumers.erase(std::remove(consumers.begin(), consumers.end(), consumer), consumers.end());
}
void OscirenderAudioProcessor::consumerStop(std::shared_ptr<BufferConsumer> consumer) {
if (consumer != nullptr) {
juce::SpinLock::ScopedLockType scope(consumerLock);
consumer->forceNotify();
}
}
void OscirenderAudioProcessor::parameterValueChanged(int parameterIndex, float newValue) {
for (auto effect : luaEffects) {
if (parameterIndex == effect->parameters[0]->getParameterIndex()) {
effect->apply();
}
}
if (parameterIndex == voices->getParameterIndex()) {
int numVoices = voices->getValueUnnormalised();
// if the number of voices has changed, update the synth without clearing all the voices
@ -1019,6 +1010,10 @@ void OscirenderAudioProcessor::envelopeChanged(EnvelopeComponent* changedEnvelop
}
}
double OscirenderAudioProcessor::getSampleRate() {
return currentSampleRate;
}
//==============================================================================
// This creates new instances of the plugin..

Wyświetl plik

@ -8,13 +8,17 @@
#pragma once
#define VERSION_HINT 2
#include <JuceHeader.h>
#include "shape/Shape.h"
#include "concurrency/BufferConsumer.h"
#include "concurrency/ConsumerManager.h"
#include "components/VisualiserSettings.h"
#include "audio/Effect.h"
#include "audio/ShapeSound.h"
#include "audio/ShapeVoice.h"
#include "audio/PublicSynthesiser.h"
#include "audio/SampleRateManager.h"
#include <numbers>
#include "audio/DelayEffect.h"
#include "audio/PitchDetector.h"
@ -29,7 +33,7 @@
//==============================================================================
/**
*/
class OscirenderAudioProcessor : public juce::AudioProcessor, juce::AudioProcessorParameter::Listener, public EnvelopeComponentListener
class OscirenderAudioProcessor : public juce::AudioProcessor, juce::AudioProcessorParameter::Listener, public EnvelopeComponentListener, public ConsumerManager, public SampleRateManager
#if JucePlugin_Enable_ARA
, public juce::AudioProcessorARAExtension
#endif
@ -65,24 +69,17 @@ public:
void changeProgramName(int index, const juce::String& newName) override;
void getStateInformation(juce::MemoryBlock& destData) override;
void setStateInformation(const void* data, int sizeInBytes) override;
std::shared_ptr<BufferConsumer> consumerRegister(std::vector<float>& buffer);
void consumerStop(std::shared_ptr<BufferConsumer> consumer);
void consumerRead(std::shared_ptr<BufferConsumer> consumer);
void parameterValueChanged(int parameterIndex, float newValue) override;
void parameterGestureChanged(int parameterIndex, bool gestureIsStarting) override;
void envelopeChanged(EnvelopeComponent* changedEnvelope) override;
int VERSION_HINT = 2;
double getSampleRate() override;
std::atomic<double> currentSampleRate = 0.0;
std::atomic<int> roughness = 4;
std::atomic<double> intensity = 1.0;
juce::SpinLock effectsLock;
std::vector<std::shared_ptr<Effect>> toggleableEffects;
std::vector<std::shared_ptr<Effect>> luaEffects;
double luaValues[26] = { 0.0 };
std::atomic<double> luaValues[26] = { 0.0 };
std::shared_ptr<Effect> frequencyEffect = std::make_shared<Effect>(
[this](int index, Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
@ -156,46 +153,8 @@ public:
new EffectParameter("Focal Length", "Controls the focal length of the 3D perspective effect. A higher focal length makes the image look more flat, and a lower focal length makes the image look more 3D.", "perspectiveFocalLength", VERSION_HINT, 2.0, 0.0, 10.0),
}
);
// visualiser settings
BooleanParameter* graticuleEnabled = new BooleanParameter("Show Graticule", "graticuleEnabled", VERSION_HINT, true, "Show the graticule or grid lines over the oscilloscope display.");
BooleanParameter* smudgesEnabled = new BooleanParameter("Show Smudges", "smudgesEnabled", VERSION_HINT, true, "Adds a subtle layer of dirt/smudges to the oscilloscope display to make it look more realistic.");
BooleanParameter* upsamplingEnabled = new BooleanParameter("Upsample Audio", "upsamplingEnabled", VERSION_HINT, false, "Upsamples the audio before visualising it to make it appear more realistic, at the expense of performance.");
BooleanParameter* legacyVisualiserEnabled = new BooleanParameter("Use Legacy Visualiser", "legacyVisualiserEnabled", VERSION_HINT, false, "Replaces the realistic oscilloscope visualiser with the legacy visualiser. This may improve performance.");
BooleanParameter* visualiserFullScreen = new BooleanParameter("Visualiser Fullscreen", "visualiserFullScreen", VERSION_HINT, false, "Makes the software visualiser fullscreen.");
std::shared_ptr<Effect> persistenceEffect = std::make_shared<Effect>(
new EffectParameter(
"Persistence",
"Controls how long the light glows for on the oscilloscope display.",
"persistence",
VERSION_HINT, 0.5, 0, 6.0
)
);
std::shared_ptr<Effect> hueEffect = std::make_shared<Effect>(
new EffectParameter(
"Hue",
"Controls the hue/colour of the oscilloscope display.",
"hue",
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 electron beam of the oscilloscope is.",
"intensity",
VERSION_HINT, 3.0, 0.0, 10.0
)
);
VisualiserParameters visualiserParameters;
BooleanParameter* midiEnabled = new BooleanParameter("MIDI Enabled", "midiEnabled", VERSION_HINT, false, "Enable MIDI input for the synth. If disabled, the synth will play a constant tone, as controlled by the frequency slider.");
BooleanParameter* inputEnabled = new BooleanParameter("Audio Input Enabled", "inputEnabled", VERSION_HINT, false, "Enable to use input audio, instead of the generated audio.");
@ -264,11 +223,6 @@ public:
);
double animationTime = 0.f;
private:
juce::SpinLock consumerLock;
std::vector<std::shared_ptr<BufferConsumer>> consumers;
public:
PitchDetector pitchDetector{*this};
std::shared_ptr<WobbleEffect> wobbleEffect = std::make_shared<WobbleEffect>(pitchDetector);

Wyświetl plik

@ -0,0 +1,151 @@
#include "SosciPluginProcessor.h"
#include "SosciPluginEditor.h"
#include <juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h>
SosciPluginEditor::SosciPluginEditor(SosciAudioProcessor& p)
: AudioProcessorEditor(&p), audioProcessor(p)
{
#if JUCE_LINUX
// use OpenGL on Linux for much better performance. The default on Mac is CoreGraphics, and on Window is Direct2D which is much faster.
openGlContext.attachTo(*getTopLevelComponent());
#endif
setLookAndFeel(&lookAndFeel);
// #if JUCE_MAC
// if (audioProcessor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone) {
// usingNativeMenuBar = true;
// menuBarModel.setMacMainMenu(&menuBarModel);
// }
// #endif
// if (!usingNativeMenuBar) {
// menuBar.setModel(&menuBarModel);
// addAndMakeVisible(menuBar);
// }
if (juce::JUCEApplicationBase::isStandaloneApp()) {
if (juce::TopLevelWindow::getNumTopLevelWindows() > 0) {
juce::TopLevelWindow* w = juce::TopLevelWindow::getTopLevelWindow(0);
juce::DocumentWindow* dw = dynamic_cast<juce::DocumentWindow*>(w);
if (dw != nullptr) {
dw->setBackgroundColour(Colours::veryDark);
dw->setColour(juce::ResizableWindow::backgroundColourId, Colours::veryDark);
dw->setTitleBarButtonsRequired(juce::DocumentWindow::allButtons, false);
dw->setUsingNativeTitleBar(true);
}
}
juce::StandalonePluginHolder* standalone = juce::StandalonePluginHolder::getInstance();
if (standalone != nullptr) {
standalone->getMuteInputValue().setValue(false);
}
}
addAndMakeVisible(visualiser);
setSize(750, 750);
setResizable(true, true);
setResizeLimits(250, 250, 999999, 999999);
}
SosciPluginEditor::~SosciPluginEditor() {
setLookAndFeel(nullptr);
juce::Desktop::getInstance().setDefaultLookAndFeel(nullptr);
// #if JUCE_MAC
// if (usingNativeMenuBar) {
// menuBarModel.setMacMainMenu(nullptr);
// }
// #endif
}
void SosciPluginEditor::paint(juce::Graphics& g) {
g.fillAll(getLookAndFeel().findColour(juce::ResizableWindow::backgroundColourId));
}
void SosciPluginEditor::resized() {
auto area = getLocalBounds();
visualiser.setBounds(area);
// if (!usingNativeMenuBar) {
// menuBar.setBounds(area.removeFromTop(25));
// }
}
bool SosciPluginEditor::keyPressed(const juce::KeyPress& key) {
if (key.getModifiers().isCommandDown() && key.getModifiers().isShiftDown() && key.getKeyCode() == 'S') {
saveProjectAs();
} else if (key.getModifiers().isCommandDown() && key.getKeyCode() == 'S') {
saveProject();
} else if (key.getModifiers().isCommandDown() && key.getKeyCode() == 'O') {
openProject();
}
return false;
}
void SosciPluginEditor::openProject() {
chooser = std::make_unique<juce::FileChooser>("Load sosci Project", audioProcessor.lastOpenedDirectory, "*.sosci");
auto flags = juce::FileBrowserComponent::openMode |
juce::FileBrowserComponent::canSelectFiles;
chooser->launchAsync(flags, [this](const juce::FileChooser& chooser) {
auto file = chooser.getResult();
if (file != juce::File()) {
auto data = juce::MemoryBlock();
if (file.loadFileAsData(data)) {
audioProcessor.setStateInformation(data.getData(), data.getSize());
}
audioProcessor.currentProjectFile = file.getFullPathName();
audioProcessor.lastOpenedDirectory = file.getParentDirectory();
updateTitle();
}
});
}
void SosciPluginEditor::saveProject() {
if (audioProcessor.currentProjectFile.isEmpty()) {
saveProjectAs();
} else {
auto data = juce::MemoryBlock();
audioProcessor.getStateInformation(data);
auto file = juce::File(audioProcessor.currentProjectFile);
file.create();
file.replaceWithData(data.getData(), data.getSize());
updateTitle();
}
}
void SosciPluginEditor::saveProjectAs() {
chooser = std::make_unique<juce::FileChooser>("Save sosci Project", audioProcessor.lastOpenedDirectory, "*.sosci");
auto flags = juce::FileBrowserComponent::saveMode;
chooser->launchAsync(flags, [this](const juce::FileChooser& chooser) {
auto file = chooser.getResult();
if (file != juce::File()) {
audioProcessor.currentProjectFile = file.getFullPathName();
saveProject();
}
});
}
void SosciPluginEditor::updateTitle() {
juce::String title = "sosci";
if (!audioProcessor.currentProjectFile.isEmpty()) {
title += " - " + audioProcessor.currentProjectFile;
}
getTopLevelComponent()->setName(title);
}
void SosciPluginEditor::openAudioSettings() {
juce::StandalonePluginHolder* standalone = juce::StandalonePluginHolder::getInstance();
standalone->showAudioSettingsDialog();
}
void SosciPluginEditor::resetToDefault() {
juce::StandaloneFilterWindow* window = findParentComponentOfClass<juce::StandaloneFilterWindow>();
if (window != nullptr) {
window->resetToDefaultState();
}
}

Wyświetl plik

@ -0,0 +1,48 @@
#pragma once
#include <JuceHeader.h>
#include "SosciPluginProcessor.h"
#include "components/VisualiserComponent.h"
#include "LookAndFeel.h"
#include "components/VisualiserSettings.h"
class SosciPluginEditor : public juce::AudioProcessorEditor {
public:
SosciPluginEditor(SosciAudioProcessor&);
~SosciPluginEditor() override;
void paint(juce::Graphics&) override;
void resized() override;
void openProject();
void saveProject();
void saveProjectAs();
void updateTitle();
void openAudioSettings();
void resetToDefault();
private:
SosciAudioProcessor& audioProcessor;
public:
OscirenderLookAndFeel lookAndFeel;
VisualiserSettings visualiserSettings = VisualiserSettings(audioProcessor.parameters);
SettingsWindow visualiserSettingsWindow = SettingsWindow("Visualiser Settings");
VisualiserComponent visualiser{audioProcessor, audioProcessor, visualiserSettings, nullptr, audioProcessor.parameters.legacyVisualiserEnabled->getBoolValue()};
std::unique_ptr<juce::FileChooser> chooser;
// MainMenuBarModel menuBarModel{audioProcessor, *this};
// juce::MenuBarComponent menuBar;
juce::TooltipWindow tooltipWindow{nullptr, 0};
//bool usingNativeMenuBar = false;
#if JUCE_LINUX
juce::OpenGLContext openGlContext;
#endif
bool keyPressed(const juce::KeyPress& key) override;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SosciPluginEditor)
};

Wyświetl plik

@ -0,0 +1,327 @@
/*
==============================================================================
This file contains the basic framework code for a JUCE plugin processor.
==============================================================================
*/
#include "SosciPluginProcessor.h"
#include "SosciPluginEditor.h"
#include "audio/EffectParameter.h"
//==============================================================================
SosciAudioProcessor::SosciAudioProcessor()
#ifndef JucePlugin_PreferredChannelConfigurations
: AudioProcessor (BusesProperties()
#if ! JucePlugin_IsMidiEffect
#if ! JucePlugin_IsSynth
.withInput ("Input", juce::AudioChannelSet::stereo(), true)
#endif
.withOutput ("Output", juce::AudioChannelSet::stereo(), true)
#endif
)
#endif
{
// locking isn't necessary here because we are in the constructor
allEffects.push_back(parameters.brightnessEffect);
allEffects.push_back(parameters.intensityEffect);
allEffects.push_back(parameters.persistenceEffect);
allEffects.push_back(parameters.hueEffect);
for (auto effect : allEffects) {
for (auto effectParameter : effect->parameters) {
auto parameters = effectParameter->getParameters();
for (auto parameter : parameters) {
addParameter(parameter);
}
}
}
booleanParameters.push_back(parameters.graticuleEnabled);
booleanParameters.push_back(parameters.smudgesEnabled);
booleanParameters.push_back(parameters.upsamplingEnabled);
booleanParameters.push_back(parameters.legacyVisualiserEnabled);
booleanParameters.push_back(parameters.visualiserFullScreen);
for (auto parameter : booleanParameters) {
addParameter(parameter);
}
for (auto parameter : floatParameters) {
addParameter(parameter);
}
for (auto parameter : intParameters) {
addParameter(parameter);
}
}
SosciAudioProcessor::~SosciAudioProcessor() {}
const juce::String SosciAudioProcessor::getName() const {
return JucePlugin_Name;
}
bool SosciAudioProcessor::acceptsMidi() const {
#if JucePlugin_WantsMidiInput
return true;
#else
return false;
#endif
}
bool SosciAudioProcessor::producesMidi() const {
#if JucePlugin_ProducesMidiOutput
return true;
#else
return false;
#endif
}
bool SosciAudioProcessor::isMidiEffect() const {
#if JucePlugin_IsMidiEffect
return true;
#else
return false;
#endif
}
double SosciAudioProcessor::getTailLengthSeconds() const {
return 0.0;
}
int SosciAudioProcessor::getNumPrograms() {
return 1; // NB: some hosts don't cope very well if you tell them there are 0 programs,
// so this should be at least 1, even if you're not really implementing programs.
}
int SosciAudioProcessor::getCurrentProgram() {
return 0;
}
void SosciAudioProcessor::setCurrentProgram(int index) {
}
const juce::String SosciAudioProcessor::getProgramName(int index) {
return {};
}
void SosciAudioProcessor::changeProgramName(int index, const juce::String& newName) {}
void SosciAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock) {
currentSampleRate = sampleRate;
for (auto& effect : allEffects) {
effect->updateSampleRate(currentSampleRate);
}
}
void SosciAudioProcessor::releaseResources() {
// When playback stops, you can use this as an opportunity to free up any
// spare memory, etc.
}
#ifndef JucePlugin_PreferredChannelConfigurations
bool SosciAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const
{
#if JucePlugin_IsMidiEffect
juce::ignoreUnused (layouts);
return true;
#else
// This is the place where you check if the layout is supported.
// In this template code we only support mono or stereo.
// Some plugin hosts, such as certain GarageBand versions, will only
// load plugins that support stereo bus layouts.
if (layouts.getMainOutputChannelSet() != juce::AudioChannelSet::mono()
&& layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo())
return false;
// This checks if the input layout matches the output layout
#if ! JucePlugin_IsSynth
if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet())
return false;
#endif
return true;
#endif
}
#endif
// effectsLock should be held when calling this
std::shared_ptr<Effect> SosciAudioProcessor::getEffect(juce::String id) {
for (auto& effect : allEffects) {
if (effect->getId() == id) {
return effect;
}
}
return nullptr;
}
// effectsLock should be held when calling this
BooleanParameter* SosciAudioProcessor::getBooleanParameter(juce::String id) {
for (auto& parameter : booleanParameters) {
if (parameter->paramID == id) {
return parameter;
}
}
return nullptr;
}
// effectsLock should be held when calling this
FloatParameter* SosciAudioProcessor::getFloatParameter(juce::String id) {
for (auto& parameter : floatParameters) {
if (parameter->paramID == id) {
return parameter;
}
}
return nullptr;
}
// effectsLock should be held when calling this
IntParameter* SosciAudioProcessor::getIntParameter(juce::String id) {
for (auto& parameter : intParameters) {
if (parameter->paramID == id) {
return parameter;
}
}
return nullptr;
}
void SosciAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages) {
juce::ScopedNoDenormals noDenormals;
// Audio info variables
int numInputs = getTotalNumInputChannels();
int numOutputs = getTotalNumOutputChannels();
double sampleRate = getSampleRate();
midiMessages.clear();
auto* channelData = buffer.getArrayOfWritePointers();
for (int sample = 0; sample < buffer.getNumSamples(); ++sample) {
juce::SpinLock::ScopedLockType scope(consumerLock);
double x = numOutputs > 0 ? channelData[0][sample] : 0;
double y = numOutputs > 1 ? channelData[1][sample] : 0;
double z = numOutputs > 2 ? channelData[2][sample] : 0;
for (auto consumer : consumers) {
consumer->write(x);
consumer->write(y);
consumer->notifyIfFull();
}
}
}
//==============================================================================
bool SosciAudioProcessor::hasEditor() const {
return true; // (change this to false if you choose to not supply an editor)
}
juce::AudioProcessorEditor* SosciAudioProcessor::createEditor() {
auto editor = new SosciPluginEditor(*this);
return editor;
}
//==============================================================================
void SosciAudioProcessor::getStateInformation(juce::MemoryBlock& destData) {
juce::SpinLock::ScopedLockType lock2(effectsLock);
std::unique_ptr<juce::XmlElement> xml = std::make_unique<juce::XmlElement>("project");
xml->setAttribute("version", ProjectInfo::versionString);
auto effectsXml = xml->createNewChildElement("effects");
for (auto effect : allEffects) {
effect->save(effectsXml->createNewChildElement("effect"));
}
auto booleanParametersXml = xml->createNewChildElement("booleanParameters");
for (auto parameter : booleanParameters) {
auto parameterXml = booleanParametersXml->createNewChildElement("parameter");
parameter->save(parameterXml);
}
auto floatParametersXml = xml->createNewChildElement("floatParameters");
for (auto parameter : floatParameters) {
auto parameterXml = floatParametersXml->createNewChildElement("parameter");
parameter->save(parameterXml);
}
auto intParametersXml = xml->createNewChildElement("intParameters");
for (auto parameter : intParameters) {
auto parameterXml = intParametersXml->createNewChildElement("parameter");
parameter->save(parameterXml);
}
copyXmlToBinary(*xml, destData);
}
void SosciAudioProcessor::setStateInformation(const void* data, int sizeInBytes) {
std::unique_ptr<juce::XmlElement> xml;
const uint32_t magicXmlNumber = 0x21324356;
if (sizeInBytes > 8 && juce::ByteOrder::littleEndianInt(data) == magicXmlNumber) {
// this is a binary xml format
xml = getXmlFromBinary(data, sizeInBytes);
} else {
// this is a text xml format
xml = juce::XmlDocument::parse(juce::String((const char*)data, sizeInBytes));
}
if (xml.get() != nullptr && xml->hasTagName("project")) {
juce::SpinLock::ScopedLockType lock2(effectsLock);
auto effectsXml = xml->getChildByName("effects");
if (effectsXml != nullptr) {
for (auto effectXml : effectsXml->getChildIterator()) {
auto effect = getEffect(effectXml->getStringAttribute("id"));
if (effect != nullptr) {
effect->load(effectXml);
}
}
}
auto booleanParametersXml = xml->getChildByName("booleanParameters");
if (booleanParametersXml != nullptr) {
for (auto parameterXml : booleanParametersXml->getChildIterator()) {
auto parameter = getBooleanParameter(parameterXml->getStringAttribute("id"));
if (parameter != nullptr) {
parameter->load(parameterXml);
}
}
}
auto floatParametersXml = xml->getChildByName("floatParameters");
if (floatParametersXml != nullptr) {
for (auto parameterXml : floatParametersXml->getChildIterator()) {
auto parameter = getFloatParameter(parameterXml->getStringAttribute("id"));
if (parameter != nullptr) {
parameter->load(parameterXml);
}
}
}
auto intParametersXml = xml->getChildByName("intParameters");
if (intParametersXml != nullptr) {
for (auto parameterXml : intParametersXml->getChildIterator()) {
auto parameter = getIntParameter(parameterXml->getStringAttribute("id"));
if (parameter != nullptr) {
parameter->load(parameterXml);
}
}
}
}
}
double SosciAudioProcessor::getSampleRate() {
return currentSampleRate;
}
//==============================================================================
// This creates new instances of the plugin..
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
return new SosciAudioProcessor();
}

Wyświetl plik

@ -0,0 +1,87 @@
/*
==============================================================================
This file contains the basic framework code for a JUCE plugin processor.
==============================================================================
*/
#pragma once
#include <JuceHeader.h>
#include "concurrency/ConsumerManager.h"
#include "audio/SampleRateManager.h"
#include "components/VisualiserSettings.h"
#include "audio/Effect.h"
//==============================================================================
/**
*/
class SosciAudioProcessor : public juce::AudioProcessor, public ConsumerManager, public SampleRateManager
#if JucePlugin_Enable_ARA
, public juce::AudioProcessorARAExtension
#endif
{
public:
SosciAudioProcessor();
~SosciAudioProcessor() override;
void prepareToPlay (double sampleRate, int samplesPerBlock) override;
void releaseResources() override;
#ifndef JucePlugin_PreferredChannelConfigurations
bool isBusesLayoutSupported (const BusesLayout& layouts) const override;
#endif
void processBlock (juce::AudioBuffer<float>&, juce::MidiBuffer&) override;
juce::AudioProcessorEditor* createEditor() override;
bool hasEditor() const override;
const juce::String getName() const override;
void setAudioThreadCallback(std::function<void(const juce::AudioBuffer<float>&)> callback);
bool acceptsMidi() const override;
bool producesMidi() const override;
bool isMidiEffect() const override;
double getTailLengthSeconds() const override;
int getNumPrograms() override;
int getCurrentProgram() override;
void setCurrentProgram(int index) override;
const juce::String getProgramName(int index) override;
void changeProgramName(int index, const juce::String& newName) override;
void getStateInformation(juce::MemoryBlock& destData) override;
void setStateInformation(const void* data, int sizeInBytes) override;
double getSampleRate() override;
std::atomic<double> currentSampleRate = 0.0;
juce::SpinLock effectsLock;
VisualiserParameters parameters;
private:
juce::SpinLock consumerLock;
std::vector<std::shared_ptr<BufferConsumer>> consumers;
public:
// shouldn't be accessed by audio thread, but needs to persist when GUI is closed
// so should only be accessed by message thread
juce::String currentProjectFile;
juce::File lastOpenedDirectory = juce::File::getSpecialLocation(juce::File::userHomeDirectory);
private:
std::vector<BooleanParameter*> booleanParameters;
std::vector<FloatParameter*> floatParameters;
std::vector<IntParameter*> intParameters;
std::vector<std::shared_ptr<Effect>> allEffects;
std::vector<std::shared_ptr<Effect>> permanentEffects;
std::shared_ptr<Effect> getEffect(juce::String id);
BooleanParameter* getBooleanParameter(juce::String id);
FloatParameter* getFloatParameter(juce::String id);
IntParameter* getIntParameter(juce::String id);
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SosciAudioProcessor)
};

Wyświetl plik

@ -5,7 +5,7 @@
const juce::String CustomEffect::UNIQUE_ID = "6a3580b0-c5fc-4b28-a33e-e26a487f052f";
const juce::String CustomEffect::FILE_NAME = "Custom Lua Effect";
CustomEffect::CustomEffect(std::function<void(int, juce::String, juce::String)> errorCallback, double (&luaValues)[26]) : errorCallback(errorCallback), luaValues(luaValues) {
CustomEffect::CustomEffect(std::function<void(int, juce::String, juce::String)> errorCallback, std::atomic<double>* luaValues) : errorCallback(errorCallback), luaValues(luaValues) {
vars.isEffect = true;
}
@ -30,7 +30,7 @@ Point CustomEffect::apply(int index, Point input, const std::vector<std::atomic<
vars.y = y;
vars.z = z;
std::copy(std::begin(luaValues), std::end(luaValues), std::begin(vars.sliders));
std::copy(luaValues, luaValues + 26, std::begin(vars.sliders));
auto result = parser->run(L, vars);
if (result.size() >= 2) {

Wyświetl plik

@ -6,7 +6,7 @@
class CustomEffect : public EffectApplication {
public:
CustomEffect(std::function<void(int, juce::String, juce::String)> errorCallback, double (&luaValues)[26]);
CustomEffect(std::function<void(int, juce::String, juce::String)> errorCallback, std::atomic<double>* luaValues);
~CustomEffect();
// arbitrary UUID
@ -32,5 +32,5 @@ private:
lua_State *L = nullptr;
LuaVariables vars;
double(&luaValues)[26];
std::atomic<double>* luaValues;
};

Wyświetl plik

@ -0,0 +1,9 @@
#pragma once
class SampleRateManager {
public:
SampleRateManager() {}
~SampleRateManager() {}
virtual double getSampleRate() = 0;
};

Wyświetl plik

@ -1,7 +1,7 @@
#include "EffectComponent.h"
#include "../LookAndFeel.h"
EffectComponent::EffectComponent(OscirenderAudioProcessor& p, Effect& effect, int index) : effect(effect), index(index), audioProcessor(p) {
EffectComponent::EffectComponent(Effect& effect, int index) : effect(effect), index(index) {
addAndMakeVisible(slider);
addChildComponent(lfoSlider);
addAndMakeVisible(lfo);
@ -46,7 +46,7 @@ EffectComponent::EffectComponent(OscirenderAudioProcessor& p, Effect& effect, in
setupComponent();
}
EffectComponent::EffectComponent(OscirenderAudioProcessor& p, Effect& effect) : EffectComponent(p, effect, 0) {}
EffectComponent::EffectComponent(Effect& effect) : EffectComponent(effect, 0) {}
void EffectComponent::setupComponent() {
EffectParameter* parameter = effect.parameters[index];
@ -167,11 +167,6 @@ void EffectComponent::parameterGestureChanged(int parameterIndex, bool gestureIs
void EffectComponent::handleAsyncUpdate() {
setupComponent();
getParentComponent()->repaint();
juce::SpinLock::ScopedLockType lock1(audioProcessor.parsersLock);
juce::SpinLock::ScopedLockType lock2(audioProcessor.effectsLock);
if (effect.getId().contains("lua")) {
effect.apply();
}
}
void EffectComponent::setComponent(std::shared_ptr<juce::Component> component) {

Wyświetl plik

@ -1,14 +1,13 @@
#pragma once
#include <JuceHeader.h>
#include "../PluginProcessor.h"
#include "../audio/Effect.h"
#include "LabelledTextBox.h"
#include "SvgButton.h"
class EffectComponent : public juce::Component, public juce::AudioProcessorParameter::Listener, juce::AsyncUpdater, public juce::SettableTooltipClient {
public:
EffectComponent(OscirenderAudioProcessor& p, Effect& effect, int index);
EffectComponent(OscirenderAudioProcessor& p, Effect& effect);
EffectComponent(Effect& effect, int index);
EffectComponent(Effect& effect);
~EffectComponent();
void resized() override;
@ -89,7 +88,6 @@ private:
bool lfoEnabled = true;
bool sidechainEnabled = true;
std::shared_ptr<juce::Component> component;
OscirenderAudioProcessor& audioProcessor;
std::unique_ptr<SvgButton> sidechainButton;

Wyświetl plik

@ -7,7 +7,7 @@ EffectsListComponent::EffectsListComponent(DraggableListBox& lb, AudioEffectList
effect(effect), audioProcessor(data.audioProcessor), editor(data.editor) {
auto parameters = effect.parameters;
for (int i = 0; i < parameters.size(); i++) {
std::shared_ptr<EffectComponent> effectComponent = std::make_shared<EffectComponent>(audioProcessor, effect, i);
std::shared_ptr<EffectComponent> effectComponent = std::make_shared<EffectComponent>(effect, i);
selected.setToggleState(effect.enabled == nullptr || effect.enabled->getValue(), juce::dontSendNotification);
// using weak_ptr to avoid circular reference and memory leak
std::weak_ptr<EffectComponent> weakEffectComponent = effectComponent;

Wyświetl plik

@ -1,7 +1,7 @@
#include "LuaListComponent.h"
LuaListComponent::LuaListComponent(OscirenderAudioProcessor& p, Effect& effect) {
effectComponent = std::make_shared<EffectComponent>(p, effect);
effectComponent = std::make_shared<EffectComponent>(effect);
effectComponent->slider.onValueChange = [this, &effect, &p] {
effect.setValue(effectComponent->slider.getValue());

Wyświetl plik

@ -3,11 +3,11 @@
#include "../PluginProcessor.h"
MainMenuBarModel::MainMenuBarModel(OscirenderAudioProcessor& p, OscirenderAudioProcessorEditor& editor) : audioProcessor(p), editor(editor) {
audioProcessor.legacyVisualiserEnabled->addListener(this);
audioProcessor.visualiserParameters.legacyVisualiserEnabled->addListener(this);
}
MainMenuBarModel::~MainMenuBarModel() {
audioProcessor.legacyVisualiserEnabled->removeListener(this);
audioProcessor.visualiserParameters.legacyVisualiserEnabled->removeListener(this);
}
void MainMenuBarModel::parameterValueChanged(int parameterIndex, float legacyVisualiserEnabled) {
@ -36,7 +36,7 @@ juce::PopupMenu MainMenuBarModel::getMenuForIndex(int topLevelMenuIndex, const j
menu.addItem(4, "Create New Project");
}
} else if (topLevelMenuIndex == 1) {
menu.addItem(1, "Use Legacy Visualiser", true, audioProcessor.legacyVisualiserEnabled->getBoolValue());
menu.addItem(1, "Use Legacy Visualiser", true, audioProcessor.visualiserParameters.legacyVisualiserEnabled->getBoolValue());
} else if (topLevelMenuIndex == 2) {
menu.addItem(1, "About osci-render");
} else if (topLevelMenuIndex == 3) {
@ -67,7 +67,7 @@ void MainMenuBarModel::menuItemSelected(int menuItemID, int topLevelMenuIndex) {
}
break;
case 1: {
audioProcessor.legacyVisualiserEnabled->setBoolValueNotifyingHost(!audioProcessor.legacyVisualiserEnabled->getBoolValue());
audioProcessor.visualiserParameters.legacyVisualiserEnabled->setBoolValueNotifyingHost(!audioProcessor.visualiserParameters.legacyVisualiserEnabled->getBoolValue());
menuItemsChanged();
} break;
case 2: {

Wyświetl plik

@ -1,5 +1,6 @@
#pragma once
#include <JuceHeader.h>
#include "../audio/BooleanParameter.h"
class SvgButton : public juce::DrawableButton, public juce::AudioProcessorParameter::Listener, public juce::AsyncUpdater {
public:

Wyświetl plik

@ -1,7 +1,7 @@
#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) {
VisualiserComponent::VisualiserComponent(SampleRateManager& sampleRateManager, ConsumerManager& consumerManager, VisualiserSettings& settings, VisualiserComponent* parent, bool useOldVisualiser) : settings(settings), backgroundColour(juce::Colours::black), waveformColour(juce::Colour(0xff00ff00)), sampleRateManager(sampleRateManager), consumerManager(consumerManager), oldVisualiser(useOldVisualiser), juce::Thread("VisualiserComponent"), parent(parent) {
resetBuffer();
if (!oldVisualiser) {
initialiseBrowser();
@ -12,13 +12,13 @@ VisualiserComponent::VisualiserComponent(OscirenderAudioProcessor& p, Visualiser
setMouseCursor(juce::MouseCursor::PointingHandCursor);
setWantsKeyboardFocus(true);
roughness.textBox.setValue(audioProcessor.roughness);
roughness.textBox.setValue(settings.parameters.roughness);
roughness.textBox.onValueChange = [this]() {
audioProcessor.roughness = (int) roughness.textBox.getValue();
this->settings.parameters.roughness = (int) roughness.textBox.getValue();
};
intensity.textBox.setValue(audioProcessor.intensity);
intensity.textBox.setValue(settings.parameters.intensity);
intensity.textBox.onValueChange = [this]() {
audioProcessor.intensity = intensity.textBox.getValue();
this->settings.parameters.intensity = intensity.textBox.getValue();
};
if (parent == nullptr) {
@ -52,7 +52,7 @@ VisualiserComponent::VisualiserComponent(OscirenderAudioProcessor& p, Visualiser
VisualiserComponent::~VisualiserComponent() {
{
juce::CriticalSection::ScopedLockType scope(consumerLock);
audioProcessor.consumerStop(consumer);
consumerManager.consumerStop(consumer);
}
stopThread(1000);
masterReference.clear();
@ -127,15 +127,15 @@ void VisualiserComponent::timerCallback() {
void VisualiserComponent::run() {
while (!threadShouldExit()) {
if (sampleRate != (int) audioProcessor.currentSampleRate) {
if (sampleRate != (int) sampleRateManager.getSampleRate()) {
resetBuffer();
}
{
juce::CriticalSection::ScopedLockType scope(consumerLock);
consumer = audioProcessor.consumerRegister(tempBuffer);
consumer = consumerManager.consumerRegister(tempBuffer);
}
audioProcessor.consumerRead(consumer);
consumerManager.consumerRead(consumer);
setBuffer(tempBuffer);
if (!oldVisualiser) {
@ -153,7 +153,7 @@ void VisualiserComponent::setPaused(bool paused) {
} else {
{
juce::CriticalSection::ScopedLockType scope(consumerLock);
audioProcessor.consumerStop(consumer);
consumerManager.consumerStop(consumer);
}
stopTimer();
stopThread(1000);
@ -329,7 +329,7 @@ void VisualiserComponent::initialiseBrowser() {
}
void VisualiserComponent::resetBuffer() {
sampleRate = (int) audioProcessor.currentSampleRate;
sampleRate = (int) sampleRateManager.getSampleRate();
tempBuffer = std::vector<float>(2 * sampleRate * BUFFER_LENGTH_SECS);
if (!oldVisualiser && isShowing()) {
restartBrowser = true;
@ -373,7 +373,7 @@ void VisualiserComponent::childChanged() {
}
void VisualiserComponent::popoutWindow() {
auto visualiser = new VisualiserComponent(audioProcessor, settings, this, oldVisualiser);
auto visualiser = new VisualiserComponent(sampleRateManager, consumerManager, settings, this, oldVisualiser);
visualiser->settings.setLookAndFeel(&getLookAndFeel());
visualiser->openSettings = openSettings;
visualiser->closeSettings = closeSettings;

Wyświetl plik

@ -3,8 +3,8 @@
#include <algorithm>
#include <JuceHeader.h>
#include "../LookAndFeel.h"
#include "../concurrency/BufferConsumer.h"
#include "../PluginProcessor.h"
#include "../concurrency/ConsumerManager.h"
#include "../audio/SampleRateManager.h"
#include "LabelledTextBox.h"
#include "SvgButton.h"
#include "VisualiserSettings.h"
@ -18,7 +18,7 @@ enum class FullScreenMode {
class VisualiserWindow;
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(SampleRateManager& sampleRateManager, ConsumerManager& consumerManager, VisualiserSettings& settings, VisualiserComponent* parent = nullptr, bool useOldVisualiser = false);
~VisualiserComponent() override;
std::function<void()> openSettings;
@ -65,7 +65,8 @@ private:
std::vector<float> buffer;
std::vector<juce::Line<float>> prevLines;
juce::Colour backgroundColour, waveformColour;
OscirenderAudioProcessor& audioProcessor;
SampleRateManager& sampleRateManager;
ConsumerManager& consumerManager;
int sampleRate = DEFAULT_SAMPLE_RATE;
LabelledTextBox roughness{"Roughness", 1, 8, 1};
LabelledTextBox intensity{"Intensity", 0, 1, 0.01};

Wyświetl plik

@ -3,7 +3,7 @@
#include "../PluginEditor.h"
VisualiserSettings::VisualiserSettings(OscirenderAudioProcessor& p) : audioProcessor(p) {
VisualiserSettings::VisualiserSettings(VisualiserParameters& parameters) : parameters(parameters) {
addAndMakeVisible(brightness);
addAndMakeVisible(intensity);
addAndMakeVisible(persistence);
@ -34,12 +34,12 @@ void VisualiserSettings::resized() {
juce::var VisualiserSettings::getSettings() {
auto settings = new juce::DynamicObject();
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());
settings->setProperty("smudges", audioProcessor.smudgesEnabled->getBoolValue());
settings->setProperty("upsampling", audioProcessor.upsamplingEnabled->getBoolValue());
settings->setProperty("brightness", parameters.brightnessEffect->getActualValue() - 2);
settings->setProperty("intensity", parameters.intensityEffect->getActualValue() / 100);
settings->setProperty("persistence", parameters.persistenceEffect->getActualValue() - 1.33);
settings->setProperty("hue", parameters.hueEffect->getActualValue());
settings->setProperty("graticule", parameters.graticuleEnabled->getBoolValue());
settings->setProperty("smudges", parameters.smudgesEnabled->getBoolValue());
settings->setProperty("upsampling", parameters.upsamplingEnabled->getBoolValue());
return juce::var(settings);
}

Wyświetl plik

@ -1,29 +1,77 @@
#pragma once
#define VERSION_HINT 2
#include <JuceHeader.h>
#include "EffectComponent.h"
#include "SvgButton.h"
#include "../LookAndFeel.h"
#include "SwitchButton.h"
class VisualiserParameters {
public:
std::atomic<int> roughness = 4;
std::atomic<double> intensity = 1.0;
BooleanParameter* graticuleEnabled = new BooleanParameter("Show Graticule", "graticuleEnabled", VERSION_HINT, true, "Show the graticule or grid lines over the oscilloscope display.");
BooleanParameter* smudgesEnabled = new BooleanParameter("Show Smudges", "smudgesEnabled", VERSION_HINT, true, "Adds a subtle layer of dirt/smudges to the oscilloscope display to make it look more realistic.");
BooleanParameter* upsamplingEnabled = new BooleanParameter("Upsample Audio", "upsamplingEnabled", VERSION_HINT, false, "Upsamples the audio before visualising it to make it appear more realistic, at the expense of performance.");
BooleanParameter* legacyVisualiserEnabled = new BooleanParameter("Use Legacy Visualiser", "legacyVisualiserEnabled", VERSION_HINT, false, "Replaces the realistic oscilloscope visualiser with the legacy visualiser. This may improve performance.");
BooleanParameter* visualiserFullScreen = new BooleanParameter("Visualiser Fullscreen", "visualiserFullScreen", VERSION_HINT, false, "Makes the software visualiser fullscreen.");
std::shared_ptr<Effect> persistenceEffect = std::make_shared<Effect>(
new EffectParameter(
"Persistence",
"Controls how long the light glows for on the oscilloscope display.",
"persistence",
VERSION_HINT, 0.5, 0, 6.0
)
);
std::shared_ptr<Effect> hueEffect = std::make_shared<Effect>(
new EffectParameter(
"Hue",
"Controls the hue/colour of the oscilloscope display.",
"hue",
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 electron beam of the oscilloscope is.",
"intensity",
VERSION_HINT, 3.0, 0.0, 10.0
)
);
};
class VisualiserSettings : public juce::Component {
public:
VisualiserSettings(OscirenderAudioProcessor&);
VisualiserSettings(VisualiserParameters&);
~VisualiserSettings();
void resized() override;
juce::var getSettings();
private:
OscirenderAudioProcessor& audioProcessor;
EffectComponent brightness{audioProcessor, *audioProcessor.brightnessEffect};
EffectComponent intensity{audioProcessor, *audioProcessor.intensityEffect};
EffectComponent persistence{audioProcessor, *audioProcessor.persistenceEffect};
EffectComponent hue{audioProcessor, *audioProcessor.hueEffect};
VisualiserParameters& parameters;
private:
EffectComponent brightness{*parameters.brightnessEffect};
EffectComponent intensity{*parameters.intensityEffect};
EffectComponent persistence{*parameters.persistenceEffect};
EffectComponent hue{*parameters.hueEffect};
jux::SwitchButton graticuleToggle{audioProcessor.graticuleEnabled};
jux::SwitchButton smudgeToggle{audioProcessor.smudgesEnabled};
jux::SwitchButton upsamplingToggle{audioProcessor.upsamplingEnabled};
jux::SwitchButton graticuleToggle{parameters.graticuleEnabled};
jux::SwitchButton smudgeToggle{parameters.smudgesEnabled};
jux::SwitchButton upsamplingToggle{parameters.upsamplingEnabled};
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(VisualiserSettings)
};

Wyświetl plik

@ -0,0 +1,36 @@
#pragma once
#include <JuceHeader.h>
#include "BufferConsumer.h"
class ConsumerManager {
public:
ConsumerManager() {}
~ConsumerManager() {}
std::shared_ptr<BufferConsumer> consumerRegister(std::vector<float>& buffer) {
std::shared_ptr<BufferConsumer> consumer = std::make_shared<BufferConsumer>(buffer);
juce::SpinLock::ScopedLockType scope(consumerLock);
consumers.push_back(consumer);
return consumer;
}
void consumerRead(std::shared_ptr<BufferConsumer> consumer) {
consumer->waitUntilFull();
juce::SpinLock::ScopedLockType scope(consumerLock);
consumers.erase(std::remove(consumers.begin(), consumers.end(), consumer), consumers.end());
}
void consumerStop(std::shared_ptr<BufferConsumer> consumer) {
if (consumer != nullptr) {
juce::SpinLock::ScopedLockType scope(consumerLock);
consumer->forceNotify();
}
}
protected:
juce::SpinLock consumerLock;
std::vector<std::shared_ptr<BufferConsumer>> consumers;
};

Wyświetl plik

@ -100,6 +100,8 @@
<FILE id="rQC2gX" name="PitchDetector.h" compile="0" resource="0" file="Source/audio/PitchDetector.h"/>
<FILE id="t5g8pf" name="PublicSynthesiser.h" compile="0" resource="0"
file="Source/audio/PublicSynthesiser.h"/>
<FILE id="Q5kjpU" name="SampleRateManager.h" compile="0" resource="0"
file="Source/audio/SampleRateManager.h"/>
<FILE id="dBaZAV" name="ShapeSound.cpp" compile="1" resource="0" file="Source/audio/ShapeSound.cpp"/>
<FILE id="VKBirB" name="ShapeSound.h" compile="0" resource="0" file="Source/audio/ShapeSound.h"/>
<FILE id="UcPZ09" name="ShapeVoice.cpp" compile="1" resource="0" file="Source/audio/ShapeVoice.cpp"/>
@ -188,6 +190,8 @@
<FILE id="F5kUMH" name="BlockingQueue.h" compile="0" resource="0" file="Source/concurrency/BlockingQueue.h"/>
<FILE id="WQ2W15" name="BufferConsumer.h" compile="0" resource="0"
file="Source/concurrency/BufferConsumer.h"/>
<FILE id="VMSdiT" name="ConsumerManager.h" compile="0" resource="0"
file="Source/concurrency/ConsumerManager.h"/>
</GROUP>
<GROUP id="{A3E24187-62A5-AB8D-8837-14043B89A640}" name="gpla">
<FILE id="KvDV8j" name="LineArtParser.cpp" compile="1" resource="0"
@ -658,9 +662,9 @@
</LINUX_MAKE>
<VS2022 targetFolder="Builds/VisualStudio2022" smallIcon="pSc1mq" bigIcon="pSc1mq">
<CONFIGURATIONS>
<CONFIGURATION isDebug="1" name="Debug" targetName="osci-render" useRuntimeLibDLL="0"/>
<CONFIGURATION isDebug="1" name="Debug" targetName="osci-render"/>
<CONFIGURATION isDebug="0" name="Release" targetName="osci-render" alwaysGenerateDebugSymbols="1"
debugInformationFormat="ProgramDatabase" useRuntimeLibDLL="0"/>
debugInformationFormat="ProgramDatabase"/>
</CONFIGURATIONS>
<MODULEPATHS>
<MODULEPATH id="juce_audio_basics" path="../../../JUCE/modules"/>

197
sosci.jucer 100644
Wyświetl plik

@ -0,0 +1,197 @@
<?xml version="1.0" encoding="UTF-8"?>
<JUCERPROJECT id="HH2E72" name="sosci" projectType="audioplug" useAppConfig="0"
addUsingNamespaceToJuceHeader="0" jucerFormatVersion="1" pluginManufacturer="jameshball"
aaxIdentifier="sh.ball.sosci" cppLanguageStandard="20" projectLineFeed="&#10;"
headerPath="./include" version="1.0.0" companyName="James H Ball"
companyWebsite="https://osci-render.com" companyEmail="james@ball.sh"
defines="NOMINMAX=1" pluginAUMainType="'aumf'" binaryDataNamespace="SosciBinaryData">
<MAINGROUP id="j5Ge2T" name="sosci">
<GROUP id="{5ABCED88-0059-A7AF-9596-DBF91DDB0292}" name="Resources">
<GROUP id="{525C568C-29E9-D0A2-9773-8A04981C5575}" name="images">
<FILE id="jI9VSZ" name="logo.png" compile="0" resource="1" file="Resources/images/logo.png"/>
</GROUP>
<GROUP id="{F3C16D02-63B4-E3DA-7498-901173C37D6C}" name="oscilloscope">
<GROUP id="{DD5ACF8F-4E4F-E277-176A-5FD4A9717037}" name="juce">
<FILE id="FCfppP" name="check_native_interop.js" compile="0" resource="1"
file="Resources/oscilloscope/juce/check_native_interop.js"/>
<FILE id="ZEUE5w" name="index.js" compile="0" resource="1" file="Resources/oscilloscope/juce/index.js"/>
<FILE id="hWk293" name="package.json" compile="0" resource="1" file="Resources/oscilloscope/juce/package.json"/>
</GROUP>
<FILE id="qpPhpN" name="empty.jpg" compile="0" resource="1" file="Resources/oscilloscope/empty.jpg"/>
<FILE id="dNtZYs" name="noise.jpg" compile="0" resource="1" file="Resources/oscilloscope/noise.jpg"/>
<FILE id="YPMnjq" name="oscilloscope.html" compile="0" resource="1"
file="Resources/oscilloscope/oscilloscope.html"/>
<FILE id="BeXHj7" name="oscilloscope.js" compile="0" resource="1" file="Resources/oscilloscope/oscilloscope.js"/>
</GROUP>
<GROUP id="{82BCD6F1-A8BF-F30B-5587-81EE70168883}" name="svg">
<FILE id="rl17ZK" name="cog.svg" compile="0" resource="1" file="Resources/svg/cog.svg"/>
<FILE id="sDajXu" name="delete.svg" compile="0" resource="1" file="Resources/svg/delete.svg"/>
<FILE id="IqXIZW" name="demo.svg" compile="0" resource="1" file="Resources/svg/demo.svg"/>
<FILE id="YwkQpy" name="fixed_rotate.svg" compile="0" resource="1"
file="Resources/svg/fixed_rotate.svg"/>
<FILE id="WIkl6l" name="fullscreen.svg" compile="0" resource="1" file="Resources/svg/fullscreen.svg"/>
<FILE id="n1esUp" name="left_arrow.svg" compile="0" resource="1" file="Resources/svg/left_arrow.svg"/>
<FILE id="PxYKbt" name="microphone.svg" compile="0" resource="1" file="Resources/svg/microphone.svg"/>
<FILE id="hJHxFY" name="open_in_new.svg" compile="0" resource="1" file="Resources/svg/open_in_new.svg"/>
<FILE id="pSc1mq" name="osci.svg" compile="0" resource="1" file="Resources/svg/osci.svg"/>
<FILE id="f2D5tv" name="pause.svg" compile="0" resource="1" file="Resources/svg/pause.svg"/>
<FILE id="D2AI1b" name="pencil.svg" compile="0" resource="1" file="Resources/svg/pencil.svg"/>
<FILE id="PFc2q2" name="random.svg" compile="0" resource="1" file="Resources/svg/random.svg"/>
<FILE id="CE6di2" name="range.svg" compile="0" resource="1" file="Resources/svg/range.svg"/>
<FILE id="n79IAy" name="record.svg" compile="0" resource="1" file="Resources/svg/record.svg"/>
<FILE id="OaqZb1" name="right_arrow.svg" compile="0" resource="1" file="Resources/svg/right_arrow.svg"/>
<FILE id="rXjNlx" name="threshold.svg" compile="0" resource="1" file="Resources/svg/threshold.svg"/>
<FILE id="rFYmV8" name="timer.svg" compile="0" resource="1" file="Resources/svg/timer.svg"/>
<FILE id="qC6QiP" name="volume.svg" compile="0" resource="1" file="Resources/svg/volume.svg"/>
</GROUP>
</GROUP>
<GROUP id="{75439074-E50C-362F-1EDF-8B4BE9011259}" name="Source">
<GROUP id="{85A33213-D880-BD92-70D8-1901DA6D23F0}" name="audio">
<FILE id="S5ChqG" name="BooleanParameter.h" compile="0" resource="0"
file="Source/audio/BooleanParameter.h"/>
<FILE id="mP5lpY" name="Effect.cpp" compile="1" resource="0" file="Source/audio/Effect.cpp"/>
<FILE id="LKeNnY" name="Effect.h" compile="0" resource="0" file="Source/audio/Effect.h"/>
<FILE id="ca2VrC" name="EffectApplication.cpp" compile="1" resource="0"
file="Source/audio/EffectApplication.cpp"/>
<FILE id="MIYJ9y" name="EffectApplication.h" compile="0" resource="0"
file="Source/audio/EffectApplication.h"/>
<FILE id="aE0RtD" name="EffectParameter.h" compile="0" resource="0"
file="Source/audio/EffectParameter.h"/>
<FILE id="aziWU2" name="SampleRateManager.h" compile="0" resource="0"
file="Source/audio/SampleRateManager.h"/>
</GROUP>
<GROUP id="{CD81913A-7F0E-5898-DA77-5EBEB369DEB1}" name="components">
<FILE id="xLAEHK" name="EffectComponent.cpp" compile="1" resource="0"
file="Source/components/EffectComponent.cpp"/>
<FILE id="u4UCwb" name="EffectComponent.h" compile="0" resource="0"
file="Source/components/EffectComponent.h"/>
<FILE id="qzfstC" name="SwitchButton.h" compile="0" resource="0" file="Source/components/SwitchButton.h"/>
<FILE id="y3UiR0" name="VisualiserComponent.cpp" compile="1" resource="0"
file="Source/components/VisualiserComponent.cpp"/>
<FILE id="ZueyNl" name="VisualiserComponent.h" compile="0" resource="0"
file="Source/components/VisualiserComponent.h"/>
<FILE id="uOtHbQ" name="VisualiserSettings.cpp" compile="1" resource="0"
file="Source/components/VisualiserSettings.cpp"/>
<FILE id="GcbeeZ" name="VisualiserSettings.h" compile="0" resource="0"
file="Source/components/VisualiserSettings.h"/>
</GROUP>
<GROUP id="{9F5970A9-8094-E7F3-7AC1-812AE5589B9F}" name="concurrency">
<FILE id="F5kUMH" name="BlockingQueue.h" compile="0" resource="0" file="Source/concurrency/BlockingQueue.h"/>
<FILE id="WQ2W15" name="BufferConsumer.h" compile="0" resource="0"
file="Source/concurrency/BufferConsumer.h"/>
<FILE id="HIYtj5" name="ConsumerManager.h" compile="0" resource="0"
file="Source/concurrency/ConsumerManager.h"/>
</GROUP>
<GROUP id="{92CEA658-C82C-9CEB-15EB-945EF6B6B5C8}" name="shape">
<FILE id="G5fbub" name="Shape.cpp" compile="1" resource="0" file="Source/shape/Shape.cpp"/>
<FILE id="nUpro8" name="Point.cpp" compile="1" resource="0" file="Source/shape/Point.cpp"/>
<FILE id="X6A0jk" name="Point.h" compile="0" resource="0" file="Source/shape/Point.h"/>
<FILE id="NmptSY" name="Shape.h" compile="0" resource="0" file="Source/shape/Shape.h"/>
</GROUP>
<FILE id="d2zFqF" name="LookAndFeel.cpp" compile="1" resource="0" file="Source/LookAndFeel.cpp"/>
<FILE id="TJDqWs" name="LookAndFeel.h" compile="0" resource="0" file="Source/LookAndFeel.h"/>
<FILE id="ajg1QS" name="SosciPluginEditor.cpp" compile="1" resource="0"
file="Source/SosciPluginEditor.cpp"/>
<FILE id="SgIFr0" name="SosciPluginEditor.h" compile="0" resource="0"
file="Source/SosciPluginEditor.h"/>
<FILE id="uSfvvP" name="SosciPluginProcessor.cpp" compile="1" resource="0"
file="Source/SosciPluginProcessor.cpp"/>
<FILE id="nSDgxn" name="SosciPluginProcessor.h" compile="0" resource="0"
file="Source/SosciPluginProcessor.h"/>
</GROUP>
</MAINGROUP>
<JUCEOPTIONS JUCE_STRICT_REFCOUNTEDPOINTER="1" JUCE_VST3_CAN_REPLACE_VST2="0"
JUCE_WIN_PER_MONITOR_DPI_AWARE="0" JUCE_USE_WIN_WEBVIEW2_WITH_STATIC_LINKING="1"
JUCE_USE_WIN_WEBVIEW2="1"/>
<EXPORTFORMATS>
<LINUX_MAKE targetFolder="Builds/sosci/LinuxMakefile" smallIcon="pSc1mq"
bigIcon="pSc1mq">
<CONFIGURATIONS>
<CONFIGURATION isDebug="1" name="Debug" targetName="osci-render"/>
<CONFIGURATION isDebug="0" name="Release" targetName="osci-render"/>
</CONFIGURATIONS>
<MODULEPATHS>
<MODULEPATH id="juce_audio_basics" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_audio_devices" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_audio_formats" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_audio_plugin_client" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_audio_processors" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_audio_utils" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_core" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_data_structures" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_events" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_graphics" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_gui_basics" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_gui_extra" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_opengl" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_dsp" path="../../JUCE/modules"/>
</MODULEPATHS>
</LINUX_MAKE>
<VS2022 targetFolder="Builds/sosci/VisualStudio2022" smallIcon="pSc1mq"
bigIcon="pSc1mq">
<CONFIGURATIONS>
<CONFIGURATION isDebug="1" name="Debug" targetName="osci-render"/>
<CONFIGURATION isDebug="0" name="Release" targetName="osci-render" alwaysGenerateDebugSymbols="1"
debugInformationFormat="ProgramDatabase"/>
</CONFIGURATIONS>
<MODULEPATHS>
<MODULEPATH id="juce_audio_basics" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_audio_devices" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_audio_formats" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_audio_plugin_client" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_audio_processors" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_audio_utils" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_core" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_data_structures" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_events" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_graphics" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_gui_basics" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_gui_extra" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_opengl" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_dsp" path="../../JUCE/modules"/>
</MODULEPATHS>
</VS2022>
<XCODE_MAC targetFolder="Builds/sosci/MacOSX" extraLinkerFlags="-Wl,-weak_reference_mismatches,weak"
extraDefs="JUCE_SILENCE_XCODE_15_LINKER_WARNING=1" smallIcon="pSc1mq"
bigIcon="pSc1mq" applicationCategory="public.app-category.music">
<CONFIGURATIONS>
<CONFIGURATION isDebug="1" name="Debug" targetName="osci-render"/>
<CONFIGURATION name="Release" targetName="osci-render"/>
</CONFIGURATIONS>
<MODULEPATHS>
<MODULEPATH id="juce_audio_basics" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_audio_devices" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_audio_formats" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_audio_plugin_client" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_audio_processors" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_audio_utils" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_core" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_data_structures" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_events" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_graphics" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_gui_basics" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_gui_extra" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_opengl" path="../../../JUCE/modules"/>
<MODULEPATH id="juce_dsp" path="../../JUCE/modules"/>
</MODULEPATHS>
</XCODE_MAC>
</EXPORTFORMATS>
<MODULES>
<MODULE id="juce_audio_basics" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_audio_devices" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_audio_formats" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_audio_plugin_client" showAllCode="1" useLocalCopy="0"
useGlobalPath="1"/>
<MODULE id="juce_audio_processors" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_audio_utils" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_core" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_data_structures" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_dsp" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_events" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_graphics" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_gui_basics" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_gui_extra" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_opengl" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
</MODULES>
</JUCERPROJECT>