NeuralPi

Raspberry Pi guitar pedal using neural networks to emulate real amps and effects
Log | Files | Refs | Submodules | README

PluginEditor.cpp (41067B)


      1 /*
      2   ==============================================================================
      3 
      4     This file was auto-generated!
      5 
      6     It contains the basic framework code for a JUCE plugin editor.
      7 
      8   ==============================================================================
      9 */
     10 
     11 #include "PluginProcessor.h"
     12 #include "PluginEditor.h"
     13 #include "AmpOSCReceiver.h"
     14 #include <stdio.h>
     15 #include <fstream>
     16 #include <iostream>
     17 
     18 
     19 //==============================================================================
     20 NeuralPiAudioProcessorEditor::NeuralPiAudioProcessorEditor (NeuralPiAudioProcessor& p)
     21     : AudioProcessorEditor (&p), processor (p)
     22 {
     23     // Make sure that before the constructor has finished, you've set the
     24     // editor's size to whatever you need it to
     25 
     26     blueLookAndFeel.setColour(juce::Slider::thumbColourId, juce::Colours::aqua);
     27     redLookAndFeel.setColour(juce::Slider::thumbColourId, juce::Colours::red);
     28 
     29     //addAndMakeVisible(modelKnob);
     30     //ampGainKnob.setLookAndFeel(&ampSilverKnobLAF);
     31     modelKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20);
     32     modelKnob.setNumDecimalPlacesToDisplay(1);
     33     modelKnob.addListener(this);
     34     //modelKnob.setRange(0, processor.jsonFiles.size() - 1);
     35     modelKnob.setRange(0.0, 1.0);
     36     modelKnob.setValue(0.0);
     37     modelKnob.setSliderStyle(juce::Slider::SliderStyle::RotaryVerticalDrag);
     38     modelKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::NoTextBox, false, 50, 20);
     39     modelKnob.setNumDecimalPlacesToDisplay(1);
     40     modelKnob.setDoubleClickReturnValue(true, 0.0);
     41 
     42     auto modelValue = getParameterValue(modelName);
     43     Slider& modelSlider = getModelSlider();
     44     modelSlider.setValue(modelValue, NotificationType::dontSendNotification);
     45 
     46     modelKnob.onValueChange = [this]
     47     {
     48         const float sliderValue = static_cast<float> (getModelSlider().getValue());
     49         const float modelValue = getParameterValue(modelName);
     50 
     51         if (!approximatelyEqual(modelValue, sliderValue))
     52         {
     53             setParameterValue(modelName, sliderValue);
     54 
     55             // create and send an OSC message with an address and a float value:
     56             float value = static_cast<float> (getModelSlider().getValue());
     57 
     58             if (!oscSender.send(modelAddressPattern, value))
     59             {
     60                 updateOutConnectedLabel(false);
     61             }
     62             else
     63             {
     64                 DBG("Sent value " + String(value) + " to AP " + modelAddressPattern);
     65             }
     66         }
     67     };
     68     
     69     
     70     addAndMakeVisible(modelSelect);
     71     modelSelect.setColour(juce::Label::textColourId, juce::Colours::black);
     72     int c = 1;
     73     for (const auto& jsonFile : processor.jsonFiles) {
     74         modelSelect.addItem(jsonFile.getFileNameWithoutExtension(), c);
     75         c += 1;
     76     }
     77     modelSelect.onChange = [this] {modelSelectChanged(); };
     78     modelSelect.setSelectedItemIndex(processor.current_model_index, juce::NotificationType::dontSendNotification);
     79     modelSelect.setScrollWheelEnabled(true);
     80 
     81     addAndMakeVisible(loadButton);
     82     loadButton.setButtonText("Import Tone");
     83     loadButton.setColour(juce::Label::textColourId, juce::Colours::black);
     84     loadButton.addListener(this);
     85 
     86 
     87     //addAndMakeVisible(irKnob);
     88     //irKnob.setLookAndFeel(&ampSilverKnobLAF);
     89     irKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20);
     90     irKnob.setNumDecimalPlacesToDisplay(1);
     91     irKnob.addListener(this);
     92     //irKnob.setRange(0, processor.irFiles.size() - 1);
     93     irKnob.setRange(0.0, 1.0);
     94     irKnob.setValue(0.0);
     95     irKnob.setSliderStyle(juce::Slider::SliderStyle::RotaryVerticalDrag);
     96     irKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::NoTextBox, false, 50, 20);
     97     irKnob.setNumDecimalPlacesToDisplay(1);
     98     irKnob.setDoubleClickReturnValue(true, 0.0);
     99 
    100     auto irValue = getParameterValue(irName);
    101     Slider& irSlider = getIrSlider();
    102     irSlider.setValue(irValue, NotificationType::dontSendNotification);
    103 
    104     irKnob.onValueChange = [this]
    105     {
    106         const float sliderValue = static_cast<float> (getIrSlider().getValue());
    107         const float irValue = getParameterValue(irName);
    108 
    109         if (!approximatelyEqual(irValue, sliderValue))
    110         {
    111             setParameterValue(irName, sliderValue);
    112 
    113             // create and send an OSC message with an address and a float value:
    114             float value = static_cast<float> (getIrSlider().getValue());
    115 
    116             if (!oscSender.send(irAddressPattern, value))
    117             {
    118                 updateOutConnectedLabel(false);
    119             }
    120             else
    121             {
    122                 DBG("Sent value " + String(value) + " to AP " + irAddressPattern);
    123             }
    124         }
    125     };
    126 
    127     addAndMakeVisible(irSelect);
    128     irSelect.setColour(juce::Label::textColourId, juce::Colours::black);
    129     int i = 1;
    130     for (const auto& jsonFile : processor.irFiles) {
    131         irSelect.addItem(jsonFile.getFileNameWithoutExtension(), i);
    132         i += 1;
    133     }
    134     irSelect.onChange = [this] {irSelectChanged(); };
    135     irSelect.setSelectedItemIndex(processor.current_ir_index, juce::NotificationType::dontSendNotification);
    136     irSelect.setScrollWheelEnabled(true);
    137 
    138     addAndMakeVisible(loadIR);
    139     loadIR.setButtonText("Import IR");
    140     loadIR.setColour(juce::Label::textColourId, juce::Colours::black);
    141     loadIR.addListener(this);
    142 
    143     // Toggle IR
    144     //addAndMakeVisible(irButton); // Toggle is for testing purposes
    145     irButton.setToggleState(true, juce::NotificationType::dontSendNotification);
    146     irButton.onClick = [this] { updateToggleState(&irButton, "IR");   };
    147 
    148     // Toggle LSTM
    149     //addAndMakeVisible(lstmButton); // Toggle is for testing purposes
    150     lstmButton.setToggleState(true, juce::NotificationType::dontSendNotification);
    151     lstmButton.onClick = [this] { updateToggleState(&lstmButton, "LSTM");   };
    152   
    153 
    154     addAndMakeVisible(ampGainKnob);
    155     ampGainKnob.setLookAndFeel(&blueLookAndFeel);
    156     ampGainKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20);
    157     ampGainKnob.setNumDecimalPlacesToDisplay(1);
    158     ampGainKnob.addListener(this);
    159     ampGainKnob.setRange(0.0, 1.0);
    160     ampGainKnob.setValue(0.5);
    161     ampGainKnob.setSliderStyle(juce::Slider::SliderStyle::RotaryVerticalDrag);
    162     ampGainKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20);
    163     ampGainKnob.setNumDecimalPlacesToDisplay(2);
    164     ampGainKnob.setDoubleClickReturnValue(true, 0.5);
    165 
    166     auto gainValue = getParameterValue(gainName);
    167     Slider& gainSlider = getGainSlider();
    168     gainSlider.setValue(gainValue, NotificationType::dontSendNotification);
    169 
    170     ampGainKnob.onValueChange = [this]
    171     {
    172         const float sliderValue = static_cast<float> (getGainSlider().getValue());
    173         const float gainValue = getParameterValue(gainName);
    174 
    175         if (!approximatelyEqual(gainValue, sliderValue))
    176         {
    177             setParameterValue(gainName, sliderValue);
    178 
    179             // create and send an OSC message with an address and a float value:
    180             float value = static_cast<float> (getGainSlider().getValue());
    181 
    182             if (!oscSender.send(gainAddressPattern, value))
    183             {
    184                 updateOutConnectedLabel(false);
    185             }
    186             else
    187             {
    188                 DBG("Sent value " + String(value) + " to AP " + gainAddressPattern);
    189             }
    190         }
    191     };
    192 
    193     addAndMakeVisible(ampMasterKnob);
    194     ampMasterKnob.setLookAndFeel(&blueLookAndFeel);
    195     ampMasterKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20);
    196     ampMasterKnob.setNumDecimalPlacesToDisplay(1);
    197     ampMasterKnob.addListener(this);
    198     ampMasterKnob.setRange(0.0, 1.0);
    199     ampMasterKnob.setValue(0.5);
    200     ampMasterKnob.setSliderStyle(juce::Slider::SliderStyle::RotaryVerticalDrag);
    201     ampMasterKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20);
    202     ampMasterKnob.setNumDecimalPlacesToDisplay(2);
    203     //ampMasterKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::NoTextBox, false, 50, 20 );
    204     //ampMasterKnob.setNumDecimalPlacesToDisplay(1);
    205     ampMasterKnob.setDoubleClickReturnValue(true, 0.5);
    206 
    207     auto masterValue = getParameterValue(masterName);
    208     Slider& masterSlider = getMasterSlider();
    209     masterSlider.setValue(masterValue, NotificationType::dontSendNotification);
    210 
    211     ampMasterKnob.onValueChange = [this]
    212     {
    213         const float sliderValue = static_cast<float> (getMasterSlider().getValue());
    214         const float masterValue = getParameterValue(masterName);
    215 
    216         if (!approximatelyEqual(masterValue, sliderValue))
    217         {
    218             setParameterValue(masterName, sliderValue);
    219 
    220             // create and send an OSC message with an address and a float value:
    221             float value = static_cast<float> (getMasterSlider().getValue());
    222 
    223             if (!oscSender.send(masterAddressPattern, value))
    224             {
    225                 updateOutConnectedLabel(false);
    226             }
    227             else
    228             {
    229                 DBG("Sent value " + String(value) + " to AP " + masterAddressPattern);
    230             }
    231         }
    232     };
    233 
    234 
    235     addAndMakeVisible(ampBassKnob);
    236     ampBassKnob.setLookAndFeel(&blueLookAndFeel);
    237     ampBassKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20);
    238     ampBassKnob.setNumDecimalPlacesToDisplay(1);
    239     ampBassKnob.addListener(this);
    240     ampBassKnob.setRange(0.0, 1.0);
    241     ampBassKnob.setValue(0.5);
    242     ampBassKnob.setSliderStyle(juce::Slider::SliderStyle::RotaryVerticalDrag);
    243     ampBassKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20);
    244     ampBassKnob.setNumDecimalPlacesToDisplay(2);
    245     ampBassKnob.setDoubleClickReturnValue(true, 0.5);
    246 
    247     auto bassValue = getParameterValue(bassName);
    248     Slider& bassSlider = getBassSlider();
    249     bassSlider.setValue(bassValue, NotificationType::dontSendNotification);
    250 
    251     ampBassKnob.onValueChange = [this]
    252     {
    253         const float sliderValue = static_cast<float> (getBassSlider().getValue());
    254         const float bassValue = getParameterValue(bassName);
    255 
    256         if (!approximatelyEqual(bassValue, sliderValue))
    257         {
    258             setParameterValue(bassName, sliderValue);
    259 
    260             // create and send an OSC message with an address and a float value:
    261             float value = static_cast<float> (getBassSlider().getValue());
    262 
    263             if (!oscSender.send(bassAddressPattern, value))
    264             {
    265                 updateOutConnectedLabel(false);
    266             }
    267             else
    268             {
    269                 DBG("Sent value " + String(value) + " to AP " + bassAddressPattern);
    270             }
    271         }
    272     };
    273 
    274     addAndMakeVisible(ampMidKnob);
    275     ampMidKnob.setLookAndFeel(&blueLookAndFeel);
    276     ampMidKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20);
    277     ampMidKnob.setNumDecimalPlacesToDisplay(1);
    278     ampMidKnob.addListener(this);
    279     ampMidKnob.setRange(0.0, 1.0);
    280     ampMidKnob.setValue(0.5);
    281     ampMidKnob.setSliderStyle(juce::Slider::SliderStyle::RotaryVerticalDrag);
    282     ampMidKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20);
    283     ampMidKnob.setNumDecimalPlacesToDisplay(2);
    284     ampMidKnob.setDoubleClickReturnValue(true, 0.5);
    285 
    286     auto midValue = getParameterValue(midName);
    287     Slider& midSlider = getMidSlider();
    288     midSlider.setValue(midValue, NotificationType::dontSendNotification);
    289 
    290     ampMidKnob.onValueChange = [this]
    291     {
    292         const float sliderValue = static_cast<float> (getMidSlider().getValue());
    293         const float midValue = getParameterValue(midName);
    294 
    295         if (!approximatelyEqual(midValue, sliderValue))
    296         {
    297             setParameterValue(midName, sliderValue);
    298 
    299             // create and send an OSC message with an address and a float value:
    300             float value = static_cast<float> (getMidSlider().getValue());
    301 
    302             if (!oscSender.send(midAddressPattern, value))
    303             {
    304                 updateOutConnectedLabel(false);
    305             }
    306             else
    307             {
    308                 DBG("Sent value " + String(value) + " to AP " + midAddressPattern);
    309             }
    310         }
    311     };
    312 
    313     addAndMakeVisible(ampTrebleKnob);
    314     ampTrebleKnob.setLookAndFeel(&blueLookAndFeel);
    315     ampTrebleKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20);
    316     ampTrebleKnob.setNumDecimalPlacesToDisplay(1);
    317     ampTrebleKnob.addListener(this);
    318     ampTrebleKnob.setRange(0.0, 1.0);
    319     ampTrebleKnob.setValue(0.5);
    320     ampTrebleKnob.setSliderStyle(juce::Slider::SliderStyle::RotaryVerticalDrag);
    321     ampTrebleKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20);
    322     ampTrebleKnob.setNumDecimalPlacesToDisplay(2);
    323     ampTrebleKnob.setDoubleClickReturnValue(true, 0.5);
    324 
    325     auto trebleValue = getParameterValue(trebleName);
    326     Slider& trebleSlider = getTrebleSlider();
    327     trebleSlider.setValue(trebleValue, NotificationType::dontSendNotification);
    328 
    329     ampTrebleKnob.onValueChange = [this]
    330     {
    331         const float sliderValue = static_cast<float> (getTrebleSlider().getValue());
    332         const float trebleValue = getParameterValue(trebleName);
    333 
    334         if (!approximatelyEqual(trebleValue, sliderValue))
    335         {
    336             setParameterValue(trebleName, sliderValue);
    337 
    338             // create and send an OSC message with an address and a float value:
    339             float value = static_cast<float> (getTrebleSlider().getValue());
    340 
    341             if (!oscSender.send(trebleAddressPattern, value))
    342             {
    343                 updateOutConnectedLabel(false);
    344             }
    345             else
    346             {
    347                 DBG("Sent value " + String(value) + " to AP " + trebleAddressPattern);
    348             }
    349         }
    350     };
    351 
    352     addAndMakeVisible(ampPresenceKnob);
    353     ampPresenceKnob.setLookAndFeel(&blueLookAndFeel);
    354     ampPresenceKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20);
    355     ampPresenceKnob.setNumDecimalPlacesToDisplay(1);
    356     ampPresenceKnob.addListener(this);
    357     ampPresenceKnob.setRange(0.0, 1.0);
    358     ampPresenceKnob.setValue(0.5);
    359     ampPresenceKnob.setSliderStyle(juce::Slider::SliderStyle::RotaryVerticalDrag);
    360     ampPresenceKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20);
    361     ampPresenceKnob.setNumDecimalPlacesToDisplay(2);
    362     ampPresenceKnob.setDoubleClickReturnValue(true, 0.5);
    363 
    364     auto presenceValue = getParameterValue(trebleName);
    365     Slider& presenceSlider = getPresenceSlider();
    366     trebleSlider.setValue(presenceValue, NotificationType::dontSendNotification);
    367 
    368     ampPresenceKnob.onValueChange = [this]
    369     {
    370         const float sliderValue = static_cast<float> (getPresenceSlider().getValue());
    371         const float presenceValue = getParameterValue(presenceName);
    372 
    373         if (!approximatelyEqual(presenceValue, sliderValue))
    374         {
    375             setParameterValue(presenceName, sliderValue);
    376 
    377             // create and send an OSC message with an address and a float value:
    378             float value = static_cast<float> (getPresenceSlider().getValue());
    379 
    380             if (!oscSender.send(presenceAddressPattern, value))
    381             {
    382                 updateOutConnectedLabel(false);
    383             }
    384             else
    385             {
    386                 DBG("Sent value " + String(value) + " to AP " + presenceAddressPattern);
    387             }
    388         }
    389     };
    390 
    391     addAndMakeVisible(ampDelayKnob);
    392     ampDelayKnob.setLookAndFeel(&blueLookAndFeel);
    393     ampDelayKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20);
    394     ampDelayKnob.setNumDecimalPlacesToDisplay(1);
    395     ampDelayKnob.addListener(this);
    396     ampDelayKnob.setRange(0.0, 1.0);
    397     ampDelayKnob.setValue(0.0);
    398     ampDelayKnob.setSliderStyle(juce::Slider::SliderStyle::RotaryVerticalDrag);
    399     ampDelayKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20);
    400     ampDelayKnob.setNumDecimalPlacesToDisplay(2);
    401     ampDelayKnob.setDoubleClickReturnValue(true, 0.0);
    402 
    403     auto delayValue = getParameterValue(delayName);
    404     Slider& delaySlider = getDelaySlider();
    405     delaySlider.setValue(delayValue, NotificationType::dontSendNotification);
    406 
    407     ampDelayKnob.onValueChange = [this]
    408     {
    409         const float sliderValue = static_cast<float> (getDelaySlider().getValue());
    410         const float delayValue = getParameterValue(delayName);
    411 
    412         if (!approximatelyEqual(delayValue, sliderValue))
    413         {
    414             setParameterValue(delayName, sliderValue);
    415 
    416             // create and send an OSC message with an address and a float value:
    417             float value = static_cast<float> (getDelaySlider().getValue());
    418 
    419             if (!oscSender.send(delayAddressPattern, value))
    420             {
    421                 updateOutConnectedLabel(false);
    422             }
    423             else
    424             {
    425                 DBG("Sent value " + String(value) + " to AP " + delayAddressPattern);
    426             }
    427         }
    428     };
    429 
    430     addAndMakeVisible(ampReverbKnob);
    431     ampReverbKnob.setLookAndFeel(&blueLookAndFeel);
    432     ampReverbKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20);
    433     ampReverbKnob.setNumDecimalPlacesToDisplay(1);
    434     ampReverbKnob.addListener(this);
    435     ampReverbKnob.setRange(0.0, 1.0);
    436     ampReverbKnob.setValue(0.0);
    437     ampReverbKnob.setSliderStyle(juce::Slider::SliderStyle::RotaryVerticalDrag);
    438     ampReverbKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20);
    439     ampReverbKnob.setNumDecimalPlacesToDisplay(2);
    440     ampReverbKnob.setDoubleClickReturnValue(true, 0.0);
    441 
    442     auto reverbValue = getParameterValue(reverbName);
    443     Slider& reverbSlider = getReverbSlider();
    444     reverbSlider.setValue(reverbValue, NotificationType::dontSendNotification);
    445 
    446     ampReverbKnob.onValueChange = [this]
    447     {
    448         const float sliderValue = static_cast<float> (getReverbSlider().getValue());
    449         const float reverbValue = getParameterValue(reverbName);
    450 
    451         if (!approximatelyEqual(reverbValue, sliderValue))
    452         {
    453             setParameterValue(reverbName, sliderValue);
    454 
    455             // create and send an OSC message with an address and a float value:
    456             float value = static_cast<float> (getReverbSlider().getValue());
    457 
    458             if (!oscSender.send(reverbAddressPattern, value))
    459             {
    460                 updateOutConnectedLabel(false);
    461             }
    462             else
    463             {
    464                 DBG("Sent value " + String(value) + " to AP " + reverbAddressPattern);
    465             }
    466         }
    467     };
    468 
    469     addAndMakeVisible(GainLabel);
    470     GainLabel.setText("Gain", juce::NotificationType::dontSendNotification);
    471     GainLabel.setJustificationType(juce::Justification::centred);
    472     addAndMakeVisible(LevelLabel);
    473     LevelLabel.setText("Level", juce::NotificationType::dontSendNotification);
    474     LevelLabel.setJustificationType(juce::Justification::centred);
    475 
    476     addAndMakeVisible(BassLabel);
    477     BassLabel.setText("Bass", juce::NotificationType::dontSendNotification);
    478     BassLabel.setJustificationType(juce::Justification::centred);
    479 
    480     addAndMakeVisible(MidLabel);
    481     MidLabel.setText("Mid", juce::NotificationType::dontSendNotification);
    482     MidLabel.setJustificationType(juce::Justification::centred);
    483 
    484     addAndMakeVisible(TrebleLabel);
    485     TrebleLabel.setText("Treble", juce::NotificationType::dontSendNotification);
    486     TrebleLabel.setJustificationType(juce::Justification::centred);
    487 
    488     addAndMakeVisible(PresenceLabel);
    489     PresenceLabel.setText("Presence", juce::NotificationType::dontSendNotification);
    490     PresenceLabel.setJustificationType(juce::Justification::centred);
    491 
    492     addAndMakeVisible(DelayLabel);
    493     DelayLabel.setText("Delay", juce::NotificationType::dontSendNotification);
    494     DelayLabel.setJustificationType(juce::Justification::centred);
    495 
    496     addAndMakeVisible(ReverbLabel);
    497     ReverbLabel.setText("Reverb", juce::NotificationType::dontSendNotification);
    498     ReverbLabel.setJustificationType(juce::Justification::centred);
    499 
    500     addAndMakeVisible(toneDropDownLabel);
    501     toneDropDownLabel.setText("Tone", juce::NotificationType::dontSendNotification);
    502     toneDropDownLabel.setJustificationType(juce::Justification::centred);
    503 
    504     addAndMakeVisible(irDropDownLabel);
    505     irDropDownLabel.setText("IR", juce::NotificationType::dontSendNotification);
    506     irDropDownLabel.setJustificationType(juce::Justification::centred);
    507 
    508     addAndMakeVisible(versionLabel);
    509     versionLabel.setText("v1.3.0", juce::NotificationType::dontSendNotification);
    510     versionLabel.setJustificationType(juce::Justification::centred);
    511 
    512     auto font = GainLabel.getFont();
    513     float height = font.getHeight();
    514     font.setHeight(height); // 0.75);
    515     GainLabel.setFont(font);
    516     LevelLabel.setFont(font);
    517     BassLabel.setFont(font);
    518     MidLabel.setFont(font);
    519     TrebleLabel.setFont(font);
    520     PresenceLabel.setFont(font);
    521     DelayLabel.setFont(font);
    522     ReverbLabel.setFont(font);
    523     toneDropDownLabel.setFont(font);
    524     irDropDownLabel.setFont(font);
    525     versionLabel.setFont(font);
    526 
    527 
    528     // Name controls:
    529     addAndMakeVisible(ampNameLabel);
    530     ampNameField.setEditable(true, true, true);
    531     addAndMakeVisible(ampNameField);
    532 
    533     // IP controls:
    534     ipField.setEditable(true, true, true);
    535     addAndMakeVisible(ipLabel);
    536     addAndMakeVisible(ipField);
    537 
    538     // Port controls:
    539     addAndMakeVisible(outPortNumberLabel);
    540     outPortNumberField.setEditable(true, true, true);
    541     addAndMakeVisible(outPortNumberField);
    542     addAndMakeVisible(outConnectedLabel);
    543 
    544     addAndMakeVisible(inPortNumberLabel);
    545     inPortNumberField.setEditable(true, true, true);
    546     addAndMakeVisible(inPortNumberField);
    547     addAndMakeVisible(inConnectedLabel);
    548 
    549 
    550     // OSC messaging
    551 
    552     getInPortNumberField().addListener(this);
    553     getAmpNameField().addListener(this);
    554     getOutPortNumberField().addListener(this);
    555     getIPField().addListener(this);
    556 
    557     oscReceiver.getGainValue().addListener(this);
    558     oscReceiver.getMasterValue().addListener(this);
    559 
    560     oscReceiver.getBassValue().addListener(this);
    561     oscReceiver.getMidValue().addListener(this);
    562     oscReceiver.getTrebleValue().addListener(this);
    563     oscReceiver.getPresenceValue().addListener(this);
    564 
    565     oscReceiver.getModelValue().addListener(this);
    566     oscReceiver.getIrValue().addListener(this);
    567 
    568     updateInConnectedLabel();
    569 
    570     connectSender();
    571 
    572     // Size of plugin GUI
    573     setSize(345, 455);
    574 
    575     // Set gain knob color based on conditioned/snapshot model 
    576     setParamKnobColor();
    577 }
    578 
    579 NeuralPiAudioProcessorEditor::~NeuralPiAudioProcessorEditor()
    580 {
    581 }
    582 
    583 //==============================================================================
    584 void NeuralPiAudioProcessorEditor::paint (Graphics& g)
    585 {
    586     // Workaround for graphics on Windows builds (clipping code doesn't work correctly on Windows)
    587 #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
    588     g.drawImageAt(background, 0, 0);  // Debug Line: Redraw entire background image
    589 #else
    590 // Redraw only the clipped part of the background image
    591     juce::Rectangle<int> ClipRect = g.getClipBounds();
    592     g.drawImage(background, ClipRect.getX(), ClipRect.getY(), ClipRect.getWidth(), ClipRect.getHeight(), ClipRect.getX(), ClipRect.getY(), ClipRect.getWidth(), ClipRect.getHeight());
    593 #endif
    594    
    595 }
    596 
    597 void NeuralPiAudioProcessorEditor::resized()
    598 {
    599     // This is generally where you'll want to lay out the positions of any
    600     // subcomponents in your editor..
    601     modelSelect.setBounds(11, 10, 270, 25);
    602     loadButton.setBounds(11, 74, 100, 25);
    603     modelKnob.setBounds(140, 40, 75, 95);
    604 
    605     irSelect.setBounds(11, 42, 270, 25);
    606     loadIR.setBounds(120, 74, 100, 25);
    607     irButton.setBounds(248, 42, 257, 25);
    608     lstmButton.setBounds(248, 10, 257, 25);
    609 
    610     // Amp Widgets
    611     ampGainKnob.setBounds(10, 120, 75, 95);
    612     ampMasterKnob.setBounds(95, 120, 75, 95);
    613     ampBassKnob.setBounds(10, 250, 75, 95);
    614     ampMidKnob.setBounds(95, 250, 75, 95);
    615     ampTrebleKnob.setBounds(180, 250, 75, 95);
    616     ampPresenceKnob.setBounds(265, 250, 75, 95);
    617 
    618     ampDelayKnob.setBounds(180, 120, 75, 95);
    619     ampReverbKnob.setBounds(265, 120, 75, 95);
    620 
    621     GainLabel.setBounds(6, 108, 80, 10);
    622     LevelLabel.setBounds(93, 108, 80, 10);
    623     BassLabel.setBounds(6, 238, 80, 10);
    624     MidLabel.setBounds(91, 238, 80, 10);
    625     TrebleLabel.setBounds(178, 238, 80, 10);
    626     PresenceLabel.setBounds(265, 238, 80, 10);
    627     DelayLabel.setBounds(178, 108, 80, 10);
    628     ReverbLabel.setBounds(265, 108, 80, 10);
    629 
    630     toneDropDownLabel.setBounds(267, 16, 80, 10);
    631     irDropDownLabel.setBounds(261, 48, 80, 10);
    632     versionLabel.setBounds(268, 431, 80, 10);
    633 
    634     addAndMakeVisible(ampNameLabel);
    635     ampNameField.setEditable(true, true, true);
    636     addAndMakeVisible(ampNameField);
    637 
    638     // IP controls:
    639     ipField.setBounds(150, 365, 100, 25);
    640     ipLabel.setBounds(15, 365, 150, 25);
    641 
    642     // Port controls:
    643     outPortNumberLabel.setBounds(15, 395, 150, 25);
    644     outPortNumberField.setBounds(160, 395, 75, 25);
    645     inPortNumberLabel.setBounds(15, 425, 150, 25);
    646     inPortNumberField.setBounds(160, 425, 75, 25);
    647 }
    648 
    649 void NeuralPiAudioProcessorEditor::modelSelectChanged()
    650 {
    651     const int selectedFileIndex = modelSelect.getSelectedItemIndex();
    652     if (selectedFileIndex >= 0 && selectedFileIndex < processor.jsonFiles.size()) {
    653         File selectedFile = processor.userAppDataDirectory_tones.getFullPathName() + "/" + modelSelect.getText() + ".json";
    654         //processor.loadConfig(processor.jsonFiles[selectedFileIndex]);
    655         processor.loadConfig(selectedFile);
    656         processor.current_model_index = selectedFileIndex;
    657     }
    658     auto newValue = static_cast<float>(processor.current_model_index / (processor.num_models - 1.0));
    659     modelKnob.setValue(newValue);
    660     setParamKnobColor();
    661 }
    662 
    663 void NeuralPiAudioProcessorEditor::irSelectChanged()
    664 {
    665     const int selectedFileIndex = irSelect.getSelectedItemIndex();
    666     if (selectedFileIndex >= 0 && selectedFileIndex < processor.irFiles.size()) {
    667         File selectedFile = processor.userAppDataDirectory_irs.getFullPathName() + "/" + irSelect.getText() + ".wav";
    668         //processor.loadIR(processor.irFiles[selectedFileIndex]);
    669         processor.loadIR(selectedFile);
    670         processor.current_ir_index = selectedFileIndex;
    671     }
    672     auto newValue = static_cast<float>(processor.current_ir_index / (processor.num_irs - 1.0));
    673     irKnob.setValue(newValue);
    674 }
    675 
    676 void NeuralPiAudioProcessorEditor::updateToggleState(juce::Button* button, juce::String name)
    677 {
    678     if (name == "IR")
    679         processor.ir_state = button->getToggleState();
    680     else
    681         processor.lstm_state = button->getToggleState();
    682 }
    683 
    684 void NeuralPiAudioProcessorEditor::loadButtonClicked()
    685 {  
    686     myChooser = std::make_unique<FileChooser> ("Select one or more .json tone files to import",
    687                                                File::getSpecialLocation (File::userDesktopDirectory),
    688                                                "*.json");
    689 
    690     auto folderChooserFlags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles | FileBrowserComponent::canSelectMultipleItems;
    691 
    692     myChooser->launchAsync (folderChooserFlags, [this] (const FileChooser& chooser)
    693     {
    694         Array<File> files = chooser.getResults();
    695         for (auto file : files) {
    696             File fullpath = processor.userAppDataDirectory_tones.getFullPathName() + "/" + file.getFileName();
    697             bool b = fullpath.existsAsFile();
    698             if (b == false) {
    699 
    700                 processor.loadConfig(file);
    701                 fname = file.getFileName();
    702                 processor.loaded_tone = file;
    703                 processor.loaded_tone_name = fname;
    704                 processor.custom_tone = 1;
    705 
    706                 // Copy selected file to model directory and load into dropdown menu
    707                 bool a = file.copyFileTo(fullpath);
    708                 if (a == true) {
    709                     modelSelect.addItem(file.getFileNameWithoutExtension(), processor.jsonFiles.size() + 1);
    710                     modelSelect.setSelectedItemIndex(processor.jsonFiles.size(), juce::NotificationType::dontSendNotification);
    711                     processor.jsonFiles.push_back(file);
    712                     processor.num_models += 1;
    713                 }
    714                 // Sort jsonFiles alphabetically
    715                 std::sort(processor.jsonFiles.begin(), processor.jsonFiles.end());
    716             }
    717         }
    718     });
    719     setParamKnobColor();
    720 }
    721 
    722 void NeuralPiAudioProcessorEditor::loadIRClicked()
    723 {
    724     myChooser = std::make_unique<FileChooser> ("Select one or more .wav IR files to import",
    725                                                File::getSpecialLocation (File::userDesktopDirectory),
    726                                                "*.wav");
    727 
    728     auto folderChooserFlags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles | FileBrowserComponent::canSelectMultipleItems;
    729 
    730     myChooser->launchAsync (folderChooserFlags, [this] (const FileChooser& chooser)   
    731     {
    732         Array<File> files = chooser.getResults();
    733         for (auto file : files) {
    734             File fullpath = processor.userAppDataDirectory_irs.getFullPathName() + "/" + file.getFileName();
    735             bool b = fullpath.existsAsFile();
    736             if (b == false) {
    737 
    738                 processor.loadIR(file);
    739                 fname = file.getFileName();
    740                 processor.loaded_ir = file;
    741                 processor.loaded_ir_name = fname;
    742                 processor.custom_ir = 1;
    743 
    744                 // Copy selected file to model directory and load into dropdown menu
    745                 bool a = file.copyFileTo(fullpath);
    746                 if (a == true) {
    747                     irSelect.addItem(file.getFileNameWithoutExtension(), processor.irFiles.size() + 1);
    748                     irSelect.setSelectedItemIndex(processor.irFiles.size(), juce::NotificationType::dontSendNotification);
    749                     processor.irFiles.push_back(file);
    750                     processor.num_irs += 1;
    751                 }
    752                 // Sort jsonFiles alphabetically
    753                 std::sort(processor.irFiles.begin(), processor.irFiles.end());
    754             }
    755         }
    756     });
    757 }
    758 
    759 
    760 void NeuralPiAudioProcessorEditor::buttonClicked(juce::Button* button)
    761 {
    762     if (button == &loadButton) {
    763         loadButtonClicked();
    764     }
    765     else
    766     {
    767         loadIRClicked();
    768     }
    769 }
    770 
    771 
    772 void NeuralPiAudioProcessorEditor::sliderValueChanged(Slider* slider)
    773 {
    774     if (slider == &modelKnob) {
    775         if (slider->getValue() >= 0 && slider->getValue() < processor.jsonFiles.size()) {
    776             modelSelect.setSelectedItemIndex(processor.getModelIndex(slider->getValue()), juce::NotificationType::dontSendNotification);
    777         }
    778     } else if (slider == &irKnob) {
    779         if (slider->getValue() >= 0 && slider->getValue() < processor.irFiles.size()) {
    780             irSelect.setSelectedItemIndex(processor.getIrIndex(slider->getValue()), juce::NotificationType::dontSendNotification);
    781         }
    782     }
    783 }
    784 
    785 // OSC Messages
    786 Slider& NeuralPiAudioProcessorEditor::getGainSlider()
    787 {
    788     return ampGainKnob;
    789 }
    790 
    791 Slider& NeuralPiAudioProcessorEditor::getMasterSlider()
    792 {
    793     return ampMasterKnob;
    794 }
    795 
    796 Slider& NeuralPiAudioProcessorEditor::getBassSlider()
    797 {
    798     return ampBassKnob;
    799 }
    800 
    801 Slider& NeuralPiAudioProcessorEditor::getMidSlider()
    802 {
    803     return ampMidKnob;
    804 }
    805 
    806 Slider& NeuralPiAudioProcessorEditor::getTrebleSlider()
    807 {
    808     return ampTrebleKnob;
    809 }
    810 
    811 Slider& NeuralPiAudioProcessorEditor::getPresenceSlider()
    812 {
    813     return ampPresenceKnob;
    814 }
    815 
    816 Slider& NeuralPiAudioProcessorEditor::getDelaySlider()
    817 {
    818     return ampDelayKnob;
    819 }
    820 
    821 Slider& NeuralPiAudioProcessorEditor::getReverbSlider()
    822 {
    823     return ampReverbKnob;
    824 }
    825 
    826 Slider& NeuralPiAudioProcessorEditor::getModelSlider()
    827 {
    828     return modelKnob;
    829 }
    830 
    831 Slider& NeuralPiAudioProcessorEditor::getIrSlider()
    832 {
    833     return irKnob;
    834 }
    835 
    836 
    837 Label& NeuralPiAudioProcessorEditor::getOutPortNumberField()
    838 {
    839     return outPortNumberField;
    840 }
    841 
    842 Label& NeuralPiAudioProcessorEditor::getInPortNumberField()
    843 {
    844     return inPortNumberField;
    845 }
    846 
    847 Label& NeuralPiAudioProcessorEditor::getIPField()
    848 {
    849     return ipField;
    850 }
    851 
    852 Label& NeuralPiAudioProcessorEditor::getAmpNameField()
    853 {
    854     return ampNameField;
    855 }
    856 
    857 Label& NeuralPiAudioProcessorEditor::getOutConnectedLabel()
    858 {
    859     return outConnectedLabel;
    860 }
    861 
    862 Label& NeuralPiAudioProcessorEditor::getInConnectedLabel()
    863 {
    864     return inConnectedLabel;
    865 }
    866 
    867 void NeuralPiAudioProcessorEditor::buildAddressPatterns()
    868 {
    869     gainAddressPattern = "/parameter/" + ampName + "/Gain";
    870     masterAddressPattern = "/parameter/" + ampName + "/Master";
    871     bassAddressPattern = "/parameter/" + ampName + "/Bass";
    872     midAddressPattern = "/parameter/" + ampName + "/Mid";
    873     trebleAddressPattern = "/parameter/" + ampName + "/Treble";
    874     presenceAddressPattern = "/parameter/" + ampName + "/Presence";
    875     delayAddressPattern = "/parameter/" + ampName + "/Delay";
    876     reverbAddressPattern = "/parameter/" + ampName + "/Reverb";
    877     modelAddressPattern = "/parameter/" + ampName + "/Model";
    878     irAddressPattern = "/parameter/" + ampName + "/Ir";
    879 }
    880 
    881 void NeuralPiAudioProcessorEditor::connectSender()
    882 {
    883     // specify here where to send OSC messages to: host URL and UDP port number
    884     if (!oscSender.connect(outgoingIP, outgoingPort))
    885     {
    886         updateOutConnectedLabel(false);
    887     }
    888     else
    889     {
    890         updateOutConnectedLabel(true);
    891     }
    892 }
    893 
    894 void NeuralPiAudioProcessorEditor::updateOutgoingIP(String ip)
    895 {
    896     outgoingIP = std::move(ip);
    897     connectSender();
    898 }
    899 
    900 void NeuralPiAudioProcessorEditor::updateOutgoingPort(int port)
    901 {
    902     outgoingPort = port;
    903     connectSender();
    904 }
    905 
    906 void NeuralPiAudioProcessorEditor::labelTextChanged(Label* labelThatHasChanged)
    907 {
    908     if (labelThatHasChanged == &getInPortNumberField())
    909     {
    910         const int newPort = getInPortNumberField().getTextValue().toString().getIntValue();
    911         oscReceiver.changePort(newPort);
    912         updateInConnectedLabel();
    913     }
    914     else if (labelThatHasChanged == &getOutPortNumberField())
    915     {
    916         const int newPort = getOutPortNumberField().getTextValue().toString().getIntValue();
    917         updateOutgoingPort(newPort);
    918     }
    919     else if (labelThatHasChanged == &getIPField())
    920     {
    921         const String newIP = getIPField().getTextValue().toString();
    922         updateOutgoingIP(newIP);
    923     }
    924     /*
    925     else if (labelThatHasChanged == getAmpNameField())
    926     {
    927         ampName = getAmpNameField().getTextValue().toString();
    928         buildAddressPatterns();
    929         oscReceiver.updateAmpName(getAmpNameField().getTextValue().toString());
    930     }
    931     */
    932 }
    933 
    934 void NeuralPiAudioProcessorEditor::updateInConnectedLabel()
    935 {
    936     const bool connected = oscReceiver.isConnected();
    937     if (connected)
    938     {
    939         getInConnectedLabel().setText("(Connected)", dontSendNotification);
    940     }
    941     else
    942     {
    943         getInConnectedLabel().setText("(Disconnected!)", dontSendNotification);
    944     }
    945 }
    946 
    947 void NeuralPiAudioProcessorEditor::updateOutConnectedLabel(bool connected)
    948 {
    949     if (connected)
    950     {
    951         getOutConnectedLabel().setText("(Connected)", dontSendNotification);
    952     }
    953     else
    954     {
    955         getOutConnectedLabel().setText("(Disconnected!)", dontSendNotification);
    956     }
    957 }
    958 
    959 // This callback is invoked if an OSC message has been received setting either value.
    960 void NeuralPiAudioProcessorEditor::valueChanged(Value& value)
    961 {
    962     if (value.refersToSameSourceAs(oscReceiver.getGainValue()))
    963     {
    964         if (!approximatelyEqual(static_cast<double> (value.getValue()), getGainSlider().getValue()))
    965         {
    966             getGainSlider().setValue(static_cast<double> (value.getValue()),
    967                 NotificationType::sendNotification);
    968         }
    969     }
    970     else if (value.refersToSameSourceAs(oscReceiver.getMasterValue()))
    971     {
    972         if (!approximatelyEqual(static_cast<double> (value.getValue()), getMasterSlider().getValue()))
    973         {
    974             getMasterSlider().setValue(static_cast<double> (value.getValue()),
    975                 NotificationType::sendNotification);
    976         }
    977     }
    978     if (value.refersToSameSourceAs(oscReceiver.getBassValue()))
    979     {
    980         if (!approximatelyEqual(static_cast<double> (value.getValue()), getBassSlider().getValue()))
    981         {
    982             getBassSlider().setValue(static_cast<double> (value.getValue()),
    983                 NotificationType::sendNotification);
    984         }
    985     }
    986     else if (value.refersToSameSourceAs(oscReceiver.getMidValue()))
    987     {
    988         if (!approximatelyEqual(static_cast<double> (value.getValue()), getMidSlider().getValue()))
    989         {
    990             getMidSlider().setValue(static_cast<double> (value.getValue()),
    991                 NotificationType::sendNotification);
    992         }
    993     }
    994     if (value.refersToSameSourceAs(oscReceiver.getTrebleValue()))
    995     {
    996         if (!approximatelyEqual(static_cast<double> (value.getValue()), getTrebleSlider().getValue()))
    997         {
    998             getTrebleSlider().setValue(static_cast<double> (value.getValue()),
    999                 NotificationType::sendNotification);
   1000         }
   1001     }
   1002     else if (value.refersToSameSourceAs(oscReceiver.getPresenceValue()))
   1003     {
   1004         if (!approximatelyEqual(static_cast<double> (value.getValue()), getPresenceSlider().getValue()))
   1005         {
   1006             getPresenceSlider().setValue(static_cast<double> (value.getValue()),
   1007                 NotificationType::sendNotification);
   1008         }
   1009     }
   1010     if (value.refersToSameSourceAs(oscReceiver.getDelayValue()))
   1011     {
   1012         if (!approximatelyEqual(static_cast<double> (value.getValue()), getDelaySlider().getValue()))
   1013         {
   1014             getDelaySlider().setValue(static_cast<double> (value.getValue()),
   1015                 NotificationType::sendNotification);
   1016         }
   1017     }
   1018     else if (value.refersToSameSourceAs(oscReceiver.getReverbValue()))
   1019     {
   1020         if (!approximatelyEqual(static_cast<double> (value.getValue()), getReverbSlider().getValue()))
   1021         {
   1022             getReverbSlider().setValue(static_cast<double> (value.getValue()),
   1023                 NotificationType::sendNotification);
   1024         }
   1025     }
   1026     else if (value.refersToSameSourceAs(oscReceiver.getModelValue()))
   1027     {
   1028         if (!approximatelyEqual(static_cast<double> (value.getValue()), getModelSlider().getValue()))
   1029         {
   1030             getModelSlider().setValue(static_cast<double> (value.getValue()),
   1031                 NotificationType::sendNotification);
   1032         }
   1033     }
   1034     else if (value.refersToSameSourceAs(oscReceiver.getIrValue()))
   1035     {
   1036         if (!approximatelyEqual(static_cast<double> (value.getValue()), getIrSlider().getValue()))
   1037         {
   1038             getIrSlider().setValue(static_cast<double> (value.getValue()),
   1039                 NotificationType::sendNotification);
   1040         }
   1041     }
   1042 }
   1043 
   1044 void NeuralPiAudioProcessorEditor::timerCallback()
   1045 {
   1046     getGainSlider().setValue(getParameterValue(gainName), NotificationType::dontSendNotification);
   1047     getMasterSlider().setValue(getParameterValue(masterName), NotificationType::dontSendNotification);
   1048     getBassSlider().setValue(getParameterValue(bassName), NotificationType::dontSendNotification);
   1049     getMidSlider().setValue(getParameterValue(midName), NotificationType::dontSendNotification);
   1050     getTrebleSlider().setValue(getParameterValue(trebleName), NotificationType::dontSendNotification);
   1051     getPresenceSlider().setValue(getParameterValue(presenceName), NotificationType::dontSendNotification);
   1052     getDelaySlider().setValue(getParameterValue(delayName), NotificationType::dontSendNotification);
   1053     getReverbSlider().setValue(getParameterValue(reverbName), NotificationType::dontSendNotification);
   1054     getModelSlider().setValue(getParameterValue(modelName), NotificationType::dontSendNotification);
   1055     getIrSlider().setValue(getParameterValue(irName), NotificationType::dontSendNotification);
   1056 }
   1057 
   1058 AudioProcessorParameter* NeuralPiAudioProcessorEditor::getParameter(const String& paramId)
   1059 {
   1060     if (auto* proc = getAudioProcessor())
   1061     {
   1062         auto& params = proc->getParameters();
   1063 
   1064         for (auto p : params)
   1065         {
   1066             if (auto* param = dynamic_cast<AudioProcessorParameterWithID*> (p))
   1067             {
   1068                 if (param->paramID == paramId)
   1069                     return param;
   1070             }
   1071         }
   1072     }
   1073 
   1074     return nullptr;
   1075 }
   1076 
   1077 float NeuralPiAudioProcessorEditor::getParameterValue(const String& paramId)
   1078 {
   1079     if (auto* param = getParameter(paramId))
   1080         return param->getValue();
   1081 
   1082     return 0.0f;
   1083 }
   1084 
   1085 void NeuralPiAudioProcessorEditor::setParameterValue(const String& paramId, float value)
   1086 {
   1087     if (auto* param = getParameter(paramId))
   1088         param->setValueNotifyingHost(value);
   1089 }
   1090 
   1091 
   1092 void NeuralPiAudioProcessorEditor::setParamKnobColor()
   1093 {
   1094     // If the knob is used for a parameter, change it to red
   1095     if (processor.params == 0) {
   1096         ampGainKnob.setLookAndFeel(&blueLookAndFeel);
   1097         ampMasterKnob.setLookAndFeel(&blueLookAndFeel);
   1098     }
   1099     else if (processor.params == 1) {
   1100         ampGainKnob.setLookAndFeel(&redLookAndFeel);
   1101         ampMasterKnob.setLookAndFeel(&blueLookAndFeel);
   1102     }
   1103     else if (processor.params == 2) {
   1104         ampGainKnob.setLookAndFeel(&redLookAndFeel);
   1105         ampMasterKnob.setLookAndFeel(&redLookAndFeel);
   1106     }
   1107 
   1108 }