Complete major refactor of osci-render and sosci plugin editor and processor files. Still some bugs...

pull/261/head
James H Ball 2024-12-14 18:15:22 +00:00 zatwierdzone przez James H Ball
rodzic 1a1229fcba
commit 6e86f97649
24 zmienionych plików z 944 dodań i 1032 usunięć

Wyświetl plik

@ -0,0 +1,162 @@
#include "CommonPluginProcessor.h"
#include "CommonPluginEditor.h"
#include <juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h>
CommonPluginEditor::CommonPluginEditor(CommonAudioProcessor& p, juce::String appName, juce::String projectFileType)
: AudioProcessorEditor(&p), audioProcessor(p), appName(appName), projectFileType(projectFileType)
{
if (!applicationFolder.exists()) {
applicationFolder.createDirectory();
}
#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);
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);
visualiser.openSettings = [this] {
openVisualiserSettings();
};
visualiser.closeSettings = [this] {
visualiserSettingsWindow.setVisible(false);
};
visualiserSettingsWindow.setResizable(false, false);
#if JUCE_WINDOWS
// if not standalone, use native title bar for compatibility with DAWs
visualiserSettingsWindow.setUsingNativeTitleBar(processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone);
#elif JUCE_MAC
visualiserSettingsWindow.setUsingNativeTitleBar(true);
#endif
visualiserSettings.setLookAndFeel(&getLookAndFeel());
visualiserSettings.setSize(550, 400);
visualiserSettingsWindow.setContentNonOwned(&visualiserSettings, true);
visualiserSettingsWindow.centreWithSize(550, 400);
menuBar.toFront(true);
setSize(700, 750);
setResizable(true, true);
setResizeLimits(250, 250, 999999, 999999);
tooltipDropShadow.setOwner(&tooltipWindow);
}
void CommonPluginEditor::initialiseMenuBar(juce::MenuBarModel& menuBarModel) {
menuBar.setModel(&menuBarModel);
}
CommonPluginEditor::~CommonPluginEditor() {
if (audioProcessor.haltRecording != nullptr) {
audioProcessor.haltRecording();
}
setLookAndFeel(nullptr);
juce::Desktop::getInstance().setDefaultLookAndFeel(nullptr);
}
bool CommonPluginEditor::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 CommonPluginEditor::openProject() {
chooser = std::make_unique<juce::FileChooser>("Load " + appName + " Project", audioProcessor.lastOpenedDirectory, "*." + projectFileType);
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 CommonPluginEditor::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 CommonPluginEditor::saveProjectAs() {
chooser = std::make_unique<juce::FileChooser>("Save " + appName + " Project", audioProcessor.lastOpenedDirectory, "*." + projectFileType);
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 CommonPluginEditor::updateTitle() {
juce::String title = appName;
if (!audioProcessor.currentProjectFile.isEmpty()) {
appName += " - " + audioProcessor.currentProjectFile;
}
getTopLevelComponent()->setName(title);
}
void CommonPluginEditor::openAudioSettings() {
juce::StandalonePluginHolder* standalone = juce::StandalonePluginHolder::getInstance();
standalone->showAudioSettingsDialog();
}
void CommonPluginEditor::resetToDefault() {
juce::StandaloneFilterWindow* window = findParentComponentOfClass<juce::StandaloneFilterWindow>();
if (window != nullptr) {
window->resetToDefaultState();
}
}
void CommonPluginEditor::openVisualiserSettings() {
visualiserSettingsWindow.setVisible(true);
visualiserSettingsWindow.toFront(true);
}

Wyświetl plik

@ -0,0 +1,65 @@
#pragma once
#include <JuceHeader.h>
#include "CommonPluginProcessor.h"
#include "visualiser/VisualiserComponent.h"
#include "LookAndFeel.h"
#include "visualiser/VisualiserSettings.h"
#include "components/SosciMainMenuBarModel.h"
#include "components/SvgButton.h"
class CommonPluginEditor : public juce::AudioProcessorEditor {
public:
CommonPluginEditor(CommonAudioProcessor&, juce::String appName, juce::String projectFileType);
~CommonPluginEditor() override;
void initialiseMenuBar(juce::MenuBarModel& menuBarModel);
void openProject();
void saveProject();
void saveProjectAs();
void updateTitle();
void openAudioSettings();
void resetToDefault();
void openVisualiserSettings();
private:
CommonAudioProcessor& audioProcessor;
juce::File applicationFolder = juce::File::getSpecialLocation(juce::File::SpecialLocationType::userApplicationDataDirectory)
#if JUCE_MAC
.getChildFile("Application Support")
#endif
.getChildFile("osci-render");
juce::String ffmpegFileName =
#if JUCE_WINDOWS
"ffmpeg.exe";
#else
"ffmpeg";
#endif
public:
OscirenderLookAndFeel lookAndFeel;
juce::String appName;
juce::String projectFileType;
VisualiserSettings visualiserSettings = VisualiserSettings(audioProcessor.visualiserParameters, 3);
SettingsWindow visualiserSettingsWindow = SettingsWindow("Visualiser Settings");
VisualiserComponent visualiser{audioProcessor.lastOpenedDirectory, applicationFolder.getChildFile(ffmpegFileName), audioProcessor.haltRecording, audioProcessor.threadManager, visualiserSettings, nullptr, appName == "sosci"};
std::unique_ptr<juce::FileChooser> chooser;
juce::MenuBarComponent menuBar;
juce::TooltipWindow tooltipWindow{nullptr, 0};
juce::DropShadower tooltipDropShadow{juce::DropShadow(juce::Colours::black.withAlpha(0.5f), 6, {0,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(CommonPluginEditor)
};

Wyświetl plik

@ -0,0 +1,176 @@
/*
==============================================================================
This file contains the basic framework code for a JUCE plugin processor.
==============================================================================
*/
#include "CommonPluginProcessor.h"
#include "CommonPluginEditor.h"
#include "audio/EffectParameter.h"
//==============================================================================
CommonAudioProcessor::CommonAudioProcessor()
#ifndef JucePlugin_PreferredChannelConfigurations
: AudioProcessor (BusesProperties().withInput("Input", juce::AudioChannelSet::namedChannelSet(3), true)
.withOutput("Output", juce::AudioChannelSet::stereo(), true))
#endif
{
// locking isn't necessary here because we are in the constructor
for (auto effect : visualiserParameters.effects) {
permanentEffects.push_back(effect);
effects.push_back(effect);
}
effects.push_back(visualiserParameters.smoothEffect);
permanentEffects.push_back(visualiserParameters.smoothEffect);
for (auto parameter : visualiserParameters.booleans) {
booleanParameters.push_back(parameter);
}
}
void CommonAudioProcessor::addAllParameters() {
for (auto effect : effects) {
for (auto effectParameter : effect->parameters) {
auto parameters = effectParameter->getParameters();
for (auto parameter : parameters) {
addParameter(parameter);
}
}
}
for (auto parameter : booleanParameters) {
addParameter(parameter);
}
for (auto parameter : floatParameters) {
addParameter(parameter);
}
for (auto parameter : intParameters) {
addParameter(parameter);
}
}
CommonAudioProcessor::~CommonAudioProcessor() {}
const juce::String CommonAudioProcessor::getName() const {
return JucePlugin_Name;
}
bool CommonAudioProcessor::acceptsMidi() const {
#if JucePlugin_WantsMidiInput
return true;
#else
return false;
#endif
}
bool CommonAudioProcessor::producesMidi() const {
#if JucePlugin_ProducesMidiOutput
return true;
#else
return false;
#endif
}
bool CommonAudioProcessor::isMidiEffect() const {
#if JucePlugin_IsMidiEffect
return true;
#else
return false;
#endif
}
double CommonAudioProcessor::getTailLengthSeconds() const {
return 0.0;
}
int CommonAudioProcessor::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 CommonAudioProcessor::getCurrentProgram() {
return 0;
}
void CommonAudioProcessor::setCurrentProgram(int index) {
}
const juce::String CommonAudioProcessor::getProgramName(int index) {
return {};
}
void CommonAudioProcessor::changeProgramName(int index, const juce::String& newName) {}
void CommonAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock) {
currentSampleRate = sampleRate;
for (auto& effect : effects) {
effect->updateSampleRate(currentSampleRate);
}
threadManager.prepare(sampleRate, samplesPerBlock);
}
void CommonAudioProcessor::releaseResources() {
// When playback stops, you can use this as an opportunity to free up any
// spare memory, etc.
}
bool CommonAudioProcessor::isBusesLayoutSupported(const BusesLayout& layouts) const {
return true;
}
// effectsLock should be held when calling this
std::shared_ptr<Effect> CommonAudioProcessor::getEffect(juce::String id) {
for (auto& effect : effects) {
if (effect->getId() == id) {
return effect;
}
}
return nullptr;
}
// effectsLock should be held when calling this
BooleanParameter* CommonAudioProcessor::getBooleanParameter(juce::String id) {
for (auto& parameter : booleanParameters) {
if (parameter->paramID == id) {
return parameter;
}
}
return nullptr;
}
// effectsLock should be held when calling this
FloatParameter* CommonAudioProcessor::getFloatParameter(juce::String id) {
for (auto& parameter : floatParameters) {
if (parameter->paramID == id) {
return parameter;
}
}
return nullptr;
}
// effectsLock should be held when calling this
IntParameter* CommonAudioProcessor::getIntParameter(juce::String id) {
for (auto& parameter : intParameters) {
if (parameter->paramID == id) {
return parameter;
}
}
return nullptr;
}
//==============================================================================
bool CommonAudioProcessor::hasEditor() const {
return true; // (change this to false if you choose to not supply an editor)
}
double CommonAudioProcessor::getSampleRate() {
return currentSampleRate;
}

Wyświetl plik

@ -0,0 +1,88 @@
/*
==============================================================================
This file contains the basic framework code for a JUCE plugin processor.
==============================================================================
*/
#pragma once
#include <JuceHeader.h>
#include "concurrency/AudioBackgroundThread.h"
#include "concurrency/AudioBackgroundThreadManager.h"
#include "audio/SampleRateManager.h"
#include "visualiser/VisualiserSettings.h"
#include "audio/Effect.h"
//==============================================================================
/**
*/
class CommonAudioProcessor : public juce::AudioProcessor, public SampleRateManager
#if JucePlugin_Enable_ARA
, public juce::AudioProcessorARAExtension
#endif
{
public:
CommonAudioProcessor();
~CommonAudioProcessor() override;
void addAllParameters();
void prepareToPlay (double sampleRate, int samplesPerBlock) override;
void releaseResources() override;
#ifndef JucePlugin_PreferredChannelConfigurations
bool isBusesLayoutSupported (const BusesLayout& layouts) const override;
#endif
virtual void processBlock(juce::AudioBuffer<float>&, juce::MidiBuffer&) = 0;
virtual juce::AudioProcessorEditor* createEditor() = 0;
bool hasEditor() const override;
const juce::String getName() const override;
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;
double getSampleRate() override;
std::atomic<double> currentSampleRate = 0.0;
juce::SpinLock effectsLock;
VisualiserParameters visualiserParameters;
AudioBackgroundThreadManager threadManager;
std::function<void()> haltRecording;
std::atomic<bool> forceDisableBrightnessInput = false;
// 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);
protected:
bool brightnessEnabled = false;
std::vector<BooleanParameter*> booleanParameters;
std::vector<FloatParameter*> floatParameters;
std::vector<IntParameter*> intParameters;
std::vector<std::shared_ptr<Effect>> effects;
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 (CommonAudioProcessor)
};

Wyświetl plik

@ -126,9 +126,6 @@ MainComponent::MainComponent(OscirenderAudioProcessor& p, OscirenderAudioProcess
BooleanParameter* visualiserFullScreen = audioProcessor.visualiserParameters.visualiserFullScreen;
if (!visualiserFullScreen->getBoolValue()) {
addAndMakeVisible(pluginEditor.visualiser);
}
pluginEditor.visualiser.setFullScreenCallback([this, visualiserFullScreen](FullScreenMode mode) {
if (mode == FullScreenMode::TOGGLE) {
visualiserFullScreen->setBoolValueNotifyingHost(!visualiserFullScreen->getBoolValue());

Wyświetl plik

@ -3,27 +3,9 @@
#include <juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h>
OscirenderAudioProcessorEditor::OscirenderAudioProcessorEditor(OscirenderAudioProcessor& p)
: AudioProcessorEditor(&p), audioProcessor(p), collapseButton("Collapse", juce::Colours::white, juce::Colours::white, juce::Colours::white)
{
if (applicationFolder.exists()) {
applicationFolder.createDirectory();
}
#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);
: CommonPluginEditor(p, "osci-render", "osci"), audioProcessor(p), collapseButton("Collapse", juce::Colours::white, juce::Colours::white, juce::Colours::white) {
addAndMakeVisible(volume);
#if JUCE_MAC
if (audioProcessor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone) {
usingNativeMenuBar = true;
menuBarModel.setMacMainMenu(&menuBarModel);
}
#endif
addAndMakeVisible(console);
console.setConsoleOpen(false);
@ -35,11 +17,6 @@ OscirenderAudioProcessorEditor::OscirenderAudioProcessorEditor(OscirenderAudioPr
console.clear();
};
if (!usingNativeMenuBar) {
menuBar.setModel(&menuBarModel);
addAndMakeVisible(menuBar);
}
addAndMakeVisible(collapseButton);
collapseButton.onClick = [this] {
{
@ -70,28 +47,6 @@ OscirenderAudioProcessorEditor::OscirenderAudioProcessorEditor(OscirenderAudioPr
audioProcessor.broadcaster.addChangeListener(this);
}
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);
}
}
setSize(1100, 750);
setResizable(true, true);
setResizeLimits(500, 400, 999999, 999999);
layout.setItemLayout(0, -0.3, -1.0, -0.7);
layout.setItemLayout(1, RESIZER_BAR_SIZE, RESIZER_BAR_SIZE, RESIZER_BAR_SIZE);
layout.setItemLayout(2, -0.0, -1.0, -0.3);
@ -107,53 +62,13 @@ OscirenderAudioProcessorEditor::OscirenderAudioProcessorEditor(OscirenderAudioPr
addAndMakeVisible(luaResizerBar);
addAndMakeVisible(visualiser);
visualiser.openSettings = [this] {
openVisualiserSettings();
};
visualiser.closeSettings = [this] {
visualiserSettingsWindow.setVisible(false);
};
visualiserSettingsWindow.setResizable(false, false);
#if JUCE_WINDOWS
// if not standalone, use native title bar for compatibility with DAWs
visualiserSettingsWindow.setUsingNativeTitleBar(processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone);
#elif JUCE_MAC
visualiserSettingsWindow.setUsingNativeTitleBar(true);
#endif
visualiserSettings.setLookAndFeel(&getLookAndFeel());
visualiserSettings.setSize(550, 470);
visualiserSettingsWindow.setContentNonOwned(&visualiserSettings, true);
visualiserSettingsWindow.centreWithSize(550, 470);
tooltipDropShadow.setOwner(&tooltipWindow);
initialiseMenuBar(model);
}
OscirenderAudioProcessorEditor::~OscirenderAudioProcessorEditor() {
if (audioProcessor.haltRecording != nullptr) {
audioProcessor.haltRecording();
}
setLookAndFeel(nullptr);
juce::Desktop::getInstance().setDefaultLookAndFeel(nullptr);
juce::MessageManagerLock lock;
audioProcessor.broadcaster.removeChangeListener(this);
audioProcessor.fileChangeBroadcaster.removeChangeListener(this);
#if JUCE_MAC
if (usingNativeMenuBar) {
menuBarModel.setMacMainMenu(nullptr);
}
#endif
}
void OscirenderAudioProcessorEditor::openVisualiserSettings() {
visualiserSettingsWindow.setVisible(true);
visualiserSettingsWindow.toFront(true);
}
void OscirenderAudioProcessorEditor::closeVisualiserSettings() {
visualiserSettingsWindow.setVisible(false);
}
bool OscirenderAudioProcessorEditor::isBinaryFile(juce::String name) {
@ -479,13 +394,7 @@ bool OscirenderAudioProcessorEditor::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();
}
CommonPluginEditor::keyPressed(key);
return consumeKey;
}
@ -505,72 +414,7 @@ void OscirenderAudioProcessorEditor::mouseMove(const juce::MouseEvent& event) {
}
}
void OscirenderAudioProcessorEditor::newProject() {
// TODO: open a default project
}
void OscirenderAudioProcessorEditor::openProject() {
chooser = std::make_unique<juce::FileChooser>("Load osci-render Project", audioProcessor.lastOpenedDirectory, "*.osci");
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 OscirenderAudioProcessorEditor::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 OscirenderAudioProcessorEditor::saveProjectAs() {
chooser = std::make_unique<juce::FileChooser>("Save osci-render Project", juce::File::getSpecialLocation(juce::File::userHomeDirectory), "*.osci");
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();
audioProcessor.lastOpenedDirectory = file.getParentDirectory();
saveProject();
}
});
}
void OscirenderAudioProcessorEditor::updateTitle() {
juce::String title = "osci-render";
if (!audioProcessor.currentProjectFile.isEmpty()) {
title += " - " + audioProcessor.currentProjectFile;
}
getTopLevelComponent()->setName(title);
}
void OscirenderAudioProcessorEditor::openAudioSettings() {
juce::StandalonePluginHolder* standalone = juce::StandalonePluginHolder::getInstance();
standalone->showAudioSettingsDialog();
}
void OscirenderAudioProcessorEditor::resetToDefault() {
juce::StandaloneFilterWindow* window = findParentComponentOfClass<juce::StandaloneFilterWindow>();
if (window != nullptr) {
window->resetToDefaultState();
}
}

Wyświetl plik

@ -5,13 +5,14 @@
#include "SettingsComponent.h"
#include "MidiComponent.h"
#include "components/VolumeComponent.h"
#include "components/MainMenuBarModel.h"
#include "components/OsciMainMenuBarModel.h"
#include "LookAndFeel.h"
#include "components/ErrorCodeEditorComponent.h"
#include "components/LuaConsole.h"
#include "visualiser/VisualiserSettings.h"
#include "CommonPluginEditor.h"
class OscirenderAudioProcessorEditor : public juce::AudioProcessorEditor, private juce::CodeDocument::Listener, public juce::AsyncUpdater, public juce::ChangeListener {
class OscirenderAudioProcessorEditor : public CommonPluginEditor, private juce::CodeDocument::Listener, public juce::AsyncUpdater, public juce::ChangeListener {
public:
OscirenderAudioProcessorEditor(OscirenderAudioProcessor&);
~OscirenderAudioProcessorEditor() override;
@ -27,48 +28,20 @@ public:
void handleAsyncUpdate() override;
void changeListenerCallback(juce::ChangeBroadcaster* source) override;
void toggleLayout(juce::StretchableLayoutManager& layout, double prefSize);
void openVisualiserSettings();
void closeVisualiserSettings();
void editCustomFunction(bool enabled);
void newProject();
void openProject();
void saveProject();
void saveProjectAs();
void updateTitle();
void openAudioSettings();
void resetToDefault();
private:
OscirenderAudioProcessor& audioProcessor;
juce::File applicationFolder = juce::File::getSpecialLocation(juce::File::SpecialLocationType::userApplicationDataDirectory)
#if JUCE_MAC
.getChildFile("Application Support")
#endif
.getChildFile("osci-render");
juce::String ffmpegFileName =
#if JUCE_WINDOWS
"ffmpeg.exe";
#else
"ffmpeg";
#endif
public:
const double CLOSED_PREF_SIZE = 30.0;
const double RESIZER_BAR_SIZE = 7.0;
OscirenderLookAndFeel lookAndFeel;
std::atomic<bool> editingCustomFunction = false;
VisualiserSettings visualiserSettings = VisualiserSettings(audioProcessor.visualiserParameters);
SettingsWindow visualiserSettingsWindow = SettingsWindow("Visualiser Settings");
VisualiserComponent visualiser{audioProcessor.lastOpenedDirectory, applicationFolder.getChildFile(ffmpegFileName), audioProcessor.haltRecording, audioProcessor.threadManager, visualiserSettings, nullptr};
SettingsComponent settings{audioProcessor, *this};
juce::ComponentAnimator codeEditorAnimator;
@ -86,9 +59,7 @@ public:
std::shared_ptr<juce::CodeDocument> customFunctionCodeDocument = std::make_shared<juce::CodeDocument>();
std::shared_ptr<OscirenderCodeEditorComponent> customFunctionCodeEditor = std::make_shared<OscirenderCodeEditorComponent>(*customFunctionCodeDocument, &luaTokeniser, audioProcessor, CustomEffect::UNIQUE_ID, CustomEffect::FILE_NAME);
std::unique_ptr<juce::FileChooser> chooser;
MainMenuBarModel menuBarModel{audioProcessor, *this};
juce::MenuBarComponent menuBar;
OsciMainMenuBarModel model{audioProcessor, *this};
juce::StretchableLayoutManager layout;
juce::StretchableLayoutResizerBar resizerBar{&layout, 1, true};
@ -96,17 +67,8 @@ public:
juce::StretchableLayoutManager luaLayout;
juce::StretchableLayoutResizerBar luaResizerBar{&luaLayout, 1, false};
juce::TooltipWindow tooltipWindow{nullptr, 0};
juce::DropShadower tooltipDropShadow{juce::DropShadow(juce::Colours::black.withAlpha(0.5f), 6, {0,0})};
std::atomic<bool> updatingDocumentsWithParserLock = false;
bool usingNativeMenuBar = false;
#if JUCE_LINUX
juce::OpenGLContext openGlContext;
#endif
void codeDocumentTextInserted(const juce::String& newText, int insertIndex) override;
void codeDocumentTextDeleted(int startIndex, int endIndex) override;
void updateCodeDocument();

Wyświetl plik

@ -18,18 +18,7 @@
#include "audio/EffectParameter.h"
//==============================================================================
OscirenderAudioProcessor::OscirenderAudioProcessor()
#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
{
OscirenderAudioProcessor::OscirenderAudioProcessor() {
// locking isn't necessary here because we are in the constructor
toggleableEffects.push_back(std::make_shared<Effect>(
@ -138,7 +127,7 @@ OscirenderAudioProcessor::OscirenderAudioProcessor()
for (int i = 0; i < toggleableEffects.size(); i++) {
auto effect = toggleableEffects[i];
effect->markEnableable(false);
addParameter(effect->enabled);
booleanParameters.push_back(effect->enabled);
effect->enabled->setValueNotifyingHost(false);
effect->setPrecedence(i);
}
@ -149,42 +138,20 @@ OscirenderAudioProcessor::OscirenderAudioProcessor()
permanentEffects.push_back(thresholdEffect);
permanentEffects.push_back(imageThreshold);
permanentEffects.push_back(imageStride);
for (auto effect : visualiserParameters.effects) {
permanentEffects.push_back(effect);
}
for (int i = 0; i < 26; i++) {
addLuaSlider();
}
allEffects = toggleableEffects;
allEffects.insert(allEffects.end(), permanentEffects.begin(), permanentEffects.end());
allEffects.insert(allEffects.end(), luaEffects.begin(), luaEffects.end());
allEffects.push_back(visualiserParameters.smoothEffect);
for (auto effect : allEffects) {
for (auto effectParameter : effect->parameters) {
auto parameters = effectParameter->getParameters();
for (auto parameter : parameters) {
addParameter(parameter);
}
}
}
effects = toggleableEffects;
effects.insert(effects.end(), permanentEffects.begin(), permanentEffects.end());
effects.insert(effects.end(), luaEffects.begin(), luaEffects.end());
booleanParameters.push_back(midiEnabled);
booleanParameters.push_back(inputEnabled);
booleanParameters.push_back(animateFrames);
booleanParameters.push_back(animationSyncBPM);
booleanParameters.push_back(invertImage);
for (auto parameter : visualiserParameters.booleans) {
booleanParameters.push_back(parameter);
}
for (auto parameter : booleanParameters) {
addParameter(parameter);
}
floatParameters.push_back(attackTime);
floatParameters.push_back(attackLevel);
@ -197,20 +164,12 @@ OscirenderAudioProcessor::OscirenderAudioProcessor()
floatParameters.push_back(animationRate);
floatParameters.push_back(animationOffset);
for (auto parameter : floatParameters) {
addParameter(parameter);
}
for (int i = 0; i < voices->getValueUnnormalised(); i++) {
synth.addVoice(new ShapeVoice(*this));
}
intParameters.push_back(voices);
for (auto parameter : intParameters) {
addParameter(parameter);
}
voices->addListener(this);
for (int i = 0; i < luaEffects.size(); i++) {
@ -218,6 +177,8 @@ OscirenderAudioProcessor::OscirenderAudioProcessor()
}
synth.addSound(defaultSound);
addAllParameters();
}
OscirenderAudioProcessor::~OscirenderAudioProcessor() {
@ -227,105 +188,19 @@ OscirenderAudioProcessor::~OscirenderAudioProcessor() {
voices->removeListener(this);
}
const juce::String OscirenderAudioProcessor::getName() const {
return JucePlugin_Name;
}
void OscirenderAudioProcessor::setAudioThreadCallback(std::function<void(const juce::AudioBuffer<float>&)> callback) {
juce::SpinLock::ScopedLockType lock(audioThreadCallbackLock);
audioThreadCallback = callback;
}
bool OscirenderAudioProcessor::acceptsMidi() const {
#if JucePlugin_WantsMidiInput
return true;
#else
return false;
#endif
}
bool OscirenderAudioProcessor::producesMidi() const {
#if JucePlugin_ProducesMidiOutput
return true;
#else
return false;
#endif
}
bool OscirenderAudioProcessor::isMidiEffect() const {
#if JucePlugin_IsMidiEffect
return true;
#else
return false;
#endif
}
double OscirenderAudioProcessor::getTailLengthSeconds() const {
return 0.0;
}
int OscirenderAudioProcessor::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 OscirenderAudioProcessor::getCurrentProgram() {
return 0;
}
void OscirenderAudioProcessor::setCurrentProgram(int index) {
}
const juce::String OscirenderAudioProcessor::getProgramName(int index) {
return {};
}
void OscirenderAudioProcessor::changeProgramName(int index, const juce::String& newName) {}
void OscirenderAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock) {
currentSampleRate = sampleRate;
CommonAudioProcessor::prepareToPlay(sampleRate, samplesPerBlock);
volumeBuffer = std::vector<double>(VOLUME_BUFFER_SECONDS * sampleRate, 0);
synth.setCurrentPlaybackSampleRate(sampleRate);
retriggerMidi = true;
for (auto& effect : allEffects) {
effect->updateSampleRate(currentSampleRate);
}
threadManager.prepare(sampleRate, samplesPerBlock);
}
void OscirenderAudioProcessor::releaseResources() {
// When playback stops, you can use this as an opportunity to free up any
// spare memory, etc.
}
#ifndef JucePlugin_PreferredChannelConfigurations
bool OscirenderAudioProcessor::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
void OscirenderAudioProcessor::addLuaSlider() {
juce::String sliderName = "";
@ -361,46 +236,6 @@ void OscirenderAudioProcessor::removeErrorListener(ErrorListener* listener) {
errorListeners.erase(std::remove(errorListeners.begin(), errorListeners.end(), listener), errorListeners.end());
}
// effectsLock should be held when calling this
std::shared_ptr<Effect> OscirenderAudioProcessor::getEffect(juce::String id) {
for (auto& effect : allEffects) {
if (effect->getId() == id) {
return effect;
}
}
return nullptr;
}
// effectsLock should be held when calling this
BooleanParameter* OscirenderAudioProcessor::getBooleanParameter(juce::String id) {
for (auto& parameter : booleanParameters) {
if (parameter->paramID == id) {
return parameter;
}
}
return nullptr;
}
// effectsLock should be held when calling this
FloatParameter* OscirenderAudioProcessor::getFloatParameter(juce::String id) {
for (auto& parameter : floatParameters) {
if (parameter->paramID == id) {
return parameter;
}
}
return nullptr;
}
// effectsLock should be held when calling this
IntParameter* OscirenderAudioProcessor::getIntParameter(juce::String id) {
for (auto& parameter : intParameters) {
if (parameter->paramID == id) {
return parameter;
}
}
return nullptr;
}
// effectsLock MUST be held when calling this
void OscirenderAudioProcessor::updateEffectPrecedence() {
auto sortFunc = [](std::shared_ptr<Effect> a, std::shared_ptr<Effect> b) {
@ -764,11 +599,6 @@ void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, ju
}
}
//==============================================================================
bool OscirenderAudioProcessor::hasEditor() const {
return true; // (change this to false if you choose to not supply an editor)
}
juce::AudioProcessorEditor* OscirenderAudioProcessor::createEditor() {
auto editor = new OscirenderAudioProcessorEditor(*this);
return editor;
@ -790,7 +620,7 @@ void OscirenderAudioProcessor::getStateInformation(juce::MemoryBlock& destData)
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) {
for (auto effect : effects) {
effect->save(effectsXml->createNewChildElement("effect"));
}
@ -1001,14 +831,6 @@ void OscirenderAudioProcessor::envelopeChanged(EnvelopeComponent* changedEnvelop
}
}
double OscirenderAudioProcessor::getSampleRate() {
return currentSampleRate;
}
//==============================================================================
// This creates new instances of the plugin..
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter() {
return new OscirenderAudioProcessor();
}

Wyświetl plik

@ -14,7 +14,6 @@
#include "shape/Shape.h"
#include "concurrency/AudioBackgroundThread.h"
#include "concurrency/AudioBackgroundThreadManager.h"
#include "visualiser/VisualiserSettings.h"
#include "audio/Effect.h"
#include "audio/ShapeSound.h"
#include "audio/ShapeVoice.h"
@ -30,11 +29,12 @@
#include "UGen/ugen_JuceEnvelopeComponent.h"
#include "audio/CustomEffect.h"
#include "audio/DashedLineEffect.h"
#include "CommonPluginProcessor.h"
//==============================================================================
/**
*/
class OscirenderAudioProcessor : public juce::AudioProcessor, juce::AudioProcessorParameter::Listener, public EnvelopeComponentListener, public SampleRateManager
class OscirenderAudioProcessor : public CommonAudioProcessor, juce::AudioProcessorParameter::Listener, public EnvelopeComponentListener
#if JucePlugin_Enable_ARA
, public juce::AudioProcessorARAExtension
#endif
@ -44,42 +44,18 @@ public:
~OscirenderAudioProcessor() 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;
void parameterValueChanged(int parameterIndex, float newValue) override;
void parameterGestureChanged(int parameterIndex, bool gestureIsStarting) override;
void envelopeChanged(EnvelopeComponent* changedEnvelope) override;
double getSampleRate() override;
std::atomic<double> currentSampleRate = 0.0;
AudioBackgroundThreadManager threadManager;
juce::SpinLock effectsLock;
std::vector<std::shared_ptr<Effect>> toggleableEffects;
std::vector<std::shared_ptr<Effect>> luaEffects;
std::atomic<double> luaValues[26] = { 0.0 };
@ -156,8 +132,6 @@ 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),
}
);
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.");
@ -230,11 +204,6 @@ public:
PitchDetector pitchDetector{*this};
std::shared_ptr<WobbleEffect> wobbleEffect = std::make_shared<WobbleEffect>(pitchDetector);
// 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);
juce::SpinLock fontLock;
juce::Font font = juce::Font(juce::Font::getDefaultSansSerifFontName(), 1.0f, juce::Font::plain);
@ -271,12 +240,6 @@ private:
juce::SpinLock audioThreadCallbackLock;
std::function<void(const juce::AudioBuffer<float>&)> audioThreadCallback;
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;
juce::SpinLock errorListenersLock;
std::vector<ErrorListener*> errorListeners;
@ -293,10 +256,6 @@ private:
double squaredVolume = 0;
double currentVolume = 0;
std::shared_ptr<Effect> getEffect(juce::String id);
BooleanParameter* getBooleanParameter(juce::String id);
FloatParameter* getFloatParameter(juce::String id);
IntParameter* getIntParameter(juce::String id);
void openLegacyProject(const juce::XmlElement* xml);
std::pair<std::shared_ptr<Effect>, EffectParameter*> effectFromLegacyId(const juce::String& id, bool updatePrecedence = false);
LfoType lfoTypeFromLegacyAnimationType(const juce::String& type);

Wyświetl plik

@ -2,76 +2,9 @@
#include "SosciPluginEditor.h"
#include <juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h>
SosciPluginEditor::SosciPluginEditor(SosciAudioProcessor& p)
: AudioProcessorEditor(&p), audioProcessor(p)
{
if (!applicationFolder.exists()) {
applicationFolder.createDirectory();
}
#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);
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);
visualiser.openSettings = [this] {
openVisualiserSettings();
};
visualiser.closeSettings = [this] {
visualiserSettingsWindow.setVisible(false);
};
visualiserSettingsWindow.setResizable(false, false);
#if JUCE_WINDOWS
// if not standalone, use native title bar for compatibility with DAWs
visualiserSettingsWindow.setUsingNativeTitleBar(processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone);
#elif JUCE_MAC
visualiserSettingsWindow.setUsingNativeTitleBar(true);
#endif
visualiserSettings.setLookAndFeel(&getLookAndFeel());
visualiserSettings.setSize(550, 400);
visualiserSettingsWindow.setContentNonOwned(&visualiserSettings, true);
visualiserSettingsWindow.centreWithSize(550, 400);
menuBar.toFront(true);
setSize(700, 750);
setResizable(true, true);
setResizeLimits(250, 250, 999999, 999999);
}
SosciPluginEditor::~SosciPluginEditor() {
if (audioProcessor.haltRecording != nullptr) {
audioProcessor.haltRecording();
}
setLookAndFeel(nullptr);
juce::Desktop::getInstance().setDefaultLookAndFeel(nullptr);
SosciPluginEditor::SosciPluginEditor(SosciAudioProcessor& p) : CommonPluginEditor(p, "sosci", "sosci"), audioProcessor(p) {
initialiseMenuBar(model);
resized();
}
void SosciPluginEditor::paint(juce::Graphics& g) {
@ -85,85 +18,3 @@ void SosciPluginEditor::resized() {
visualiserArea.removeFromTop(25);
visualiser.setBounds(visualiserArea);
}
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();
}
}
void SosciPluginEditor::openVisualiserSettings() {
visualiserSettingsWindow.setVisible(true);
visualiserSettingsWindow.toFront(true);
}

Wyświetl plik

@ -2,63 +2,24 @@
#include <JuceHeader.h>
#include "SosciPluginProcessor.h"
#include "CommonPluginEditor.h"
#include "visualiser/VisualiserComponent.h"
#include "LookAndFeel.h"
#include "visualiser/VisualiserSettings.h"
#include "components/SosciMainMenuBarModel.h"
#include "components/SvgButton.h"
class SosciPluginEditor : public juce::AudioProcessorEditor {
class SosciPluginEditor : public CommonPluginEditor {
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();
void openVisualiserSettings();
private:
SosciAudioProcessor& audioProcessor;
juce::File applicationFolder = juce::File::getSpecialLocation(juce::File::SpecialLocationType::userApplicationDataDirectory)
#if JUCE_MAC
.getChildFile("Application Support")
#endif
.getChildFile("osci-render");
juce::String ffmpegFileName =
#if JUCE_WINDOWS
"ffmpeg.exe";
#else
"ffmpeg";
#endif
public:
OscirenderLookAndFeel lookAndFeel;
VisualiserSettings visualiserSettings = VisualiserSettings(audioProcessor.parameters, 3);
SettingsWindow visualiserSettingsWindow = SettingsWindow("Visualiser Settings");
VisualiserComponent visualiser{audioProcessor.lastOpenedDirectory, applicationFolder.getChildFile(ffmpegFileName), audioProcessor.haltRecording, audioProcessor.threadManager, visualiserSettings, nullptr, true};
std::unique_ptr<juce::FileChooser> chooser;
SosciMainMenuBarModel menuBarModel{*this, audioProcessor};
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;
SosciMainMenuBarModel model{*this, audioProcessor};
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SosciPluginEditor)
};

Wyświetl plik

@ -1,167 +1,11 @@
/*
==============================================================================
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().withInput("Input", juce::AudioChannelSet::namedChannelSet(3), true)
.withOutput("Output", juce::AudioChannelSet::stereo(), true))
#endif
{
// locking isn't necessary here because we are in the constructor
for (auto effect : parameters.effects) {
allEffects.push_back(effect);
}
allEffects.push_back(parameters.smoothEffect);
for (auto effect : allEffects) {
for (auto effectParameter : effect->parameters) {
auto parameters = effectParameter->getParameters();
for (auto parameter : parameters) {
addParameter(parameter);
}
}
}
for (auto parameter : parameters.booleans) {
booleanParameters.push_back(parameter);
}
for (auto parameter : booleanParameters) {
addParameter(parameter);
}
for (auto parameter : floatParameters) {
addParameter(parameter);
}
for (auto parameter : intParameters) {
addParameter(parameter);
}
}
SosciAudioProcessor::SosciAudioProcessor() {}
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);
}
threadManager.prepare(sampleRate, samplesPerBlock);
}
void SosciAudioProcessor::releaseResources() {
// When playback stops, you can use this as an opportunity to free up any
// spare memory, etc.
}
bool SosciAudioProcessor::isBusesLayoutSupported(const BusesLayout& layouts) const {
return true;
}
// 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;
@ -189,7 +33,7 @@ void SosciAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce::M
OsciPoint point = { x, y, brightness };
for (auto& effect : allEffects) {
for (auto& effect : effects) {
point = effect->apply(sample, point);
}
@ -197,17 +41,6 @@ void SosciAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce::M
}
}
//==============================================================================
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) {
// we need to stop recording the visualiser when saving the state, otherwise
// there are issues. This is the only place we can do this because there is
@ -222,7 +55,7 @@ void SosciAudioProcessor::getStateInformation(juce::MemoryBlock& destData) {
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) {
for (auto effect : effects) {
effect->save(effectsXml->createNewChildElement("effect"));
}
@ -304,14 +137,11 @@ void SosciAudioProcessor::setStateInformation(const void* data, int sizeInBytes)
}
}
double SosciAudioProcessor::getSampleRate() {
return currentSampleRate;
juce::AudioProcessorEditor* SosciAudioProcessor::createEditor() {
auto editor = new SosciPluginEditor(*this);
return editor;
}
//==============================================================================
// This creates new instances of the plugin..
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter() {
return new SosciAudioProcessor();
}

Wyświetl plik

@ -9,6 +9,7 @@
#pragma once
#include <JuceHeader.h>
#include "CommonPluginProcessor.h"
#include "concurrency/AudioBackgroundThread.h"
#include "concurrency/AudioBackgroundThreadManager.h"
#include "audio/SampleRateManager.h"
@ -18,72 +19,18 @@
//==============================================================================
/**
*/
class SosciAudioProcessor : public juce::AudioProcessor, public SampleRateManager
#if JucePlugin_Enable_ARA
, public juce::AudioProcessorARAExtension
#endif
class SosciAudioProcessor : public CommonAudioProcessor
{
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;
AudioBackgroundThreadManager threadManager;
std::function<void()> haltRecording;
std::atomic<bool> forceDisableBrightnessInput = false;
// 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:
bool brightnessEnabled = false;
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::AudioProcessorEditor* createEditor() override;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SosciAudioProcessor)

Wyświetl plik

@ -1,91 +1,36 @@
#include "MainMenuBarModel.h"
#include "../PluginEditor.h"
#include "../PluginProcessor.h"
MainMenuBarModel::MainMenuBarModel(OscirenderAudioProcessor& p, OscirenderAudioProcessorEditor& editor) : audioProcessor(p), editor(editor) {}
MainMenuBarModel::MainMenuBarModel() {}
MainMenuBarModel::~MainMenuBarModel() {}
void MainMenuBarModel::addTopLevelMenu(const juce::String& name) {
topLevelMenuNames.add(name);
menuItems.push_back(std::vector<std::pair<juce::String, std::function<void()>>>());
menuItemsChanged();
}
void MainMenuBarModel::addMenuItem(int topLevelMenuIndex, const juce::String& name, std::function<void()> action) {
menuItems[topLevelMenuIndex].push_back(std::make_pair(name, action));
menuItemsChanged();
}
juce::StringArray MainMenuBarModel::getMenuBarNames() {
if (editor.processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone) {
return juce::StringArray("File", "About", "Audio");
} else {
return juce::StringArray("File", "About");
}
return topLevelMenuNames;
}
juce::PopupMenu MainMenuBarModel::getMenuForIndex(int topLevelMenuIndex, const juce::String& menuName) {
juce::PopupMenu menu;
if (topLevelMenuIndex == 0) {
menu.addItem(1, "Open");
menu.addItem(2, "Save");
menu.addItem(3, "Save As");
if (editor.processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone) {
menu.addItem(4, "Create New Project");
}
} else if (topLevelMenuIndex == 1) {
menu.addItem(1, "About osci-render");
} else if (topLevelMenuIndex == 2) {
menu.addItem(1, "Settings");
for (int i = 0; i < menuItems[topLevelMenuIndex].size(); i++) {
menu.addItem(i + 1, menuItems[topLevelMenuIndex][i].first);
}
return menu;
}
void MainMenuBarModel::menuItemSelected(int menuItemID, int topLevelMenuIndex) {
switch (topLevelMenuIndex) {
case 0:
switch (menuItemID) {
case 1:
editor.openProject();
break;
case 2:
editor.saveProject();
break;
case 3:
editor.saveProjectAs();
break;
case 4:
editor.resetToDefault();
break;
default:
break;
}
break;
case 1: {
juce::DialogWindow::LaunchOptions options;
AboutComponent* about = new AboutComponent(BinaryData::logo_png, BinaryData::logo_pngSize,
juce::String(ProjectInfo::projectName) + " by " + ProjectInfo::companyName + "\n"
"Version " + ProjectInfo::versionString + "\n\n"
"A huge thank you to:\n"
"DJ_Level_3, for contributing several features to osci-render\n"
"BUS ERROR Collective, for providing the source code for the Hilligoss encoder\n"
"All the community, for suggesting features and reporting issues!\n\n"
"I am open for commissions! Email me at james@ball.sh."
);
options.content.setOwned(about);
options.content->setSize(500, 270);
options.dialogTitle = "About";
options.dialogBackgroundColour = Colours::dark;
options.escapeKeyTriggersCloseButton = true;
#if JUCE_WINDOWS
// if not standalone, use native title bar for compatibility with DAWs
options.useNativeTitleBar = editor.processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone;
#elif JUCE_MAC
options.useNativeTitleBar = true;
#endif
options.resizable = false;
juce::DialogWindow* dw = options.launchAsync();
} break;
case 2:
editor.openAudioSettings();
break;
default:
break;
}
menuItems[topLevelMenuIndex][menuItemID - 1].second();
}
void MainMenuBarModel::menuBarActivated(bool isActive) {}

Wyświetl plik

@ -1,21 +1,20 @@
#pragma once
#include <JuceHeader.h>
#include "AboutComponent.h"
class OscirenderAudioProcessorEditor;
class OscirenderAudioProcessor;
class MainMenuBarModel : public juce::MenuBarModel {
public:
MainMenuBarModel(OscirenderAudioProcessor& p, OscirenderAudioProcessorEditor& editor);
MainMenuBarModel();
~MainMenuBarModel();
void addTopLevelMenu(const juce::String& name);
void addMenuItem(int topLevelMenuIndex, const juce::String& name, std::function<void()> action);
private:
juce::StringArray getMenuBarNames() override;
juce::PopupMenu getMenuForIndex(int topLevelMenuIndex, const juce::String& menuName) override;
void menuItemSelected(int menuItemID, int topLevelMenuIndex) override;
void menuBarActivated(bool isActive);
private:
OscirenderAudioProcessor& audioProcessor;
OscirenderAudioProcessorEditor& editor;
juce::StringArray topLevelMenuNames;
std::vector<std::vector<std::pair<juce::String, std::function<void()>>>> menuItems;
};

Wyświetl plik

@ -0,0 +1,54 @@
#include "OsciMainMenuBarModel.h"
#include "../PluginEditor.h"
#include "../PluginProcessor.h"
OsciMainMenuBarModel::OsciMainMenuBarModel(OscirenderAudioProcessor& p, OscirenderAudioProcessorEditor& e) : audioProcessor(p), editor(e) {
addTopLevelMenu("File");
addTopLevelMenu("About");
addTopLevelMenu("Recording");
if (editor.processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone) {
addTopLevelMenu("Audio");
}
addMenuItem(0, "Open", [this] { editor.openProject(); });
addMenuItem(0, "Save", [this] { editor.saveProject(); });
addMenuItem(0, "Save As", [this] { editor.saveProjectAs(); });
if (editor.processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone) {
addMenuItem(0, "Create New Project", [this] { editor.resetToDefault(); });
}
addMenuItem(1, "About osci-render", [this] {
juce::DialogWindow::LaunchOptions options;
AboutComponent* about = new AboutComponent(BinaryData::logo_png, BinaryData::logo_pngSize,
juce::String(ProjectInfo::projectName) + " by " + ProjectInfo::companyName + "\n"
"Version " + ProjectInfo::versionString + "\n\n"
"A huge thank you to:\n"
"DJ_Level_3, for contributing several features to osci-render\n"
"BUS ERROR Collective, for providing the source code for the Hilligoss encoder\n"
"All the community, for suggesting features and reporting issues!\n\n"
"I am open for commissions! Email me at james@ball.sh."
);
options.content.setOwned(about);
options.content->setSize(500, 270);
options.dialogTitle = "About";
options.dialogBackgroundColour = Colours::dark;
options.escapeKeyTriggersCloseButton = true;
#if JUCE_WINDOWS
// if not standalone, use native title bar for compatibility with DAWs
options.useNativeTitleBar = editor.processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone;
#elif JUCE_MAC
options.useNativeTitleBar = true;
#endif
options.resizable = false;
juce::DialogWindow* dw = options.launchAsync();
});
addMenuItem(2, "Settings...", [this] {
//editor.openRecordingSettings();
});
addMenuItem(3, "Settings...", [this] {
editor.openAudioSettings();
});
}

Wyświetl plik

@ -0,0 +1,16 @@
#pragma once
#include <JuceHeader.h>
#include "AboutComponent.h"
#include "MainMenuBarModel.h"
class OscirenderAudioProcessorEditor;
class OscirenderAudioProcessor;
class OsciMainMenuBarModel : public MainMenuBarModel {
public:
OsciMainMenuBarModel(OscirenderAudioProcessor& p, OscirenderAudioProcessorEditor& editor);
private:
OscirenderAudioProcessor& audioProcessor;
OscirenderAudioProcessorEditor& editor;
};

Wyświetl plik

@ -2,91 +2,43 @@
#include "../SosciPluginEditor.h"
#include "../SosciPluginProcessor.h"
SosciMainMenuBarModel::SosciMainMenuBarModel(SosciPluginEditor& editor, SosciAudioProcessor& processor) : editor(editor), processor(processor) {}
SosciMainMenuBarModel::SosciMainMenuBarModel(SosciPluginEditor& editor, SosciAudioProcessor& processor) : editor(editor), processor(processor) {
addTopLevelMenu("File");
addTopLevelMenu("About");
addTopLevelMenu("Audio");
SosciMainMenuBarModel::~SosciMainMenuBarModel() {}
juce::StringArray SosciMainMenuBarModel::getMenuBarNames() {
return juce::StringArray("File", "About", "Audio");
}
juce::PopupMenu SosciMainMenuBarModel::getMenuForIndex(int topLevelMenuIndex, const juce::String& menuName) {
juce::PopupMenu menu;
if (topLevelMenuIndex == 0) {
menu.addItem(1, "Open");
menu.addItem(2, "Save");
menu.addItem(3, "Save As");
if (editor.processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone) {
menu.addItem(4, "Create New Project");
}
} else if (topLevelMenuIndex == 1) {
menu.addItem(1, "About sosci");
} else if (topLevelMenuIndex == 2) {
menu.addItem(1, "Force Disable Brightness Input", true, processor.forceDisableBrightnessInput);
if (editor.processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone) {
menu.addItem(2, "Settings...");
}
addMenuItem(0, "Open", [&]() { editor.openProject(); });
addMenuItem(0, "Save", [&]() { editor.saveProject(); });
addMenuItem(0, "Save As", [&]() { editor.saveProjectAs(); });
if (editor.processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone) {
addMenuItem(0, "Create New Project", [&]() { editor.resetToDefault(); });
}
return menu;
}
void SosciMainMenuBarModel::menuItemSelected(int menuItemID, int topLevelMenuIndex) {
switch (topLevelMenuIndex) {
case 0:
switch (menuItemID) {
case 1:
editor.openProject();
break;
case 2:
editor.saveProject();
break;
case 3:
editor.saveProjectAs();
break;
case 4:
editor.resetToDefault();
break;
default:
break;
}
break;
case 1: {
juce::DialogWindow::LaunchOptions options;
AboutComponent* about = new AboutComponent(BinaryData::logo_png, BinaryData::logo_pngSize, "Sosci");
options.content.setOwned(about);
options.content->setSize(500, 270);
options.dialogTitle = "About";
options.dialogBackgroundColour = Colours::dark;
options.escapeKeyTriggersCloseButton = true;
addMenuItem(1, "About sosci", [&]() {
juce::DialogWindow::LaunchOptions options;
AboutComponent* about = new AboutComponent(BinaryData::logo_png, BinaryData::logo_pngSize, "Sosci");
options.content.setOwned(about);
options.content->setSize(500, 270);
options.dialogTitle = "About";
options.dialogBackgroundColour = Colours::dark;
options.escapeKeyTriggersCloseButton = true;
#if JUCE_WINDOWS
// if not standalone, use native title bar for compatibility with DAWs
options.useNativeTitleBar = editor.processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone;
// if not standalone, use native title bar for compatibility with DAWs
options.useNativeTitleBar = editor.processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone;
#elif JUCE_MAC
options.useNativeTitleBar = true;
options.useNativeTitleBar = true;
#endif
options.resizable = false;
juce::DialogWindow* dw = options.launchAsync();
} break;
case 2:
switch (menuItemID) {
case 1:
processor.forceDisableBrightnessInput = !processor.forceDisableBrightnessInput;
menuItemsChanged();
break;
case 2:
editor.openAudioSettings();
break;
default:
break;
}
break;
default:
break;
}
}
options.resizable = false;
void SosciMainMenuBarModel::menuBarActivated(bool isActive) {}
juce::DialogWindow* dw = options.launchAsync();
});
addMenuItem(2, "Force Disable Brightness Input", [&]() {
processor.forceDisableBrightnessInput = !processor.forceDisableBrightnessInput;
menuItemsChanged();
});
if (editor.processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone) {
addMenuItem(2, "Settings...", [&]() { editor.openAudioSettings(); });
}
}

Wyświetl plik

@ -1,21 +1,15 @@
#pragma once
#include <JuceHeader.h>
#include "AboutComponent.h"
#include "MainMenuBarModel.h"
class SosciPluginEditor;
class SosciAudioProcessor;
class SosciMainMenuBarModel : public juce::MenuBarModel {
class SosciMainMenuBarModel : public MainMenuBarModel {
public:
SosciMainMenuBarModel(SosciPluginEditor& editor, SosciAudioProcessor& processor);
~SosciMainMenuBarModel();
juce::StringArray getMenuBarNames() override;
juce::PopupMenu getMenuForIndex(int topLevelMenuIndex, const juce::String& menuName) override;
void menuItemSelected(int menuItemID, int topLevelMenuIndex) override;
void menuBarActivated(bool isActive);
private:
SosciPluginEditor& editor;
SosciAudioProcessor& processor;
};

Wyświetl plik

@ -0,0 +1,61 @@
#include "VisualiserSettings.h"
#include "VisualiserComponent.h"
#include "../PluginEditor.h"
VisualiserSettings::VisualiserSettings(VisualiserParameters& parameters, int numChannels) : parameters(parameters), numChannels(numChannels) {
addAndMakeVisible(brightness);
addAndMakeVisible(intensity);
addAndMakeVisible(persistence);
addAndMakeVisible(hue);
addAndMakeVisible(saturation);
addAndMakeVisible(focus);
addAndMakeVisible(noise);
addAndMakeVisible(glow);
addAndMakeVisible(smooth);
addChildComponent(sweepMs);
addAndMakeVisible(graticuleToggle);
addAndMakeVisible(smudgeToggle);
addAndMakeVisible(upsamplingToggle);
addAndMakeVisible(sweepToggle);
brightness.setSliderOnValueChange();
intensity.setSliderOnValueChange();
persistence.setSliderOnValueChange();
hue.setSliderOnValueChange();
saturation.setSliderOnValueChange();
focus.setSliderOnValueChange();
noise.setSliderOnValueChange();
glow.setSliderOnValueChange();
smooth.setSliderOnValueChange();
sweepMs.setSliderOnValueChange();
sweepToggle.onClick = [this] {
sweepMs.setVisible(sweepToggle.getToggleState());
resized();
};
}
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));
saturation.setBounds(area.removeFromTop(rowHeight));
focus.setBounds(area.removeFromTop(rowHeight));
noise.setBounds(area.removeFromTop(rowHeight));
glow.setBounds(area.removeFromTop(rowHeight));
smooth.setBounds(area.removeFromTop(rowHeight));
graticuleToggle.setBounds(area.removeFromTop(rowHeight));
smudgeToggle.setBounds(area.removeFromTop(rowHeight));
upsamplingToggle.setBounds(area.removeFromTop(rowHeight));
sweepToggle.setBounds(area.removeFromTop(rowHeight));
if (sweepToggle.getToggleState()) {
sweepMs.setBounds(area.removeFromTop(rowHeight));
}
}

Wyświetl plik

@ -0,0 +1,187 @@
#pragma once
#define VERSION_HINT 2
#include <JuceHeader.h>
#include "../components/EffectComponent.h"
#include "../components/SvgButton.h"
#include "../LookAndFeel.h"
#include "../components/SwitchButton.h"
#include "../audio/SmoothEffect.h"
class VisualiserParameters {
public:
BooleanParameter* graticuleEnabled = new BooleanParameter("Show Graticule", "graticuleEnabled", VERSION_HINT, false, "Show the graticule or grid lines over the oscilloscope display.");
BooleanParameter* smudgesEnabled = new BooleanParameter("Show Smudges", "smudgesEnabled", VERSION_HINT, false, "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, true, "Upsamples the audio before visualising it to make it appear more realistic, at the expense of performance.");
BooleanParameter* sweepEnabled = new BooleanParameter("Sweep", "sweepEnabled", VERSION_HINT, false, "Plots the audio signal over time, sweeping from left to right");
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, 2.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, 5.0, 0.0, 10.0
)
);
std::shared_ptr<Effect> saturationEffect = std::make_shared<Effect>(
new EffectParameter(
"Saturation",
"Controls how saturated the colours are on the oscilloscope.",
"saturation",
VERSION_HINT, 1.0, 0.0, 5.0
)
);
std::shared_ptr<Effect> focusEffect = std::make_shared<Effect>(
new EffectParameter(
"Focus",
"Controls how focused the electron beam of the oscilloscope is.",
"focus",
VERSION_HINT, 1.0, 0.01, 10.0
)
);
std::shared_ptr<Effect> noiseEffect = std::make_shared<Effect>(
new EffectParameter(
"Noise",
"Controls how much noise/grain is added to the oscilloscope display.",
"noise",
VERSION_HINT, 0.0, 0.0, 1.0
)
);
std::shared_ptr<Effect> glowEffect = std::make_shared<Effect>(
new EffectParameter(
"Glow",
"Controls how much the light glows on the oscilloscope display.",
"glow",
VERSION_HINT, 0.3, 0.0, 1.0
)
);
std::shared_ptr<Effect> smoothEffect = std::make_shared<Effect>(
std::make_shared<SmoothEffect>(),
new EffectParameter(
"Smoothing",
"This works as a low-pass frequency filter, effectively reducing the sample rate of the audio being visualised.",
"visualiserSmoothing",
VERSION_HINT, 0, 0.0, 1.0
)
);
std::shared_ptr<Effect> sweepMsEffect = std::make_shared<Effect>(
new EffectParameter(
"Sweep (ms)",
"The number of milliseconds it takes for the oscilloscope to sweep from left to right.",
"sweepMs",
VERSION_HINT, 0.3, 0.0, 1.0
)
);
std::vector<std::shared_ptr<Effect>> effects = {persistenceEffect, hueEffect, brightnessEffect, intensityEffect, saturationEffect, focusEffect, noiseEffect, glowEffect, sweepMsEffect};
std::vector<BooleanParameter*> booleans = {graticuleEnabled, smudgesEnabled, upsamplingEnabled, visualiserFullScreen, sweepEnabled};
};
class VisualiserSettings : public juce::Component {
public:
VisualiserSettings(VisualiserParameters&, int numChannels = 2);
~VisualiserSettings();
void resized() override;
double getBrightness() {
return parameters.brightnessEffect->getActualValue() - 2;
}
double getIntensity() {
return parameters.intensityEffect->getActualValue() / 100;
}
double getPersistence() {
return parameters.persistenceEffect->getActualValue() - 1.33;
}
double getHue() {
return parameters.hueEffect->getActualValue();
}
double getSaturation() {
return parameters.saturationEffect->getActualValue();
}
double getFocus() {
return parameters.focusEffect->getActualValue() / 100;
}
double getNoise() {
return parameters.noiseEffect->getActualValue();
}
double getGlow() {
return parameters.glowEffect->getActualValue() * 3;
}
bool getGraticuleEnabled() {
return parameters.graticuleEnabled->getBoolValue();
}
bool getSmudgesEnabled() {
return parameters.smudgesEnabled->getBoolValue();
}
bool getUpsamplingEnabled() {
return parameters.upsamplingEnabled->getBoolValue();
}
VisualiserParameters& parameters;
int numChannels;
private:
EffectComponent brightness{*parameters.brightnessEffect};
EffectComponent intensity{*parameters.intensityEffect};
EffectComponent persistence{*parameters.persistenceEffect};
EffectComponent hue{*parameters.hueEffect};
EffectComponent saturation{*parameters.saturationEffect};
EffectComponent focus{*parameters.focusEffect};
EffectComponent noise{*parameters.noiseEffect};
EffectComponent glow{*parameters.glowEffect};
EffectComponent smooth{*parameters.smoothEffect};
EffectComponent sweepMs{*parameters.sweepMsEffect};
jux::SwitchButton graticuleToggle{parameters.graticuleEnabled};
jux::SwitchButton smudgeToggle{parameters.smudgesEnabled};
jux::SwitchButton upsamplingToggle{parameters.upsamplingEnabled};
jux::SwitchButton sweepToggle{parameters.sweepEnabled};
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(VisualiserSettings)
};
class SettingsWindow : public juce::DocumentWindow {
public:
SettingsWindow(juce::String name) : juce::DocumentWindow(name, Colours::darker, juce::DocumentWindow::TitleBarButtons::closeButton) {}
void closeButtonPressed() override {
setVisible(false);
}
};

Wyświetl plik

@ -707,6 +707,8 @@ void VisualiserComponent::fade() {
void VisualiserComponent::drawCRT() {
using namespace juce::gl;
saveTextureToQOI(lineTexture, juce::File::getSpecialLocation(juce::File::SpecialLocationType::userDocumentsDirectory).getChildFile("line.qoi"));
setNormalBlending();
@ -715,34 +717,46 @@ void VisualiserComponent::drawCRT() {
texturedShader->setUniform("uResizeForCanvas", lineTexture.width / 1024.0f);
drawTexture(lineTexture);
saveTextureToQOI(blur1Texture, juce::File::getSpecialLocation(juce::File::SpecialLocationType::userDocumentsDirectory).getChildFile("blur1.qoi"));
//horizontal blur 256x256
activateTargetTexture(blur2Texture);
setShader(blurShader.get());
blurShader->setUniform("uOffset", 1.0f / 256.0f, 0.0f);
drawTexture(blur1Texture);
saveTextureToQOI(blur2Texture, juce::File::getSpecialLocation(juce::File::SpecialLocationType::userDocumentsDirectory).getChildFile("blur2.qoi"));
//vertical blur 256x256
activateTargetTexture(blur1Texture);
blurShader->setUniform("uOffset", 0.0f, 1.0f / 256.0f);
drawTexture(blur2Texture);
saveTextureToQOI(blur1Texture, juce::File::getSpecialLocation(juce::File::SpecialLocationType::userDocumentsDirectory).getChildFile("blur1_1.qoi"));
//preserve blur1 for later
activateTargetTexture(blur3Texture);
setShader(texturedShader.get());
texturedShader->setUniform("uResizeForCanvas", 1.0f);
drawTexture(blur1Texture);
saveTextureToQOI(blur3Texture, juce::File::getSpecialLocation(juce::File::SpecialLocationType::userDocumentsDirectory).getChildFile("blur3.qoi"));
//horizontal blur 64x64
activateTargetTexture(blur4Texture);
setShader(blurShader.get());
blurShader->setUniform("uOffset", 1.0f / 32.0f, 1.0f / 60.0f);
drawTexture(blur3Texture);
saveTextureToQOI(blur4Texture, juce::File::getSpecialLocation(juce::File::SpecialLocationType::userDocumentsDirectory).getChildFile("blur4.qoi"));
//vertical blur 64x64
activateTargetTexture(blur3Texture);
blurShader->setUniform("uOffset", -1.0f / 60.0f, 1.0f / 32.0f);
drawTexture(blur4Texture);
saveTextureToQOI(blur3Texture, juce::File::getSpecialLocation(juce::File::SpecialLocationType::userDocumentsDirectory).getChildFile("blur3_1.qoi"));
activateTargetTexture(renderTexture);
setShader(outputShader.get());
float brightness = std::pow(2, settings.getBrightness() - 2);
@ -756,6 +770,8 @@ void VisualiserComponent::drawCRT() {
outputShader->setUniform("uColour", colour.getFloatRed(), colour.getFloatGreen(), colour.getFloatBlue());
activateTargetTexture(renderTexture);
drawTexture(lineTexture, blur1Texture, blur3Texture, screenTexture);
saveTextureToQOI(renderTexture, juce::File::getSpecialLocation(juce::File::SpecialLocationType::userDocumentsDirectory).getChildFile("render.qoi"));
}
Texture VisualiserComponent::createScreenTexture() {

Wyświetl plik

@ -173,6 +173,10 @@
file="Source/components/MainMenuBarModel.cpp"/>
<FILE id="ubDcnO" name="MainMenuBarModel.h" compile="0" resource="0"
file="Source/components/MainMenuBarModel.h"/>
<FILE id="FbaaC9" name="OsciMainMenuBarModel.cpp" compile="1" resource="0"
file="Source/components/OsciMainMenuBarModel.cpp"/>
<FILE id="qAklil" name="OsciMainMenuBarModel.h" compile="0" resource="0"
file="Source/components/OsciMainMenuBarModel.h"/>
<FILE id="QQzSwh" name="SliderTextBox.h" compile="0" resource="0" file="Source/components/SliderTextBox.h"/>
<FILE id="QrDKRZ" name="SvgButton.h" compile="0" resource="0" file="Source/components/SvgButton.h"/>
<FILE id="qzfstC" name="SwitchButton.h" compile="0" resource="0" file="Source/components/SwitchButton.h"/>
@ -629,6 +633,14 @@
<FILE id="vYzJlF" name="WavParser.cpp" compile="1" resource="0" file="Source/wav/WavParser.cpp"/>
<FILE id="ZRT5Xk" name="WavParser.h" compile="0" resource="0" file="Source/wav/WavParser.h"/>
</GROUP>
<FILE id="fWbyS1" name="CommonPluginEditor.cpp" compile="1" resource="0"
file="Source/CommonPluginEditor.cpp"/>
<FILE id="nKwxzJ" name="CommonPluginEditor.h" compile="0" resource="0"
file="Source/CommonPluginEditor.h"/>
<FILE id="fg6MpN" name="CommonPluginProcessor.cpp" compile="1" resource="0"
file="Source/CommonPluginProcessor.cpp"/>
<FILE id="PGeV6d" name="CommonPluginProcessor.h" compile="0" resource="0"
file="Source/CommonPluginProcessor.h"/>
<FILE id="I44EdJ" name="EffectsComponent.cpp" compile="1" resource="0"
file="Source/EffectsComponent.cpp"/>
<FILE id="qxxNX3" name="EffectsComponent.h" compile="0" resource="0"

Wyświetl plik

@ -47,6 +47,14 @@
</GROUP>
</GROUP>
<GROUP id="{75439074-E50C-362F-1EDF-8B4BE9011259}" name="Source">
<FILE id="qJ122J" name="CommonPluginEditor.cpp" compile="1" resource="0"
file="Source/CommonPluginEditor.cpp"/>
<FILE id="ANZRtQ" name="CommonPluginEditor.h" compile="0" resource="0"
file="Source/CommonPluginEditor.h"/>
<FILE id="bR8U39" name="CommonPluginProcessor.cpp" compile="1" resource="0"
file="Source/CommonPluginProcessor.cpp"/>
<FILE id="r2FzhS" name="CommonPluginProcessor.h" compile="0" resource="0"
file="Source/CommonPluginProcessor.h"/>
<GROUP id="{14EE14E4-7A22-160E-CA5B-BD291DB4B4A3}" name="img">
<FILE id="Zum0qz" name="qoixx.hpp" compile="0" resource="0" file="Source/img/qoixx.hpp"/>
</GROUP>
@ -81,6 +89,10 @@
file="Source/components/EffectComponent.cpp"/>
<FILE id="u4UCwb" name="EffectComponent.h" compile="0" resource="0"
file="Source/components/EffectComponent.h"/>
<FILE id="ka6rAh" name="MainMenuBarModel.cpp" compile="1" resource="0"
file="Source/components/MainMenuBarModel.cpp"/>
<FILE id="t6oUhv" name="MainMenuBarModel.h" compile="0" resource="0"
file="Source/components/MainMenuBarModel.h"/>
<FILE id="eaHM09" name="SosciMainMenuBarModel.cpp" compile="1" resource="0"
file="Source/components/SosciMainMenuBarModel.cpp"/>
<FILE id="EymEr3" name="SosciMainMenuBarModel.h" compile="0" resource="0"