AnalogTapeModel

Physical modelling signal processing for analog tape recording
Log | Files | Refs | Submodules | README | LICENSE

commit 98490f256def869a332452e70824fcd028e9718b
parent 8ee48bbbac3e10439b1972ac70b0f405dfaff948
Author: jatinchowdhury18 <[email protected]>
Date:   Fri, 15 May 2020 09:57:26 -0700

Presets (#32)

* Start working on presets

* New presets, and updated preset mechanisms

* Fix listener deletion bug, etc

* Ignore Windows travis (again)

Co-authored-by: jatinchowdhury18 <[email protected]>
Diffstat:
MPlugin/CHOWTapeModel.jucer | 22+++++++++++++++++++---
APlugin/Source/GUI/gui.xml | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APlugin/Source/GUI/preset_save_gui.xml | 91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MPlugin/Source/PluginProcessor.cpp | 31++++++++++++++++++++++++++-----
MPlugin/Source/PluginProcessor.h | 5++++-
APlugin/Source/Presets/Default.xml | 25+++++++++++++++++++++++++
APlugin/Source/Presets/LoFi.xml | 25+++++++++++++++++++++++++
APlugin/Source/Presets/OldTape.xml | 25+++++++++++++++++++++++++
APlugin/Source/Presets/PresetManager.cpp | 187+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
APlugin/Source/Presets/PresetManager.h | 50++++++++++++++++++++++++++++++++++++++++++++++++++
APlugin/Source/Presets/TC260.xml | 25+++++++++++++++++++++++++
APlugin/Source/Presets/Underbiased.xml | 25+++++++++++++++++++++++++
APlugin/Source/Presets/WoozyChorus.xml | 25+++++++++++++++++++++++++
DPlugin/Source/gui.xml | 89-------------------------------------------------------------------------------
14 files changed, 617 insertions(+), 98 deletions(-)

diff --git a/Plugin/CHOWTapeModel.jucer b/Plugin/CHOWTapeModel.jucer @@ -3,10 +3,25 @@ <JUCERPROJECT id="jDoXPz" name="CHOWTapeModel" projectType="audioplug" jucerVersion="5.4.7" version="2.2.0" pluginFormats="buildAU,buildStandalone,buildVST,buildVST3" cppLanguageStandard="17" companyName="chowdsp" companyEmail="[email protected]" - defines="FOLEYS_SHOW_GUI_EDITOR_PALLETTE=0"> + defines="SAVE_PRESETS=0"> <MAINGROUP id="pXbPvR" name="CHOWTapeModel"> <GROUP id="{0178B10A-4A61-796A-5AB2-915D32AF6EEE}" name="Source"> - <FILE id="Xn3V5v" name="gui.xml" compile="0" resource="1" file="Source/gui.xml"/> + <GROUP id="{8D673967-9B5D-8254-9062-4C4B14D4EAD9}" name="GUI"> + <FILE id="KmBito" name="gui.xml" compile="0" resource="1" file="Source/GUI/gui.xml"/> + <FILE id="kmORjM" name="preset_save_gui.xml" compile="0" resource="1" + file="Source/GUI/preset_save_gui.xml"/> + </GROUP> + <GROUP id="{71C1FCA8-E7B0-3B66-1340-F140C452FF6F}" name="Presets"> + <FILE id="ihP3jp" name="Default.xml" compile="0" resource="1" file="Source/Presets/Default.xml"/> + <FILE id="b5EimY" name="LoFi.xml" compile="0" resource="1" file="Source/Presets/LoFi.xml"/> + <FILE id="MQFCLN" name="OldTape.xml" compile="0" resource="1" file="Source/Presets/OldTape.xml"/> + <FILE id="u8p0u2" name="PresetManager.cpp" compile="1" resource="0" + file="Source/Presets/PresetManager.cpp"/> + <FILE id="hN3w9Y" name="PresetManager.h" compile="0" resource="0" file="Source/Presets/PresetManager.h"/> + <FILE id="kNY0o3" name="TC260.xml" compile="0" resource="1" file="Source/Presets/TC260.xml"/> + <FILE id="UtYbHz" name="Underbiased.xml" compile="0" resource="1" file="Source/Presets/Underbiased.xml"/> + <FILE id="Sa9RRP" name="WoozyChorus.xml" compile="0" resource="1" file="Source/Presets/WoozyChorus.xml"/> + </GROUP> <GROUP id="{43BBFC88-4D0A-01B8-2635-3748470B94F4}" name="Processors"> <GROUP id="{2F544AA2-63D9-E54A-A753-2E6249E85E60}" name="Chew"> <FILE id="S5eP4b" name="ChewProcessor.cpp" compile="1" resource="0" @@ -148,5 +163,6 @@ <LIVE_SETTINGS> <WINDOWS/> </LIVE_SETTINGS> - <JUCEOPTIONS JUCE_VST3_CAN_REPLACE_VST2="0" JUCE_STRICT_REFCOUNTEDPOINTER="1"/> + <JUCEOPTIONS JUCE_VST3_CAN_REPLACE_VST2="0" JUCE_STRICT_REFCOUNTEDPOINTER="1" + FOLEYS_SHOW_GUI_EDITOR_PALLETTE="0"/> </JUCERPROJECT> diff --git a/Plugin/Source/GUI/gui.xml b/Plugin/Source/GUI/gui.xml @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<magic> + <Styles> + <Style name="default"> + <Nodes/> + <Classes> + <plot-view border="2" background-color="black" border-color="silver" display="contents"/> + <nomargin margin="0" padding="0" border="0"/> + <group margin="5" padding="5" border="2" flex-direction="column"/> + </Classes> + <Types> + <Slider border="0" slider-type="rotary" slider-textbox="textbox-below"/> + <ToggleButton border="0" max-height="50" caption-size="0" text="Active"/> + <TextButton border="0" max-height="50" caption-size="0"/> + <ComboBox border="0" max-height="50" caption-size="0"/> + <Plot border="0" margin="0" padding="0" background-color="00000000" + radius="0"/> + <XYDragComponent border="0" margin="0" padding="0" background-color="00000000" + radius="0"/> + </Types> + </Style> + </Styles> + <View id="root" resizable="1" resize-corner="1" flex-direction="column" + background-color="FF7D2C2C" padding="0" width="600" height="650"> + <View max-height="100" padding="0" background-color="FF7D2C2C" margin="0"> + <Label text="CHOW Tape Model" font-size="20" padding="0" background-color="FF7D2C2C" + border="" max-width="180"/> + <Plot source="scope" plot-fill-color="FF000000" plot-color="FFCC7D12" + padding="0" background-color="FF000000" plot-decay="0.0"/> + </View> + <View background-color="FF7D2C2C" padding="0" margin=""> + <View flex-direction="column" margin="5" padding=""> + <Label max-height="5"/> + <Slider caption="Input Gain [dB]" parameter="ingain" slider-textbox="textbox-below" + slider-type="rotary" max-height="160"/> + <ComboBox caption="Oversampling" parameter="os" max-height="70"/> + <Slider caption="Dry/Wet" parameter="drywet" slider-textbox="textbox-below" + slider-type="rotary" max-height="170"/> + <Slider caption="Output Gain [dB]" parameter="outgain" slider-textbox="textbox-below" + slider-type="rotary" max-height="170"/> + </View> + <View flex-direction="column" tab-color=""> + <Label max-height="40" text="Hysteresis" font-size="18" justification="centred"/> + <Slider caption="Bias" parameter="width" slider-type="rotary" slider-textbox="textbox-below"/> + <Slider caption="Saturation" parameter="sat" slider-textbox="textbox-below" + slider-type="rotary"/> + <Slider caption="Drive" parameter="drive" slider-type="rotary" slider-textbox="textbox-below"/> + </View> + <View display="tabbed" padding="0"> + <View flex-direction="column" tab-caption="Loss" tab-color="" background-color="" + padding="0" margin="0"> + <Label max-height="40" text="Loss" justification="centred" font-size="18" + padding="0" margin="0"/> + <Slider caption="Gap [mm]" parameter="gap" slider-type="linear-horizontal" + slider-textbox="textbox-below" background-color="" padding="0"/> + <Slider caption="Thickness [mm]" parameter="thick" slider-textbox="textbox-below" + slider-type="linear-horizontal" padding="0"/> + <Slider caption="Spacing [mm]" parameter="spacing" slider-type="linear-horizontal" + slider-textbox="textbox-below" lookAndFeel="LookAndFeel_V4" padding="0"/> + <Slider caption="Speed [ips]" parameter="speed" slider-type="linear-horizontal" + slider-textbox="textbox-below" padding="0"/> + </View> + <View tab-caption="Degr." padding="0" flex-direction="column"> + <Label text="Degrade" justification="centred" font-size="18" max-height="40"/> + <Slider parameter="deg_depth" slider-type="rotary" slider-textbox="textbox-below" + caption="Depth"/> + <Slider caption="Amount" parameter="deg_amt" slider-type="rotary" slider-textbox="textbox-below"/> + <Slider parameter="deg_var" slider-type="rotary" slider-textbox="textbox-below" + caption="Variance"/> + </View> + <View tab-caption="CHEW" padding="0" flex-direction="column"> + <Label text="CHEW" justification="centred" font-size="18" max-height="40"/> + <Slider parameter="chew_depth" slider-type="rotary" slider-textbox="textbox-below" + caption="Depth" max-height="180"/> + <Slider caption="Frequency" parameter="chew_freq" slider-type="rotary" + slider-textbox="textbox-below" max-height="200"/> + </View> + </View> + <View flex-direction="column"> + <Label max-height="40" text="Flutter" justification="centred" font-size="18"/> + <Slider caption="Depth" parameter="depth" slider-type="rotary" slider-textbox="textbox-below" + max-height="200"/> + <Slider caption="Rate" parameter="rate" slider-textbox="textbox-below" + slider-type="rotary" max-height="200"/> + </View> + </View> + <presets max-height="30" margin="0" padding="0"/> + </View> +</magic> diff --git a/Plugin/Source/GUI/preset_save_gui.xml b/Plugin/Source/GUI/preset_save_gui.xml @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<magic> + <Styles> + <Style name="default"> + <Nodes/> + <Classes> + <plot-view border="2" background-color="black" border-color="silver" display="contents"/> + <nomargin margin="0" padding="0" border="0"/> + <group margin="5" padding="5" border="2" flex-direction="column"/> + </Classes> + <Types> + <Slider border="0" slider-type="rotary" slider-textbox="textbox-below"/> + <ToggleButton border="0" max-height="50" caption-size="0" text="Active"/> + <TextButton border="0" max-height="50" caption-size="0"/> + <ComboBox border="0" max-height="50" caption-size="0"/> + <Plot border="0" margin="0" padding="0" background-color="00000000" + radius="0"/> + <XYDragComponent border="0" margin="0" padding="0" background-color="00000000" + radius="0"/> + </Types> + </Style> + </Styles> + <View id="root" resizable="1" resize-corner="1" flex-direction="column" + background-color="FF7D2C2C" padding="0" width="600" height="650"> + <View max-height="100" padding="0" background-color="FF7D2C2C" margin="0"> + <Label text="CHOW Tape Model" font-size="20" padding="0" background-color="FF7D2C2C" + border="" max-width="180"/> + <Plot source="scope" plot-fill-color="FF000000" plot-color="FFCC7D12" + padding="0" background-color="FF000000" plot-decay="0.0"/> + </View> + <View background-color="FF7D2C2C" padding="0" margin=""> + <View flex-direction="column" margin="5" padding=""> + <Label max-height="5"/> + <Slider caption="Input Gain [dB]" parameter="ingain" slider-textbox="textbox-below" + slider-type="rotary" max-height="160"/> + <ComboBox caption="Oversampling" parameter="os" max-height="70"/> + <Slider caption="Dry/Wet" parameter="drywet" slider-textbox="textbox-below" + slider-type="rotary" max-height="170"/> + <Slider caption="Output Gain [dB]" parameter="outgain" slider-textbox="textbox-below" + slider-type="rotary" max-height="170"/> + </View> + <View flex-direction="column" tab-color=""> + <Label max-height="40" text="Hysteresis" font-size="18" justification="centred"/> + <Slider caption="Bias" parameter="width" slider-type="rotary" slider-textbox="textbox-below"/> + <Slider caption="Saturation" parameter="sat" slider-textbox="textbox-below" + slider-type="rotary"/> + <Slider caption="Drive" parameter="drive" slider-type="rotary" slider-textbox="textbox-below"/> + </View> + <View display="tabbed" padding="0"> + <View flex-direction="column" tab-caption="Loss" tab-color="" background-color="" + padding="0" margin="0"> + <Label max-height="40" text="Loss" justification="centred" font-size="18" + padding="0" margin="0"/> + <Slider caption="Gap [mm]" parameter="gap" slider-type="linear-horizontal" + slider-textbox="textbox-below" background-color="" padding="0"/> + <Slider caption="Thickness [mm]" parameter="thick" slider-textbox="textbox-below" + slider-type="linear-horizontal" padding="0"/> + <Slider caption="Spacing [mm]" parameter="spacing" slider-type="linear-horizontal" + slider-textbox="textbox-below" lookAndFeel="LookAndFeel_V4" padding="0"/> + <Slider caption="Speed [ips]" parameter="speed" slider-type="linear-horizontal" + slider-textbox="textbox-below" padding="0"/> + </View> + <View tab-caption="Degr." padding="0" flex-direction="column"> + <Label text="Degrade" justification="centred" font-size="18" max-height="40"/> + <Slider parameter="deg_depth" slider-type="rotary" slider-textbox="textbox-below" + caption="Depth"/> + <Slider caption="Amount" parameter="deg_amt" slider-type="rotary" slider-textbox="textbox-below"/> + <Slider parameter="deg_var" slider-type="rotary" slider-textbox="textbox-below" + caption="Variance"/> + </View> + <View tab-caption="CHEW" padding="0" flex-direction="column"> + <Label text="CHEW" justification="centred" font-size="18" max-height="40"/> + <Slider parameter="chew_depth" slider-type="rotary" slider-textbox="textbox-below" + caption="Depth" max-height="180"/> + <Slider caption="Frequency" parameter="chew_freq" slider-type="rotary" + slider-textbox="textbox-below" max-height="200"/> + </View> + </View> + <View flex-direction="column"> + <Label max-height="40" text="Flutter" justification="centred" font-size="18"/> + <Slider caption="Depth" parameter="depth" slider-type="rotary" slider-textbox="textbox-below" + max-height="200"/> + <Slider caption="Rate" parameter="rate" slider-textbox="textbox-below" + slider-type="rotary" max-height="200"/> + <TextButton max-height="50" text="Save" onClick="savepreset"/> + </View> + </View> + <presets max-height="30" margin="0" padding="0"/> + </View> +</magic> diff --git a/Plugin/Source/PluginProcessor.cpp b/Plugin/Source/PluginProcessor.cpp @@ -45,6 +45,7 @@ AudioProcessorValueTreeState::ParameterLayout ChowtapeModelAudioProcessor::creat params.push_back (std::make_unique<AudioParameterFloat> ("ingain", "Input Gain [dB]", -30.0f, 6.0f, 0.0f)); params.push_back (std::make_unique<AudioParameterFloat> ("outgain", "Output Gain [dB]", -30.0f, 30.0f, 0.0f)); params.push_back (std::make_unique<AudioParameterFloat> ("drywet", "Dry/Wet", 0.0f, 100.0f, 100.0f)); + params.push_back (std::make_unique<AudioParameterInt> ("preset", "Preset", 0, 1, 0)); HysteresisProcessor::createParameterLayout (params); LossFilter::createParameterLayout (params); @@ -95,26 +96,28 @@ double ChowtapeModelAudioProcessor::getTailLengthSeconds() const int ChowtapeModelAudioProcessor::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. + return presetManager.getNumPresets(); } int ChowtapeModelAudioProcessor::getCurrentProgram() { - return 0; + return (int) *vts.getRawParameterValue ("preset"); } void ChowtapeModelAudioProcessor::setCurrentProgram (int index) { + *vts.getRawParameterValue ("preset") = (float) index; + presetManager.setPreset (vts, index); } const String ChowtapeModelAudioProcessor::getProgramName (int index) { - return {}; + return presetManager.getPresetName (index); } void ChowtapeModelAudioProcessor::changeProgramName (int index, const String& newName) { + ignoreUnused (index, newName); } //============================================================================== @@ -202,7 +205,23 @@ bool ChowtapeModelAudioProcessor::hasEditor() const AudioProcessorEditor* ChowtapeModelAudioProcessor::createEditor() { - return new foleys::MagicPluginEditor (magicState, BinaryData::gui_xml, BinaryData::gui_xmlSize); + auto builder = std::make_unique<foleys::MagicGUIBuilder> (&magicState); + builder->registerJUCEFactories(); + presetManager.registerPresetsComponent (*builder, this); + +#if SAVE_PRESETS // Add button to save new presets + magicState.addTrigger ("savepreset", [=] + { + File xmlFile ("D:\\preset.xml"); + xmlFile.deleteFile(); + xmlFile.create(); + xmlFile.replaceWithText (vts.state.toXmlString()); + }); + + return new foleys::MagicPluginEditor (magicState, BinaryData::preset_save_gui_xml, BinaryData::preset_save_gui_xmlSize, std::move (builder)); +#else + return new foleys::MagicPluginEditor (magicState, BinaryData::gui_xml, BinaryData::gui_xmlSize, std::move (builder)); +#endif } //============================================================================== @@ -213,7 +232,9 @@ void ChowtapeModelAudioProcessor::getStateInformation (MemoryBlock& destData) void ChowtapeModelAudioProcessor::setStateInformation (const void* data, int sizeInBytes) { + MessageManagerLock mml; magicState.setStateInformation (data, sizeInBytes, getActiveEditor()); + presetManager.presetUpdated(); } //============================================================================== diff --git a/Plugin/Source/PluginProcessor.h b/Plugin/Source/PluginProcessor.h @@ -18,11 +18,12 @@ #include "Processors/Degrade/DegradeProcessor.h" #include "Processors/Chew/ChewProcessor.h" #include "Processors/DryWetProcessor.h" +#include "Presets/PresetManager.h" //============================================================================== /** */ -class ChowtapeModelAudioProcessor : public AudioProcessor +class ChowtapeModelAudioProcessor : public AudioProcessor { public: //============================================================================== @@ -82,6 +83,8 @@ private: foleys::MagicProcessorState magicState { *this, vts }; foleys::MagicPlotSource* scope = nullptr; + PresetManager presetManager; + //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChowtapeModelAudioProcessor) }; diff --git a/Plugin/Source/Presets/Default.xml b/Plugin/Source/Presets/Default.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<Preset name="Default"> + <Parameters> + <PARAM id="chew_depth" value="0.0"/> + <PARAM id="chew_freq" value="0.0"/> + <PARAM id="deg_amt" value="0.0"/> + <PARAM id="deg_depth" value="0.0"/> + <PARAM id="deg_var" value="0.0"/> + <PARAM id="depth" value="0.0"/> + <PARAM id="drive" value="0.5"/> + <PARAM id="drywet" value="100.0"/> + <PARAM id="gap" value="9.999999974752427e-7"/> + <PARAM id="ingain" value="0.0"/> + <PARAM id="os" value="1.0"/> + <PARAM id="outgain" value="0.0"/> + <PARAM id="rate" value="0.2999999821186066"/> + <PARAM id="sat" value="0.5"/> + <PARAM id="spacing" value="9.999999974752427e-7"/> + <PARAM id="speed" value="15.00000095367432"/> + <PARAM id="thick" value="9.999999974752427e-7"/> + <PARAM id="width" value="0.5"/> + <PARAM id="preset" value="0"/> + </Parameters> +</Preset> diff --git a/Plugin/Source/Presets/LoFi.xml b/Plugin/Source/Presets/LoFi.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<Preset name="LoFi"> + <Parameters> + <PARAM id="chew_depth" value="0.0"/> + <PARAM id="chew_freq" value="0.0"/> + <PARAM id="deg_amt" value="0.0"/> + <PARAM id="deg_depth" value="0.0"/> + <PARAM id="deg_var" value="0.0"/> + <PARAM id="depth" value="0.0"/> + <PARAM id="drive" value="1.0"/> + <PARAM id="drywet" value="100.0"/> + <PARAM id="gap" value="9.999999974752427e-7"/> + <PARAM id="ingain" value="0.0"/> + <PARAM id="os" value="1.0"/> + <PARAM id="outgain" value="0.0"/> + <PARAM id="rate" value="0.2999999821186066"/> + <PARAM id="sat" value="1.0"/> + <PARAM id="spacing" value="0.002999998396262527"/> + <PARAM id="speed" value="7.500000476837158"/> + <PARAM id="thick" value="0.004999995231628418"/> + <PARAM id="width" value="0.5999999642372131"/> + <PARAM id="preset" value="3"/> + </Parameters> +</Preset> diff --git a/Plugin/Source/Presets/OldTape.xml b/Plugin/Source/Presets/OldTape.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<Preset name="Old Tape"> + <Parameters> + <PARAM id="chew_depth" value="0.0"/> + <PARAM id="chew_freq" value="0.0"/> + <PARAM id="deg_amt" value="0.1499999910593033"/> + <PARAM id="deg_depth" value="0.199999988079071"/> + <PARAM id="deg_var" value="0.25"/> + <PARAM id="depth" value="0.0"/> + <PARAM id="drive" value="0.5"/> + <PARAM id="drywet" value="100.0"/> + <PARAM id="gap" value="9.999999974752427e-7"/> + <PARAM id="ingain" value="0.0"/> + <PARAM id="os" value="1.0"/> + <PARAM id="outgain" value="0.0"/> + <PARAM id="rate" value="0.2999999821186066"/> + <PARAM id="sat" value="0.5"/> + <PARAM id="spacing" value="9.999999974752427e-7"/> + <PARAM id="speed" value="15.00000095367432"/> + <PARAM id="thick" value="9.999999974752427e-7"/> + <PARAM id="width" value="0.5"/> + <PARAM id="preset" value="4"/> + </Parameters> +</Preset> diff --git a/Plugin/Source/Presets/PresetManager.cpp b/Plugin/Source/Presets/PresetManager.cpp @@ -0,0 +1,187 @@ +#include "PresetManager.h" +#include "../PluginProcessor.h" + +Preset::Preset (String presetFile) +{ + // load xml text from BinaryData + String xmlText; + for (int i = 0; i < BinaryData::namedResourceListSize; ++i) + { + if (String (BinaryData::originalFilenames[i]) == presetFile) + { + int dummy = 0; + xmlText = String (BinaryData::getNamedResource (BinaryData::namedResourceList[i], dummy)); + } + } + + jassert (xmlText.isNotEmpty()); // preset does not exist!! + + ValueTree parentTree = ValueTree::fromXml (xmlText); + name = parentTree.getProperty ("name").toString(); + jassert (name.isNotEmpty()); // Preset name not found!! + + state = parentTree.getChildWithName ("Parameters"); + for (int i = 0; i < state.getNumChildren(); ++i) + { + auto child = state.getChild (i); + if (child.getProperty ("id").toString() == "preset") + index = (int) child.getProperty ("value"); + } +} + +//==================================================== +class PresetComp : public Component, + private PresetManager::Listener +{ +public: + PresetComp (ChowtapeModelAudioProcessor& proc, PresetManager& manager) : + proc (proc), + manager (manager) + { + manager.addListener (this); + + addAndMakeVisible (presetBox); + presetBox.setJustificationType (Justification::centred); + presetBox.setColour (ComboBox::ColourIds::backgroundColourId, Colours::transparentWhite); + presetBox.addItemList (manager.getPresetChoices(), 1); + presetBox.setColour (PopupMenu::ColourIds::backgroundColourId, Colour (0xFF434352)); + + presetBox.setSelectedItemIndex (proc.getCurrentProgram(), dontSendNotification); + presetBox.onChange = [=, &proc, &manager] { proc.setCurrentProgram (presetBox.getSelectedItemIndex()); }; + } + + ~PresetComp() + { + manager.removeListener (this); + } + + void paint (Graphics& g) + { + Colour background (0xFF434352); + g.fillAll (background); + + g.setColour (Colours::white); + g.setFont (getHeight() * 0.6f); + auto presetBounds = presetBox.getBounds(); + presetBounds.setWidth (100); + presetBounds.translate (-110, 0); + g.drawFittedText ("Presets:", presetBounds, Justification::centredRight, 1); + + g.setFont (getHeight() * 0.4f); + auto versionBounds = getLocalBounds().removeFromRight (50); + g.drawFittedText ("v" + String (JucePlugin_VersionString), versionBounds, Justification::centred, 1); + } + + void resized() + { + auto boxWidth = jmin (getWidth() / 3, 200); + presetBox.setBounds ((getWidth() - boxWidth) / 2, 2, boxWidth, getHeight() - 4); + repaint(); + } + + void presetUpdated() override + { + presetBox.setSelectedItemIndex (proc.getCurrentProgram(), dontSendNotification); + } + +private: + class CustomComboBox : public ComboBox + { + public: + CustomComboBox (const String& name = {}) : + ComboBox (name) + { + setLookAndFeel (&comboLNF); + } + + ~CustomComboBox() + { + setLookAndFeel (nullptr); + } + + private: + class ComboLNF : public LookAndFeel_V4 + { + void drawPopupMenuItem (Graphics& g, const Rectangle<int>& area, + const bool isSeparator, const bool isActive, + const bool isHighlighted, const bool /*isTicked*/, + const bool hasSubMenu, const String& text, + const String& shortcutKeyText, + const Drawable* icon, const Colour* const textColourToUse) override + { + LookAndFeel_V4::drawPopupMenuItem (g, area, isSeparator, isActive, + isHighlighted, false /*isTicked*/, hasSubMenu, text, + shortcutKeyText, icon, textColourToUse); + } + }; + + ComboLNF comboLNF; + }; + + ChowtapeModelAudioProcessor& proc; + PresetManager& manager; + CustomComboBox presetBox; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PresetComp) +}; + +//==================================================== +PresetManager::PresetManager() +{ + loadPresets(); +} + +StringArray PresetManager::getPresetChoices() +{ + StringArray choices; + for (int i = 0; i < maxIdx; ++i) + { + if (presetMap[i] != nullptr) + choices.add (presetMap[i]->name); + } + + return choices; +} + +void PresetManager::loadPresets() +{ + presets.add (new Preset ("Default.xml")); + presets.add (new Preset ("TC260.xml")); + presets.add (new Preset ("LoFi.xml")); + presets.add (new Preset ("WoozyChorus.xml")); + presets.add (new Preset ("OldTape.xml")); + presets.add (new Preset ("Underbiased.xml")); + + for (auto* p : presets) + { + jassert (! presetMap.contains (p->index)); // no two presets can have the same index + presetMap.set (p->index, p); + maxIdx = jmax (maxIdx, p->index); + } + + maxIdx++; +} + +String PresetManager::getPresetName (int idx) +{ + jassert (isPositiveAndBelow (idx, presets.size())); + return presetMap[idx]->name; +} + +void PresetManager::setPreset (AudioProcessorValueTreeState& vts, int idx) +{ + jassert (isPositiveAndBelow (idx, presets.size())); + auto newState = presetMap[idx]->state.createCopy(); + vts.replaceState (newState); +} + +void PresetManager::registerPresetsComponent (foleys::MagicGUIBuilder& builder, AudioProcessor* proc) +{ + auto procCast = dynamic_cast<ChowtapeModelAudioProcessor*> (proc); + + static Identifier presetsID { "presets" }; + builder.registerFactory (presetsID, [=] (const ValueTree&) + { + return std::make_unique<PresetComp> (*procCast, *this); + }); +} diff --git a/Plugin/Source/Presets/PresetManager.h b/Plugin/Source/Presets/PresetManager.h @@ -0,0 +1,50 @@ +#ifndef PRESETMANAGER_H_INCLUDED +#define PRESETMANAGER_H_INCLUDED + +#include "JuceHeader.h" + +struct Preset +{ + Preset (String presetFile); + + String name; + ValueTree state; + int index; +}; + +//==================================================== +class PresetManager +{ +public: + PresetManager(); + + StringArray getPresetChoices(); + void loadPresets(); + + int getNumPresets() const { return presets.size(); } + String getPresetName (int idx); + void setPreset (AudioProcessorValueTreeState& vts, int idx); + + void registerPresetsComponent (foleys::MagicGUIBuilder&, AudioProcessor* proc); + void presetUpdated() { listeners.call (&Listener::presetUpdated); } + + struct Listener + { + virtual ~Listener() {} + virtual void presetUpdated() {} + }; + + void addListener (Listener* l) { listeners.add (l); } + void removeListener (Listener* l) { listeners.remove (l); } + +private: + HashMap<int, Preset*> presetMap; + OwnedArray<Preset> presets; + int maxIdx = 0; + + ListenerList<Listener> listeners; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PresetManager) +}; + +#endif // PRESETMANAGER_H_INCLUDED diff --git a/Plugin/Source/Presets/TC260.xml b/Plugin/Source/Presets/TC260.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<Preset name="TC-260"> + <Parameters> + <PARAM id="chew_depth" value="0.0"/> + <PARAM id="chew_freq" value="0.0"/> + <PARAM id="deg_amt" value="0.0"/> + <PARAM id="deg_depth" value="0.0"/> + <PARAM id="deg_var" value="0.0"/> + <PARAM id="depth" value="0.199999988079071"/> + <PARAM id="drive" value="0.6599999666213989"/> + <PARAM id="drywet" value="100.0"/> + <PARAM id="gap" value="1.614829443497001e-6"/> + <PARAM id="ingain" value="0.0"/> + <PARAM id="os" value="1.0"/> + <PARAM id="outgain" value="0.0"/> + <PARAM id="rate" value="0.199999988079071"/> + <PARAM id="sat" value="0.75"/> + <PARAM id="spacing" value="0.0002769171842373908"/> + <PARAM id="speed" value="15.00000095367432"/> + <PARAM id="thick" value="0.004748197738081217"/> + <PARAM id="width" value="0.3599999845027924"/> + <PARAM id="preset" value="1"/> + </Parameters> +</Preset> diff --git a/Plugin/Source/Presets/Underbiased.xml b/Plugin/Source/Presets/Underbiased.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<Preset name="Underbiased"> + <Parameters> + <PARAM id="chew_depth" value="0.0"/> + <PARAM id="chew_freq" value="0.0"/> + <PARAM id="deg_amt" value="0.0"/> + <PARAM id="deg_depth" value="0.0"/> + <PARAM id="deg_var" value="0.0"/> + <PARAM id="depth" value="0.0"/> + <PARAM id="drive" value="0.25"/> + <PARAM id="drywet" value="100.0"/> + <PARAM id="gap" value="9.999999974752427e-7"/> + <PARAM id="ingain" value="0.0"/> + <PARAM id="os" value="1.0"/> + <PARAM id="outgain" value="0.0"/> + <PARAM id="rate" value="0.2999999821186066"/> + <PARAM id="sat" value="0.699999988079071"/> + <PARAM id="spacing" value="9.999999974752427e-7"/> + <PARAM id="speed" value="15.00000095367432"/> + <PARAM id="thick" value="9.999999974752427e-7"/> + <PARAM id="width" value="0.04999999701976776"/> + <PARAM id="preset" value="5"/> + </Parameters> +</Preset> diff --git a/Plugin/Source/Presets/WoozyChorus.xml b/Plugin/Source/Presets/WoozyChorus.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<Preset name="Woozy Chorus"> + <Parameters> + <PARAM id="chew_depth" value="0.25"/> + <PARAM id="chew_freq" value="0.5999999642372131"/> + <PARAM id="deg_amt" value="0.0"/> + <PARAM id="deg_depth" value="0.0"/> + <PARAM id="deg_var" value="0.0"/> + <PARAM id="depth" value="0.7999999523162842"/> + <PARAM id="drive" value="0.5"/> + <PARAM id="drywet" value="65.0"/> + <PARAM id="gap" value="9.999999974752427e-7"/> + <PARAM id="ingain" value="0.0"/> + <PARAM id="os" value="1.0"/> + <PARAM id="outgain" value="0.0"/> + <PARAM id="rate" value="0.4399999976158142"/> + <PARAM id="sat" value="0.5"/> + <PARAM id="spacing" value="9.999999974752427e-7"/> + <PARAM id="speed" value="15.00000095367432"/> + <PARAM id="thick" value="9.999999974752427e-7"/> + <PARAM id="width" value="0.5"/> + <PARAM id="preset" value="2"/> + </Parameters> +</Preset> diff --git a/Plugin/Source/gui.xml b/Plugin/Source/gui.xml @@ -1,89 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<magic> - <Styles> - <Style name="default"> - <Nodes/> - <Classes> - <plot-view border="2" background-color="black" border-color="silver" display="contents"/> - <nomargin margin="0" padding="0" border="0"/> - <group margin="5" padding="5" border="2" flex-direction="column"/> - </Classes> - <Types> - <Slider border="0" slider-type="rotary" slider-textbox="textbox-below"/> - <ToggleButton border="0" max-height="50" caption-size="0" text="Active"/> - <TextButton border="0" max-height="50" caption-size="0"/> - <ComboBox border="0" max-height="50" caption-size="0"/> - <Plot border="0" margin="0" padding="0" background-color="00000000" - radius="0"/> - <XYDragComponent border="0" margin="0" padding="0" background-color="00000000" - radius="0"/> - </Types> - </Style> - </Styles> - <View id="root" resizable="1" resize-corner="1" flex-direction="column" - background-color="FF7D2C2C" padding="0" width="600" height="600"> - <View max-height="100" padding="0" background-color="FF7D2C2C" margin="0"> - <Label text="CHOW Tape Model" font-size="20" padding="0" background-color="FF7D2C2C" - border="" max-width="180"/> - <Plot source="scope" plot-fill-color="FF000000" plot-color="FFCC7D12" - padding="0" background-color="FF000000" plot-decay="0.0"/> - </View> - <View background-color="FF7D2C2C" padding="0" margin=""> - <View flex-direction="column" margin="5" padding=""> - <Label max-height="5"/> - <Slider caption="Input Gain [dB]" parameter="ingain" slider-textbox="textbox-below" - slider-type="rotary" max-height="160"/> - <ComboBox caption="Oversampling" parameter="os" max-height="70"/> - <Slider caption="Dry/Wet" parameter="drywet" slider-textbox="textbox-below" - slider-type="rotary" max-height="170"/> - <Slider caption="Output Gain [dB]" parameter="outgain" slider-textbox="textbox-below" - slider-type="rotary" max-height="170"/> - </View> - <View flex-direction="column" tab-color=""> - <Label max-height="40" text="Hysteresis" font-size="18" justification="centred"/> - <Slider caption="Bias" parameter="width" slider-type="rotary" slider-textbox="textbox-below"/> - <Slider caption="Saturation" parameter="sat" slider-textbox="textbox-below" - slider-type="rotary"/> - <Slider caption="Drive" parameter="drive" slider-type="rotary" slider-textbox="textbox-below"/> - </View> - <View display="tabbed" padding="0"> - <View flex-direction="column" tab-caption="Loss" tab-color="" background-color="" - padding="0" margin="0"> - <Label max-height="40" text="Loss" justification="centred" font-size="18" - padding="0" margin="0"/> - <Slider caption="Gap [mm]" parameter="gap" slider-type="linear-horizontal" - slider-textbox="textbox-below" background-color="" padding="0"/> - <Slider caption="Thickness [mm]" parameter="thick" slider-textbox="textbox-below" - slider-type="linear-horizontal" padding="0"/> - <Slider caption="Spacing [mm]" parameter="spacing" slider-type="linear-horizontal" - slider-textbox="textbox-below" lookAndFeel="LookAndFeel_V4" padding="0"/> - <Slider caption="Speed [ips]" parameter="speed" slider-type="linear-horizontal" - slider-textbox="textbox-below" padding="0"/> - </View> - <View tab-caption="Degr." padding="0" flex-direction="column"> - <Label text="Degrade" justification="centred" font-size="18" max-height="40"/> - <Slider parameter="deg_depth" slider-type="rotary" slider-textbox="textbox-below" - caption="Depth"/> - <Slider caption="Amount" parameter="deg_amt" slider-type="rotary" slider-textbox="textbox-below"/> - <Slider parameter="deg_var" slider-type="rotary" slider-textbox="textbox-below" - caption="Variance"/> - </View> - <View tab-caption="CHEW" padding="0" flex-direction="column"> - <Label text="CHEW" justification="centred" font-size="18" max-height="40"/> - <Slider parameter="chew_depth" slider-type="rotary" slider-textbox="textbox-below" - caption="Depth" max-height="180"/> - <Slider caption="Frequency" parameter="chew_freq" slider-type="rotary" - slider-textbox="textbox-below" max-height="200"/> - </View> - </View> - <View flex-direction="column"> - <Label max-height="40" text="Flutter" justification="centred" font-size="18"/> - <Slider caption="Depth" parameter="depth" slider-type="rotary" slider-textbox="textbox-below" - max-height="200"/> - <Slider caption="Rate" parameter="rate" slider-textbox="textbox-below" - slider-type="rotary" max-height="200"/> - </View> - </View> - </View> -</magic>