kopia lustrzana https://github.com/jameshball/osci-render
Complete major refactor of osci-render and sosci plugin editor and processor files. Still some bugs...
rodzic
1a1229fcba
commit
6e86f97649
|
@ -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);
|
||||
}
|
|
@ -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)
|
||||
};
|
|
@ -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;
|
||||
}
|
|
@ -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)
|
||||
};
|
|
@ -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());
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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(); });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
};
|
|
@ -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() {
|
||||
|
|
|
@ -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"
|
||||
|
|
12
sosci.jucer
12
sosci.jucer
|
@ -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"
|
||||
|
|
Ładowanie…
Reference in New Issue