NeuralPi

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

commit f1393acd2b9ad9d34a1d74ee4aadeafa5c1ea2ef
parent db8f783fe0c4bb49998b66f0a257ddbb860c6434
Author: keith <kbloemer89@gmail.com>
Date:   Sun, 11 Jul 2021 09:12:35 -0500

Initial IR select implementation

Diffstat:
MSource/CabSim.h | 9+++++++++
MSource/PluginEditor.cpp | 112++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
MSource/PluginEditor.h | 4++++
MSource/PluginProcessor.cpp | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
MSource/PluginProcessor.h | 13+++++++++++++
5 files changed, 170 insertions(+), 21 deletions(-)

diff --git a/Source/CabSim.h b/Source/CabSim.h @@ -53,6 +53,15 @@ public: processorChain.reset(); } + void load(File irFile) noexcept + { + auto& convolution = processorChain.template get<convolutionIndex>(); + convolution.loadImpulseResponse(irFile, + juce::dsp::Convolution::Stereo::yes, + juce::dsp::Convolution::Trim::no, + 1024); + } + private: //============================================================================== enum diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp @@ -80,6 +80,22 @@ NeuralPiAudioProcessorEditor::NeuralPiAudioProcessorEditor (NeuralPiAudioProcess loadButton.setColour(juce::Label::textColourId, juce::Colours::black); loadButton.addListener(this); + addAndMakeVisible(irSelect); + irSelect.setColour(juce::Label::textColourId, juce::Colours::black); + int i = 1; + for (const auto& jsonFile : processor.irFiles) { + irSelect.addItem(jsonFile.getFileNameWithoutExtension(), i); + i += 1; + } + irSelect.onChange = [this] {irSelectChanged(); }; + irSelect.setSelectedItemIndex(processor.current_ir_index, juce::NotificationType::dontSendNotification); + irSelect.setScrollWheelEnabled(true); + + addAndMakeVisible(loadIR); + loadIR.setButtonText("Import IR"); + loadIR.setColour(juce::Label::textColourId, juce::Colours::black); + loadIR.addListener(this); + //gainSliderAttach = std::make_unique<AudioProcessorValueTreeState::SliderAttachment>(processor.treeState, GAIN_ID, ampGainKnob); addAndMakeVisible(ampGainKnob); //ampGainKnob.setLookAndFeel(&ampSilverKnobLAF); @@ -388,7 +404,7 @@ NeuralPiAudioProcessorEditor::NeuralPiAudioProcessorEditor (NeuralPiAudioProcess connectSender(); // Size of plugin GUI - setSize(276, 430); + setSize(276, 455); } @@ -415,37 +431,40 @@ void NeuralPiAudioProcessorEditor::resized() // This is generally where you'll want to lay out the positions of any // subcomponents in your editor.. modelSelect.setBounds(19, 10, 234, 25); - loadButton.setBounds(19, 42, 100, 25); + loadButton.setBounds(19, 74, 100, 25); modelKnob.setBounds(140, 40, 75, 95); + irSelect.setBounds(19, 42, 234, 25); + loadIR.setBounds(125, 74, 100, 25); + // Amp Widgets - ampGainKnob.setBounds(15, 90, 75, 95); - ampMasterKnob.setBounds(100, 90, 75, 95); - ampBassKnob.setBounds(15, 225, 75, 95); - ampMidKnob.setBounds(100, 225, 75, 95); - ampTrebleKnob.setBounds(185, 225, 75, 95); - ampPresenceKnob.setBounds(185, 90, 75, 95); - - GainLabel.setBounds(11, 78, 80, 10); - LevelLabel.setBounds(98, 78, 80, 10); - BassLabel.setBounds(11, 213, 80, 10); - MidLabel.setBounds(97, 213, 80, 10); - TrebleLabel.setBounds(183, 213, 80, 10); - PresenceLabel.setBounds(183, 78, 80, 10); + ampGainKnob.setBounds(15, 120, 75, 95); + ampMasterKnob.setBounds(100, 120, 75, 95); + ampBassKnob.setBounds(15, 250, 75, 95); + ampMidKnob.setBounds(100, 250, 75, 95); + ampTrebleKnob.setBounds(185, 250, 75, 95); + ampPresenceKnob.setBounds(185, 120, 75, 95); + + GainLabel.setBounds(11, 108, 80, 10); + LevelLabel.setBounds(98, 108, 80, 10); + BassLabel.setBounds(11, 238, 80, 10); + MidLabel.setBounds(97, 238, 80, 10); + TrebleLabel.setBounds(183, 238, 80, 10); + PresenceLabel.setBounds(183, 108, 80, 10); addAndMakeVisible(ampNameLabel); ampNameField.setEditable(true, true, true); addAndMakeVisible(ampNameField); // IP controls: - ipField.setBounds(150, 340, 100, 25); - ipLabel.setBounds(15, 340, 150, 25); + ipField.setBounds(150, 365, 100, 25); + ipLabel.setBounds(15, 365, 150, 25); // Port controls: - outPortNumberLabel.setBounds(15, 370, 150, 25); - outPortNumberField.setBounds(160, 370, 75, 25); - inPortNumberLabel.setBounds(15, 400, 150, 25); - inPortNumberField.setBounds(160, 400, 75, 25); + outPortNumberLabel.setBounds(15, 395, 150, 25); + outPortNumberField.setBounds(160, 395, 75, 25); + inPortNumberLabel.setBounds(15, 425, 150, 25); + inPortNumberField.setBounds(160, 425, 75, 25); } void NeuralPiAudioProcessorEditor::modelSelectChanged() @@ -460,6 +479,18 @@ void NeuralPiAudioProcessorEditor::modelSelectChanged() //modelKnob.setValue(processor.current_model_index); } +void NeuralPiAudioProcessorEditor::irSelectChanged() +{ + const int selectedFileIndex = irSelect.getSelectedItemIndex(); + if (selectedFileIndex >= 0 && selectedFileIndex < processor.jsonFiles.size()) { + processor.loadIR(processor.irFiles[selectedFileIndex]); + processor.current_ir_index = irSelect.getSelectedItemIndex(); + } + //auto newValue = static_cast<float>(processor.current_ir_index / (processor.num_irs - 1.0)); + //modelKnob.setValue(newValue); + //modelKnob.setValue(processor.current_model_index); +} + void NeuralPiAudioProcessorEditor::loadButtonClicked() { FileChooser chooser("Select one or more .json tone files to import", @@ -495,12 +526,51 @@ void NeuralPiAudioProcessorEditor::loadButtonClicked() } } +void NeuralPiAudioProcessorEditor::loadIRClicked() +{ + FileChooser chooser("Select one or more .wav IR files to import", + {}, + "*.wav"); + if (chooser.browseForMultipleFilesToOpen()) + { + int import_fail = 1; + Array<File> files = chooser.getResults(); + for (auto file : files) { + File fullpath = processor.userAppDataDirectory_irs.getFullPathName() + "/" + file.getFileName(); + bool b = fullpath.existsAsFile(); + if (b == false) { + + processor.loadIR(file); + fname = file.getFileName(); + processor.loaded_ir = file; + processor.loaded_ir_name = fname; + processor.custom_ir = 1; + + // Copy selected file to model directory and load into dropdown menu + bool a = file.copyFileTo(fullpath); + if (a == true) { + irSelect.addItem(file.getFileNameWithoutExtension(), processor.irFiles.size() + 1); + irSelect.setSelectedItemIndex(processor.irFiles.size(), juce::NotificationType::dontSendNotification); + processor.irFiles.push_back(file); + //processor.num_models += 1; + } + // Sort jsonFiles alphabetically + std::sort(processor.irFiles.begin(), processor.irFiles.end()); + } + } + } +} + void NeuralPiAudioProcessorEditor::buttonClicked(juce::Button* button) { if (button == &loadButton) { loadButtonClicked(); } + else + { + loadIRClicked(); + } } diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h @@ -75,6 +75,7 @@ private: //ImageButton ampOnButton; //ImageButton ampLED; ComboBox modelSelect; + ComboBox irSelect; Slider ampBassKnob; Slider ampMidKnob; Slider ampTrebleKnob; @@ -91,10 +92,13 @@ private: File model_folder; TextButton loadButton; + TextButton loadIR; juce::String fname; virtual void buttonClicked(Button* button) override; void modelSelectChanged(); void loadButtonClicked(); + void irSelectChanged(); + void loadIRClicked(); virtual void sliderValueChanged(Slider* slider) override; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp @@ -37,6 +37,12 @@ NeuralPiAudioProcessor::NeuralPiAudioProcessor() // Sort jsonFiles alphabetically std::sort(jsonFiles.begin(), jsonFiles.end()); + if (irFiles.size() > 0) { + loadIR(irFiles[current_ir_index]); + } + // Sort irFiles alphabetically + std::sort(irFiles.begin(), irFiles.end()); + // initialize parameters: addParameter(gainParam = new AudioParameterFloat(GAIN_ID, GAIN_NAME, NormalisableRange<float>(0.0f, 1.0f, 0.01f), 0.5f)); addParameter(masterParam = new AudioParameterFloat(MASTER_ID, MASTER_NAME, NormalisableRange<float>(0.0f, 1.0f, 0.01f), 0.5f)); @@ -276,6 +282,18 @@ void NeuralPiAudioProcessor::loadConfig(File configFile) this->suspendProcessing(false); } +void NeuralPiAudioProcessor::loadIR(File irFile) +{ + this->suspendProcessing(true); + ir_loaded = 1; + //String path = irFile.getFullPathName(); + //char_filename = path.toUTF8(); + // TODO Add check here for invalid files + cabSimIR.load(irFile); + + this->suspendProcessing(false); +} + void NeuralPiAudioProcessor::resetDirectory(const File& file) { jsonFiles.clear(); @@ -288,6 +306,18 @@ void NeuralPiAudioProcessor::resetDirectory(const File& file) } } +void NeuralPiAudioProcessor::resetDirectoryIR(const File& file) +{ + irFiles.clear(); + if (file.isDirectory()) + { + juce::Array<juce::File> results; + file.findChildFiles(results, juce::File::findFiles, false, "*.wav"); + for (int i = results.size(); --i >= 0;) + irFiles.push_back(File(results.getReference(i).getFullPathName())); + } +} + void NeuralPiAudioProcessor::addDirectory(const File& file) { if (file.isDirectory()) @@ -302,6 +332,20 @@ void NeuralPiAudioProcessor::addDirectory(const File& file) } } +void NeuralPiAudioProcessor::addDirectoryIR(const File& file) +{ + if (file.isDirectory()) + { + juce::Array<juce::File> results; + file.findChildFiles(results, juce::File::findFiles, false, "*.wav"); + for (int i = results.size(); --i >= 0;) + { + irFiles.push_back(File(results.getReference(i).getFullPathName())); + num_irs = num_irs + 1.0; + } + } +} + void NeuralPiAudioProcessor::setupDataDirectories() { // User app data directory @@ -309,6 +353,7 @@ void NeuralPiAudioProcessor::setupDataDirectories() File userAppDataTempFile_tones = userAppDataDirectory_tones.getChildFile("tmp.pdl"); + File userAppDataTempFile_irs = userAppDataDirectory_irs.getChildFile("tmp.pdl"); // Create (and delete) temp file if necessary, so that user doesn't have // to manually create directories @@ -326,9 +371,17 @@ void NeuralPiAudioProcessor::setupDataDirectories() userAppDataTempFile_tones.deleteFile(); } + if (!userAppDataDirectory_irs.exists()) { + userAppDataTempFile_irs.create(); + } + if (userAppDataTempFile_irs.existsAsFile()) { + userAppDataTempFile_irs.deleteFile(); + } + // Add the tones directory and update tone list addDirectory(userAppDataDirectory_tones); + addDirectoryIR(userAppDataDirectory_irs); } void NeuralPiAudioProcessor::installTones() diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h @@ -79,6 +79,7 @@ public: bool compareFunction(juce::File a, juce::File b); int getModelIndex(float model_param); void loadConfig(File configFile); + void loadIR(File irFile); void setupDataDirectories(); void installTones(); @@ -89,11 +90,15 @@ public: float decibelToLinear(float dbValue); void addDirectory(const File& file); + void addDirectoryIR(const File& file); void resetDirectory(const File& file); + void resetDirectoryIR(const File& file); std::vector<File> jsonFiles; + std::vector<File> irFiles; File currentDirectory = File::getCurrentWorkingDirectory().getFullPathName(); File userAppDataDirectory = File::getSpecialLocation(File::userDocumentsDirectory).getChildFile(JucePlugin_Manufacturer).getChildFile(JucePlugin_Name); File userAppDataDirectory_tones = userAppDataDirectory.getFullPathName() + "/tones"; + File userAppDataDirectory_irs = userAppDataDirectory.getFullPathName() + "/irs"; // Pedal/amp states int amp_state = 1; // 0 = off, 1 = on @@ -106,6 +111,14 @@ public: float num_models = 0.0; int model_index = 0; // Used in processBlock when converting slider param to model index + juce::String loaded_ir_name; + float num_irs = 0.0; + int ir_loaded = 0; + int custom_ir = 0; // 0 = custom tone loaded, 1 = default channel tone + File loaded_ir; + + int current_ir_index = 0; + RT_LSTM LSTM; private: