From 05faa299885480f2a5e5dd31db6c64a67e9544fb Mon Sep 17 00:00:00 2001 From: Patric Schmitz Date: Sat, 29 Feb 2020 21:25:17 +0000 Subject: [PATCH] rework config into PresetParser, start ControlElementHost YAML config encapsulated in PresetParser removed show-names config option (always show names) removed auto-connect config option (always autoconnect) renamed type knob -> float start ControlElementHost implementation truncate log file on every run --- Presets/etudes/sinusoids.yaml | 15 ++-- Presets/panoramix-track-1.yaml | 47 ++++++------- Presets/panoramix-track-2.yaml | 47 ++++++------- Source/ControlContainer.h | 8 ++- Source/ControlElement.h | 12 +++- Source/ControlElementFactory.cpp | 30 +------- Source/ControlElementFactory.h | 5 +- Source/ControlElementHost.cpp | 30 ++++++++ Source/ControlElementHost.h | 33 +++++++++ Source/ControlElementKnob.cpp | 23 +++--- Source/ControlElementUI.cpp | 5 ++ Source/PluginEditor.cpp | 60 +++++----------- Source/PluginProcessor.cpp | 36 +++++++--- Source/PluginProcessor.h | 3 +- Source/PresetParser.cpp | 117 +++++++++++++++++++++++++++++++ Source/PresetParser.h | 49 +++++++++++++ oscsend-vst.jucer | 25 ++++--- 17 files changed, 375 insertions(+), 170 deletions(-) create mode 100644 Source/ControlElementHost.cpp create mode 100644 Source/ControlElementHost.h create mode 100644 Source/PresetParser.cpp create mode 100644 Source/PresetParser.h diff --git a/Presets/etudes/sinusoids.yaml b/Presets/etudes/sinusoids.yaml index fbee64a..824042f 100644 --- a/Presets/etudes/sinusoids.yaml +++ b/Presets/etudes/sinusoids.yaml @@ -2,24 +2,21 @@ network: host: 127.0.0.1 port: 8888 -interface: - show-names: true - controls: - name: frequency - message: /sinusoids/sinusoids/freq + type: float range: [0, 10] default: 0.1 - type: knob + message: /sinusoids/sinusoids/freq - name: lambda - message: /sinusoids/sinusoids/lambda + type: float range: [0, 10] default: 0.1 - type: knob + message: /sinusoids/sinusoids/lambda - name: stroke width - message: /sinusoids/sinusoids/stroke-width + type: float range: [0, 1] default: 0.5 - type: knob + message: /sinusoids/sinusoids/stroke-width diff --git a/Presets/panoramix-track-1.yaml b/Presets/panoramix-track-1.yaml index 9ccbd21..6e03c0e 100644 --- a/Presets/panoramix-track-1.yaml +++ b/Presets/panoramix-track-1.yaml @@ -2,60 +2,57 @@ network: host: 127.0.0.1 port: 6789 -interface: - show-names: true - controls: - name: azimuth - message: /track/1/azim + type: float range: [-180, 180] default: 0 - type: knob + message: /track/1/azim - name: distance - message: /track/1/dist + type: float range: [1, 100] default: 1 - type: knob + message: /track/1/dist - name: gain-A - message: /track/1/bus/A/gain - message-mute: /track/1/bus/A/mute + type: float range: [-24, 6] default: -6 - type: knob + message: /track/1/bus/A/gain + message-mute: /track/1/bus/A/mute - name: gain-B - message: /track/1/bus/B/gain - message-mute: /track/1/bus/B/mute + type: float range: [-24, 6] default: -6 - type: knob + message: /track/1/bus/B/gain + message-mute: /track/1/bus/B/mute - name: gain-C - message: /track/1/bus/C/gain - message-mute: /track/1/bus/C/mute + type: float range: [-24, 6] default: -6 - type: knob + message: /track/1/bus/C/gain + message-mute: /track/1/bus/C/mute - name: gain-D - message: /track/1/bus/D/gain - message-mute: /track/1/bus/D/mute + type: float range: [-24, 6] default: -6 - type: knob + message: /track/1/bus/D/gain + message-mute: /track/1/bus/D/mute - name: gain-E - message: /track/1/bus/E/gain - message-mute: /track/1/bus/E/mute + type: float range: [-24, 6] default: -6 - type: knob + message: /track/1/bus/E/gain + message-mute: /track/1/bus/E/mute - name: gain-F - message: /track/1/bus/F/gain - message-mute: /track/1/bus/F/mute + type: float range: [-24, 6] default: -6 - type: knob + message: /track/1/bus/F/gain + message-mute: /track/1/bus/F/mute diff --git a/Presets/panoramix-track-2.yaml b/Presets/panoramix-track-2.yaml index ae38602..f7fa40c 100644 --- a/Presets/panoramix-track-2.yaml +++ b/Presets/panoramix-track-2.yaml @@ -2,60 +2,57 @@ network: host: 127.0.0.1 port: 6789 -interface: - show-names: true - controls: - name: azimuth - message: /track/2/azim + type: float range: [-180, 180] default: 0 - type: knob + message: /track/2/azim - name: distance - message: /track/2/dist + type: float range: [1, 100] default: 1 - type: knob + message: /track/2/dist - name: gain-A - message: /track/2/bus/A/gain - message-mute: /track/2/bus/A/mute + type: float range: [-24, 6] default: -6 - type: knob + message: /track/2/bus/A/gain + message-mute: /track/2/bus/A/mute - name: gain-B - message: /track/2/bus/B/gain - message-mute: /track/2/bus/B/mute + type: float range: [-24, 6] default: -6 - type: knob + message: /track/2/bus/B/gain + message-mute: /track/2/bus/B/mute - name: gain-C - message: /track/2/bus/C/gain - message-mute: /track/2/bus/C/mute + type: float range: [-24, 6] default: -6 - type: knob + message: /track/2/bus/C/gain + message-mute: /track/2/bus/C/mute - name: gain-D - message: /track/2/bus/D/gain - message-mute: /track/2/bus/D/mute + type: float range: [-24, 6] default: -6 - type: knob + message: /track/2/bus/D/gain + message-mute: /track/2/bus/D/mute - name: gain-E - message: /track/2/bus/E/gain - message-mute: /track/2/bus/E/mute + type: float range: [-24, 6] default: -6 - type: knob + message: /track/2/bus/E/gain + message-mute: /track/2/bus/E/mute - name: gain-F - message: /track/2/bus/F/gain - message-mute: /track/2/bus/F/mute + type: float range: [-24, 6] default: -6 - type: knob + message: /track/2/bus/F/gain + message-mute: /track/2/bus/F/mute diff --git a/Source/ControlContainer.h b/Source/ControlContainer.h index 76a15e5..889a920 100644 --- a/Source/ControlContainer.h +++ b/Source/ControlContainer.h @@ -31,14 +31,16 @@ class ControlContainer : public Component { public: - void resized() override; - std::list & getElementList(); + void resized() override; OSCSender & getOSCSender(); + std::list & getElementList(); + private: - std::list listControlElements; + OSCSender oscSender; + std::list listControlElements; }; using ControlContainerUniq = std::unique_ptr; diff --git a/Source/ControlElement.h b/Source/ControlElement.h index befa0d5..b5f1ee2 100644 --- a/Source/ControlElement.h +++ b/Source/ControlElement.h @@ -27,15 +27,21 @@ class ControlElement { public: + + enum class Type { + Float, + Bool + }; + struct CreateInfo { + std::string name; + Type type; + std::pair range; float defaultValue; - std::string name; std::string message; std::string messageMute; - - bool showNames; }; ControlElement diff --git a/Source/ControlElementFactory.cpp b/Source/ControlElementFactory.cpp index 339a747..dd35ac8 100644 --- a/Source/ControlElementFactory.cpp +++ b/Source/ControlElementFactory.cpp @@ -38,35 +38,11 @@ ControlElementFactory std::unique_ptr ControlElementFactory:: createControlElementUI -(YAML::Node configElement, YAML::Node configInterface) +(const ControlElement::CreateInfo & createInfo) { - ControlElement::CreateInfo info; - - auto name = configElement["name"].as(); - auto type = configElement["type"].as(); - - if(!configInterface.IsNull()) { - auto showNames = configInterface["show-names"]; - info.showNames = - showNames.IsScalar() ? showNames.as() : false; - } - std::unique_ptr product; - if(type == "knob") { - - auto range = configElement["range"]; - info.range = std::make_pair(range[0].as(), - range[1].as()); - info.defaultValue = configElement["default"].as(); - - info.name = configElement["name"].as(); - info.message = configElement["message"].as(); - - auto messageMute = configElement["message-mute"]; - info.messageMute = - messageMute.IsScalar() ? messageMute.as() : ""; - - product = std::make_unique(info, oscSender); + if(createInfo.type == ControlElement::Type::Float) { + product = std::make_unique(createInfo, oscSender); product->registerSendValue(); } diff --git a/Source/ControlElementFactory.h b/Source/ControlElementFactory.h index bf3f4d1..1c8a5cb 100644 --- a/Source/ControlElementFactory.h +++ b/Source/ControlElementFactory.h @@ -22,8 +22,6 @@ #include -#include - #include class OscsendvstAudioProcessor; @@ -37,8 +35,7 @@ class ControlElementFactory { std::unique_ptr createControlElementUI - (YAML::Node configElement, - YAML::Node configInterface); + (const ControlElement::CreateInfo & createInfo); private: OSCSender & oscSender; diff --git a/Source/ControlElementHost.cpp b/Source/ControlElementHost.cpp new file mode 100644 index 0000000..f93fbc1 --- /dev/null +++ b/Source/ControlElementHost.cpp @@ -0,0 +1,30 @@ +/* + + oscsend-vst - An audio plugin that speaks OSC. + Copyright (C) 2020 Patric Schmitz + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +*/ + +#include "ControlElementHost.h" + + +ControlElementHost:: +ControlElementHost +(const CreateInfo & info, + OSCSender & oscSender) : + ControlElement(info, oscSender) +{ +} diff --git a/Source/ControlElementHost.h b/Source/ControlElementHost.h new file mode 100644 index 0000000..de5c539 --- /dev/null +++ b/Source/ControlElementHost.h @@ -0,0 +1,33 @@ +/* + + oscsend-vst - An audio plugin that speaks OSC. + Copyright (C) 2020 Patric Schmitz + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +*/ + +#pragma once + +#include "ControlElement.h" + +class ControlElementHost : + public ControlElement +{ +public: + ControlElementHost + (const CreateInfo & info, + OSCSender & oscSender); + +}; diff --git a/Source/ControlElementKnob.cpp b/Source/ControlElementKnob.cpp index ae449ad..cb549a5 100644 --- a/Source/ControlElementKnob.cpp +++ b/Source/ControlElementKnob.cpp @@ -34,30 +34,25 @@ ControlElementKnob knob->setSliderStyle (Slider::RotaryVerticalDrag); knob->setTextBoxStyle (Slider::NoTextBox, false, 80, 20); knob->setRange (createInfo.range.first, createInfo.range.second, 0); - knob->setValue (createInfo.defaultValue, - NotificationType::dontSendNotification); + knob->setValue (createInfo.defaultValue); knob->setPaintingIsUnclipped (true); addAndMakeVisible (knob.get()); textEditor.reset (new TextEditor ("textEditor")); + textEditor->setText (createInfo.name); textEditor->setMultiLine (false); textEditor->setReturnKeyStartsNewLine (false); textEditor->setScrollbarsShown (false); textEditor->setCaretVisible (true); textEditor->setPopupMenuEnabled (false); - if(createInfo.showNames) { - textEditor->setReadOnly (true); - auto backgroundColour = - textEditor->findColour(TextEditor::backgroundColourId); - textEditor->setColour - (TextEditor::outlineColourId, + textEditor->setReadOnly (true); + + auto backgroundColour = + textEditor->findColour(TextEditor::backgroundColourId); + textEditor->setColour + (TextEditor::outlineColourId, backgroundColour); - textEditor->setText (createInfo.name); - } - else { - textEditor->setReadOnly (false); - textEditor->setText (createInfo.message); - } + addAndMakeVisible (textEditor.get()); } diff --git a/Source/ControlElementUI.cpp b/Source/ControlElementUI.cpp index 1552051..251a949 100644 --- a/Source/ControlElementUI.cpp +++ b/Source/ControlElementUI.cpp @@ -64,6 +64,11 @@ registerSendValue() sendValue.referTo (getSpecificSendValue()); sendValue.addListener(this); + + if (sendValue.getValue().isDouble() && + float(sendValue.getValue()) == 0.f) { + send(); + } } void diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 708b747..f8bba7e 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -18,18 +18,17 @@ */ -#include - -#include "PluginProcessor.h" -#include "PluginEditor.h" - #include "BinaryData.h" #include "LayoutHints.h" +#include "PresetParser.h" + #include "ControlElementUI.h" #include "ControlElementFactory.h" +#include "PluginProcessor.h" +#include "PluginEditor.h" OscsendvstAudioProcessorEditor:: OscsendvstAudioProcessorEditor @@ -136,9 +135,6 @@ paint { g.fillAll(getLookAndFeel().findColour (ResizableWindow::backgroundColourId)); - - g.setColour(Colours::white); - g.setFont(15.0f); } void @@ -193,7 +189,6 @@ buttonClicked handlePresetButton(); } else if (button == &buttonReset) { - pageMap[activePage]->connected = false; loadPreset(activePage); switchToPage(activePage); } @@ -320,49 +315,29 @@ pickPresetFile() void OscsendvstAudioProcessorEditor:: loadPreset -(File preset) +(File filePreset) { - auto presetPath = preset.getFullPathName(); - auto container = pageMap[presetPath]->container.get(); - - container->getElementList().clear(); + PresetParser preset(filePreset); + auto presetPath = filePreset.getFullPathName(); - YAML::Node config; - try { - config = YAML::LoadFile(presetPath.toStdString()); - } - catch (YAML::BadFile &e) { - std::string message = - "Unable to load config: " + presetPath.toStdString(); - throw std::runtime_error(message); - } - - if (config.IsNull()) { - return; - } - - auto configNetwork = config["network"]; - - pageMap[presetPath]->host = String(configNetwork["host"].as()); - pageMap[presetPath]->port = String(configNetwork["port"].as()); + pageMap[presetPath]->host = preset.getHost(); + pageMap[presetPath]->port = preset.getPort(); + pageMap[presetPath]->connected = true; - auto autoConnect = configNetwork["auto-connect"]; - pageMap[presetPath]->connected = - autoConnect.IsScalar() ? autoConnect.as() : true; + auto container = pageMap[presetPath]->container.get(); + container->getElementList().clear(); ControlElementFactory factory (container->getOSCSender(), processor); - YAML::Node controls = config["controls"]; - YAML::Node interface = config["interface"]; + int accumulatedHeight = 0; - for(auto control : controls) { - auto element = factory.createControlElementUI(control, interface); + for(auto control : preset.getControlElements()) { + auto createInfo = preset.getControlElementCreateInfo(control); + auto element = factory.createControlElementUI(createInfo); accumulatedHeight += element->getNumberOfRows() * LayoutHints::heightRow; - element->setEnabled(pageMap[presetPath]->connected.getValue()); - container->addAndMakeVisible(element.get()); container->getElementList().push_back(std::move(element)); } @@ -374,6 +349,8 @@ OscsendvstAudioProcessorEditor:: switchToPage (String presetPath) { + Logger::writeToLog("switchToPage"); + activePage = presetPath; connectActivePageValues(); @@ -399,7 +376,6 @@ connectOsc(String host, int port) for (auto & control : container->getElementList()) { control->setEnabled(true); - control->send(); } } diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 9127c4c..44f7e45 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -47,7 +47,7 @@ File juce_getExecutableFile() OscsendvstAudioProcessor:: OscsendvstAudioProcessor() : AudioProcessor (BusesProperties()), - fileLogger(File("~/.oscsend-vst.log"), "oscsend-vst debug log"), + fileLogger(File("~/.oscsend-vst.log"), "oscsend-vst debug log", 0), hasUserInterface(true) { Logger::setCurrentLogger(&fileLogger); @@ -68,9 +68,10 @@ OscsendvstAudioProcessor() : if(presetToLoad.isNotEmpty()) { Logger::writeToLog("PRESET NAME: " + presetToLoad); - hasUserInterface = false; + auto presetFile = locatePresetFile(presetToLoad); - initializeHeadless(); + hasUserInterface = false; + initializeHeadless(presetFile); } } else { @@ -84,14 +85,33 @@ OscsendvstAudioProcessor:: Logger::writeToLog("~OscsendvstAudioProcessor"); } +File +OscsendvstAudioProcessor:: +locatePresetFile +(String namePreset) +{ + auto filenamePreset = namePreset + ".yaml"; + auto files = pathPreset.findChildFiles + (File::findFiles, true, filenamePreset); + + if(files.isEmpty()) { + Logger::writeToLog + ("error: could not locate preset file: " + filenamePreset); + // TODO freak out!! + } + + // TODO resolve possible alternatives + return files[0]; +} + void OscsendvstAudioProcessor:: -initializeHeadless() +initializeHeadless +(File filePreset) { Logger::writeToLog("initializeHeadless"); - // initialize DAW controls from so-named preset - // create and connect processor (this) OSC sender + // initialize DAW controls from so-named preset } const String @@ -130,7 +150,7 @@ int OscsendvstAudioProcessor:: getNumPrograms() { - Logger::writeToLog("getNumPrograms"); + // Logger::writeToLog("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 @@ -141,7 +161,7 @@ int OscsendvstAudioProcessor:: getCurrentProgram() { - Logger::writeToLog("getCurrentProgram"); + // Logger::writeToLog("getCurrentProgram"); return 0; } diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index b1b599e..c356a49 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -29,7 +29,8 @@ class OscsendvstAudioProcessor : OscsendvstAudioProcessor(); ~OscsendvstAudioProcessor(); - void initializeHeadless(); + File locatePresetFile(String namePreset); + void initializeHeadless(File filePreset); void prepareToPlay (double sampleRate, int samplesPerBlock) override; void releaseResources() override; diff --git a/Source/PresetParser.cpp b/Source/PresetParser.cpp new file mode 100644 index 0000000..8f078e3 --- /dev/null +++ b/Source/PresetParser.cpp @@ -0,0 +1,117 @@ +/* + + oscsend-vst - An audio plugin that speaks OSC. + Copyright (C) 2020 Patric Schmitz + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +*/ + +#include "ControlElement.h" + +#include "PresetParser.h" + +const std::map +PresetParser::mapTypeNames = +{ + {"float", ControlElement::Type::Float}, + {"bool", ControlElement::Type::Bool}, +}; + +PresetParser:: +PresetParser +(File filePreset) +{ + auto pathPreset = filePreset.getFullPathName().toStdString(); + try { + config = YAML::LoadFile(pathPreset); + } + catch (YAML::BadFile &e) { + std::string message = "Unable to load config: " + pathPreset; + throw std::runtime_error(message); + } + + if(config.IsNull()) { + std::string message = "Empty config: " + pathPreset; + throw std::runtime_error(message); + } +} + +String +PresetParser:: +getHost +() const +{ + auto configNetwork = config["network"]; + return String(configNetwork["host"].as()); +} + +int +PresetParser:: +getPort +() const +{ + auto configNetwork = config["network"]; + return configNetwork["port"].as(); +} + +StringArray +PresetParser:: +getControlElements +() const +{ + StringArray elements; + auto controls = config["controls"]; + for(auto control : controls) { + elements.add(control["name"].as()); + } + return elements; +} + +ControlElement::CreateInfo +PresetParser:: +getControlElementCreateInfo +(String element) const +{ + Logger::writeToLog("creating element: " + element); + + ControlElement::CreateInfo info; + + auto controls = config["controls"]; + for(auto control : controls) { + + if(control["name"].as() != element) { + continue; + } + + info.name = control["name"].as(); + + auto typeName = control["type"].as(); + info.type = mapTypeNames.at(typeName); + + auto range = control["range"]; + info.range = std::make_pair + (range[0].as(), range[1].as()); + + info.defaultValue = control["default"].as(); + + info.message = control["message"].as(); + + auto messageMute = control["message-mute"]; + info.messageMute = + messageMute.IsScalar() ? messageMute.as() : ""; + } + + return info; +} diff --git a/Source/PresetParser.h b/Source/PresetParser.h new file mode 100644 index 0000000..36273a3 --- /dev/null +++ b/Source/PresetParser.h @@ -0,0 +1,49 @@ +/* + + oscsend-vst - An audio plugin that speaks OSC. + Copyright (C) 2020 Patric Schmitz + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +*/ + +#pragma once + +#include + +#include + +#include "ControlElement.h" + +class PresetParser +{ +public: + + PresetParser(File filePreset); + + String getHost() const; + int getPort() const; + + StringArray getControlElements() const; + + ControlElement::CreateInfo + getControlElementCreateInfo + (String element) const; + +private: + + static const std::map mapTypeNames; + + YAML::Node config; // TODO pimpl? +}; diff --git a/oscsend-vst.jucer b/oscsend-vst.jucer index 5032e61..8c2f12c 100644 --- a/oscsend-vst.jucer +++ b/oscsend-vst.jucer @@ -28,19 +28,23 @@ file="Source/ControlElement.cpp"/> - - + + + + + + - - + + @@ -77,7 +84,7 @@ -