Proteus

Guitar amp and pedal capture plugin using neural networks
Log | Files | Refs | Submodules | README

PluginEditor.cpp (16262B)


      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 
     14 //==============================================================================
     15 ProteusAudioProcessorEditor::ProteusAudioProcessorEditor (ProteusAudioProcessor& p)
     16     : AudioProcessorEditor (&p), processor (p)
     17 {
     18     // Make sure that before the constructor has finished, you've set the
     19     // editor's size to whatever you need it to
     20 
     21     // Overall Widgets
     22     addAndMakeVisible(loadButton);
     23     loadButton.setButtonText("LOAD MODEL");
     24     loadButton.addListener(this);
     25 
     26     addAndMakeVisible(modelSelect);
     27     modelSelect.setColour(juce::Label::textColourId, juce::Colours::black);
     28     modelSelect.setScrollWheelEnabled(true);
     29     int c = 1;
     30     for (const auto& jsonFile : processor.jsonFiles) {
     31         modelSelect.addItem(jsonFile.getFileName(), c);
     32         c += 1;
     33     }
     34     modelSelect.onChange = [this] {modelSelectChanged();};
     35 
     36     auto font = modelLabel.getFont();
     37     float height = font.getHeight();
     38     font.setHeight(height);
     39 
     40     // Set Widget Graphics
     41     bigKnobLAF.setLookAndFeel(ImageCache::getFromMemory(BinaryData::big_knob_png, BinaryData::big_knob_pngSize));
     42     smallKnobLAF.setLookAndFeel(ImageCache::getFromMemory(BinaryData::small_knob_png, BinaryData::small_knob_pngSize));
     43 
     44     // Pre Amp Pedal Widgets
     45  
     46     /*
     47     // Overdrive
     48     odFootSw.setImages(true, true, true,
     49         ImageCache::getFromMemory(BinaryData::footswitch_up_png, BinaryData::footswitch_up_pngSize), 1.0, Colours::transparentWhite,
     50         Image(), 1.0, Colours::transparentWhite,
     51         ImageCache::getFromMemory(BinaryData::footswitch_down_png, BinaryData::footswitch_down_pngSize), 1.0, Colours::transparentWhite,
     52         0.0);
     53     addAndMakeVisible(odFootSw);
     54     odFootSw.addListener(this);
     55     */
     56 
     57     cabOnButton.setImages(true, true, true,
     58         ImageCache::getFromMemory(BinaryData::cab_switch_on_png, BinaryData::cab_switch_on_pngSize), 1.0, Colours::transparentWhite,
     59         Image(), 1.0, Colours::transparentWhite,
     60         ImageCache::getFromMemory(BinaryData::cab_switch_on_png, BinaryData::cab_switch_on_pngSize), 1.0, Colours::transparentWhite,
     61         0.0);
     62     addAndMakeVisible(cabOnButton);
     63     cabOnButton.addListener(this);
     64 
     65     driveSliderAttach = std::make_unique<AudioProcessorValueTreeState::SliderAttachment>(processor.treeState, GAIN_ID, odDriveKnob);
     66     addAndMakeVisible(odDriveKnob);
     67     odDriveKnob.setLookAndFeel(&bigKnobLAF);
     68     odDriveKnob.addListener(this);
     69     odDriveKnob.setSliderStyle(juce::Slider::SliderStyle::RotaryVerticalDrag);
     70     odDriveKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::NoTextBox, false, 50, 20);
     71     odDriveKnob.setDoubleClickReturnValue(true, 0.5);
     72 
     73     masterSliderAttach = std::make_unique<AudioProcessorValueTreeState::SliderAttachment>(processor.treeState, MASTER_ID, odLevelKnob);
     74     addAndMakeVisible(odLevelKnob);
     75     odLevelKnob.setLookAndFeel(&smallKnobLAF);
     76     odLevelKnob.addListener(this);
     77     odLevelKnob.setSliderStyle(juce::Slider::SliderStyle::RotaryVerticalDrag);
     78     odLevelKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::NoTextBox, false, 50, 20);
     79     odLevelKnob.setDoubleClickReturnValue(true, 0.5);
     80 
     81     bassSliderAttach = std::make_unique<AudioProcessorValueTreeState::SliderAttachment>(processor.treeState, BASS_ID, ampBassKnob);    	    
     82     addAndMakeVisible(ampBassKnob);
     83     ampBassKnob.setLookAndFeel(&smallKnobLAF);
     84     ampBassKnob.addListener(this);
     85     ampBassKnob.setSliderStyle(juce::Slider::SliderStyle::RotaryVerticalDrag);
     86     ampBassKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::NoTextBox, false, 50, 20);
     87     ampBassKnob.setDoubleClickReturnValue(true, 0.0);
     88 
     89     midSliderAttach = std::make_unique<AudioProcessorValueTreeState::SliderAttachment>(processor.treeState, MID_ID, ampMidKnob);    
     90     addAndMakeVisible(ampMidKnob);
     91     ampMidKnob.setLookAndFeel(&smallKnobLAF);
     92     ampMidKnob.addListener(this);
     93     ampMidKnob.setSliderStyle(juce::Slider::SliderStyle::RotaryVerticalDrag);
     94     ampMidKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::NoTextBox, false, 50, 20);
     95     ampMidKnob.setDoubleClickReturnValue(true, 0.0);
     96 
     97     trebleSliderAttach = std::make_unique<AudioProcessorValueTreeState::SliderAttachment>(processor.treeState, TREBLE_ID, ampTrebleKnob);
     98     addAndMakeVisible(ampTrebleKnob);
     99     ampTrebleKnob.setLookAndFeel(&smallKnobLAF);
    100     ampTrebleKnob.addListener(this);
    101     ampTrebleKnob.setSliderStyle(juce::Slider::SliderStyle::RotaryVerticalDrag);
    102     ampTrebleKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::NoTextBox, false, 50, 20);
    103     ampTrebleKnob.setDoubleClickReturnValue(true, 0.0);
    104 
    105     addAndMakeVisible(versionLabel);
    106     versionLabel.setText("v1.2", juce::NotificationType::dontSendNotification);
    107     versionLabel.setJustificationType(juce::Justification::left);
    108     versionLabel.setColour(juce::Label::textColourId, juce::Colours::white);
    109     versionLabel.setFont(font);
    110 
    111     // Size of plugin GUI
    112     setSize (500, 650);
    113 
    114     resetImages();
    115 
    116     loadFromFolder();
    117 }
    118 
    119 ProteusAudioProcessorEditor::~ProteusAudioProcessorEditor()
    120 {
    121     odDriveKnob.setLookAndFeel(nullptr);
    122     odLevelKnob.setLookAndFeel(nullptr);
    123     ampBassKnob.setLookAndFeel(nullptr);
    124     ampMidKnob.setLookAndFeel(nullptr);
    125     ampTrebleKnob.setLookAndFeel(nullptr);
    126 }
    127 
    128 //==============================================================================
    129 void ProteusAudioProcessorEditor::paint (Graphics& g)
    130 {
    131     // Workaround for graphics on Windows builds (clipping code doesn't work correctly on Windows)
    132 #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
    133     //if (processor.fw_state == 0) {
    134     //    g.drawImageAt(background_off, 0, 0);  // Debug Line: Redraw entire background image
    135     if (processor.fw_state == 1 && processor.conditioned == true) {
    136         g.drawImageAt(background_on, 0, 0);  // Debug Line: Redraw entire background image
    137     } else if (processor.fw_state == 1 && processor.conditioned == false) {
    138         g.drawImageAt(background_on_blue, 0, 0);  // Debug Line: Redraw entire background image
    139     }
    140 #else
    141 // Redraw only the clipped part of the background image
    142 
    143     juce::Rectangle<int> ClipRect = g.getClipBounds();
    144     //if (processor.fw_state == 0) {
    145     //    g.drawImage(background_off, ClipRect.getX(), ClipRect.getY(), ClipRect.getWidth(), ClipRect.getHeight(), ClipRect.getX(), ClipRect.getY(), ClipRect.getWidth(), ClipRect.getHeight());
    146     if (processor.fw_state == 1 && processor.conditioned == true) {
    147         g.drawImage(background_on, ClipRect.getX(), ClipRect.getY(), ClipRect.getWidth(), ClipRect.getHeight(), ClipRect.getX(), ClipRect.getY(), ClipRect.getWidth(), ClipRect.getHeight());
    148     } else if (processor.fw_state == 1 && processor.conditioned == false)
    149         g.drawImage(background_on_blue, ClipRect.getX(), ClipRect.getY(), ClipRect.getWidth(), ClipRect.getHeight(), ClipRect.getX(), ClipRect.getY(), ClipRect.getWidth(), ClipRect.getHeight());
    150 #endif
    151 }
    152 
    153 void ProteusAudioProcessorEditor::resized()
    154 {
    155     // This is generally where you'll want to lay out the positions of any
    156     // subcomponents in your editor..
    157 
    158     //Overall Widgets
    159     loadButton.setBounds(186, 48, 120, 24);
    160     modelSelect.setBounds(52, 11, 400, 28);
    161     //modelLabel.setBounds(197, 2, 90, 25);
    162     versionLabel.setBounds(462, 632, 60, 10);
    163     cabOnButton.setBounds(115, 233, 53, 39);
    164 
    165     // Overdrive Widgets
    166     odDriveKnob.setBounds(168, 242, 190, 190);
    167     odLevelKnob.setBounds(340, 225, 62, 62);
    168     //odFootSw.setBounds(185, 416, 175, 160);
    169 
    170     ampBassKnob.setBounds(113, 131, 62, 62);
    171     ampMidKnob.setBounds(227, 131, 62, 62);
    172     ampTrebleKnob.setBounds(340, 131, 62, 62);
    173 }
    174 
    175 bool ProteusAudioProcessorEditor::isValidFormat(File configFile)
    176 {
    177     // Read in the JSON file
    178     String path = configFile.getFullPathName();
    179     const char* char_filename = path.toUTF8();
    180 
    181     std::ifstream i2(char_filename);
    182     nlohmann::json weights_json;
    183     i2 >> weights_json;
    184 
    185     int hidden_size_temp = 0;
    186     std::string network = "";
    187 
    188     // Check that the hidden_size and unit_type fields exist and are correct
    189     if (weights_json.contains("/model_data/unit_type"_json_pointer) == true && weights_json.contains("/model_data/hidden_size"_json_pointer) == true) {
    190         // Get the input size of the JSON file
    191         int input_size_json = weights_json["/model_data/hidden_size"_json_pointer];
    192         std::string network_temp = weights_json["/model_data/unit_type"_json_pointer];
    193 
    194         network = network_temp;
    195         hidden_size_temp = input_size_json;
    196     } else {
    197         return false;
    198     }
    199     
    200     if (hidden_size_temp == 40 && network == "LSTM") {
    201         return true;
    202     } else {
    203         return false;
    204     }
    205 }
    206 
    207 void ProteusAudioProcessorEditor::loadButtonClicked()
    208 { 
    209     myChooser = std::make_unique<FileChooser> ("Select a folder to load models from",
    210                                                processor.folder,
    211                                                "*.json");
    212  
    213     auto folderChooserFlags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectDirectories | FileBrowserComponent::canSelectFiles;
    214  
    215     myChooser->launchAsync (folderChooserFlags, [this] (const FileChooser& chooser)                
    216     {
    217         if (!chooser.getResult().exists()) {
    218                 return;
    219         }
    220         processor.model_loaded = false;
    221         Array<File> files;
    222         if (chooser.getResult().existsAsFile()) { // If a file is selected
    223 
    224             if (isValidFormat(chooser.getResult())) {
    225                 processor.saved_model = chooser.getResult();
    226             }
    227 
    228             files = chooser.getResult().getParentDirectory().findChildFiles(2, false, "*.json");
    229             processor.folder = chooser.getResult().getParentDirectory();
    230 
    231         } else if (chooser.getResult().isDirectory()){ // Else folder is selected
    232             files = chooser.getResult().findChildFiles(2, false, "*.json");
    233             processor.folder = chooser.getResult();
    234         }
    235         
    236         processor.jsonFiles.clear();
    237 
    238         modelSelect.clear();
    239 
    240         if (files.size() > 0) {
    241             for (auto file : files) {
    242 
    243                 if (isValidFormat(file)) {
    244                     modelSelect.addItem(file.getFileNameWithoutExtension(), processor.jsonFiles.size() + 1);
    245                     processor.jsonFiles.push_back(file);
    246                     processor.num_models += 1;
    247                 }
    248             }
    249             if (chooser.getResult().existsAsFile()) {
    250                 
    251                 if (isValidFormat(chooser.getResult()) == true) {
    252                     modelSelect.setText(processor.saved_model.getFileNameWithoutExtension());
    253                     processor.loadConfig(processor.saved_model);
    254                 }
    255             }
    256             else {
    257                 if (!processor.jsonFiles.empty()) {
    258                     modelSelect.setSelectedItemIndex(0, juce::NotificationType::dontSendNotification);
    259                     modelSelectChanged();
    260                 }
    261             }
    262         } else {
    263             processor.saved_model = ""; // Clear the saved model since there's nothing in the dropdown
    264         }
    265     });
    266     
    267 }
    268 
    269 void ProteusAudioProcessorEditor::loadFromFolder()
    270 {
    271     processor.model_loaded = false;
    272     Array<File> files;
    273     files = processor.folder.findChildFiles(2, false, "*.json");
    274 
    275     processor.jsonFiles.clear();
    276     modelSelect.clear();
    277 
    278     if (files.size() > 0) {
    279         for (auto file : files) {
    280             
    281             if (isValidFormat(file)) {
    282                 modelSelect.addItem(file.getFileNameWithoutExtension(), processor.jsonFiles.size() + 1);
    283                 processor.jsonFiles.push_back(file);
    284                 processor.num_models += 1;
    285             }
    286         }
    287         // Try to load model from saved_model, if it doesnt exist and jsonFiles is not empty, load the first model (if it exists and is valid format)
    288         if (!processor.jsonFiles.empty()) {
    289             if (processor.saved_model.existsAsFile() && isValidFormat(processor.saved_model)) {
    290                 processor.loadConfig(processor.saved_model);
    291                 modelSelect.setText(processor.saved_model.getFileNameWithoutExtension(), juce::NotificationType::dontSendNotification);
    292             } else {
    293                 if (processor.jsonFiles[0].existsAsFile() && isValidFormat(processor.jsonFiles[0])) {
    294                     processor.loadConfig(processor.jsonFiles[0]);
    295                     modelSelect.setText(processor.jsonFiles[0].getFileNameWithoutExtension(), juce::NotificationType::dontSendNotification);
    296                 }
    297             }
    298         }
    299     }
    300 }
    301 
    302 
    303 void ProteusAudioProcessorEditor::buttonClicked(juce::Button* button)
    304 {
    305     //if (button == &odFootSw) {
    306     //    odFootSwClicked();
    307     if (button == &loadButton) {
    308         loadButtonClicked();
    309     } else if (button == &cabOnButton) {
    310         cabOnButtonClicked();
    311     }
    312 }
    313 
    314 void ProteusAudioProcessorEditor::odFootSwClicked() {
    315     //if (processor.fw_state == 0)
    316     //    processor.fw_state = 1;
    317     //else
    318     //    processor.fw_state = 0;
    319     //resetImages();
    320 }
    321 
    322 void ProteusAudioProcessorEditor::cabOnButtonClicked() {
    323     if (processor.cab_state == 0) {
    324         processor.cab_state = 1;
    325     }
    326     else {
    327         processor.cab_state = 0;
    328     }
    329     resetImages();
    330     repaint();
    331 }
    332 
    333 void ProteusAudioProcessorEditor::sliderValueChanged(Slider* slider)
    334 {
    335     // Amp
    336     if (slider == &ampBassKnob || slider == &ampMidKnob || slider == &ampTrebleKnob) {
    337         processor.set_ampEQ(ampBassKnob.getValue(), ampMidKnob.getValue(), ampTrebleKnob.getValue());
    338     }
    339 }
    340 
    341 void ProteusAudioProcessorEditor::modelSelectChanged()
    342 {
    343     const int selectedFileIndex = modelSelect.getSelectedItemIndex();
    344     if (selectedFileIndex >= 0 && selectedFileIndex < processor.jsonFiles.size() && processor.jsonFiles.empty() == false) { //check if correct 
    345         if (processor.jsonFiles[selectedFileIndex].existsAsFile() && isValidFormat(processor.jsonFiles[selectedFileIndex])) {
    346             processor.loadConfig(processor.jsonFiles[selectedFileIndex]);
    347             processor.current_model_index = selectedFileIndex;
    348             processor.saved_model = processor.jsonFiles[selectedFileIndex];
    349         }
    350     }
    351     repaint();
    352 }
    353 
    354 
    355 void ProteusAudioProcessorEditor::resetImages()
    356 {
    357     repaint();
    358     /*
    359     if (processor.fw_state == 0) {
    360         odFootSw.setImages(true, true, true,
    361             ImageCache::getFromMemory(BinaryData::footswitch_up_png, BinaryData::footswitch_up_pngSize), 1.0, Colours::transparentWhite,
    362             Image(), 1.0, Colours::transparentWhite,
    363             ImageCache::getFromMemory(BinaryData::footswitch_up_png, BinaryData::footswitch_up_pngSize), 1.0, Colours::transparentWhite,
    364             0.0);
    365     }
    366     else {
    367         odFootSw.setImages(true, true, true,
    368             ImageCache::getFromMemory(BinaryData::footswitch_down_png, BinaryData::footswitch_down_pngSize), 1.0, Colours::transparentWhite,
    369             Image(), 1.0, Colours::transparentWhite,
    370             ImageCache::getFromMemory(BinaryData::footswitch_down_png, BinaryData::footswitch_down_pngSize), 1.0, Colours::transparentWhite,
    371             0.0);
    372     }
    373     */
    374     // Set On/Off cab graphic
    375     if (processor.cab_state == 0) {
    376         cabOnButton.setImages(true, true, true,
    377             ImageCache::getFromMemory(BinaryData::cab_switch_off_png, BinaryData::cab_switch_off_pngSize), 1.0, Colours::transparentWhite,
    378             Image(), 1.0, Colours::transparentWhite,
    379             ImageCache::getFromMemory(BinaryData::cab_switch_off_png, BinaryData::cab_switch_off_pngSize), 1.0, Colours::transparentWhite,
    380             0.0);
    381     }
    382     else {
    383         cabOnButton.setImages(true, true, true,
    384             ImageCache::getFromMemory(BinaryData::cab_switch_on_png, BinaryData::cab_switch_on_pngSize), 1.0, Colours::transparentWhite,
    385             Image(), 1.0, Colours::transparentWhite,
    386             ImageCache::getFromMemory(BinaryData::cab_switch_on_png, BinaryData::cab_switch_on_pngSize), 1.0, Colours::transparentWhite,
    387             0.0);
    388     }
    389 }