NeuralPi

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

commit 811bd50a819f38911d80664c10a55c0bf48f2ae0
parent 04365a1e8e23e0e50a5d3296e742432082f84701
Author: Keith Bloemer <32459398+GuitarML@users.noreply.github.com>
Date:   Sat, 19 Jun 2021 10:31:54 -0500

Merge pull request #5 from GuitarML/osc-user-controls

Osc user controls
Diffstat:
M.gitmodules | 9+++++++++
ACMakeLists.txt | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
MNeuralPi.jucer | 13++++++++++---
MREADME.md | 49++++++++++++++++++++++++++++++-------------------
ASource/AmpOSCReceiver.h | 154+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASource/CMakeLists.txt | 15+++++++++++++++
ASource/Eq4Band.cpp | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ASource/Eq4Band.h | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
DSource/ModelLoader.h | 32--------------------------------
MSource/PluginEditor.cpp | 675+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
MSource/PluginEditor.h | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
MSource/PluginProcessor.cpp | 110+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
MSource/PluginProcessor.h | 50++++++++++++++++++++++++++++++++++++++------------
Ainstallers/mac/Intro.txt | 1+
Ainstallers/mac/NeuralPi.pkgproj | 1999+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainstallers/mac/build_mac_installer.sh | 38++++++++++++++++++++++++++++++++++++++
Ainstallers/windows/NeuralPi_Install_Script.iss | 221+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainstallers/windows/build_win_installer.sh | 20++++++++++++++++++++
Amac_builds.sh | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amodules/CMakeLists.txt | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amodules/DISTRHO-JUCE | 1+
Amodules/JUCE | 1+
Amodules/cmake/SubprojectVersion.cmake | 20++++++++++++++++++++
Amodules/cmake/WarningFlags.cmake | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Amodules/json | 1+
Aresources/CMakeLists.txt | 10++++++++++
Aresources/logo.png | 0
Aresources/neuralpi.ico | 0
Mresources/neuralpi_pic.jpg | 0
Aresources/npi_background.jpg | 0
Ascripts/update_models.bat | 31+++++++++++++++++++++++++++++++
Ascripts/update_models.sh | 30++++++++++++++++++++++++++++++
Avalidate.sh | 41+++++++++++++++++++++++++++++++++++++++++
Awin_builds.sh | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
34 files changed, 3924 insertions(+), 126 deletions(-)

diff --git a/.gitmodules b/.gitmodules @@ -1,3 +1,12 @@ [submodule "modules/RTNeural"] path = modules/RTNeural url = https://github.com/jatinchowdhury18/RTNeural +[submodule "modules/JUCE"] + path = modules/JUCE + url = https://github.com/juce-framework/JUCE.git +[submodule "modules/DISTRHO-JUCE"] + path = modules/DISTRHO-JUCE + url = https://github.com/Chowdhury-DSP/DISTRHO-JUCE.git +[submodule "modules/json"] + path = modules/json + url = https://github.com/nlohmann/json.git diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -0,0 +1,52 @@ +cmake_minimum_required(VERSION 3.15) +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.12" CACHE STRING "Minimum OS X deployment target") +project(NeuralPi VERSION 1.1.0) + +set(CMAKE_CXX_STANDARD 17) + +set(RTNEURAL_XSIMD ON CACHE BOOL "Use RTNeural with this backend" FORCE) +add_subdirectory(modules/RTNeural) + +add_subdirectory(modules) +include_directories(modules) + + +juce_add_plugin(NeuralPi + COMPANY_NAME GuitarML + PLUGIN_MANUFACTURER_CODE GtML + PLUGIN_CODE Npi3 + FORMATS AU VST3 Standalone LV2 + ProductName "NeuralPi" + LV2_URI https://github.com/GuitarML/NeuralPi + ICON_BIG resources/logo.png + MICROPHONE_PERMISSION_ENABLED TRUE +) + +# create JUCE header +juce_generate_juce_header(NeuralPi) + +# add sources +add_subdirectory(Source) +include_directories(Source) +add_subdirectory(resources) + +target_compile_definitions(NeuralPi + PUBLIC + JUCE_DISPLAY_SPLASH_SCREEN=0 + JUCE_REPORT_APP_USAGE=0 + JUCE_WEB_BROWSER=0 + JUCE_USE_CURL=0 + JUCE_VST3_CAN_REPLACE_VST2=0 +) + +target_link_libraries(NeuralPi PUBLIC + juce_plugin_modules +) + +# we need these flags for notarization on MacOS +option(MACOS_RELEASE "Set build flags for MacOS Release" OFF) +if(MACOS_RELEASE) + message(STATUS "Setting MacOS release flags...") + set_target_properties(NeuralPi_Standalone PROPERTIES + XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME YES) +endif() diff --git a/NeuralPi.jucer b/NeuralPi.jucer @@ -1,18 +1,24 @@ <?xml version="1.0" encoding="UTF-8"?> -<JUCERPROJECT name="NeuralPi" companyName="GuitarML" version="1.0.0" companyWebsite="http://juce.com" +<JUCERPROJECT name="NeuralPi" companyName="GuitarML" version="1.0.0" companyWebsite="https://guitarml.com" defines="PIP_JUCE_EXAMPLES_DIRECTORY=L2hvbWUvaWxpYXMvd29ya3NwYWNlcy9KVUNFL2V4YW1wbGVz" - projectType="audioplug" pluginManufacturer="JUCE" pluginAUIsSandboxSafe="1" + projectType="audioplug" pluginManufacturer="GuitarML" pluginAUIsSandboxSafe="1" pluginFormats="buildStandalone,buildVST3" id="vXK47v" jucerFormatVersion="1" displaySplashScreen="1"> <MAINGROUP id="zI9r2C" name="NeuralPi"> <GROUP id="{810D2D07-FCAC-0F25-F63F-DCF49FEE10C7}" name="Resources"> <FILE id="V1lm9D" name="bj_model_best.json" compile="0" resource="1" file="models/bj_model_best.json"/> + <FILE id="JubKNX" name="npi_background.jpg" compile="0" resource="1" + file="resources/npi_background.jpg"/> <FILE id="lkEjdY" name="ts9_model_best.json" compile="0" resource="1" file="models/ts9_model_best.json"/> </GROUP> <GROUP id="{70CE292C-E9C5-C029-B95A-F7DF41E5F74C}" name="Source"> + <FILE id="VgCJPH" name="AmpOSCReceiver.h" compile="0" resource="0" + file="Source/AmpOSCReceiver.h"/> + <FILE id="s1HQuK" name="Eq4Band.cpp" compile="1" resource="0" file="Source/Eq4Band.cpp"/> + <FILE id="xtLEtv" name="Eq4Band.h" compile="0" resource="0" file="Source/Eq4Band.h"/> <FILE id="hNjQV9" name="PluginEditor.cpp" compile="1" resource="0" file="Source/PluginEditor.cpp"/> <FILE id="BweFTe" name="PluginEditor.h" compile="0" resource="0" file="Source/PluginEditor.h"/> @@ -68,7 +74,8 @@ </LINUX_MAKE> <VS2019 targetFolder="Builds/VisualStudio2019"> <CONFIGURATIONS> - <CONFIGURATION isDebug="1" name="Debug" targetName="NeuralPi"/> + <CONFIGURATION isDebug="1" name="Debug" targetName="NeuralPi" headerPath="C:\Users\rache\Desktop\dev\json-develop\include&#10;C:\Users\rache\Desktop\dev\NeuralPi\modules\RTNeural&#10;C:\Users\rache\Desktop\dev\NeuralPi\modules\RTNeural\modules\xsimd\include" + defines="USE_XSIMD=1"/> <CONFIGURATION isDebug="0" name="Release" targetName="NeuralPi" headerPath="C:\Users\rache\Desktop\dev\json-develop\include&#10;C:\Users\rache\Desktop\dev\NeuralPi\modules\RTNeural&#10;C:\Users\rache\Desktop\dev\NeuralPi\modules\RTNeural\modules\xsimd\include" defines="USE_XSIMD=1"/> </CONFIGURATIONS> diff --git a/README.md b/README.md @@ -1,6 +1,6 @@ # NeuralPi -NeuralPi is a guitar pedal using neural networks to emulate real amps and pedals on a Raspberry Pi 4. The NeuralPi software is a VST3 plugin built with JUCE, which can be run as a normal audio plugin or cross-compiled to run on the Raspberry Pi 4 with [Elk Audio OS](https://elk.audio/). NeuralPi is intended as a bare-bones plugin to build on. The pedal runs high quality amp/pedal models on an economical DIY setup, costing around $120 for hardware to build yourself. <br> +NeuralPi is a guitar pedal using neural networks to emulate real amps and pedals on a Raspberry Pi 4. The NeuralPi software is a VST3 plugin built with JUCE, which can be run as a normal audio plugin or cross-compiled to run on the Raspberry Pi 4 with [Elk Audio OS](https://elk.audio/). The NeuralPi includes model selection, EQ, and gain/volume controls from a remote instance of the plugin over WiFi. The pedal runs high quality amp/pedal models on an economical DIY setup, costing around $120 for hardware to build yourself. <br> Check out a video demo on [YouTube](https://www.youtube.com/watch?v=_3zFD6h6Wrc)<br> Check out the step by step build guide published on [Towards Data Science](https://towardsdatascience.com/neural-networks-for-real-time-audio-raspberry-pi-guitar-pedal-bded4b6b7f31) @@ -16,34 +16,29 @@ There are four main components to the guitar pedal: 4. NeuralPi VST3 plugin ![app](https://github.com/GuitarML/NeuralPi/blob/main/resources/neuralpi_pic.jpg) -<br>This is the normal VST3 plugin, which can be compiled for Windows/Mac/Linux using JUCE. It includes a model import feature, dropdown menu, and gain/level controls. The plugin compiled for Elk Audio OS is headless, meaning there is no GUI. +<br>This is the normal plugin, available for Windows (Standalone, VST3) and Mac (Standalone, AU, VST3) ## Installing the plugin -For the cross-compiled Raspberry Pi / Elk Audio OS compatible VST3 plugin, download [here](https://github.com/GuitarML/NeuralPi/releases/tag/v1.0). +See the Release page for the cross-compiled Raspberry Pi / Elk Audio OS compatible VST3 plugin and Win/Mac installers. WARNING: The audio output of the NeuralPi is at line level. Guitar amplifiers expect a low level electric guitar signal (instrument level). When using the NeuralPi with a guitar amp, start with the master volume at 0 and SLOWLY increase from there. -## Changing Models +## Adding New Models -The NeuralPi running on Elk will load one model per instance of the plugin. By default this is the "bj_model_best.json" file. Model select controls will be added in the future, but for now you must overwrite this file to load different models. On Elk Audio OS, the models will be installed here after running the plugin for the first time: +Once your NeuralPi is set up, you can add new models from a remote computer using the following steps: -```/home/mind/.config/JUCE/NeuralPi/tones``` - -Backup the Blues Junior amp model with this: - -```cp bj_model_best.json bj_backup.json``` - -And overwrite the original model to try a new one: - -```cp ts9_model_best.json bj_model_best.json``` - -If you need to revert back to the original state, remove the ```/tones``` folder and restart the plugin. The "bj_model_best.json" is a Fender Blues Jr. amplifier at full gain, and the "ts9_model_best.json" is the Ibanez TS9 overdrive pedal at full drive. +1. From the remote computer, run the plugin and add new models using the "Import Tone" button. Optionally, you can manually add new json files to the ```Documents/GuitarML/Chameleon/tones``` directory. + Note: The "tones" directory is created the first time you run NeuralPi. +2. Turn on your WiFi enabled NeuralPi (see [Elk documentation](https://elk-audio.github.io/elk-docs/html/documents/working_with_elk_board.html?highlight=wifi#connecting-to-your-board) for connecting the Raspberry Pi to a local WiFi network) +3. Download the ```update_models.bat```(Windows) or ```update_models.sh```(Mac/Linux) to your remote computer. These scripts are located in the "scripts/" directory of this repository. You must change the ```rpi_ip_address``` and ```host_model_path``` to the Raspberry Pi's IP address and path to your json tones (on remote computer). The json files will be first copied from the remote computer to the NeuralPi, and then back from the NeuralPi to the remote computer. This allows updating models from the NeuralPi when you connect a new remote computer. +4. From the remote computer connected to the same local WiFi network as NeuralPi, run the ```update_models.bat```(Windows) or ```update_models.sh```(Mac/Linux) from a cmd terminal. <br><br> +Note: It is important that all models files have unique names with no spaces. <br> +Note: Ensure from the terminal output that you were able to connect over WiFi, and that the model files were copied properly. <br><br> +6. Restart both the NeuralPi and the remote instance of the NeuralPi plugin. From the remote NeuralPi GUI, enter the Raspberry Pi's IP address. As long as both devices are connected to the local WiFi network, you will be able select models from the NeuralPi plugin dropdown list to change models running on the Raspberry Pi. ## To Do -Currently, the NeuralPi plugin running on Elk OS has no user controls. It runs a single model that can be swapped out before running the plugin. The next step is to add user controls via OSC messages, so that a remote instance of the plugin can control the NeuralPi over Wifi. These controls will include Gain/Volume, EQ, and model selection. - Elk Audio OS also supports physical controls through [Sensei](https://github.com/elk-audio/sensei). Gain/Volume and EQ knobs can be added, as well as a LCD screen for selecting different models. One could build an actual guitar pedal with NeuralPi and any number of other digital effects and controls. While running PyTorch locally on the Raspberry Pi might be a stretch, it is fully capable of recording high quality audio with the HiFiBerry hat. Implement a capture feature by automating the recording of input/output samples, pushing to remote computer for training, then updating the Pi with the newly trained model. @@ -62,7 +57,23 @@ The HiFiBerry DAC+ADC card used for this project provides 192kHz/24bit Analog-to To build the plugin for use on the Raspberry Pi with Elk Audio OS, see the official [Elk Audio Documentation](https://elk-audio.github.io/elk-docs/html/documents/building_plugins_for_elk.html#vst-plugins-using-juce) -To build for Windows/Mac/Linux: +### Build with Cmake + +```bash +# Clone the repository +$ git clone https://github.com/GuitarML/NeuralPi.git +$ cd NeuralPi + +# initialize and set up submodules +$ git submodule update --init --recursive + +# build with CMake +$ cmake -Bbuild +$ cmake --build build --config Release +``` +The binaries will be located in `NeuralPi/build/NeuralPi_artefacts/` + +### Build with Projucer 1. Clone or download this repository. 2. Download and install [JUCE](https://juce.com/) This project uses the "Projucer" application from the JUCE website. diff --git a/Source/AmpOSCReceiver.h b/Source/AmpOSCReceiver.h @@ -0,0 +1,154 @@ +#include "../JuceLibraryCode/JuceHeader.h" + +#pragma once + +class AmpOSCReceiver : + private OSCReceiver, + private OSCReceiver::Listener <OSCReceiver::MessageLoopCallback> +{ +public: + AmpOSCReceiver() + { + changePort (defaultPort); + + addListener (this); + } + + Value& getGainValue() + { + return gainValue; + } + + Value& getMasterValue() + { + return masterValue; + } + + Value& getBassValue() + { + return bassValue; + } + + Value& getMidValue() + { + return midValue; + } + + Value& getTrebleValue() + { + return trebleValue; + } + + Value& getPresenceValue() + { + return presenceValue; + } + + Value& getModelValue() + { + return modelValue; + } + + void changePort (int port) + { + if (! connect (port)) + { + connected = false; + DBG ("Connection Failed"); + } + else + { + connected = true; + DBG("Connection Succeeded"); + } + } + + void updateAmpName (String name) + { + ampName = name; + buildAddressPatterns(); + } + + bool isConnected() + { + return connected; + } + +private: + void buildAddressPatterns() + { + gainAddressPattern = "/parameter/" + ampName + "/Gain"; + masterAddressPattern = "/parameter/" + ampName + "/Master"; + bassAddressPattern = "/parameter/" + ampName + "/Bass"; + midAddressPattern = "/parameter/" + ampName + "/Mid"; + trebleAddressPattern = "/parameter/" + ampName + "/Treble"; + presenceAddressPattern = "/parameter/" + ampName + "/Presence"; + modelAddressPattern = "/parameter/" + ampName + "/Model"; + } + + void oscMessageReceived(const OSCMessage& message) override + { + DBG("Message Received: "); + + if (message.size() == 1 && message[0].isFloat32()) + { + DBG(" value " + String(message[0].getFloat32()) + " to AP " + message.getAddressPattern().toString()); + + if (message.getAddressPattern().matches(gainAddressPattern)) + { + gainValue.setValue(jlimit(0.0f, 1.0f, message[0].getFloat32())); + } + else if (message.getAddressPattern().matches(masterAddressPattern)) + { + masterValue.setValue(jlimit(0.0f, 1.0f, message[0].getFloat32())); + } + + if (message.getAddressPattern().matches(bassAddressPattern)) + { + bassValue.setValue(jlimit(0.0f, 1.0f, message[0].getFloat32())); + } + else if (message.getAddressPattern().matches(midAddressPattern)) + { + midValue.setValue(jlimit(0.0f, 1.0f, message[0].getFloat32())); + } + + if (message.getAddressPattern().matches(trebleAddressPattern)) + { + trebleValue.setValue(jlimit(0.0f, 1.0f, message[0].getFloat32())); + } + else if (message.getAddressPattern().matches(presenceAddressPattern)) + { + presenceValue.setValue(jlimit(0.0f, 1.0f, message[0].getFloat32())); + } + else if (message.getAddressPattern().matches(modelAddressPattern)) + { + modelValue.setValue(jlimit(0.0f, 1.0f, message[0].getFloat32())); + } + + } + } + + int defaultPort {25024}; + + String ampName {"NeuralPi"}; + String gainAddressPattern {"/parameter/elk_juce_example/Gain"}; + String masterAddressPattern {"/parameter/elk_juce_example/Master"}; + String bassAddressPattern {"/parameter/elk_juce_example/Bass"}; + String midAddressPattern {"/parameter/elk_juce_example/Mid"}; + String trebleAddressPattern {"/parameter/elk_juce_example/Treble"}; + String presenceAddressPattern {"/parameter/elk_juce_example/Presence"}; + String modelAddressPattern {"/parameter/elk_juce_example/Model"}; + + Value gainValue {0.5f}; + Value masterValue {0.5f}; + Value bassValue {0.5f}; + Value midValue {0.5f}; + Value trebleValue {0.5f}; + Value presenceValue {0.5f}; + + Value modelValue {0.0f}; + + bool connected = false; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AmpOSCReceiver) +}; diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt @@ -0,0 +1,15 @@ +#add_subdirectory(headless) + +target_sources(NeuralPi PRIVATE + AmpOSCReceiver.h + Eq4Band.cpp + Eq4Band.h + PluginEditor.cpp + PluginEditor.h + PluginProcessor.cpp + PluginProcessor.h + RTNeuralLSTM.cpp + RTNeuralLSTM.h +) + +#target_precompile_headers(NeuralPi PRIVATE pch.h) diff --git a/Source/Eq4Band.cpp b/Source/Eq4Band.cpp @@ -0,0 +1,76 @@ +/* + ============================================================================== + + Eq4Band + + ============================================================================== +*/ + +#include "Eq4Band.h" + +Eq4Band::Eq4Band() +{ + setParameters(0.0, 0.0, 0.0, 0.0); +} + +void Eq4Band::process (const float* inData, float* outData, + MidiBuffer& midiMessages, + const int numSamples, + const int numInputChannels, + const int sampleRate) +{ + // Reset params if new sampleRate detected + if (srate != sampleRate) { + srate = sampleRate; + resetSampleRate(); + } + for (int sample = 0; sample < numSamples; ++sample) { + spl0 = inData[sample]; + s0 = spl0; + low0 = (tmplMID = a0MID * s0 - b1MID * tmplMID + cDenorm); + spl0 = (tmplLOW = a0LOW * low0 - b1LOW * tmplLOW + cDenorm); + lowS0 = low0 - spl0; + hi0 = s0 - low0; + midS0 = (tmplHI = a0HI * hi0 - b1HI * tmplHI + cDenorm); + highS0 = hi0 - midS0; + spl0 = (spl0 * lVol + lowS0 * lmVol + midS0 * hmVol + highS0 * hVol);// * outVol; + + outData[sample] = spl0; + } +} + +void Eq4Band::setParameters(float bass_slider, float mid_slider, float treble_slider, float presence_slider) +{ + lVol = exp(bass_slider / cAmpDB); + lmVol = exp(mid_slider / cAmpDB); + hmVol = exp(treble_slider / cAmpDB); + hVol = exp(presence_slider / cAmpDB); + outVol = exp(0.0 / cAmpDB); + + xHI = exp(-2.0 * pi * treble_frequency / srate); + a0HI = 1.0 - xHI; + b1HI = -xHI; + + xMID = exp(-2.0 * pi * mid_frequency / srate); + a0MID = 1.0 - xMID; + b1MID = -xMID; + + xLOW = exp(-2.0 * pi * bass_frequency / srate); + a0LOW = 1.0 - xLOW; + b1LOW = -xLOW; +} + +void Eq4Band::resetSampleRate() +{ + xHI = exp(-2.0 * pi * treble_frequency / srate); + a0HI = 1.0 - xHI; + b1HI = -xHI; + + xMID = exp(-2.0 * pi * mid_frequency / srate); + a0MID = 1.0 - xMID; + b1MID = -xMID; + + xLOW = exp(-2.0 * pi * bass_frequency / srate); + a0LOW = 1.0 - xLOW; + b1LOW = -xLOW; +} +\ No newline at end of file diff --git a/Source/Eq4Band.h b/Source/Eq4Band.h @@ -0,0 +1,68 @@ +/* + ============================================================================== + + Eq4Band + + ============================================================================== +*/ + +#pragma once + +#include "../JuceLibraryCode/JuceHeader.h" + + +//============================================================================== + +class Eq4Band +{ +public: + Eq4Band(); + void process (const float* inData, float* outData, MidiBuffer& midiMessages, const int numSamples, const int numInputChannels, const int sampleRate); + void setParameters(float bass_slider, float mid_slider, float treble_slider, float presence_slider); + void resetSampleRate(); + +private: + // Tone Knob related variables + float cDenorm = 10e-30; + float cAmpDB = 8.65617025; + + int bass_frequency = 200; + int mid_frequency = 2000; + int treble_frequency = 5000; + //int presence_frequency = 5500; + + int srate = 44100; // Set default + + float pi = 3.1415926; + + float outVol; + float xHI = 0.0;// + float a0HI = 0.0;// + float b1HI = 0.0; + float xMID = 0.0; + float a0MID = 0.0; + float b1MID = 0.0; + float xLOW = 0.0; + float a0LOW = 0.0; + float b1LOW = 0.0; + + float lVol = 0.0; + float lmVol = 0.0; + float hmVol = 0.0; + float hVol = 0.0; + + float s0 = 0.0; + float low0 = 0.0; + float tmplMID = 0.0; + float spl0 = 0.0; + float hi0 = 0.0; + float midS0 = 0.0; + float highS0 = 0.0; + float tmplHI = 0.0; + float lowS0 = 0.0; + float tmplLOW = 0.0; + + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Eq4Band) +}; diff --git a/Source/ModelLoader.h b/Source/ModelLoader.h @@ -1,31 +0,0 @@ -#include <iostream> -#include <nlohmann/json.hpp> -#include "NumCpp.hpp" -#include <string> - -class ModelLoader -{ - -public: - ModelLoader(); - nc::NdArray<float> vector_to_nc(std::vector<float> in_vec); - nc::NdArray<float> matrix_to_nc(std::vector<std::vector<float>> in_mat); - - void load_json(const char *filename); - - int hidden_size = 32; - - nc::NdArray<float> lstm_bias_ih_nc; - nc::NdArray<float> lstm_weights_ih_nc; - - nc::NdArray<float> lstm_bias_hh_nc; - nc::NdArray<float> lstm_weights_hh_nc; - - nc::NdArray<float> lstm_bias_nc; - - nc::NdArray<float> dense_bias_nc; - nc::NdArray<float> dense_weights_nc; - -private: - -}; -\ No newline at end of file diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp @@ -10,6 +10,7 @@ #include "PluginProcessor.h" #include "PluginEditor.h" +#include "AmpOSCReceiver.h" #include <stdio.h> #include <fstream> #include <iostream> @@ -22,6 +23,47 @@ NeuralPiAudioProcessorEditor::NeuralPiAudioProcessorEditor (NeuralPiAudioProcess // Make sure that before the constructor has finished, you've set the // editor's size to whatever you need it to + //addAndMakeVisible(modelKnob); + //ampGainKnob.setLookAndFeel(&ampSilverKnobLAF); + modelKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20); + modelKnob.setNumDecimalPlacesToDisplay(1); + modelKnob.addListener(this); + //modelKnob.setRange(0, processor.jsonFiles.size() - 1); + modelKnob.setRange(0.0, 1.0); + modelKnob.setValue(0.0); + modelKnob.setSliderStyle(juce::Slider::SliderStyle::RotaryVerticalDrag); + modelKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::NoTextBox, false, 50, 20); + modelKnob.setNumDecimalPlacesToDisplay(1); + modelKnob.setDoubleClickReturnValue(true, 0.0); + + auto modelValue = getParameterValue(modelName); + Slider& modelSlider = getModelSlider(); + modelSlider.setValue(modelValue, NotificationType::dontSendNotification); + + modelKnob.onValueChange = [this] + { + const float sliderValue = static_cast<float> (getModelSlider().getValue()); + const float modelValue = getParameterValue(modelName); + + if (!approximatelyEqual(modelValue, sliderValue)) + { + setParameterValue(modelName, sliderValue); + + // create and send an OSC message with an address and a float value: + float value = static_cast<float> (getModelSlider().getValue()); + + if (!oscSender.send(modelAddressPattern, value)) + { + updateOutConnectedLabel(false); + } + else + { + DBG("Sent value " + String(value) + " to AP " + modelAddressPattern); + } + } + }; + + addAndMakeVisible(modelSelect); modelSelect.setColour(juce::Label::textColourId, juce::Colours::black); int c = 1; @@ -38,29 +80,239 @@ NeuralPiAudioProcessorEditor::NeuralPiAudioProcessorEditor (NeuralPiAudioProcess loadButton.setColour(juce::Label::textColourId, juce::Colours::black); loadButton.addListener(this); + //gainSliderAttach = std::make_unique<AudioProcessorValueTreeState::SliderAttachment>(processor.treeState, GAIN_ID, ampGainKnob); addAndMakeVisible(ampGainKnob); //ampGainKnob.setLookAndFeel(&ampSilverKnobLAF); ampGainKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20); ampGainKnob.setNumDecimalPlacesToDisplay(1); ampGainKnob.addListener(this); - ampGainKnob.setRange(-12.0, 12.0); - ampGainKnob.setValue(processor.ampGainKnobState); + ampGainKnob.setRange(0.0, 1.0); + ampGainKnob.setValue(0.5); ampGainKnob.setSliderStyle(juce::Slider::SliderStyle::RotaryVerticalDrag); - ampGainKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::NoTextBox, false, 50, 20); - ampGainKnob.setNumDecimalPlacesToDisplay(1); - ampGainKnob.setDoubleClickReturnValue(true, 0.0); + ampGainKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20); + ampGainKnob.setNumDecimalPlacesToDisplay(2); + ampGainKnob.setDoubleClickReturnValue(true, 0.5); + + auto gainValue = getParameterValue(gainName); + Slider& gainSlider = getGainSlider(); + gainSlider.setValue(gainValue, NotificationType::dontSendNotification); + + ampGainKnob.onValueChange = [this] + { + const float sliderValue = static_cast<float> (getGainSlider().getValue()); + const float gainValue = getParameterValue(gainName); + + if (!approximatelyEqual(gainValue, sliderValue)) + { + setParameterValue(gainName, sliderValue); + + // create and send an OSC message with an address and a float value: + float value = static_cast<float> (getGainSlider().getValue()); + + if (!oscSender.send(gainAddressPattern, value)) + { + updateOutConnectedLabel(false); + } + else + { + DBG("Sent value " + String(value) + " to AP " + gainAddressPattern); + } + } + }; addAndMakeVisible(ampMasterKnob); //ampMasterKnob.setLookAndFeel(&ampSilverKnobLAF); ampMasterKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20); ampMasterKnob.setNumDecimalPlacesToDisplay(1); ampMasterKnob.addListener(this); - ampMasterKnob.setRange(-48.0, 0.0); - ampMasterKnob.setValue(processor.ampMasterKnobState); + ampMasterKnob.setRange(0.0, 1.0); + ampMasterKnob.setValue(0.5); ampMasterKnob.setSliderStyle(juce::Slider::SliderStyle::RotaryVerticalDrag); - ampMasterKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::NoTextBox, false, 50, 20 ); - ampMasterKnob.setNumDecimalPlacesToDisplay(1); - ampMasterKnob.setDoubleClickReturnValue(true, -24.0); + ampMasterKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20); + ampMasterKnob.setNumDecimalPlacesToDisplay(2); + //ampMasterKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::NoTextBox, false, 50, 20 ); + //ampMasterKnob.setNumDecimalPlacesToDisplay(1); + ampMasterKnob.setDoubleClickReturnValue(true, 0.5); + + auto masterValue = getParameterValue(masterName); + Slider& masterSlider = getMasterSlider(); + masterSlider.setValue(masterValue, NotificationType::dontSendNotification); + + ampMasterKnob.onValueChange = [this] + { + const float sliderValue = static_cast<float> (getMasterSlider().getValue()); + const float masterValue = getParameterValue(masterName); + + if (!approximatelyEqual(masterValue, sliderValue)) + { + setParameterValue(masterName, sliderValue); + + // create and send an OSC message with an address and a float value: + float value = static_cast<float> (getMasterSlider().getValue()); + + if (!oscSender.send(masterAddressPattern, value)) + { + updateOutConnectedLabel(false); + } + else + { + DBG("Sent value " + String(value) + " to AP " + masterAddressPattern); + } + } + }; + + + addAndMakeVisible(ampBassKnob); + ampBassKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20); + ampBassKnob.setNumDecimalPlacesToDisplay(1); + ampBassKnob.addListener(this); + ampBassKnob.setRange(0.0, 1.0); + ampBassKnob.setValue(0.5); + ampBassKnob.setSliderStyle(juce::Slider::SliderStyle::RotaryVerticalDrag); + ampBassKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20); + ampBassKnob.setNumDecimalPlacesToDisplay(2); + ampBassKnob.setDoubleClickReturnValue(true, 0.5); + + auto bassValue = getParameterValue(bassName); + Slider& bassSlider = getBassSlider(); + bassSlider.setValue(bassValue, NotificationType::dontSendNotification); + + ampBassKnob.onValueChange = [this] + { + const float sliderValue = static_cast<float> (getBassSlider().getValue()); + const float bassValue = getParameterValue(bassName); + + if (!approximatelyEqual(bassValue, sliderValue)) + { + setParameterValue(bassName, sliderValue); + + // create and send an OSC message with an address and a float value: + float value = static_cast<float> (getBassSlider().getValue()); + + if (!oscSender.send(bassAddressPattern, value)) + { + updateOutConnectedLabel(false); + } + else + { + DBG("Sent value " + String(value) + " to AP " + bassAddressPattern); + } + } + }; + + addAndMakeVisible(ampMidKnob); + ampMidKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20); + ampMidKnob.setNumDecimalPlacesToDisplay(1); + ampMidKnob.addListener(this); + ampMidKnob.setRange(0.0, 1.0); + ampMidKnob.setValue(0.5); + ampMidKnob.setSliderStyle(juce::Slider::SliderStyle::RotaryVerticalDrag); + ampMidKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20); + ampMidKnob.setNumDecimalPlacesToDisplay(2); + ampMidKnob.setDoubleClickReturnValue(true, 0.5); + + auto midValue = getParameterValue(midName); + Slider& midSlider = getMidSlider(); + midSlider.setValue(midValue, NotificationType::dontSendNotification); + + ampMidKnob.onValueChange = [this] + { + const float sliderValue = static_cast<float> (getMidSlider().getValue()); + const float midValue = getParameterValue(midName); + + if (!approximatelyEqual(midValue, sliderValue)) + { + setParameterValue(midName, sliderValue); + + // create and send an OSC message with an address and a float value: + float value = static_cast<float> (getMidSlider().getValue()); + + if (!oscSender.send(midAddressPattern, value)) + { + updateOutConnectedLabel(false); + } + else + { + DBG("Sent value " + String(value) + " to AP " + midAddressPattern); + } + } + }; + + addAndMakeVisible(ampTrebleKnob); + ampTrebleKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20); + ampTrebleKnob.setNumDecimalPlacesToDisplay(1); + ampTrebleKnob.addListener(this); + ampTrebleKnob.setRange(0.0, 1.0); + ampTrebleKnob.setValue(0.5); + ampTrebleKnob.setSliderStyle(juce::Slider::SliderStyle::RotaryVerticalDrag); + ampTrebleKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20); + ampTrebleKnob.setNumDecimalPlacesToDisplay(2); + ampTrebleKnob.setDoubleClickReturnValue(true, 0.5); + + auto trebleValue = getParameterValue(trebleName); + Slider& trebleSlider = getTrebleSlider(); + trebleSlider.setValue(trebleValue, NotificationType::dontSendNotification); + + ampTrebleKnob.onValueChange = [this] + { + const float sliderValue = static_cast<float> (getTrebleSlider().getValue()); + const float trebleValue = getParameterValue(trebleName); + + if (!approximatelyEqual(trebleValue, sliderValue)) + { + setParameterValue(trebleName, sliderValue); + + // create and send an OSC message with an address and a float value: + float value = static_cast<float> (getTrebleSlider().getValue()); + + if (!oscSender.send(trebleAddressPattern, value)) + { + updateOutConnectedLabel(false); + } + else + { + DBG("Sent value " + String(value) + " to AP " + trebleAddressPattern); + } + } + }; + + addAndMakeVisible(ampPresenceKnob); + ampPresenceKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20); + ampPresenceKnob.setNumDecimalPlacesToDisplay(1); + ampPresenceKnob.addListener(this); + ampPresenceKnob.setRange(0.0, 1.0); + ampPresenceKnob.setValue(0.5); + ampPresenceKnob.setSliderStyle(juce::Slider::SliderStyle::RotaryVerticalDrag); + ampPresenceKnob.setTextBoxStyle(juce::Slider::TextEntryBoxPosition::TextBoxBelow, false, 50, 20); + ampPresenceKnob.setNumDecimalPlacesToDisplay(2); + ampPresenceKnob.setDoubleClickReturnValue(true, 0.5); + + auto presenceValue = getParameterValue(trebleName); + Slider& presenceSlider = getPresenceSlider(); + trebleSlider.setValue(presenceValue, NotificationType::dontSendNotification); + + ampPresenceKnob.onValueChange = [this] + { + const float sliderValue = static_cast<float> (getPresenceSlider().getValue()); + const float presenceValue = getParameterValue(presenceName); + + if (!approximatelyEqual(presenceValue, sliderValue)) + { + setParameterValue(presenceName, sliderValue); + + // create and send an OSC message with an address and a float value: + float value = static_cast<float> (getPresenceSlider().getValue()); + + if (!oscSender.send(presenceAddressPattern, value)) + { + updateOutConnectedLabel(false); + } + else + { + DBG("Sent value " + String(value) + " to AP " + presenceAddressPattern); + } + } + }; addAndMakeVisible(GainLabel); GainLabel.setText("Gain", juce::NotificationType::dontSendNotification); @@ -68,14 +320,75 @@ NeuralPiAudioProcessorEditor::NeuralPiAudioProcessorEditor (NeuralPiAudioProcess addAndMakeVisible(LevelLabel); LevelLabel.setText("Level", juce::NotificationType::dontSendNotification); LevelLabel.setJustificationType(juce::Justification::centred); + + addAndMakeVisible(BassLabel); + BassLabel.setText("Bass", juce::NotificationType::dontSendNotification); + BassLabel.setJustificationType(juce::Justification::centred); + addAndMakeVisible(MidLabel); + MidLabel.setText("Mid", juce::NotificationType::dontSendNotification); + MidLabel.setJustificationType(juce::Justification::centred); + addAndMakeVisible(TrebleLabel); + TrebleLabel.setText("Treble", juce::NotificationType::dontSendNotification); + TrebleLabel.setJustificationType(juce::Justification::centred); + addAndMakeVisible(PresenceLabel); + PresenceLabel.setText("Presence", juce::NotificationType::dontSendNotification); + PresenceLabel.setJustificationType(juce::Justification::centred); + auto font = GainLabel.getFont(); float height = font.getHeight(); font.setHeight(height); // 0.75); GainLabel.setFont(font); LevelLabel.setFont(font); + BassLabel.setFont(font); + MidLabel.setFont(font); + TrebleLabel.setFont(font); + PresenceLabel.setFont(font); + + // Name controls: + addAndMakeVisible(ampNameLabel); + ampNameField.setEditable(true, true, true); + addAndMakeVisible(ampNameField); + + // IP controls: + ipField.setEditable(true, true, true); + addAndMakeVisible(ipLabel); + addAndMakeVisible(ipField); + + // Port controls: + addAndMakeVisible(outPortNumberLabel); + outPortNumberField.setEditable(true, true, true); + addAndMakeVisible(outPortNumberField); + addAndMakeVisible(outConnectedLabel); + + addAndMakeVisible(inPortNumberLabel); + inPortNumberField.setEditable(true, true, true); + addAndMakeVisible(inPortNumberField); + addAndMakeVisible(inConnectedLabel); + + + // OSC messaging + + getInPortNumberField().addListener(this); + getAmpNameField().addListener(this); + getOutPortNumberField().addListener(this); + getIPField().addListener(this); + + oscReceiver.getGainValue().addListener(this); + oscReceiver.getMasterValue().addListener(this); + + oscReceiver.getBassValue().addListener(this); + oscReceiver.getMidValue().addListener(this); + oscReceiver.getTrebleValue().addListener(this); + oscReceiver.getPresenceValue().addListener(this); + + oscReceiver.getModelValue().addListener(this); + + updateInConnectedLabel(); + + connectSender(); // Size of plugin GUI - setSize(250, 200); + setSize(276, 430); } @@ -86,27 +399,53 @@ NeuralPiAudioProcessorEditor::~NeuralPiAudioProcessorEditor() //============================================================================== void NeuralPiAudioProcessorEditor::paint (Graphics& g) { - //background = ImageCache::getFromMemory(BinaryData::pedal_blank_png, BinaryData::pedal_blank_pngSize); + // Workaround for graphics on Windows builds (clipping code doesn't work correctly on Windows) +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) + g.drawImageAt(background, 0, 0); // Debug Line: Redraw entire background image +#else +// Redraw only the clipped part of the background image + juce::Rectangle<int> ClipRect = g.getClipBounds(); + g.drawImage(background, ClipRect.getX(), ClipRect.getY(), ClipRect.getWidth(), ClipRect.getHeight(), ClipRect.getX(), ClipRect.getY(), ClipRect.getWidth(), ClipRect.getHeight()); +#endif - //g.drawImageAt(background, 0, 0); - - g.setColour (Colours::white); - g.setFont (15.0f); - } void NeuralPiAudioProcessorEditor::resized() { // This is generally where you'll want to lay out the positions of any // subcomponents in your editor.. - modelSelect.setBounds(15, 10, 210, 25); - loadButton.setBounds(15, 42, 100, 25); + modelSelect.setBounds(19, 10, 234, 25); + loadButton.setBounds(19, 42, 100, 25); + modelKnob.setBounds(140, 40, 75, 95); // Amp Widgets - ampGainKnob.setBounds(30, 85, 75, 95); - ampMasterKnob.setBounds(140, 85, 75, 95); - GainLabel.setBounds(28, 163, 80, 10); - LevelLabel.setBounds(138, 163, 80, 10); + 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); + + addAndMakeVisible(ampNameLabel); + ampNameField.setEditable(true, true, true); + addAndMakeVisible(ampNameField); + + // IP controls: + ipField.setBounds(150, 340, 100, 25); + ipLabel.setBounds(15, 340, 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); } void NeuralPiAudioProcessorEditor::modelSelectChanged() @@ -116,6 +455,9 @@ void NeuralPiAudioProcessorEditor::modelSelectChanged() processor.loadConfig(processor.jsonFiles[selectedFileIndex]); processor.current_model_index = modelSelect.getSelectedItemIndex(); } + auto newValue = static_cast<float>(processor.current_model_index / (processor.num_models - 1.0)); + modelKnob.setValue(newValue); + //modelKnob.setValue(processor.current_model_index); } void NeuralPiAudioProcessorEditor::loadButtonClicked() @@ -144,7 +486,10 @@ void NeuralPiAudioProcessorEditor::loadButtonClicked() modelSelect.addItem(file.getFileNameWithoutExtension(), processor.jsonFiles.size() + 1); modelSelect.setSelectedItemIndex(processor.jsonFiles.size(), juce::NotificationType::dontSendNotification); processor.jsonFiles.push_back(file); + //processor.num_models += 1; } + // Sort jsonFiles alphabetically + std::sort(processor.jsonFiles.begin(), processor.jsonFiles.end()); } } } @@ -161,9 +506,283 @@ void NeuralPiAudioProcessorEditor::buttonClicked(juce::Button* button) void NeuralPiAudioProcessorEditor::sliderValueChanged(Slider* slider) { - // Amp - if (slider == &ampGainKnob) - processor.set_ampDrive(slider->getValue()); - else if (slider == &ampMasterKnob) - processor.set_ampMaster(slider->getValue()); + if (slider == &modelKnob) + if (slider->getValue() >= 0 && slider->getValue() < processor.jsonFiles.size()) { + modelSelect.setSelectedItemIndex(processor.getModelIndex(slider->getValue()), juce::NotificationType::dontSendNotification); + } +} +/* + else if (slider == &ampBassKnob || slider == &ampMidKnob || slider == &ampTrebleKnob) { + processor.set_ampEQ(ampBassKnob.getValue(), ampMidKnob.getValue(), ampTrebleKnob.getValue(), ampPresenceKnob.getValue()); + // Set knob states for saving positions when closing/reopening GUI + processor.ampBassKnobState = ampBassKnob.getValue(); + processor.ampMidKnobState = ampMidKnob.getValue(); + processor.ampTrebleKnobState = ampTrebleKnob.getValue(); + } + else if (slider == &ampPresenceKnob) { + processor.set_ampEQ(ampBassKnob.getValue(), ampMidKnob.getValue(), ampTrebleKnob.getValue(), ampPresenceKnob.getValue()); + } +} +*/ + + +// OSC Messages +Slider& NeuralPiAudioProcessorEditor::getGainSlider() +{ + return ampGainKnob; +} + +Slider& NeuralPiAudioProcessorEditor::getMasterSlider() +{ + return ampMasterKnob; +} + +Slider& NeuralPiAudioProcessorEditor::getBassSlider() +{ + return ampBassKnob; +} + +Slider& NeuralPiAudioProcessorEditor::getMidSlider() +{ + return ampMidKnob; +} + +Slider& NeuralPiAudioProcessorEditor::getTrebleSlider() +{ + return ampTrebleKnob; +} + +Slider& NeuralPiAudioProcessorEditor::getPresenceSlider() +{ + return ampPresenceKnob; +} + +Slider& NeuralPiAudioProcessorEditor::getModelSlider() +{ + return modelKnob; +} + + +Label& NeuralPiAudioProcessorEditor::getOutPortNumberField() +{ + return outPortNumberField; +} + +Label& NeuralPiAudioProcessorEditor::getInPortNumberField() +{ + return inPortNumberField; +} + +Label& NeuralPiAudioProcessorEditor::getIPField() +{ + return ipField; +} + +Label& NeuralPiAudioProcessorEditor::getAmpNameField() +{ + return ampNameField; +} + +Label& NeuralPiAudioProcessorEditor::getOutConnectedLabel() +{ + return outConnectedLabel; +} + +Label& NeuralPiAudioProcessorEditor::getInConnectedLabel() +{ + return inConnectedLabel; +} + +void NeuralPiAudioProcessorEditor::buildAddressPatterns() +{ + gainAddressPattern = "/parameter/" + ampName + "/Gain"; + masterAddressPattern = "/parameter/" + ampName + "/Master"; + bassAddressPattern = "/parameter/" + ampName + "/Bass"; + midAddressPattern = "/parameter/" + ampName + "/Mid"; + trebleAddressPattern = "/parameter/" + ampName + "/Treble"; + presenceAddressPattern = "/parameter/" + ampName + "/Presence"; + modelAddressPattern = "/parameter/" + ampName + "/Model"; +} + +void NeuralPiAudioProcessorEditor::connectSender() +{ + // specify here where to send OSC messages to: host URL and UDP port number + if (!oscSender.connect(outgoingIP, outgoingPort)) + { + updateOutConnectedLabel(false); + } + else + { + updateOutConnectedLabel(true); + } +} + +void NeuralPiAudioProcessorEditor::updateOutgoingIP(String ip) +{ + outgoingIP = ip; + connectSender(); +} + +void NeuralPiAudioProcessorEditor::updateOutgoingPort(int port) +{ + outgoingPort = port; + connectSender(); +} + +void NeuralPiAudioProcessorEditor::labelTextChanged(Label* labelThatHasChanged) +{ + if (labelThatHasChanged == &getInPortNumberField()) + { + const int newPort = getInPortNumberField().getTextValue().toString().getIntValue(); + oscReceiver.changePort(newPort); + updateInConnectedLabel(); + } + else if (labelThatHasChanged == &getOutPortNumberField()) + { + const int newPort = getOutPortNumberField().getTextValue().toString().getIntValue(); + updateOutgoingPort(newPort); + } + else if (labelThatHasChanged == &getIPField()) + { + const String newIP = getIPField().getTextValue().toString(); + updateOutgoingIP(newIP); + } + /* + else if (labelThatHasChanged == getAmpNameField()) + { + ampName = getAmpNameField().getTextValue().toString(); + buildAddressPatterns(); + oscReceiver.updateAmpName(getAmpNameField().getTextValue().toString()); + } + */ +} + +void NeuralPiAudioProcessorEditor::updateInConnectedLabel() +{ + const bool connected = oscReceiver.isConnected(); + if (connected) + { + getInConnectedLabel().setText("(Connected)", dontSendNotification); + } + else + { + getInConnectedLabel().setText("(Disconnected!)", dontSendNotification); + } +} + +void NeuralPiAudioProcessorEditor::updateOutConnectedLabel(bool connected) +{ + if (connected) + { + getOutConnectedLabel().setText("(Connected)", dontSendNotification); + } + else + { + getOutConnectedLabel().setText("(Disconnected!)", dontSendNotification); + } +} + +// This callback is invoked if an OSC message has been received setting either value. +void NeuralPiAudioProcessorEditor::valueChanged(Value& value) +{ + if (value.refersToSameSourceAs(oscReceiver.getGainValue())) + { + if (!approximatelyEqual(static_cast<double> (value.getValue()), getGainSlider().getValue())) + { + getGainSlider().setValue(static_cast<double> (value.getValue()), + NotificationType::sendNotification); + } + } + else if (value.refersToSameSourceAs(oscReceiver.getMasterValue())) + { + if (!approximatelyEqual(static_cast<double> (value.getValue()), getMasterSlider().getValue())) + { + getMasterSlider().setValue(static_cast<double> (value.getValue()), + NotificationType::sendNotification); + } + } + if (value.refersToSameSourceAs(oscReceiver.getBassValue())) + { + if (!approximatelyEqual(static_cast<double> (value.getValue()), getBassSlider().getValue())) + { + getBassSlider().setValue(static_cast<double> (value.getValue()), + NotificationType::sendNotification); + } + } + else if (value.refersToSameSourceAs(oscReceiver.getMidValue())) + { + if (!approximatelyEqual(static_cast<double> (value.getValue()), getMidSlider().getValue())) + { + getMidSlider().setValue(static_cast<double> (value.getValue()), + NotificationType::sendNotification); + } + } + if (value.refersToSameSourceAs(oscReceiver.getTrebleValue())) + { + if (!approximatelyEqual(static_cast<double> (value.getValue()), getTrebleSlider().getValue())) + { + getTrebleSlider().setValue(static_cast<double> (value.getValue()), + NotificationType::sendNotification); + } + } + else if (value.refersToSameSourceAs(oscReceiver.getPresenceValue())) + { + if (!approximatelyEqual(static_cast<double> (value.getValue()), getPresenceSlider().getValue())) + { + getPresenceSlider().setValue(static_cast<double> (value.getValue()), + NotificationType::sendNotification); + } + } + else if (value.refersToSameSourceAs(oscReceiver.getModelValue())) + { + if (!approximatelyEqual(static_cast<double> (value.getValue()), getModelSlider().getValue())) + { + getModelSlider().setValue(static_cast<double> (value.getValue()), + NotificationType::sendNotification); + } + } +} + +void NeuralPiAudioProcessorEditor::timerCallback() +{ + getGainSlider().setValue(getParameterValue(gainName), NotificationType::dontSendNotification); + getMasterSlider().setValue(getParameterValue(masterName), NotificationType::dontSendNotification); + getBassSlider().setValue(getParameterValue(bassName), NotificationType::dontSendNotification); + getMidSlider().setValue(getParameterValue(midName), NotificationType::dontSendNotification); + getTrebleSlider().setValue(getParameterValue(trebleName), NotificationType::dontSendNotification); + getPresenceSlider().setValue(getParameterValue(presenceName), NotificationType::dontSendNotification); + getModelSlider().setValue(getParameterValue(modelName), NotificationType::dontSendNotification); +} + +AudioProcessorParameter* NeuralPiAudioProcessorEditor::getParameter(const String& paramId) +{ + if (auto* proc = getAudioProcessor()) + { + auto& params = proc->getParameters(); + + for (auto p : params) + { + if (auto* param = dynamic_cast<AudioProcessorParameterWithID*> (p)) + { + if (param->paramID == paramId) + return param; + } + } + } + + return nullptr; +} + +float NeuralPiAudioProcessorEditor::getParameterValue(const String& paramId) +{ + if (auto* param = getParameter(paramId)) + return param->getValue(); + + return 0.0f; +} + +void NeuralPiAudioProcessorEditor::setParameterValue(const String& paramId, float value) +{ + if (auto* param = getParameter(paramId)) + param->setValueNotifyingHost(value); } \ No newline at end of file diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h @@ -12,6 +12,7 @@ #include "../JuceLibraryCode/JuceHeader.h" #include "PluginProcessor.h" +#include "AmpOSCReceiver.h" //#include "myLookAndFeel.h" #include <stdlib.h> @@ -19,8 +20,11 @@ /** */ class NeuralPiAudioProcessorEditor : public AudioProcessorEditor, - private Button::Listener, - private Slider::Listener + private Button::Listener, + private Slider::Listener, + private Value::Listener, + private Label::Listener, + private Timer { public: @@ -31,20 +35,57 @@ public: void paint (Graphics&) override; void resized() override; + AmpOSCReceiver oscReceiver; + OSCSender oscSender; + + String outgoingIP{ "127.0.0.1" }; + int outgoingPort{ 24024 }; + int incomingPort{ 25024 }; + + String ampName{ "NeuralPi" }; + String gainAddressPattern{ "/parameter/NeuralPi/Gain" }; + String masterAddressPattern{ "/parameter/NeuralPi/Master" }; + String modelAddressPattern{ "/parameter/NeuralPi/Model" }; + String bassAddressPattern{ "/parameter/NeuralPi/Bass" }; + String midAddressPattern{ "/parameter/NeuralPi/Mid" }; + String trebleAddressPattern{ "/parameter/NeuralPi/Treble" }; + String presenceAddressPattern{ "/parameter/NeuralPi/Presence" }; + + const String gainName{ "gain" }; + const String masterName{ "master" }; + const String bassName{ "bass" }; + const String midName{ "mid" }; + const String trebleName{ "treble" }; + const String presenceName{ "presence" }; + + const String modelName{ "model" }; + private: // This reference is provided as a quick way for your editor to // access the processor object that created it. NeuralPiAudioProcessor& processor; + Image background = ImageCache::getFromMemory(BinaryData::npi_background_jpg, BinaryData::npi_background_jpgSize); + // Amp Widgets Slider ampGainKnob; Slider ampMasterKnob; + Slider modelKnob; //ImageButton ampOnButton; //ImageButton ampLED; ComboBox modelSelect; + Slider ampBassKnob; + Slider ampMidKnob; + Slider ampTrebleKnob; + Slider ampPresenceKnob; + Label GainLabel; Label LevelLabel; + Label BassLabel; + Label MidLabel; + Label TrebleLabel; + Label PresenceLabel; File test_file; File model_folder; @@ -56,5 +97,57 @@ private: void loadButtonClicked(); virtual void sliderValueChanged(Slider* slider) override; + + Label ampNameLabel{ {}, "Amp Name (no spaces): " }; + Label ampNameField{ {}, "NeuralPi" }; + + Label ipLabel{ {}, "Target IP Address: " }; + Label ipField{ {}, "127.0.0.1" }; + + Label outPortNumberLabel{ {}, "Outgoing OSC Port: " }; + Label outPortNumberField{ {}, "24024" }; + + Label inPortNumberLabel{ {}, "Incoming OSC Port: " }; + Label inPortNumberField{ {}, "25024" }; + + Label gainLabel{ {}, "Gain" }; + Label masterLabel{ {}, "Master" }; + + Label modelLabel{ {}, "Model" }; + + Label inConnectedLabel{ "(connected)" }; + Label outConnectedLabel{ "(connected)" }; + + // OSC Messages + Slider& getGainSlider(); + Slider& getMasterSlider(); + Slider& getModelSlider(); + Slider& getBassSlider(); + Slider& getMidSlider(); + Slider& getTrebleSlider(); + Slider& getPresenceSlider(); + + Label& getOutPortNumberField(); + Label& getInPortNumberField(); + Label& getIPField(); + Label& getAmpNameField(); + Label& getOutConnectedLabel(); + Label& getInConnectedLabel(); + void buildAddressPatterns(); + void connectSender(); + void updateOutgoingIP(String ip); + void updateOutgoingPort(int port); + void labelTextChanged(Label* labelThatHasChanged) override; + void updateInConnectedLabel(); + void updateOutConnectedLabel(bool connected); + // This callback is invoked if an OSC message has been received setting either value. + void valueChanged(Value& value) override; + void timerCallback() override; + + AudioProcessorParameter* getParameter(const String& paramId); + + float getParameterValue(const String& paramId); + void setParameterValue(const String& paramId, float value); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NeuralPiAudioProcessorEditor) }; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp @@ -33,8 +33,21 @@ NeuralPiAudioProcessor::NeuralPiAudioProcessor() if (jsonFiles.size() > 0) { loadConfig(jsonFiles[current_model_index]); } + + // Sort jsonFiles alphabetically + std::sort(jsonFiles.begin(), jsonFiles.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)); + addParameter(bassParam = new AudioParameterFloat(BASS_ID, BASS_NAME, NormalisableRange<float>(0.0f, 1.0f, 0.01f), 0.5f)); + addParameter(midParam = new AudioParameterFloat(MID_ID, MID_NAME, NormalisableRange<float>(0.0f, 1.0f, 0.01f), 0.5f)); + addParameter(trebleParam = new AudioParameterFloat(TREBLE_ID, TREBLE_NAME, NormalisableRange<float>(0.0f, 1.0f, 0.01f), 0.5f)); + addParameter(presenceParam = new AudioParameterFloat(PRESENCE_ID, PRESENCE_NAME, NormalisableRange<float>(0.0f, 1.0f, 0.01f), 0.5f)); + addParameter(modelParam = new AudioParameterFloat(MODEL_ID, MODEL_NAME, NormalisableRange<float>(0.0f, 1.0f, 0.001f), 0.0f)); } + NeuralPiAudioProcessor::~NeuralPiAudioProcessor() { } @@ -107,6 +120,11 @@ void NeuralPiAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlo // Use this method as the place to do any pre-playback // initialisation that you need.. LSTM.reset(); + + // set up DC blocker + dcBlocker.coefficients = dsp::IIR::Coefficients<float>::makeHighPass(sampleRate, 35.0f); + dsp::ProcessSpec spec{ sampleRate, static_cast<uint32> (samplesPerBlock), 2 }; + dcBlocker.prepare(spec); } void NeuralPiAudioProcessor::releaseResources() @@ -147,20 +165,41 @@ void NeuralPiAudioProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffe // Setup Audio Data const int numSamples = buffer.getNumSamples(); const int numInputChannels = getTotalNumInputChannels(); + const int sampleRate = getSampleRate(); // Amp ============================================================================= if (amp_state == 1) { - - buffer.applyGain(ampDrive); - - // Apply LSTM model + auto gain = static_cast<float> (gainParam->get()); + auto master = static_cast<float> (masterParam->get()); + // Note: Default 0.0 -> 1.0 param range is converted to +-8.0 here + auto bass = (static_cast<float> (bassParam->get() - 0.5) * 16.0); + auto mid = (static_cast<float> (midParam->get() - 0.5) * 16.0); + auto treble = (static_cast<float> (trebleParam->get() - 0.5) * 16.0); + auto presence = (static_cast<float> (presenceParam->get() - 0.5) * 16.0); + + auto model = static_cast<float> (modelParam->get()); + model_index = getModelIndex(model); + + buffer.applyGain(gain * 2.0); + eq4band.setParameters(bass, mid, treble, presence);// Better to move this somewhere else? Only need to set when value changes + eq4band.process(buffer.getReadPointer(0), buffer.getWritePointer(0), midiMessages, numSamples, numInputChannels, sampleRate); + + // Apply LSTM model if (model_loaded == 1) { + if (current_model_index != model_index) { + loadConfig(jsonFiles[model_index]); + current_model_index = model_index; + } LSTM.process(buffer.getReadPointer(0), buffer.getWritePointer(0), numSamples); } // Master Volume - buffer.applyGain(ampMaster); + buffer.applyGain(master); } + + // process DC blocker + auto monoBlock = dsp::AudioBlock<float>(buffer).getSingleChannelBlock(0); + dcBlocker.process(dsp::ProcessContextReplacing<float>(monoBlock)); for (int ch = 1; ch < buffer.getNumChannels(); ++ch) buffer.copyFrom(ch, 0, buffer, 0, 0, buffer.getNumSamples()); @@ -178,17 +217,36 @@ AudioProcessorEditor* NeuralPiAudioProcessor::createEditor() } //============================================================================== -void NeuralPiAudioProcessor::getStateInformation (MemoryBlock& destData) +void NeuralPiAudioProcessor::getStateInformation(MemoryBlock& destData) { - // You should use this method to store your parameters in the memory block. - // You could do that either as raw data, or use the XML or ValueTree classes - // as intermediaries to make it easy to save and load complex data. + MemoryOutputStream stream(destData, true); + + stream.writeFloat(*gainParam); + stream.writeFloat(*masterParam); + stream.writeFloat(*bassParam); + stream.writeFloat(*midParam); + stream.writeFloat(*trebleParam); + stream.writeFloat(*presenceParam); + stream.writeFloat(*modelParam); } -void NeuralPiAudioProcessor::setStateInformation (const void* data, int sizeInBytes) +void NeuralPiAudioProcessor::setStateInformation(const void* data, int sizeInBytes) { - // You should use this method to restore your parameters from this memory block, - // whose contents will have been created by the getStateInformation() call. + MemoryInputStream stream(data, static_cast<size_t> (sizeInBytes), false); + + gainParam->setValueNotifyingHost(stream.readFloat()); + masterParam->setValueNotifyingHost(stream.readFloat()); + bassParam->setValueNotifyingHost(stream.readFloat()); + midParam->setValueNotifyingHost(stream.readFloat()); + trebleParam->setValueNotifyingHost(stream.readFloat()); + presenceParam->setValueNotifyingHost(stream.readFloat()); + modelParam->setValueNotifyingHost(stream.readFloat()); +} + +int NeuralPiAudioProcessor::getModelIndex(float model_param) +{ + //return static_cast<int>(model_param * (jsonFiles.size() - 1.0)); + return static_cast<int>(model_param * (num_models - 1.0)); } void NeuralPiAudioProcessor::loadConfig(File configFile) @@ -197,6 +255,7 @@ void NeuralPiAudioProcessor::loadConfig(File configFile) model_loaded = 1; String path = configFile.getFullPathName(); char_filename = path.toUTF8(); + // TODO Add check here for invalid files LSTM.load_json(char_filename); @@ -222,7 +281,10 @@ void NeuralPiAudioProcessor::addDirectory(const File& file) juce::Array<juce::File> results; file.findChildFiles(results, juce::File::findFiles, false, "*.json"); for (int i = results.size(); --i >= 0;) + { jsonFiles.push_back(File(results.getReference(i).getFullPathName())); + num_models = num_models + 1.0; + } } } @@ -263,12 +325,10 @@ void NeuralPiAudioProcessor::installTones() // //==================================================================== { - // Default tones File ts9_tone = userAppDataDirectory_tones.getFullPathName() + "/ts9_model_best.json"; File bjdirty_tone = userAppDataDirectory_tones.getFullPathName() + "/bj_model_best.json"; - if (ts9_tone.existsAsFile() == false) { std::string string_command = ts9_tone.getFullPathName().toStdString(); const char* char_ts9_tone = &string_command[0]; @@ -293,6 +353,11 @@ void NeuralPiAudioProcessor::installTones() } +void NeuralPiAudioProcessor::set_ampEQ(float bass_slider, float mid_slider, float treble_slider, float presence_slider) +{ + eq4band.setParameters(bass_slider, mid_slider, treble_slider, presence_slider); +} + float NeuralPiAudioProcessor::convertLogScale(float in_value, float x_min, float x_max, float y_min, float y_max) { float b = log(y_max / y_min) / (x_max - x_min); @@ -301,28 +366,13 @@ float NeuralPiAudioProcessor::convertLogScale(float in_value, float x_min, float return converted_value; } -void NeuralPiAudioProcessor::set_ampDrive(float db_ampDrive) -{ - ampDrive = decibelToLinear(db_ampDrive); - ampGainKnobState = db_ampDrive; -} - -void NeuralPiAudioProcessor::set_ampMaster(float db_ampMaster) -{ - ampMasterKnobState = db_ampMaster; - if (db_ampMaster == -48.0) { - ampMaster = decibelToLinear(-100.0); - } else { - ampMaster = decibelToLinear(db_ampMaster); - } -} - float NeuralPiAudioProcessor::decibelToLinear(float dbValue) { return powf(10.0, dbValue/20.0); } + //============================================================================== // This creates new instances of the plugin.. AudioProcessor* JUCE_CALLTYPE createPluginFilter() diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h @@ -10,11 +10,27 @@ #include <nlohmann/json.hpp> #include "RTNeuralLSTM.h" +#include "AmpOSCReceiver.h" +#include "Eq4Band.h" #pragma once #include "../JuceLibraryCode/JuceHeader.h" -// USE_RTNEURAL 1 + +#define GAIN_ID "gain" +#define GAIN_NAME "Gain" +#define MODEL_ID "model" +#define MODEL_NAME "Model" +#define MASTER_ID "master" +#define MASTER_NAME "Master" +#define BASS_ID "bass" +#define BASS_NAME "Bass" +#define MID_ID "mid" +#define MID_NAME "Mid" +#define TREBLE_ID "treble" +#define TREBLE_NAME "Treble" +#define PRESENCE_ID "presence" +#define PRESENCE_NAME "Presence" //============================================================================== /** @@ -59,24 +75,30 @@ public: void getStateInformation (MemoryBlock& destData) override; void setStateInformation (const void* data, int sizeInBytes) override; + bool compareFunction(juce::File a, juce::File b); + int getModelIndex(float model_param); void loadConfig(File configFile); void setupDataDirectories(); void installTones(); + + void set_ampEQ(float bass_slider, float mid_slider, float treble_slider, float presence_slider); // Overdrive Pedal float convertLogScale(float in_value, float x_min, float x_max, float y_min, float y_max); // Amp + /* void set_ampDrive(float db_ampCleanDrive); void set_ampMaster(float db_ampMaster); - + void set_ampEQ(float bass_slider, float mid_slider, float treble_slider, float presence_slider); + */ float decibelToLinear(float dbValue); void addDirectory(const File& file); void resetDirectory(const File& file); std::vector<File> jsonFiles; File currentDirectory = File::getCurrentWorkingDirectory().getFullPathName(); - File userAppDataDirectory = File::getSpecialLocation(File::userApplicationDataDirectory).getChildFile(JucePlugin_Manufacturer).getChildFile(JucePlugin_Name); + File userAppDataDirectory = File::getSpecialLocation(File::userDocumentsDirectory).getChildFile(JucePlugin_Manufacturer).getChildFile(JucePlugin_Name); File userAppDataDirectory_tones = userAppDataDirectory.getFullPathName() + "/tones"; // Pedal/amp states @@ -87,20 +109,24 @@ public: const char* char_filename = ""; int model_loaded = 0; int current_model_index = 0; - - // Amp knob states - float ampGainKnobState = 0.0; - float ampMasterKnobState = -24.0; + float num_models = 0.0; + int model_index = 0; // Used in processBlock when converting slider param to model index RT_LSTM LSTM; - private: - // Amp - float ampDrive = 1.0; - float ampMaster = 1.0; - var dummyVar; + Eq4Band eq4band; // Amp EQ + + AudioParameterFloat* gainParam; + AudioParameterFloat* masterParam; + AudioParameterFloat* bassParam; + AudioParameterFloat* midParam; + AudioParameterFloat* trebleParam; + AudioParameterFloat* presenceParam; + AudioParameterFloat* modelParam; + + dsp::IIR::Filter<float> dcBlocker; //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NeuralPiAudioProcessor) diff --git a/installers/mac/Intro.txt b/installers/mac/Intro.txt @@ -0,0 +1 @@ +This application will install the NeuralPi audio plugin version ##APPVERSION## to your computer. diff --git a/installers/mac/NeuralPi.pkgproj b/installers/mac/NeuralPi.pkgproj @@ -0,0 +1,1999 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>PACKAGES</key> + <array> + <dict> + <key>MUST-CLOSE-APPLICATION-ITEMS</key> + <array/> + <key>MUST-CLOSE-APPLICATIONS</key> + <false/> + <key>PACKAGE_FILES</key> + <dict> + <key>DEFAULT_INSTALL_LOCATION</key> + <string>/</string> + <key>HIERARCHY</key> + <dict> + <key>CHILDREN</key> + <array> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>80</integer> + <key>PATH</key> + <string>Applications</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>509</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>80</integer> + <key>PATH</key> + <string>Application Support</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array> + <dict> + <key>CHILDREN</key> + <array> + <dict> + <key>CHILDREN</key> + <array> + <dict> + <key>BUNDLE_CAN_DOWNGRADE</key> + <false/> + <key>BUNDLE_POSTINSTALL_PATH</key> + <dict> + <key>PATH_TYPE</key> + <integer>0</integer> + </dict> + <key>BUNDLE_PREINSTALL_PATH</key> + <dict> + <key>PATH_TYPE</key> + <integer>0</integer> + </dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>../../bin/Mac/NeuralPi.component</string> + <key>PATH_TYPE</key> + <integer>1</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>3</integer> + <key>UID</key> + <integer>0</integer> + </dict> + </array> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Components</string> + <key>PATH_TYPE</key> + <integer>2</integer> + <key>PERMISSIONS</key> + <integer>509</integer> + <key>TYPE</key> + <integer>2</integer> + <key>UID</key> + <integer>0</integer> + </dict> + </array> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Plug-Ins</string> + <key>PATH_TYPE</key> + <integer>2</integer> + <key>PERMISSIONS</key> + <integer>509</integer> + <key>TYPE</key> + <integer>2</integer> + <key>UID</key> + <integer>0</integer> + </dict> + </array> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Audio</string> + <key>PATH_TYPE</key> + <integer>2</integer> + <key>PERMISSIONS</key> + <integer>509</integer> + <key>TYPE</key> + <integer>2</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Automator</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Documentation</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Extensions</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Filesystems</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Frameworks</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Input Methods</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Internet Plug-Ins</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>LaunchAgents</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>LaunchDaemons</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>PreferencePanes</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Preferences</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>80</integer> + <key>PATH</key> + <string>Printers</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>PrivilegedHelperTools</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>1005</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>QuickLook</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>QuickTime</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Screen Savers</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Scripts</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Services</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Widgets</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + </array> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Library</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Shared</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>1023</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + </array> + <key>GID</key> + <integer>80</integer> + <key>PATH</key> + <string>Users</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + </array> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>/</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <key>PAYLOAD_TYPE</key> + <integer>0</integer> + <key>PRESERVE_EXTENDED_ATTRIBUTES</key> + <false/> + <key>SHOW_INVISIBLE</key> + <false/> + <key>SPLIT_FORKS</key> + <true/> + <key>TREAT_MISSING_FILES_AS_WARNING</key> + <false/> + <key>VERSION</key> + <integer>5</integer> + </dict> + <key>PACKAGE_SCRIPTS</key> + <dict> + <key>POSTINSTALL_PATH</key> + <dict> + <key>PATH_TYPE</key> + <integer>0</integer> + </dict> + <key>PREINSTALL_PATH</key> + <dict> + <key>PATH_TYPE</key> + <integer>0</integer> + </dict> + <key>RESOURCES</key> + <array/> + </dict> + <key>PACKAGE_SETTINGS</key> + <dict> + <key>AUTHENTICATION</key> + <integer>1</integer> + <key>CONCLUSION_ACTION</key> + <integer>0</integer> + <key>FOLLOW_SYMBOLIC_LINKS</key> + <false/> + <key>IDENTIFIER</key> + <string>com.GuitarML.NeuralPi.NeuralPiAU</string> + <key>LOCATION</key> + <integer>0</integer> + <key>NAME</key> + <string>AU</string> + <key>OVERWRITE_PERMISSIONS</key> + <false/> + <key>PAYLOAD_SIZE</key> + <integer>-1</integer> + <key>REFERENCE_PATH</key> + <string></string> + <key>RELOCATABLE</key> + <false/> + <key>USE_HFS+_COMPRESSION</key> + <false/> + <key>VERSION</key> + <string>##APPVERSION##</string> + </dict> + <key>TYPE</key> + <integer>0</integer> + <key>UUID</key> + <string>69EED16E-B119-4D35-B464-12717823DE0E</string> + </dict> + <dict> + <key>MUST-CLOSE-APPLICATION-ITEMS</key> + <array/> + <key>MUST-CLOSE-APPLICATIONS</key> + <false/> + <key>PACKAGE_FILES</key> + <dict> + <key>DEFAULT_INSTALL_LOCATION</key> + <string>/</string> + <key>HIERARCHY</key> + <dict> + <key>CHILDREN</key> + <array> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>80</integer> + <key>PATH</key> + <string>Applications</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>509</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>80</integer> + <key>PATH</key> + <string>Application Support</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array> + <dict> + <key>CHILDREN</key> + <array> + <dict> + <key>CHILDREN</key> + <array> + <dict> + <key>BUNDLE_CAN_DOWNGRADE</key> + <false/> + <key>BUNDLE_POSTINSTALL_PATH</key> + <dict> + <key>PATH_TYPE</key> + <integer>0</integer> + </dict> + <key>BUNDLE_PREINSTALL_PATH</key> + <dict> + <key>PATH_TYPE</key> + <integer>0</integer> + </dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>../../bin/Mac/NeuralPi.vst3</string> + <key>PATH_TYPE</key> + <integer>1</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>3</integer> + <key>UID</key> + <integer>0</integer> + </dict> + </array> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>VST3</string> + <key>PATH_TYPE</key> + <integer>2</integer> + <key>PERMISSIONS</key> + <integer>509</integer> + <key>TYPE</key> + <integer>2</integer> + <key>UID</key> + <integer>0</integer> + </dict> + </array> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Plug-Ins</string> + <key>PATH_TYPE</key> + <integer>2</integer> + <key>PERMISSIONS</key> + <integer>509</integer> + <key>TYPE</key> + <integer>2</integer> + <key>UID</key> + <integer>0</integer> + </dict> + </array> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Audio</string> + <key>PATH_TYPE</key> + <integer>2</integer> + <key>PERMISSIONS</key> + <integer>509</integer> + <key>TYPE</key> + <integer>2</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Automator</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Documentation</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Extensions</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Filesystems</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Frameworks</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Input Methods</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Internet Plug-Ins</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>LaunchAgents</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>LaunchDaemons</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>PreferencePanes</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Preferences</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>80</integer> + <key>PATH</key> + <string>Printers</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>PrivilegedHelperTools</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>1005</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>QuickLook</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>QuickTime</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Screen Savers</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Scripts</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Services</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Widgets</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + </array> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Library</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Shared</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>1023</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + </array> + <key>GID</key> + <integer>80</integer> + <key>PATH</key> + <string>Users</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + </array> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>/</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <key>PAYLOAD_TYPE</key> + <integer>0</integer> + <key>PRESERVE_EXTENDED_ATTRIBUTES</key> + <false/> + <key>SHOW_INVISIBLE</key> + <false/> + <key>SPLIT_FORKS</key> + <true/> + <key>TREAT_MISSING_FILES_AS_WARNING</key> + <false/> + <key>VERSION</key> + <integer>5</integer> + </dict> + <key>PACKAGE_SETTINGS</key> + <dict> + <key>AUTHENTICATION</key> + <integer>1</integer> + <key>CONCLUSION_ACTION</key> + <integer>0</integer> + <key>FOLLOW_SYMBOLIC_LINKS</key> + <false/> + <key>IDENTIFIER</key> + <string>com.GuitarML.NeuralPi.NeuralPiVST3</string> + <key>LOCATION</key> + <integer>0</integer> + <key>NAME</key> + <string>VST3</string> + <key>OVERWRITE_PERMISSIONS</key> + <false/> + <key>PAYLOAD_SIZE</key> + <integer>-1</integer> + <key>REFERENCE_PATH</key> + <string></string> + <key>RELOCATABLE</key> + <false/> + <key>USE_HFS+_COMPRESSION</key> + <false/> + <key>VERSION</key> + <string>##APPVERSION##</string> + </dict> + <key>TYPE</key> + <integer>0</integer> + <key>UUID</key> + <string>17D06D06-18AD-4175-AA45-047F4984BE1A</string> + </dict> + <dict> + <key>MUST-CLOSE-APPLICATION-ITEMS</key> + <array/> + <key>MUST-CLOSE-APPLICATIONS</key> + <false/> + <key>PACKAGE_FILES</key> + <dict> + <key>DEFAULT_INSTALL_LOCATION</key> + <string>/</string> + <key>HIERARCHY</key> + <dict> + <key>CHILDREN</key> + <array> + <dict> + <key>CHILDREN</key> + <array> + <dict> + <key>BUNDLE_CAN_DOWNGRADE</key> + <false/> + <key>BUNDLE_POSTINSTALL_PATH</key> + <dict> + <key>PATH_TYPE</key> + <integer>0</integer> + </dict> + <key>BUNDLE_PREINSTALL_PATH</key> + <dict> + <key>PATH_TYPE</key> + <integer>0</integer> + </dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>80</integer> + <key>PATH</key> + <string>../../bin/Mac/NeuralPi.app</string> + <key>PATH_TYPE</key> + <integer>1</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>3</integer> + <key>UID</key> + <integer>0</integer> + </dict> + </array> + <key>GID</key> + <integer>80</integer> + <key>PATH</key> + <string>Applications</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>509</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>80</integer> + <key>PATH</key> + <string>Application Support</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Automator</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Documentation</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Extensions</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Filesystems</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Frameworks</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Input Methods</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Internet Plug-Ins</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>LaunchAgents</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>LaunchDaemons</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>PreferencePanes</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Preferences</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>80</integer> + <key>PATH</key> + <string>Printers</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>PrivilegedHelperTools</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>1005</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>QuickLook</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>QuickTime</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Screen Savers</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Scripts</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Services</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Widgets</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + </array> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Library</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <dict> + <key>CHILDREN</key> + <array> + <dict> + <key>CHILDREN</key> + <array/> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>Shared</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>1023</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + </array> + <key>GID</key> + <integer>80</integer> + <key>PATH</key> + <string>Users</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + </array> + <key>GID</key> + <integer>0</integer> + <key>PATH</key> + <string>/</string> + <key>PATH_TYPE</key> + <integer>0</integer> + <key>PERMISSIONS</key> + <integer>493</integer> + <key>TYPE</key> + <integer>1</integer> + <key>UID</key> + <integer>0</integer> + </dict> + <key>PAYLOAD_TYPE</key> + <integer>0</integer> + <key>PRESERVE_EXTENDED_ATTRIBUTES</key> + <false/> + <key>SHOW_INVISIBLE</key> + <false/> + <key>SPLIT_FORKS</key> + <true/> + <key>TREAT_MISSING_FILES_AS_WARNING</key> + <false/> + <key>VERSION</key> + <integer>5</integer> + </dict> + <key>PACKAGE_SETTINGS</key> + <dict> + <key>AUTHENTICATION</key> + <integer>1</integer> + <key>CONCLUSION_ACTION</key> + <integer>0</integer> + <key>FOLLOW_SYMBOLIC_LINKS</key> + <false/> + <key>IDENTIFIER</key> + <string>com.GuitarML.NeuralPi</string> + <key>LOCATION</key> + <integer>0</integer> + <key>NAME</key> + <string>Standalone</string> + <key>OVERWRITE_PERMISSIONS</key> + <false/> + <key>PAYLOAD_SIZE</key> + <integer>-1</integer> + <key>REFERENCE_PATH</key> + <string></string> + <key>RELOCATABLE</key> + <false/> + <key>USE_HFS+_COMPRESSION</key> + <false/> + <key>VERSION</key> + <string>##APPVERSION##</string> + </dict> + <key>TYPE</key> + <integer>0</integer> + <key>UUID</key> + <string>54266BE4-CE88-4033-9C8D-248D3D38D1E1</string> + </dict> + </array> + <key>PROJECT</key> + <dict> + <key>PROJECT_COMMENTS</key> + <dict> + <key>NOTES</key> + <data> + </data> + </dict> + <key>PROJECT_PRESENTATION</key> + <dict> + <key>BACKGROUND</key> + <dict> + <key>APPAREANCES</key> + <dict> + <key>DARK_AQUA</key> + <dict/> + <key>LIGHT_AQUA</key> + <dict/> + </dict> + <key>SHARED_SETTINGS_FOR_ALL_APPAREANCES</key> + <true/> + </dict> + <key>INSTALLATION TYPE</key> + <dict> + <key>HIERARCHIES</key> + <dict> + <key>INSTALLER</key> + <dict> + <key>LIST</key> + <array> + <dict> + <key>CHILDREN</key> + <array/> + <key>DESCRIPTION</key> + <array/> + <key>OPTIONS</key> + <dict> + <key>HIDDEN</key> + <false/> + <key>STATE</key> + <integer>1</integer> + </dict> + <key>PACKAGE_UUID</key> + <string>69EED16E-B119-4D35-B464-12717823DE0E</string> + <key>TITLE</key> + <array/> + <key>TYPE</key> + <integer>0</integer> + <key>UUID</key> + <string>32A671B5-085A-4D25-9B73-CA9157DA33C8</string> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>DESCRIPTION</key> + <array/> + <key>OPTIONS</key> + <dict> + <key>HIDDEN</key> + <false/> + <key>STATE</key> + <integer>1</integer> + </dict> + <key>PACKAGE_UUID</key> + <string>17D06D06-18AD-4175-AA45-047F4984BE1A</string> + <key>TITLE</key> + <array/> + <key>TYPE</key> + <integer>0</integer> + <key>UUID</key> + <string>6B0DDE9A-47F8-4615-BC79-4C475F3E41F7</string> + </dict> + <dict> + <key>CHILDREN</key> + <array/> + <key>DESCRIPTION</key> + <array/> + <key>OPTIONS</key> + <dict> + <key>HIDDEN</key> + <false/> + <key>STATE</key> + <integer>1</integer> + </dict> + <key>PACKAGE_UUID</key> + <string>54266BE4-CE88-4033-9C8D-248D3D38D1E1</string> + <key>TITLE</key> + <array/> + <key>TYPE</key> + <integer>0</integer> + <key>UUID</key> + <string>7BA12230-EFE4-49DA-B02C-EAB67D03EF2D</string> + </dict> + </array> + <key>REMOVED</key> + <dict/> + </dict> + </dict> + <key>MODE</key> + <integer>2</integer> + </dict> + <key>INSTALLATION_STEPS</key> + <array> + <dict> + <key>ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS</key> + <string>ICPresentationViewIntroductionController</string> + <key>INSTALLER_PLUGIN</key> + <string>Introduction</string> + <key>LIST_TITLE_KEY</key> + <string>InstallerSectionTitle</string> + </dict> + <dict> + <key>ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS</key> + <string>ICPresentationViewReadMeController</string> + <key>INSTALLER_PLUGIN</key> + <string>ReadMe</string> + <key>LIST_TITLE_KEY</key> + <string>InstallerSectionTitle</string> + </dict> + <dict> + <key>ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS</key> + <string>ICPresentationViewLicenseController</string> + <key>INSTALLER_PLUGIN</key> + <string>License</string> + <key>LIST_TITLE_KEY</key> + <string>InstallerSectionTitle</string> + </dict> + <dict> + <key>ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS</key> + <string>ICPresentationViewDestinationSelectController</string> + <key>INSTALLER_PLUGIN</key> + <string>TargetSelect</string> + <key>LIST_TITLE_KEY</key> + <string>InstallerSectionTitle</string> + </dict> + <dict> + <key>ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS</key> + <string>ICPresentationViewInstallationTypeController</string> + <key>INSTALLER_PLUGIN</key> + <string>PackageSelection</string> + <key>LIST_TITLE_KEY</key> + <string>InstallerSectionTitle</string> + </dict> + <dict> + <key>ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS</key> + <string>ICPresentationViewInstallationController</string> + <key>INSTALLER_PLUGIN</key> + <string>Install</string> + <key>LIST_TITLE_KEY</key> + <string>InstallerSectionTitle</string> + </dict> + <dict> + <key>ICPRESENTATION_CHAPTER_VIEW_CONTROLLER_CLASS</key> + <string>ICPresentationViewSummaryController</string> + <key>INSTALLER_PLUGIN</key> + <string>Summary</string> + <key>LIST_TITLE_KEY</key> + <string>InstallerSectionTitle</string> + </dict> + </array> + <key>INTRODUCTION</key> + <dict> + <key>LOCALIZATIONS</key> + <array> + <dict> + <key>LANGUAGE</key> + <string>English</string> + <key>VALUE</key> + <dict> + <key>PATH</key> + <string>Intro.txt</string> + <key>PATH_TYPE</key> + <integer>3</integer> + </dict> + </dict> + </array> + </dict> + <key>LICENSE</key> + <dict> + <key>LOCALIZATIONS</key> + <array> + <dict> + <key>LANGUAGE</key> + <string>English</string> + <key>VALUE</key> + <dict> + <key>PATH</key> + <string>LICENSE.txt</string> + <key>PATH_TYPE</key> + <integer>3</integer> + </dict> + </dict> + </array> + <key>MODE</key> + <integer>0</integer> + </dict> + <key>README</key> + <dict> + <key>LOCALIZATIONS</key> + <array/> + </dict> + <key>TITLE</key> + <dict> + <key>LOCALIZATIONS</key> + <array> + <dict> + <key>LANGUAGE</key> + <string>English</string> + <key>VALUE</key> + <string>NeuralPi</string> + </dict> + </array> + </dict> + </dict> + <key>PROJECT_REQUIREMENTS</key> + <dict> + <key>LIST</key> + <array/> + <key>RESOURCES</key> + <array/> + <key>ROOT_VOLUME_ONLY</key> + <true/> + </dict> + <key>PROJECT_SETTINGS</key> + <dict> + <key>BUILD_FORMAT</key> + <integer>0</integer> + <key>BUILD_PATH</key> + <dict> + <key>PATH</key> + <string>../../build</string> + <key>PATH_TYPE</key> + <integer>1</integer> + </dict> + <key>EXCLUDED_FILES</key> + <array> + <dict> + <key>PATTERNS_ARRAY</key> + <array> + <dict> + <key>REGULAR_EXPRESSION</key> + <false/> + <key>STRING</key> + <string>.DS_Store</string> + <key>TYPE</key> + <integer>0</integer> + </dict> + </array> + <key>PROTECTED</key> + <true/> + <key>PROXY_NAME</key> + <string>Remove .DS_Store files</string> + <key>PROXY_TOOLTIP</key> + <string>Remove ".DS_Store" files created by the Finder.</string> + <key>STATE</key> + <true/> + </dict> + <dict> + <key>PATTERNS_ARRAY</key> + <array> + <dict> + <key>REGULAR_EXPRESSION</key> + <false/> + <key>STRING</key> + <string>.pbdevelopment</string> + <key>TYPE</key> + <integer>0</integer> + </dict> + </array> + <key>PROTECTED</key> + <true/> + <key>PROXY_NAME</key> + <string>Remove .pbdevelopment files</string> + <key>PROXY_TOOLTIP</key> + <string>Remove ".pbdevelopment" files created by ProjectBuilder or Xcode.</string> + <key>STATE</key> + <true/> + </dict> + <dict> + <key>PATTERNS_ARRAY</key> + <array> + <dict> + <key>REGULAR_EXPRESSION</key> + <false/> + <key>STRING</key> + <string>CVS</string> + <key>TYPE</key> + <integer>1</integer> + </dict> + <dict> + <key>REGULAR_EXPRESSION</key> + <false/> + <key>STRING</key> + <string>.cvsignore</string> + <key>TYPE</key> + <integer>0</integer> + </dict> + <dict> + <key>REGULAR_EXPRESSION</key> + <false/> + <key>STRING</key> + <string>.cvspass</string> + <key>TYPE</key> + <integer>0</integer> + </dict> + <dict> + <key>REGULAR_EXPRESSION</key> + <false/> + <key>STRING</key> + <string>.svn</string> + <key>TYPE</key> + <integer>1</integer> + </dict> + <dict> + <key>REGULAR_EXPRESSION</key> + <false/> + <key>STRING</key> + <string>.git</string> + <key>TYPE</key> + <integer>1</integer> + </dict> + <dict> + <key>REGULAR_EXPRESSION</key> + <false/> + <key>STRING</key> + <string>.gitignore</string> + <key>TYPE</key> + <integer>0</integer> + </dict> + </array> + <key>PROTECTED</key> + <true/> + <key>PROXY_NAME</key> + <string>Remove SCM metadata</string> + <key>PROXY_TOOLTIP</key> + <string>Remove helper files and folders used by the CVS, SVN or Git Source Code Management systems.</string> + <key>STATE</key> + <true/> + </dict> + <dict> + <key>PATTERNS_ARRAY</key> + <array> + <dict> + <key>REGULAR_EXPRESSION</key> + <false/> + <key>STRING</key> + <string>classes.nib</string> + <key>TYPE</key> + <integer>0</integer> + </dict> + <dict> + <key>REGULAR_EXPRESSION</key> + <false/> + <key>STRING</key> + <string>designable.db</string> + <key>TYPE</key> + <integer>0</integer> + </dict> + <dict> + <key>REGULAR_EXPRESSION</key> + <false/> + <key>STRING</key> + <string>info.nib</string> + <key>TYPE</key> + <integer>0</integer> + </dict> + </array> + <key>PROTECTED</key> + <true/> + <key>PROXY_NAME</key> + <string>Optimize nib files</string> + <key>PROXY_TOOLTIP</key> + <string>Remove "classes.nib", "info.nib" and "designable.nib" files within .nib bundles.</string> + <key>STATE</key> + <true/> + </dict> + <dict> + <key>PATTERNS_ARRAY</key> + <array> + <dict> + <key>REGULAR_EXPRESSION</key> + <false/> + <key>STRING</key> + <string>Resources Disabled</string> + <key>TYPE</key> + <integer>1</integer> + </dict> + </array> + <key>PROTECTED</key> + <true/> + <key>PROXY_NAME</key> + <string>Remove Resources Disabled folders</string> + <key>PROXY_TOOLTIP</key> + <string>Remove "Resources Disabled" folders.</string> + <key>STATE</key> + <true/> + </dict> + <dict> + <key>SEPARATOR</key> + <true/> + </dict> + </array> + <key>NAME</key> + <string>NeuralPi</string> + <key>PAYLOAD_ONLY</key> + <false/> + <key>TREAT_MISSING_PRESENTATION_DOCUMENTS_AS_WARNING</key> + <false/> + </dict> + </dict> + <key>TYPE</key> + <integer>0</integer> + <key>VERSION</key> + <integer>2</integer> +</dict> +</plist> diff --git a/installers/mac/build_mac_installer.sh b/installers/mac/build_mac_installer.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +script_file=NeuralPi.pkgproj + +app_version=$(cut -f 2 -d '=' <<< "$(grep 'CMAKE_PROJECT_VERSION:STATIC' ../../build/CMakeCache.txt)") +echo "Setting app version: $app_version..." +sed -i '' "s/##APPVERSION##/${app_version}/g" $script_file +sed -i '' "s/##APPVERSION##/${app_version}/g" Intro.txt + +echo "Copying License..." +cp ../../LICENSE.txt LICENSE.txt + +# build installer +echo Building... +/usr/local/bin/packagesbuild $script_file + +# reset version number +sed -i '' "s/${app_version}/##APPVERSION##/g" $script_file +sed -i '' "s/${app_version}/##APPVERSION##/g" Intro.txt + +# clean up license file +rm LICENSE.txt + +# sign the installer package +echo "Signing installer package..." +TEAM_ID=$(more ~/Developer/mac_id) +pkg_dir=NeuralPi_Installer_Packaged +rm -Rf $pkg_dir +mkdir $pkg_dir +productsign -s "$TEAM_ID" ../../build/NeuralPi.pkg $pkg_dir/NeuralPi-signed.pkg + +echo "Notarizing installer package..." +INSTALLER_PASS=$(more ~/Developer/mac_installer_pass) +npx notarize-cli --file $pkg_dir/NeuralPi-signed.pkg --bundle-id com.GuitarML.NeuralPi --asc-provider "$TEAM_ID" --username smartguitarml@gmail.com --password "$INSTALLER_PASS" + +echo "Building disk image..." +vol_name=Install_NeuralPi-$app_version +hdiutil create "$vol_name.dmg" -fs HFS+ -srcfolder $pkg_dir -format UDZO -volname "$vol_name" diff --git a/installers/windows/NeuralPi_Install_Script.iss b/installers/windows/NeuralPi_Install_Script.iss @@ -0,0 +1,221 @@ +[Setup] +AppName=NeuralPi +AppVersion=##APPVERSION## +DisableWelcomePage=no +DisableDirPage=yes +DefaultDirName={commoncf64} +DefaultGroupName=NeuralPi +OutputBaseFilename="NeuralPi-Win-##APPVERSION##" +OutputDir=. +LicenseFile=../../LICENSE.txt +SetupIconFile=../../resources/neuralpi.ico + +[Types] +Name: "full"; Description: "Full installation" +Name: "custom"; Description: "Custom installation"; Flags: iscustom + +[Tasks] +Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; \ + GroupDescription: "{cm:AdditionalIcons}" + +[Components] +Name: "VST3_64"; Description: "VST3 Plugin 64-bit"; Types: full +Name: "VST3_32"; Description: "VST3 Plugin 32-bit"; Types: full +Name: "STANDALONE_64"; Description: "Standalone 64-bit"; Types: full +Name: "STANDALONE_32"; Description: "Standalone 32-bit"; Types: full +; Name: "AAX"; Description: "AAX Plugin"; Types: full + +[Files] +Source: "../../bin/Win64/NeuralPi.vst3"; DestDir: "{code:GetDir|VST3_64}"; Components: VST3_64; Flags: ignoreversion recursesubdirs createallsubdirs +Source: "../../bin/Win32/NeuralPi.vst3"; DestDir: "{code:GetDir|VST3_32}"; Components: VST3_32; Flags: ignoreversion recursesubdirs createallsubdirs +Source: "../../bin/Win64/NeuralPi.exe"; DestDir: "{code:GetDir|STANDALONE_64}"; Components: STANDALONE_64; Flags: ignoreversion recursesubdirs createallsubdirs +Source: "../../bin/Win32/NeuralPi.exe"; DestDir: "{code:GetDir|STANDALONE_32}"; Components: STANDALONE_32; Flags: ignoreversion recursesubdirs createallsubdirs +Source: "../../resources/neuralpi.ico"; Components: STANDALONE_64; DestDir: "{pf64}\GuitarML" +Source: "../../resources/neuralpi.ico"; Components: STANDALONE_32; DestDir: "{pf32}\GuitarML" + +[Icons] +Name: "{userdesktop}\NeuralPi"; Filename: "{pf64}\GuitarML\NeuralPi.exe"; \ + IconFilename: "{pf64}\GuitarML\neuralpi.ico"; Tasks: desktopicon; +Name: "{userdesktop}\NeuralPi32"; Filename: "{pf32}\GuitarML\NeuralPi.exe"; \ + IconFilename: "{pf32}\GuitarML\neuralpi.ico"; Tasks: desktopicon; + +[Code] +var + AAXDirPage: TInputDirWizardPage; + Vst3_64DirPage: TinputDirWizardPage; + Vst3_32DirPage: TinputDirWizardPage; + Standalone_64DirPage: TinputDirWizardPage; + Standalone_32DirPage: TinputDirWizardPage; + +procedure InitializeWizard; +begin + Log('Initializing extra pages') + //AAX Dir Page + AAXDirPage := CreateInputDirPage(wpSelectComponents, + 'Select AAX Install Location', 'Where would you like to install the AAX plugin?', + 'AAX plugin will be installed in the following folder.'#13#10#13#10 + + 'To continue, click Next. If you would like to select a different folder, click Browse.', + False, 'New Folder'); + + AAXDirPage.add(''); + AAXDirPage.values[0] := ExpandConstant('{commoncf64}\Avid\Audio\Plug-Ins'); + + //VST3 64-bit Dir Page + Vst3_64DirPage := CreateInputDirPage(AAXDirPage.ID, + 'Select Install Location for VST3 64-bit', 'Where would you like to install the plugin?', + 'VST3 64-bit plugin will be installed in the following folder.'#13#10#13#10 + + 'To continue, click Next. If you would like to select a different folder, click Browse.', + False, 'New Folder'); + + Vst3_64DirPage.add(''); + Vst3_64DirPage.values[0] := ExpandConstant('{commoncf64}\VST3'); + + + //VST3 32-bit Dir Page + Vst3_32DirPage := CreateInputDirPage(Vst3_64DirPage.ID, + 'Select Install Location for VST3 32-bit', 'Where would you like to install the plugin?', + 'VST3 32-bit plugin will be installed in the following folder.'#13#10#13#10 + + 'To continue, click Next. If you would like to select a different folder, click Browse.', + False, 'New Folder'); + + Vst3_32DirPage.add(''); + Vst3_32DirPage.values[0] := ExpandConstant('{commoncf32}\VST3'); + + //Standalone 64-bit Dir Page + Standalone_64DirPage := CreateInputDirPage(Vst3_32DirPage.ID, + 'Select Install Location for Standalone 64-bit', 'Where would you like to install the plugin?', + 'Standalone 64-bit plugin will be installed in the following folder.'#13#10#13#10 + + 'To continue, click Next. If you would like to select a different folder, click Browse.', + False, 'New Folder'); + + Standalone_64DirPage.add(''); + Standalone_64DirPage.values[0] := ExpandConstant('{pf64}\GuitarML'); + + + //Standalone 32-bit Dir Page + Standalone_32DirPage := CreateInputDirPage(Standalone_64DirPage.ID, + 'Select Install Location for Standalone 32-bit', 'Where would you like to install the plugin?', + 'Standalone 32-bit plugin will be installed in the following folder.'#13#10#13#10 + + 'To continue, click Next. If you would like to select a different folder, click Browse.', + False, 'New Folder'); + + Standalone_32DirPage.add(''); + Standalone_32DirPage.values[0] := ExpandConstant('{pf32}\GuitarML'); + +end; + +function IsSelected(Param: String) : Boolean; +begin + if not (Pos(Param, WizardSelectedComponents(False)) = 0) then // WizardSelectedComponents(False)) then + Result := True +end; + +function ShouldSkipPage(PageID: Integer): Boolean; +begin + { Skip pages that shouldn't be shown } + Result := False; + + if (PageID = AAXDirPage.ID) then + begin + Result := True; + Log('Selected 1: ' + WizardSelectedComponents(False)); + + if IsSelected ('aax') then + begin + Log('Not Skipping page'); + Result := False; + end + end + + else if (PageID = Vst3_64DirPage.ID) then + begin + Result := True; + Log('Selected 2: ' + WizardSelectedComponents(False)); + + if IsSelected ('vst3_64') then + begin + Log('Not Skipping'); + Result := False; + end + end + + else if (PageID = Vst3_32DirPage.ID) then + begin + Result := True; + Log('Selected 3: ' + WizardSelectedComponents(False)); + + if IsSelected ('vst3_32') then + begin + Log('Not Skipping'); + Result := False; + end + end + + else if (PageID = Standalone_64DirPage.ID) then + begin + Result := True; + Log('Selected 4: ' + WizardSelectedComponents(False)); + + if IsSelected ('standalone_64') then + begin + Log('Not Skipping'); + Result := False; + end + end + + else if (PageID = Standalone_32DirPage.ID) then + begin + Result := True; + Log('Selected 5: ' + WizardSelectedComponents(False)); + + if IsSelected ('standalone_32') then + begin + Log('Not Skipping'); + Result := False; + end + end + +end; + +function GetDir(Param: String) : String; +begin + if (Param = 'AAX') then + Result := AAXDirPage.values[0] + else if (Param = 'VST3_64') then + Result := Vst3_64DirPage.values[0] + else if (Param = 'VST3_32') then + Result := Vst3_32DirPage.values[0] + else if (Param = 'STANDALONE_64') then + Result := Standalone_64DirPage.values[0] + else if (Param = 'STANDALONE_32') then + Result := Standalone_32DirPage.values[0] +end; + +function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo, + MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String; +var + S: String; +begin + { Fill the 'Ready Memo' with the normal settings and the custom settings } + S := ''; + S := S + MemoTypeInfo + NewLine + NewLine; + S := S + MemoComponentsInfo + NewLine + NewLine; + S := S + 'Destination Location:' + NewLine; + + if IsSelected('aax') then + S := S + Space + GetDir('AAX') + ' (AAX)' + NewLine; + + if IsSelected('vst3_64') then + S := S + Space + GetDir('VST3_64') + ' (VST3 64-bit)' + NewLine; + + if IsSelected('vst3_32') then + S := S + Space + GetDir('VST3_32') + ' (VST3 32-bit)' + NewLine; + + if IsSelected('standalone_64') then + S := S + Space + GetDir('STANDALONE_64') + ' (Standalone 64-bit)' + NewLine; + + if IsSelected('standalone_32') then + S := S + Space + GetDir('STANDALONE_32') + ' (Standalone 32-bit)' + NewLine; + + Result := S; +end; diff --git a/installers/windows/build_win_installer.sh b/installers/windows/build_win_installer.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +script_file=NeuralPi_Install_Script.iss + +app_version=$(cut -f 2 -d '=' <<< "$(grep 'CMAKE_PROJECT_VERSION:STATIC' ../../build/CMakeCache.txt)") +echo "Setting app version: $app_version..." +sed -i "s/##APPVERSION##/${app_version}/g" $script_file + +# build installer +echo Building... +$"C:\Program Files (x86)\Inno Setup 6\ISCC.exe" $script_file + +# reset version number +sed -i "s/${app_version}/##APPVERSION##/g" $script_file + +exec="NeuralPi-Win-$app_version.exe" +direc=$PWD + + +echo SUCCESS diff --git a/mac_builds.sh b/mac_builds.sh @@ -0,0 +1,66 @@ +#!/bin/bash + +# exit on failure +set -e + +# clean up old builds +rm -Rf build/ +rm -Rf bin/*Mac* + + +# cmake new builds +TEAM_ID=$(more ~/Developer/mac_id) +cmake -Bbuild -DMACOS_RELEASE=ON -GXcode -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY="Developer ID Application" \ + -DCMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM="$TEAM_ID" \ + -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_STYLE="Manual" \ + -D"CMAKE_OSX_ARCHITECTURES=arm64;x86_64" \ + -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_INJECT_BASE_ENTITLEMENTS=NO \ + -DCMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS="--timestamp" \ + -DMACOS_RELEASE=ON +cmake --build build --config Release -j8 | xcpretty + +# copy builds to bin +mkdir -p bin/Mac +declare -a plugins=("NeuralPi") +for plugin in "${plugins[@]}"; do + cp -R build/${plugin}_artefacts/Release/Standalone/${plugin}.app bin/Mac/${plugin}.app + cp -R build/${plugin}_artefacts/Release/VST3/${plugin}.vst3 bin/Mac/${plugin}.vst3 + cp -R build/${plugin}_artefacts/Release/AU/${plugin}.component bin/Mac/${plugin}.component +done + + +# run auval +echo "Running AU validation..." +rm -Rf ~/Library/Audio/Plug-Ins/Components/${plugin}.component +cp -R build/${plugin}_artefacts/Release/AU/${plugin}.component ~/Library/Audio/Plug-Ins/Components +manu=$(cut -f 6 -d ' ' <<< "$(grep 'PLUGIN_MANUFACTURER_CODE' CMakeLists.txt)") +code=$(cut -f 6 -d ' ' <<< "$(grep 'PLUGIN_CODE' CMakeLists.txt)") + +set +e +auval_result=$(auval -v aufx "$code" "$manu") +auval_code="$?" +echo "AUVAL code: $auval_code" + +if [ "$auval_code" != 0 ]; then + echo "$auval_result" + echo "auval FAIL!!!" + #exit 1 +else + echo "auval PASSED" +fi + +# zip builds +echo "Zipping builds..." +VERSION=$(cut -f 2 -d '=' <<< "$(grep 'CMAKE_PROJECT_VERSION:STATIC' build/CMakeCache.txt)") +( + cd bin + rm -f "NeuralPi-Mac-${VERSION}.zip" + zip -r "NeuralPi-Mac-${VERSION}.zip" Mac +) + +# create installer +echo "Creating installer..." +( + cd installers/mac + bash build_mac_installer.sh +) diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt @@ -0,0 +1,57 @@ +# use DISTRHO-JUCE on Linux to support LV2 +if(UNIX AND NOT APPLE) + add_subdirectory(DISTRHO-JUCE) +else() + add_subdirectory(JUCE) +endif() + +include(cmake/SubprojectVersion.cmake) +subproject_version(JUCE juce_version) +message(STATUS "VERSION for JUCE: ${juce_version}") + +include(cmake/WarningFlags.cmake) +add_library(juce_plugin_modules STATIC) +add_subdirectory(json) +#add_subdirectory(RTNeural) +include_directories(RTNeural) + +target_link_libraries(juce_plugin_modules + PRIVATE + BinaryData + juce::juce_audio_utils + juce::juce_audio_plugin_client + juce::juce_dsp + juce::juce_osc + nlohmann_json::nlohmann_json + RTNeural + PUBLIC + juce::juce_recommended_config_flags + juce::juce_recommended_lto_flags + warning_flags +) + +target_compile_definitions(juce_plugin_modules + PUBLIC + JUCE_DISPLAY_SPLASH_SCREEN=0 + JUCE_REPORT_APP_USAGE=0 + JUCE_WEB_BROWSER=0 + JUCE_USE_CURL=0 + JUCE_VST3_CAN_REPLACE_VST2=0 + JucePlugin_Manufacturer="GuitarML" + JucePlugin_VersionString="${CMAKE_PROJECT_VERSION}" + JucePlugin_Name="${CMAKE_PROJECT_NAME}" + INTERFACE + $<TARGET_PROPERTY:juce_plugin_modules,COMPILE_DEFINITIONS> +) + +target_include_directories(juce_plugin_modules + INTERFACE + $<TARGET_PROPERTY:juce_plugin_modules,INCLUDE_DIRECTORIES> +) + +set_target_properties(juce_plugin_modules PROPERTIES + POSITION_INDEPENDENT_CODE TRUE + VISIBILITY_INLINES_HIDDEN TRUE + C_VISBILITY_PRESET hidden + CXX_VISIBILITY_PRESET hidden +) diff --git a/modules/DISTRHO-JUCE b/modules/DISTRHO-JUCE @@ -0,0 +1 @@ +Subproject commit 3bc7305ed68285902cac3d006beecd1c38c3f1fb diff --git a/modules/JUCE b/modules/JUCE @@ -0,0 +1 @@ +Subproject commit 90e8da0cfb54ac593cdbed74c3d0c9b09bad3a9f diff --git a/modules/cmake/SubprojectVersion.cmake b/modules/cmake/SubprojectVersion.cmake @@ -0,0 +1,20 @@ +# subproject_version(<subproject-name> <result-variable>) +# +# Extract version of a sub-project, which was previously included with add_subdirectory(). +function(subproject_version subproject_name VERSION_VAR) + # Read CMakeLists.txt for subproject and extract project() call(s) from it. + file(STRINGS "${${subproject_name}_SOURCE_DIR}/CMakeLists.txt" project_calls REGEX "[ \t]*project\\(") + # For every project() call try to extract its VERSION option + foreach(project_call ${project_calls}) + string(REGEX MATCH "VERSION[ ]+([^ )]+)" version_param "${project_call}") + if(version_param) + set(version_value "${CMAKE_MATCH_1}") + endif() + endforeach() + if(version_value) + set(${VERSION_VAR} "${version_value}" PARENT_SCOPE) + else() + message("WARNING: Cannot extract version for subproject '${subproject_name}'") + endif() + +endfunction(subproject_version) diff --git a/modules/cmake/WarningFlags.cmake b/modules/cmake/WarningFlags.cmake @@ -0,0 +1,49 @@ +add_library(warning_flags INTERFACE) + +if((CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") OR (CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC")) + target_compile_options(warning_flags INTERFACE + /W4 # base warning level + #/wd4458 # declaration hides class member (from Foley's GUI Magic) + /wd4505 # since VS2019 doesn't handle [[ maybe_unused ]] for static functions (RTNeural::debug_print) + /wd4244 # for XSIMD + ) +elseif((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") OR (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")) + target_compile_options(warning_flags INTERFACE + -Wall -Wshadow-all -Wshorten-64-to-32 -Wstrict-aliasing -Wuninitialized + -Wunused-parameter -Wconversion -Wsign-compare -Wint-conversion + -Wconditional-uninitialized -Woverloaded-virtual -Wreorder + -Wconstant-conversion -Wsign-conversion -Wunused-private-field + -Wbool-conversion -Wno-extra-semi -Wunreachable-code + -Wzero-as-null-pointer-constant -Wcast-align + -Wno-inconsistent-missing-destructor-override -Wshift-sign-overflow + -Wnullable-to-nonnull-conversion -Wno-missing-field-initializers + -Wno-ignored-qualifiers -Wpedantic -Wno-pessimizing-move + # These lines suppress some custom warnings. + # Comment them out to be more strict. + -Wno-shadow-field-in-constructor + # Supress warnings from xsimd + -Wno-cast-align -Wno-shadow -Wno-implicit-int-conversion + -Wno-zero-as-null-pointer-constant -Wno-sign-conversion + # Needed for ARM processor, OSX versions below 10.14 + -fno-aligned-allocation + ) +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(warning_flags INTERFACE + -Wall -Wextra -Wstrict-aliasing -Wuninitialized -Wunused-parameter + -Wsign-compare -Woverloaded-virtual -Wreorder -Wunreachable-code + -Wzero-as-null-pointer-constant -Wcast-align -Wno-implicit-fallthrough + -Wno-maybe-uninitialized -Wno-missing-field-initializers -Wno-pedantic + -Wno-ignored-qualifiers -Wno-unused-function -Wno-pessimizing-move + # From LV2 Wrapper + -Wno-parentheses -Wno-deprecated-declarations -Wno-redundant-decls + # For XSIMD + -Wno-zero-as-null-pointer-constant + # These lines suppress some custom warnings. + # Comment them out to be more strict. + -Wno-redundant-move + ) + + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "7.0.0") + target_compile_options(warning_flags INTERFACE "-Wno-strict-overflow") + endif() +endif() diff --git a/modules/json b/modules/json @@ -0,0 +1 @@ +Subproject commit 18a5f4c7ca30d9e0a2e4f12240c13f3e8ff4c82e diff --git a/resources/CMakeLists.txt b/resources/CMakeLists.txt @@ -0,0 +1,10 @@ +juce_add_binary_data(BinaryData SOURCES + logo.png + npi_background.jpg + ../models/bj_model_best.json + ../models/ts9_model_best.json +) + +# Need to build BinaryData with -fPIC flag on Linux +set_target_properties(BinaryData PROPERTIES + POSITION_INDEPENDENT_CODE TRUE) diff --git a/resources/logo.png b/resources/logo.png Binary files differ. diff --git a/resources/neuralpi.ico b/resources/neuralpi.ico Binary files differ. diff --git a/resources/neuralpi_pic.jpg b/resources/neuralpi_pic.jpg Binary files differ. diff --git a/resources/npi_background.jpg b/resources/npi_background.jpg Binary files differ. diff --git a/scripts/update_models.bat b/scripts/update_models.bat @@ -0,0 +1,31 @@ +::############################################################################ +:: NeuralPi - Update Models - Windows Script +:: +:: This script transfers models from a Windows computer to the NeuralPi, +:: and from the NeuralPi back to the host computer. Edit the Raspberry Pi +:: IP address (after connecting to a local Wifi Network), and run this +:: script from a Windows computer running the NeuralPi plugin. +:: +:: Note: Ensure OpenSSH is installed. This comes installed with Windows as of 2018. +::############################################################################ + +:: USER INPUTS + +:: Update this field with the Raspberry Pi's IP address +set "rpi_ip_address=127.0.0.1" + + +:: Typical Windows 10 Path, edit <YOUR_USERNAME> with your Windows Username +set "host_model_path=C:/Users/<YOUR_USERNAME>/Documents/GuitarML/NeuralPi/tones" + + +:: Rpi with Elk OS Path (shouldn't need to change) +set "rpi_model_path=/home/mind/Documents/GuitarML/NeuralPi/tones" + +:: ############################################################################ + +:: Copy all models from local computer to Rpi +scp %host_model_path%/*.json root@%rpi_ip_address%:%rpi_model_path%/ + +:: Copy all models from Rpi to local computer +scp root@%rpi_ip_address%:%rpi_model_path%/*.json %host_model_path%/ diff --git a/scripts/update_models.sh b/scripts/update_models.sh @@ -0,0 +1,30 @@ +############################################################################# +# NeuralPi - Update Models - Mac/Linux Script +# +# This script transfers models from a Linux or Mac computer to the NeuralPi, +# and from the NeuralPi back to the host computer. Edit the Raspberry Pi +# IP address (after connecting to a local Wifi Network), and run this +# script from a Linux/Mac computer running the NeuralPi plugin. +# +# Note: Ensure that all models have unique names or they will be overwritten +# Note: If prompted when connecting, type "yes" and hit enter +############################################################################# + +# USER INPUTS # + +rpi_ip_address=127.0.0.1 # Update this field with the Raspberry Pi's IP address + + +# Uncomment the appropriate path for your computer: + +host_model_path=~/Documents/GuitarML/NeuralPi/tones #Typical Mac/Linux Path (shouldn't need to change) + + +rpi_model_path=/home/mind/Documents/GuitarML/NeuralPi/tones # Rpi with Elk OS Path (shouldn't need to change) + +############################################################################# +echo "Copying all models from local computer to Rpi.." +scp $host_model_path/*.json root@$rpi_ip_address:$rpi_model_path/ + +echo "Copying all models from Rpi to local computer.." +scp root@$rpi_ip_address:$rpi_model_path/*.json $host_model_path/ diff --git a/validate.sh b/validate.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +# install functions +install_pluginval_linux() +{ + curl -L "https://github.com/Tracktion/pluginval/releases/download/latest_release/pluginval_Linux.zip" -o pluginval.zip + unzip pluginval > /dev/null + echo "./pluginval" +} + +install_pluginval_mac() +{ + curl -L "https://github.com/Tracktion/pluginval/releases/download/latest_release/pluginval_macOS.zip" -o pluginval.zip + unzip pluginval > /dev/null + echo "pluginval.app/Contents/MacOS/pluginval" +} + +install_pluginval_win() +{ + powershell -Command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; Invoke-WebRequest https://github.com/Tracktion/pluginval/releases/download/latest_release/pluginval_Windows.zip -OutFile pluginval.zip" + powershell -Command "Expand-Archive pluginval.zip -DestinationPath ." + echo "./pluginval.exe" +} + +# install +if [[ "$OSTYPE" == "linux-gnu"* ]]; then + pluginval=$(install_pluginval_linux) + plugin="build/NeuralPi_artefacts/Release/VST3/NeuralPi.vst3" +elif [[ "$OSTYPE" == "darwin"* ]]; then + pluginval=$(install_pluginval_mac) + plugin="build/NeuralPi_artefacts/VST3/NeuralPi.vst3" +fi + +echo "Pluginval installed at ${pluginval}" +echo "Validating ${plugin}" +$pluginval --strictness-level 8 --validate-in-process --validate $plugin --timeout-ms 600000 +result=$? + +# clean up +rm -Rf pluginval* +exit $result diff --git a/win_builds.sh b/win_builds.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +build64(){ + cmake -Bbuild -G"Visual Studio 15 2017 Win64" + #cmake -Bbuild -G"Visual Studio 16 2019 Win64" + cmake --build build --config Release -j4 +} + +build32(){ + cmake -Bbuild32 -G"Visual Studio 15 2017" + cmake --build build32 --config Release -j4 +} + +# exit on failure +set -e + +# clean up old builds +rm -Rf build/ +rm -Rf build32/ +rm -Rf bin/*Win64* +rm -Rf bin/*Win32* + +# set up VST and ASIO paths +sed -i -e "9s/#//" CMakeLists.txt +sed -i -e "10s/#//" CMakeLists.txt +sed -i -e '16s/#//' CMakeLists.txt + +# cmake new builds +build64 & +build32 & +wait + +# copy builds to bin +mkdir -p bin/Win64 +mkdir -p bin/Win32 +declare -a plugins=("NeuralPi") +for plugin in "${plugins[@]}"; do + cp -R build/${plugin}_artefacts/Release/Standalone/${plugin}.exe bin/Win64/${plugin}.exe + cp -R build/${plugin}_artefacts/Release/VST3/${plugin}.vst3 bin/Win64/${plugin}.vst3 + + cp -R build32/${plugin}_artefacts/Release/Standalone/${plugin}.exe bin/Win32/${plugin}.exe + cp -R build32/${plugin}_artefacts/Release/VST3/${plugin}.vst3 bin/Win32/${plugin}.vst3 +done + +# reset CMakeLists.txt +#git restore CMakeLists.txt + +# zip builds +VERSION=$(cut -f 2 -d '=' <<< "$(grep 'CMAKE_PROJECT_VERSION:STATIC' build/CMakeCache.txt)") +( + cd bin + rm -f "NeuralPi-Win64-${VERSION}.zip" + rm -f "NeuralPi-Win32-${VERSION}.zip" + tar -a -c -f "NeuralPi-Win64-${VERSION}.zip" Win64 + tar -a -c -f "NeuralPi-Win32-${VERSION}.zip" Win32 +) + +# create installer +echo "Creating installer..." +( + cd installers/windows + bash build_win_installer.sh +)