diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 4349183..28f77c3 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -22,6 +22,7 @@ jobs: - name: Install dependencies run: | + npm view npm get version of @babel/plugin-proposal-private-property-in-object version command sudo apt-get update sudo apt install gcc-10 g++-10 sudo apt-get install liblilv-dev libboost-dev libjack-jackd2-dev libnl-3-dev libnl-genl-3-dev libsystemd-dev catch diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f4a856..f47d767 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,10 @@ cmake_minimum_required(VERSION 3.16.0) project(pipedal - VERSION 1.1.27 + VERSION 1.1.28 DESCRIPTION "PiPedal Guitar Effect Pedal For Raspberry Pi" HOMEPAGE_URL "https://rerdavies.github.io/pipedal" ) -set (DISPLAY_VERSION "v1.1.27") +set (DISPLAY_VERSION "v1.1.28") set (CMAKE_INSTALL_PREFIX "/usr/") diff --git a/README.md b/README.md index f6e482c..1e017a0 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@
-Download: v1.1.27 +Download: v1.1.28 Website: [https://rerdavies.github.io/pipedal](https://rerdavies.github.io/pipedal). -> NEW version 1.1.27 release. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details. +> NEW version 1.1.28 release. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details. Use your Raspberry Pi as a guitar effects pedal. Configure and control PiPedal with your phone or tablet. PiPedal running on a Raspberry Pi 4 provides stable super-low-latency audio via external USB audio devices, or internal Raspberry Pi audio hats. diff --git a/debian/control b/debian/control index f961726..2cde9f0 100644 --- a/debian/control +++ b/debian/control @@ -9,6 +9,6 @@ Package: pipedal Pre-Depends: hostapd;authbind;dnsmasq Priority: optional Section: sound -Version: 1.1.27 +Version: 1.1.28 Installed-Size: 15147 diff --git a/docs/Installing.md b/docs/Installing.md index e3dcff2..ccbd7ab 100644 --- a/docs/Installing.md +++ b/docs/Installing.md @@ -13,14 +13,14 @@ page_icon: img/Install4.jpg Download the most recent Debian (.deb) package for your platform: -- [Ubuntu/Raspberry Pi OS (64-bit) v1.1.27](https://github.com/rerdavies/pipedal/releases/download/v1.1.27/pipedal_1.1.27_arm64.deb) +- [Ubuntu/Raspberry Pi OS (64-bit) v1.1.28](https://github.com/rerdavies/pipedal/releases/download/v1.1.28/pipedal_1.1.28_arm64.deb) Install the package by running ``` sudo apt update cd ~/Downloads - sudo apt-get install ./pipedal_1.1.27_arm64.deb + sudo apt-get install ./pipedal_1.1.28_arm64.deb ``` On Raspberry Pi OS, if you have a graphical desktop installed, you can also install the package by double-clicking on the downloaded package in the File Manager. diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index 8042be0..d948f82 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -1,5 +1,18 @@ # Release Notes +## PiPedal 1.1.28 + +New in this release. + +- TooB Neural Amp Modeler - an LV2 port of Steven Atkinson's astounding Neural Amp Modeler. Donwload model files from [ToneHunt.org](https://tonehunt.org). CPU is very high -- it runs on a Raspberry Pi 4 with just enough CPU left over for a couple of extra effects; but the results are worth it. TooB ML provides Neural Net modelled guitar amps with more modest CPU use (but considerably less flexibility). + +- Dark mode. Go to the settings dialog to turn on Dark Mode. + +Bug fixes: + +- Plugin information is updated in the web app after reconnecting, in order to pick up new plugins that may have been detected while the web server was being restarted. + + ## PiPedal 1.1.27 - Add missing lv2-dev dependency. diff --git a/docs/download.md b/docs/download.md index a9b6ecf..d936325 100644 --- a/docs/download.md +++ b/docs/download.md @@ -4,14 +4,14 @@ Download the most recent Debian (.deb) package for your platform: -- [Ubuntu or Raspberry Pi OS (64-bit)](https://github.com/rerdavies/pipedal/releases/download/v1.1.27/pipedal_1.1.27_arm64.deb) v1.1.27 +- [Ubuntu or Raspberry Pi OS (64-bit)](https://github.com/rerdavies/pipedal/releases/download/v1.1.28/pipedal_1.1.28_arm64.deb) v1.1.28 Install the package by running ``` sudo apt update cd ~/Downloads - sudo apt-get install ./pipedal_1.1.27_arm64.deb + sudo apt-get install ./pipedal_1.1.28_arm64.deb ``` Follow the instructions in [_Configuring PiPedal After Installation_](https://rerdavies.github.io/pipedal/Configuring.html) to complete the installation. diff --git a/docs/index.md b/docs/index.md index 3da59a5..2cddeeb 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,13 +1,13 @@ -v1.1.27 +v1.1.28   To download PiPedal, click [here](download.md). -> NEW version 1.1.27 release. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details. +> NEW version 1.1.28 release. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details. Use your Raspberry Pi as a guitar effects pedal. Configure and control PiPedal with your phone or tablet. diff --git a/lv2/aarch64/ToobAmp.lv2/CabSim.ttl b/lv2/aarch64/ToobAmp.lv2/CabSim.ttl index 19afc85..eeaa340 100644 --- a/lv2/aarch64/ToobAmp.lv2/CabSim.ttl +++ b/lv2/aarch64/ToobAmp.lv2/CabSim.ttl @@ -53,7 +53,7 @@ rdfs:comment "TooB amplifier cabinet simulator." ; mod:brand "TooB"; - mod:label "Cab Simulator"; + mod:label "TooB CabSim"; lv2:requiredFeature urid:map ; lv2:optionalFeature lv2:hardRTCapable; patch:readable diff --git a/lv2/aarch64/ToobAmp.lv2/ConvolutionReverb.ttl b/lv2/aarch64/ToobAmp.lv2/ConvolutionReverb.ttl index 3c3899b..26ce1e3 100644 --- a/lv2/aarch64/ToobAmp.lv2/ConvolutionReverb.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ConvolutionReverb.ttl @@ -152,7 +152,7 @@ Source: https://www.openair.hosted.york.ac.uk/?page_id=483 """ ; mod:brand "TooB"; - mod:label "Convolution Reverb"; + mod:label "TooB CReverb"; lv2:requiredFeature urid:map; lv2:optionalFeature state::freePath, state:makePath, state:mapPath, work:schedule ; lv2:extensionData state:interface; diff --git a/lv2/aarch64/ToobAmp.lv2/ConvolutionReverbStereo.ttl b/lv2/aarch64/ToobAmp.lv2/ConvolutionReverbStereo.ttl index b8fcc90..bdf5e47 100644 --- a/lv2/aarch64/ToobAmp.lv2/ConvolutionReverbStereo.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ConvolutionReverbStereo.ttl @@ -152,7 +152,7 @@ Source: https://www.openair.hosted.york.ac.uk/?page_id=483 """ ; mod:brand "TooB"; - mod:label "Convolution Reverb (Stereo)"; + mod:label "TooB CReverb Stereo"; lv2:requiredFeature urid:map; lv2:optionalFeature state::freePath, state:makePath, state:mapPath, work:schedule ; lv2:extensionData state:interface; diff --git a/lv2/aarch64/ToobAmp.lv2/InputStage.ttl b/lv2/aarch64/ToobAmp.lv2/InputStage.ttl index a06205c..e910c7a 100644 --- a/lv2/aarch64/ToobAmp.lv2/InputStage.ttl +++ b/lv2/aarch64/ToobAmp.lv2/InputStage.ttl @@ -62,7 +62,7 @@ inputStage:filterGroup lv2:microVersion 16 ; mod:brand "TooB"; - mod:label "Input Stage"; + mod:label "TooB Input"; lv2:requiredFeature urid:map ; lv2:optionalFeature lv2:hardRTCapable; diff --git a/lv2/aarch64/ToobAmp.lv2/ToobAmp.so.0 b/lv2/aarch64/ToobAmp.lv2/ToobAmp.so.0 index e6c1782..0c867e8 120000 --- a/lv2/aarch64/ToobAmp.lv2/ToobAmp.so.0 +++ b/lv2/aarch64/ToobAmp.lv2/ToobAmp.so.0 @@ -1 +1 @@ -ToobAmp.so.1.0.16 \ No newline at end of file +ToobAmp.so.1.0.17 \ No newline at end of file diff --git a/lv2/aarch64/ToobAmp.lv2/ToobAmp.so.1.0.16 b/lv2/aarch64/ToobAmp.lv2/ToobAmp.so.1.0.16 deleted file mode 100644 index d42f7ed..0000000 Binary files a/lv2/aarch64/ToobAmp.lv2/ToobAmp.so.1.0.16 and /dev/null differ diff --git a/lv2/aarch64/ToobAmp.lv2/ToobAmp.so.1.0.17 b/lv2/aarch64/ToobAmp.lv2/ToobAmp.so.1.0.17 new file mode 100644 index 0000000..90b5a3d Binary files /dev/null and b/lv2/aarch64/ToobAmp.lv2/ToobAmp.so.1.0.17 differ diff --git a/lv2/aarch64/ToobAmp.lv2/ToobChorus.ttl b/lv2/aarch64/ToobAmp.lv2/ToobChorus.ttl index c9e66c9..8de8594 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobChorus.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobChorus.ttl @@ -46,7 +46,7 @@ Emulation of a Boss CE-2 Chorus. """ ; mod:brand "TooB"; - mod:label "CE-2 Chorus"; + mod:label "TooB CE-2"; lv2:optionalFeature lv2:hardRTCapable; lv2:port diff --git a/lv2/aarch64/ToobAmp.lv2/ToobDelay.ttl b/lv2/aarch64/ToobAmp.lv2/ToobDelay.ttl index 190a483..7445cab 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobDelay.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobDelay.ttl @@ -46,7 +46,7 @@ A straighforward no-frills digital delay. """ ; mod:brand "TooB"; - mod:label "Delay"; + mod:label "TooB Delay"; lv2:optionalFeature lv2:hardRTCapable; lv2:port diff --git a/lv2/aarch64/ToobAmp.lv2/ToobFlanger.ttl b/lv2/aarch64/ToobAmp.lv2/ToobFlanger.ttl index 45235e2..7cd9ba6 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobFlanger.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobFlanger.ttl @@ -49,7 +49,7 @@ Emulation of a Boss BF-2 Flanger*. """ ; mod:brand "TooB"; - mod:label "BF-2 Flanger"; + mod:label "TooB BF-2"; lv2:optionalFeature lv2:hardRTCapable; lv2:port diff --git a/lv2/aarch64/ToobAmp.lv2/ToobFlangerStereo.ttl b/lv2/aarch64/ToobAmp.lv2/ToobFlangerStereo.ttl index e2267a5..4063130 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobFlangerStereo.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobFlangerStereo.ttl @@ -52,7 +52,7 @@ should use the monophonic version of this effect. """ ; mod:brand "TooB"; - mod:label "BF-2 Stereo Flanger"; + mod:label "TooB BF-2 Stereo"; lv2:optionalFeature lv2:hardRTCapable; lv2:port diff --git a/lv2/aarch64/ToobAmp.lv2/ToobFreeverb.ttl b/lv2/aarch64/ToobAmp.lv2/ToobFreeverb.ttl index b8a6e0c..c93bcd8 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobFreeverb.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobFreeverb.ttl @@ -48,7 +48,7 @@ The core implementation is taken from the The Synthesis Toolkit in C++ (STK), ht """ ; mod:brand "TooB"; - mod:label "Freeverb"; + mod:label "TooB Freeverb"; lv2:optionalFeature lv2:hardRTCapable; lv2:port diff --git a/lv2/aarch64/ToobAmp.lv2/ToobML.ttl b/lv2/aarch64/ToobAmp.lv2/ToobML.ttl index 85da755..c3b0065 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobML.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobML.ttl @@ -40,7 +40,7 @@ toobml:filterGroup a lv2:Plugin , - lv2:SimulatorPlugin ; + lv2:AmplifierPlugin ; doap:name "TooB ML Amplifier" , "TooB ML Amplifier"@en-gb ; @@ -63,7 +63,7 @@ and uses model files from from the GuitarML Neural Pi project (https://github.co """ ; mod:brand "TooB"; - mod:label "ML Amplifier"; + mod:label "TooB ML"; lv2:requiredFeature urid:map, work:schedule ; lv2:optionalFeature lv2:hardRTCapable; lv2:extensionData state:interface, work:interface; diff --git a/lv2/aarch64/ToobAmp.lv2/ToobNeuralAmpModeler.ttl b/lv2/aarch64/ToobAmp.lv2/ToobNeuralAmpModeler.ttl new file mode 100644 index 0000000..127b268 --- /dev/null +++ b/lv2/aarch64/ToobAmp.lv2/ToobNeuralAmpModeler.ttl @@ -0,0 +1,310 @@ +@prefix doap: . +@prefix lv2: . +@prefix rdf: . +@prefix rdfs: . +@prefix units: . +@prefix urid: . +@prefix atom: . +@prefix midi: . +@prefix epp: . +@prefix uiext: . +@prefix idpy: . +@prefix foaf: . +@prefix mod: . +@prefix param: . +@prefix work: . +@prefix pg: . +@prefix pipedal_ui: . +@prefix ui: . + +@prefix atom: . +@prefix patch: . +@prefix rdfs: . +@prefix state: . +@prefix urid: . +@prefix xsd: . +@prefix toobNam: . + + + + a foaf:Person ; + foaf:name "Robin Davies" ; + foaf:mbox ; + foaf:homepage . + + +toobNam:modelFile + a lv2:Parameter; + rdfs:label "Model"; + rdfs:range atom:Path. + +toobNam:eqGroup + a param:ControlGroup , + pg:InputGroup ; + lv2:name "Tone" ; + lv2:symbol "eqGroup" . + + + + a lv2:Plugin , + lv2:AmplifierPlugin ; + doap:name "TooB Neural Amp Modeler" , + "TooB Neural Amp Modeler"@en-gb + ; + doap:license ; + doap:maintainer ; + lv2:minorVersion 0 ; + lv2:microVersion 16 ; + rdfs:comment """ +A port of Steven Atkinson's Neural Amp Modeler to LV2. + +TooB Neural Amp Modeler uses uplodable .nam model files. Download .nam files from http://tonehunt.org, and then upload them to TooB Neural Amp Modeler. + +https://tonehunt.org provides a huge selection of downloadable amp model files. + +TooB Neural Amp Modeler supports a much wider range of amp models than ToobML, but usually uses much more CPU. +You will need at least a Pi 4 to use Toob Neural Amp Modeler. If you are having trouble with CPU usage, tonehunt.org +does contain some smaller amp models. Search for the "feather" tag to find models that use less CPU. + +If you are interested in building your own amp models, please visit https://www.neuralampmodeler.com/ + +TooB Neural Amp Modeler uses code from the NeuralAmp Modeler Core project. The TooB Team wishes to express gratitude to Steven Atkinson for +making this extraordinary technology available as open-source code. + +Code from the the NeuralAmpModelerCore project (https://github.com/sdatkinson/NeuralAmpModelerCore) is provided under the following license. + +MIT License + +Copyright (c) 2023 Steven Atkinson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +""" ; + + mod:brand "TooB"; + mod:label "TooB NAM"; + lv2:optionalFeature lv2:hardRTCapable; + + patch:readable + toobNam:modelFile; + patch:writable + toobNam:modelFile; + + lv2:extensionData state:interface, + work:interface; + + + lv2:port + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 0; + lv2:symbol "inputGain" ; + lv2:name "Input Gain"; + lv2:default 0.0 ; + lv2:minimum -20.0; + lv2:maximum 20.0; + units:unit units:db; + rdfs:comment "Input gain"; + + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 1; + lv2:symbol "outputGain" ; + lv2:name "Output Gain"; + lv2:default 0.0 ; + lv2:minimum -40.0; + lv2:maximum 40.0; + units:unit units:db; + rdfs:comment "Output gain"; + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 2; + lv2:symbol "gate" ; + lv2:name "Noise Gate"; + lv2:default -80.0 ; + lv2:minimum -100.0; + lv2:maximum 0.0; + rdfs:comment "Noise gate threshold. Set to minimum to disable."; + units:unit units:db; + lv2:scalePoint [ + rdfs:label "Off" ; + rdf:value -100.0 + ]; + + + ], + [ + a lv2:OutputPort , + lv2:ControlPort ; + + lv2:index 3; + lv2:symbol "gateOut" ; + lv2:name "\u00A0"; + lv2:default 0.0; + lv2:minimum 0.0; + lv2:maximum 1.0; + rdfs:comment "Gate Status"; + lv2:portProperty lv2:toggled; + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 4; + lv2:symbol "bass" ; + lv2:name "Bass"; + lv2:default 5.0; + lv2:minimum 0.0; + lv2:maximum 10.0; + rdfs:comment "Bass"; + pg:group toobNam:eqGroup ; + + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 5; + lv2:symbol "mid" ; + lv2:name "Mid"; + lv2:default 5.0; + lv2:minimum 0.0; + lv2:maximum 10.0; + rdfs:comment "Mid"; + pg:group toobNam:eqGroup ; + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 6; + lv2:symbol "treble" ; + lv2:name "Treble"; + lv2:default 5.0; + lv2:minimum 0.0; + lv2:maximum 10.0; + rdfs:comment "Bass"; + pg:group toobNam:eqGroup ; + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 7 ; + lv2:symbol "toneStack" ; + lv2:name "Type"; + lv2:default 0.0 ; + lv2:minimum 2.0 ; + lv2:maximum 3.0; + lv2:portProperty lv2:enumeration ; + + lv2:scalePoint [ + rdfs:label "Bassman" ; + rdf:value 0.0 + ] , [ + rdfs:label "JCM8000" ; + rdf:value 1.0 + ], [ + rdfs:label "Baxandall" ; + rdf:value 2.0 + ],[ + rdfs:label "Bypass" ; + rdf:value 3.0 + ]; + pg:group toobNam:eqGroup ; + + ], + [ + a lv2:AudioPort , + lv2:InputPort ; + lv2:index 8 ; + lv2:symbol "in" ; + lv2:name "In" + ], + [ + a lv2:AudioPort , + lv2:OutputPort ; + lv2:index 9 ; + lv2:symbol "out" ; + lv2:name "Out" + ], + [ + a atom:AtomPort , + lv2:InputPort; + atom:bufferType atom:Sequence ; + atom:supports patch:Message; + lv2:designation lv2:control ; + atom:supports patch:Message ; + + lv2:index 10 ; + lv2:symbol "control" ; + lv2:name "Control" ; + rdfs:comment "Control" ; + ] , [ + a atom:AtomPort , + lv2:OutputPort ; + atom:bufferType atom:Sequence ; + atom:supports patch:Message; + lv2:designation lv2:control ; + lv2:index 11; + lv2:symbol "notify" ; + lv2:name "Notify" ; + rdfs:comment "Notification" ; + ] + . + + pipedal_ui:ui toobNam:ui . + + +toobNam:ui + a pipedal_ui:ui ; + pipedal_ui:fileProperties + [ + a pipedal_ui:fileProperty; + rdfs:label "Model (*.nam)" ; + pipedal_ui:directory "NeuralAmpModels"; + lv2:index 8 ; + pipedal_ui:patchProperty toobNam:modelFile ; + pipedal_ui:fileTypes + [ + a pipedal_ui:fileType; + rdfs:label ".nam file"; + pipedal_ui:fileExtension ".nam"; + pipedal_ui:mimeType "application/octet-stream"; + ]; + ]; + pipedal_ui:frequencyPlot + [ + pipedal_ui:patchProperty toobNam:FrequencyResponse; + lv2:index 7 ; + pg:group toobNam:eqGroup ; + + pipedal_ui:width: 60; + ] + + . + diff --git a/lv2/aarch64/ToobAmp.lv2/manifest.ttl b/lv2/aarch64/ToobAmp.lv2/manifest.ttl index e54157f..b0ba1f6 100644 --- a/lv2/aarch64/ToobAmp.lv2/manifest.ttl +++ b/lv2/aarch64/ToobAmp.lv2/manifest.ttl @@ -12,6 +12,10 @@ lv2:binary ; rdfs:seeAlso . + a lv2:Plugin ; + lv2:binary ; + rdfs:seeAlso . + a lv2:Plugin ; lv2:binary ; rdfs:seeAlso . diff --git a/react/CMakeLists.txt b/react/CMakeLists.txt index d70a1f0..bc702e4 100644 --- a/react/CMakeLists.txt +++ b/react/CMakeLists.txt @@ -21,6 +21,7 @@ add_custom_command( WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/react DEPENDS + src/SelectThemeDialog.tsx src/OkCancelDialog.tsx src/PluginControl.tsx src/reportWebVitals.ts @@ -122,19 +123,20 @@ add_custom_command( src/PluginIcon.tsx src/UploadFileDialog.tsx - public/ - public/css public/css/roboto.css public/logo192x.png public/robots.txt - public/img public/img/ic_drawer_2.png public/img/fx_constant.svg + public/img/ic_presets_white.svg public/img/cx_mono.svg public/img/cx_stereo.svg public/img/fx_distortion.svg + public/img/Pi-Logo-3.png public/img/fx_oscillator.svg - public/img/settings_black_24dp.svg + src/svg/ic_save_bank_as.svg + src/svg/ic_edit_banks.svg + src/svg/ic_settings.svg public/img/fx_filter.svg public/img/delete_outline_black_24dp.svg public/img/fx_function.svg @@ -142,19 +144,16 @@ add_custom_command( public/img/old_delete_outline_black_24dp.svg public/img/ic_bank.svg public/img/fx_compressor.svg - public/img/file_download_black_24dp.svg public/img/fx_filter_hp.svg public/img/fx_plugin.svg public/img/fx_error.svg public/img/fx_gate.svg public/img/cc-by.png - public/img/ic_midi.svg - public/img/ic_pluginpreset.svg public/img/fx_split_a.svg public/img/fx_utility.svg + src/svg/ic_help_outline.svg public/img/ic_navigate_before.svg public/img/ic_drawer_2.svg - public/img/ic_pluginpreset2.svg public/img/fx_parametric_eq.svg public/img/fx_terminal.svg public/img/ic_presets.svg @@ -163,29 +162,25 @@ add_custom_command( public/img/fx_analyzer.svg public/img/fx_delay.svg public/img/fx_spectral.svg - public/img/edit_banks.svg public/img/fx_chorus.svg public/img/fx_simulator.svg - public/img/drive_file_rename_outline_black_24dp.svg public/img/fx_mixer.svg public/img/fx_eq.svg public/img/fx_modulator.svg - public/img/file_upload_black_24dp.svg public/img/fx_pitch.svg public/img/ic_navigate_next.svg public/img/fx_phaser.svg public/img/ic_drawer.svg public/img/fx_amplifier.svg - public/img/save_bank_as.svg public/img/fx_lr.svg public/img/fx_instrument.svg + public/img/Pi-Logo-3.svg public/img/fx_reverb.svg public/img/fx_limiter.svg public/img/VST_Logo_Steinberg.png public/img/fx_empty.svg public/img/fx_dial.svg public/img/ic_logo.svg - public/img/help_outline_black_24dp.svg public/img/fx_flanger2.svg public/img/old_delete_outline_white_24dp.svg public/img/fx_split_b.svg @@ -195,7 +190,6 @@ add_custom_command( public/img/vst.png public/sample_lv2_plugins.json public/serve.json - public/fonts public/fonts/Roboto-LightItalic.woff2 public/fonts/Roboto-Light.woff2 public/fonts/Roboto-BlackItalic.woff2 @@ -215,12 +209,55 @@ add_custom_command( public/logo512.png public/index.html public/manifest.json - public/var public/var/config.json public/var/current_pedalboard.json public/var/uiplugins.json public/var/plugin_classes.json - + src/svg/fx_constant.svg + src/svg/fx_distortion.svg + src/svg/fx_oscillator.svg + src/svg/fx_filter.svg + src/svg/fx_function.svg + src/svg/fx_generator.svg + src/svg/fx_compressor.svg + src/svg/file_download_black_24dp.svg + src/svg/fx_filter_hp.svg + src/svg/fx_plugin.svg + src/svg/fx_error.svg + src/svg/fx_gate.svg + src/svg/ic_midi.svg + src/svg/ic_pluginpreset.svg + src/svg/fx_split_a.svg + src/svg/fx_utility.svg + src/svg/ic_pluginpreset2.svg + src/svg/fx_parametric_eq.svg + src/svg/fx_terminal.svg + src/svg/ic_presets.svg + src/svg/fx_flanger.svg + src/svg/fx_analyzer.svg + src/svg/fx_delay.svg + src/svg/fx_spectral.svg + src/svg/fx_chorus.svg + src/svg/fx_simulator.svg + src/svg/fx_mixer.svg + src/svg/fx_eq.svg + src/svg/fx_modulator.svg + src/svg/file_upload_black_24dp.svg + src/svg/fx_pitch.svg + src/svg/fx_phaser.svg + src/svg/fx_amplifier.svg + src/svg/fx_lr.svg + src/svg/fx_instrument.svg + src/svg/fx_reverb.svg + src/svg/fx_limiter.svg + src/svg/fx_empty.svg + src/svg/fx_dial.svg + src/svg/fx_flanger2.svg + src/svg/fx_split_b.svg + src/svg/fx_spatial.svg + src/svg/fx_converter.svg + src/svg/old_delete_outline_24dp.svg + ) add_custom_target ( diff --git a/react/package.json b/react/package.json index 55a887c..32b1da0 100644 --- a/react/package.json +++ b/react/package.json @@ -18,11 +18,14 @@ "react": "^17.0.2", "react-dom": "^17.0.2", "react-remark": "^2.1.0", - "react-scripts": "5.0.0", + "react-scripts": "^5.0.1", "react-virtualized-auto-sizer": "^1.0.6", "react-window": "^1.8.6", "typescript": "^4.3.4", - "web-vitals": "^1.1.2" + "web-vitals": "^1.1.2", + + "@babel/plugin-proposal-private-property-in-object": "^7.2.11" + }, "scripts": { "start": "react-scripts start", diff --git a/react/public/img/Pi-Logo-3.png b/react/public/img/Pi-Logo-3.png new file mode 100644 index 0000000..d070532 Binary files /dev/null and b/react/public/img/Pi-Logo-3.png differ diff --git a/react/public/img/Pi-Logo-3.svg b/react/public/img/Pi-Logo-3.svg new file mode 100644 index 0000000..06525d6 --- /dev/null +++ b/react/public/img/Pi-Logo-3.svg @@ -0,0 +1,1227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/react/public/img/drive_file_rename_outline_black_24dp.svg b/react/public/img/drive_file_rename_outline_black_24dp.svg deleted file mode 100644 index c95c5b3..0000000 --- a/react/public/img/drive_file_rename_outline_black_24dp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/react/public/img/edit_banks_white.svg b/react/public/img/edit_banks_white.svg new file mode 100644 index 0000000..140c557 --- /dev/null +++ b/react/public/img/edit_banks_white.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + diff --git a/react/public/img/fx_flanger.svg b/react/public/img/fx_flanger.svg index cf33dd9..4c55387 100644 --- a/react/public/img/fx_flanger.svg +++ b/react/public/img/fx_flanger.svg @@ -1,72 +1,114 @@ - - - - - + + + + + + + + + + diff --git a/react/public/img/ic_presets_white.svg b/react/public/img/ic_presets_white.svg new file mode 100644 index 0000000..931ebb2 --- /dev/null +++ b/react/public/img/ic_presets_white.svg @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/react/public/img/settings_white_24dp.svg b/react/public/img/settings_white_24dp.svg new file mode 100644 index 0000000..1dbcba0 --- /dev/null +++ b/react/public/img/settings_white_24dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/react/public/index.html b/react/public/index.html index 3efcea9..87fec91 100644 --- a/react/public/index.html +++ b/react/public/index.html @@ -1,26 +1,24 @@ - - - - - - - - - - - - - - PiPedal + PiPedal + + + + - +
- + \ No newline at end of file diff --git a/react/src/App.tsx b/react/src/App.tsx index fb590a7..9efc4dd 100644 --- a/react/src/App.tsx +++ b/react/src/App.tsx @@ -21,6 +21,19 @@ import React from 'react'; import { ThemeProvider, createTheme, StyledEngineProvider, Theme } from '@mui/material/styles'; import AppThemed from "./AppThemed"; +import isDarkMode from './DarkMode'; + +declare module '@mui/material/styles' { + interface Theme { + mainBackground: React.CSSProperties['color']; + toolbarColor: React.CSSProperties['color']; + } + interface ThemeOptions { + mainBackground?: React.CSSProperties['color']; + toolbarColor?: React.CSSProperties['color']; + } + +} @@ -33,16 +46,38 @@ declare module '@mui/styles/defaultTheme' { -const theme = createTheme({ - palette: { - primary: { - main: "#6750A4" // #5B5690 #60529A #5C5694 +const theme = createTheme( + isDarkMode() ? + { + palette: { + mode: 'dark', + primary: { + main: '#A770E4'// #6750A4" // #5B5690 #60529A #5C5694 + }, + secondary: { + main: "#FF6060" + }, }, - secondary: { - main: "#FF6060" - } + mainBackground: "#222", + toolbarColor: '#FFFFFF' } -}); + : + { + palette: { + primary: { + main: "#6750A4" // #5B5690 #60529A #5C5694 + }, + secondary: { + main: "#FF6060" + } + + }, + mainBackground: "#FFFFFF", + toolbarColor: '#FFFFFF' + + + } +); diff --git a/react/src/AppThemed.css b/react/src/AppThemed.css index d169d81..e007307 100644 --- a/react/src/AppThemed.css +++ b/react/src/AppThemed.css @@ -15,3 +15,4 @@ div { min-width: 0; } + diff --git a/react/src/AppThemed.tsx b/react/src/AppThemed.tsx index d1195c3..aeb2ffa 100644 --- a/react/src/AppThemed.tsx +++ b/react/src/AppThemed.tsx @@ -60,10 +60,33 @@ import { BankIndex, BankIndexEntry } from './Banks'; import RenameDialog from './RenameDialog'; import JackStatusView from './JackStatusView'; import { Theme } from '@mui/material/styles'; +import isDarkMode from './DarkMode'; +import {ReactComponent as RenameOutlineIcon} from './svg/drive_file_rename_outline_black_24dp.svg'; +import {ReactComponent as SaveBankAsIcon} from './svg/ic_save_bank_as.svg'; +import {ReactComponent as EditBanksIcon} from './svg/ic_edit_banks.svg'; +import {ReactComponent as SettingsIcon} from './svg/ic_settings.svg'; +import {ReactComponent as HelpOutlineIcon} from './svg/ic_help_outline.svg'; const appStyles = (theme: Theme) => createStyles({ + "&": { // :root + colorScheme: (isDarkMode()? "dark": "light") + }, + menuListItem: { + color: "#FE8!important" as any, //theme.palette.text.primary, + fill: "#FE8!important" as any, //theme.palette.text.primary, + }, + menuIcon: { + fill: (theme.palette.text.primary + "!important") as any, //theme.palette.text.primary, + opacity: 0.6 + }, + toolBar: { + color: "white" + }, + listSubheader: { + backgroundImage: "linear-gradient(255,255,255,0.15),rgba(255,255,255,0.15)" + }, loadingContent: { display: "block", position: "absolute", @@ -72,7 +95,7 @@ const appStyles = (theme: Theme) => createStyles({ top: "0px", width: "100%", height: "100%", - background: "#DDD", + background: isDarkMode() ? "#222": "#DDD", opacity: "0.95", justifyContent: "center", textAlign: "center", @@ -91,13 +114,13 @@ const appStyles = (theme: Theme) => createStyles({ top: "0px", width: "100%", height: "100%", - color: "#444", + color: isDarkMode() ? "#CCC": "#444", zIndex: 2000 }, errorContentMask: { position: "absolute", left: 0, top: 0, width: "100%", height: "100%", - background: "#BBB", + background: isDarkMode()? "#121212": "#BBB", opacity: 0.95, zIndex: 1999 }, @@ -131,7 +154,7 @@ const appStyles = (theme: Theme) => createStyles({ }, errorMessage: { - color: '#000', + color: theme.palette.text.secondary, textAlign: "left", zIndex: 2010 @@ -140,7 +163,7 @@ const appStyles = (theme: Theme) => createStyles({ position: "relative", top: "20%", width: "240px", - color: "#888", + color: isDarkMode() ? theme.palette.text.secondary : "#888", marginLeft: "auto", marginRight: "auto", // border: "3px solid #888", @@ -167,7 +190,7 @@ const appStyles = (theme: Theme) => createStyles({ toolBarSpacer: { - position: "relative", flex: "0 0 auto" + position: "relative", flex: "0 0 auto", }, @@ -190,7 +213,7 @@ const appStyles = (theme: Theme) => createStyles({ }, heroContent: { - backgroundColor: "#FFFFFF", + backgroundColor: theme.mainBackground, position: "relative", height: "100%", width: "100%" @@ -198,7 +221,6 @@ const appStyles = (theme: Theme) => createStyles({ drawerItem: { width: 350, - background: "#FF8080" }, drawerItemFullWidth: { width: 'auto', @@ -490,7 +512,7 @@ const AppThemed = withStyles(appStyles)(class extends ResizeResponsiveComponent< private unmountListener(e: Event) { e.preventDefault(); - if (this.model_.state.get() === State.Ready && !this.model_.isAndroidHosted()) { + if ((!this.model_.reloadRequested) && this.model_.state.get() === State.Ready && !this.model_.isAndroidHosted()) { (e as any).returnValue = "Are you sure you want to leave this page?"; return "Are you sure you want to leave this page?"; } @@ -643,6 +665,7 @@ render() { return (
{(!this.state.tinyToolBar) ? ( - - + + { this.showDrawer() }} size="large"> - +
@@ -674,11 +698,12 @@ render() { { this.toggleFullScreen(); }} + color="inherit" size="large"> {this.state.isFullScreen ? ( - + ) : ( - + )} @@ -692,6 +717,7 @@ render() { style={{ position: "absolute", left: 12, top: 8, zIndex: 2 }} aria-label="menu" onClick={() => { this.showDrawer() }} + color="inherit" size="large"> @@ -699,6 +725,7 @@ render() { { this.toggleFullScreen(); }} size="large"> {this.state.isFullScreen ? ( @@ -714,9 +741,10 @@ render() { )} { this.hideDrawer(); }} > - Banks - }> + + Banks + + { shortBankList.map((bank) => { return ( @@ -746,18 +774,20 @@ render() { { this.handleDrawerRenameBank() }}> - + + + { this.handleDrawerSaveBankAs() }} > - + - { this.handleDrawerManageBanks(); }}> + { this.handleDrawerManageBanks(); }}> - + @@ -766,19 +796,19 @@ render() { { this.handleDrawerSettingsClick() }}> - + { this.handleDrawerAboutClick() }}> - + { this.handleDrawerDonationClick() }}> - - + + @@ -786,7 +816,8 @@ render() { {!this.state.tinyToolBar && ( - + )}
@@ -807,7 +838,7 @@ render() { { this.setState({ renameBankDialogOpen: false, diff --git a/react/src/BankDialog.tsx b/react/src/BankDialog.tsx index 3824983..7e380dd 100644 --- a/react/src/BankDialog.tsx +++ b/react/src/BankDialog.tsx @@ -46,6 +46,8 @@ import MenuItem from '@mui/material/MenuItem'; import DialogEx from './DialogEx'; import DialogContent from '@mui/material/DialogContent'; import DialogActions from '@mui/material/DialogActions'; +import { ReactComponent as DownloadIcon} from './svg/file_download_black_24dp.svg'; +import { ReactComponent as UploadIcon} from './svg/file_upload_black_24dp.svg'; interface BankDialogProps extends WithStyles { show: boolean; @@ -70,6 +72,10 @@ interface BankDialogState { const styles = (theme: Theme) => createStyles({ + listIcon: { + width: 24, height: 24, + opacity: 0.6, fill: theme.palette.text.primary + }, dialogAppBar: { position: 'relative', top: 0, left: 0 @@ -476,7 +482,8 @@ const BankDialog = withStyles(styles, { withTheme: true })( > { this.handleDownloadBank(); }} > - + Download bank @@ -488,7 +495,8 @@ const BankDialog = withStyles(styles, { withTheme: true })( - + Upload bank diff --git a/react/src/DarkMode.tsx b/react/src/DarkMode.tsx new file mode 100644 index 0000000..4bf6189 --- /dev/null +++ b/react/src/DarkMode.tsx @@ -0,0 +1,28 @@ +// Copyright (c) 2022 Robin Davies +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +export default function isDarkMode(): boolean { + let value = localStorage.getItem("darkMode"); + return value === "true"; +} + +export function setDarkMode(value: boolean): void { + localStorage.setItem("darkMode",value? "true": "false"); +} diff --git a/react/src/DraggableGrid.tsx b/react/src/DraggableGrid.tsx index eb17a5a..0a1e967 100644 --- a/react/src/DraggableGrid.tsx +++ b/react/src/DraggableGrid.tsx @@ -21,6 +21,7 @@ import { Theme } from '@mui/material/styles'; import createStyles from '@mui/styles/createStyles'; import { Property } from "csstype"; import { nullCast} from './Utility' +import isDarkMode from './DarkMode'; const SELECT_SCALE = 1.0; @@ -714,7 +715,7 @@ const DraggableGrid = this.savedIndex = gridElement.style.zIndex; gridElement.style.zIndex = "3"; this.savedBackground = gridElement.style.background; - gridElement.style.background = "#EEE"; + gridElement.style.background = isDarkMode()? "333": "#EEE"; gridElement.style.opacity = "0.8"; } } diff --git a/react/src/FilePropertyControl.tsx b/react/src/FilePropertyControl.tsx index eb2acc6..1e29b65 100644 --- a/react/src/FilePropertyControl.tsx +++ b/react/src/FilePropertyControl.tsx @@ -33,6 +33,7 @@ import { PiPedalModel, PiPedalModelFactory, ListenHandle} from './PiPedalModel'; import ButtonBase from '@mui/material/ButtonBase' import MoreHorizIcon from '@mui/icons-material/MoreHoriz'; import {PedalboardItem} from './Pedalboard'; +import isDarkMode from './DarkMode'; export const StandardItemSize = { width: 80, height: 140 } @@ -204,14 +205,16 @@ const FilePropertyControl =
{ this.onFileClick() }} > -
+
{value}
-
 
+
 
diff --git a/react/src/GxTunerControl.tsx b/react/src/GxTunerControl.tsx index ca81fdf..e222533 100644 --- a/react/src/GxTunerControl.tsx +++ b/react/src/GxTunerControl.tsx @@ -24,6 +24,7 @@ import createStyles from '@mui/styles/createStyles'; import withStyles from '@mui/styles/withStyles'; import { MonitorPortHandle, PiPedalModel, State, PiPedalModelFactory } from "./PiPedalModel"; import SvgPathBuilder from './SvgPathBuilder' +import isDarkMode from './DarkMode'; //const char* model[] = {"12-TET","19-TET","24-TET", "31-TET", "53-TET"}; // set_adjustment(ui->widget[2]->adj,440.0, 440.0, 427.0, 453.0, 0.1, CL_CONTINUOS); @@ -386,15 +387,19 @@ const GxTunerControl = } render() { - + let textColor = isDarkMode() ? "#999": "#444"; return (
- {this.state.pitchInfo.name} + {this.state.pitchInfo.name}
- {this.state.pitchInfo.fractionText} + {this.state.pitchInfo.fractionText}
{ this.renderDial(this.state.pitchInfo) } diff --git a/react/src/JackHostStatus.tsx b/react/src/JackHostStatus.tsx index 7adbd4d..dc831f0 100644 --- a/react/src/JackHostStatus.tsx +++ b/react/src/JackHostStatus.tsx @@ -19,10 +19,11 @@ import React from 'react'; import Typography from '@mui/material/Typography'; +import isDarkMode from './DarkMode'; -const RED_COLOR = "#C00"; -const GREEN_COLOR = "#666"; +const RED_COLOR = isDarkMode()? "#F88":"#C00"; +const GREEN_COLOR = isDarkMode()? "rgba(255,255,255,0.7)": "#666"; @@ -85,13 +86,13 @@ export default class JackHostStatus { static getCpuInfo(label: string, status?: JackHostStatus): React.ReactNode { if (!status) { return (
- {label} + {label}  
); } return (
- {label} - + {label} + { (status.cpuFreqMax === status.cpuFreqMin)? ( @@ -110,14 +111,14 @@ export default class JackHostStatus { static getDisplayView(label: string, status?: JackHostStatus): React.ReactNode { if (!status) { return (
- {label} + {label}  
); } if (status.restarting) { return (
- {label} + {label} Restarting   @@ -135,7 +136,7 @@ export default class JackHostStatus { } else if (!status.active) { return (
- {label} + {label} {status.errorMessage === "" ? "Stopped" : status.errorMessage}   @@ -154,7 +155,7 @@ export default class JackHostStatus { let underrunError = status.msSinceLastUnderrun < 15 * 1000; return (
- {label} + {label} XRuns: {status.underruns + ""}   diff --git a/react/src/LoadPluginDialog.tsx b/react/src/LoadPluginDialog.tsx index 54005fc..f818115 100644 --- a/react/src/LoadPluginDialog.tsx +++ b/react/src/LoadPluginDialog.tsx @@ -86,7 +86,7 @@ const pluginGridStyles = (theme: Theme) => createStyles({ justifyContent: "space-between", paddingLeft: "24px", paddingRight: "48px", - background: theme.palette.background.paper, + }, paper: { position: "relative", @@ -602,7 +602,7 @@ export const LoadPluginDialog =
- +
diff --git a/react/src/Lv2Plugin.tsx b/react/src/Lv2Plugin.tsx index 8f24139..8dd200a 100644 --- a/react/src/Lv2Plugin.tsx +++ b/react/src/Lv2Plugin.tsx @@ -204,6 +204,42 @@ export class UiPropertyNotification { plugin: string = ""; protocol: string = ""; +}; + +export class UiFrequencyPlot { + deserialize(input: any): UiFrequencyPlot + { + this.patchProperty = input.patchProperty; + this.index = input.index; + this.portGroup = input.portGroup; + this.xLeft = input.xLeft; + this.xRight = input.xRight; + this.xLog = input.xLog; + this.yTop = input.yTop; + this.yBottom = input.yBottom; + this.width = input.width; + return this; + } + static deserialize_array(input: any): UiFrequencyPlot[] + { + let result: UiFrequencyPlot[] = []; + for (let i = 0; i < input.length; ++i) + { + result[i] = new UiFrequencyPlot().deserialize(input[i]); + } + return result; + } + + patchProperty: string = ""; + index: number = -1; + portGroup: string = ""; + xLeft: number = -1; + xRight: number = -1; + xLog: boolean = true; + yTop: number = -1; + yBottom: number = -1; + width: number = -1; + }; export class UiFileProperty { deserialize(input: any): UiFileProperty @@ -253,6 +289,10 @@ export class UiFileProperty { { return true; } + if (fileType.fileExtension === "*" || fileType.fileExtension === "") + { + return true; + } } return false; } @@ -270,6 +310,8 @@ export class Lv2Plugin implements Deserializable { { this.uri = input.uri; this.name = input.name; + this.brand = input.name? input.name: ""; + this.label = input.label? input.label: this.name; this.plugin_class = input.plugin_class; this.supported_features = input.supported_features; this.required_features = input.required_features; @@ -285,6 +327,13 @@ export class Lv2Plugin implements Deserializable { } else { this.fileProperties = []; } + if (input.frequencyPlots) + { + this.frequencyPlots = UiFrequencyPlot.deserialize_array(input.frequencyPlots) + } else { + this.frequencyPlots = []; + } + if (input.uiPortNotifications) { this.uiPortNotifications = UiPropertyNotification.deserialize_array(input.uiPortNotifications); @@ -297,6 +346,8 @@ export class Lv2Plugin implements Deserializable { uri: string = ""; name: string = ""; + brand: string = ""; + label: string = ""; plugin_class: string = ""; supported_features: string[] = Lv2Plugin.EmptyFeatures; required_features: string[] = Lv2Plugin.EmptyFeatures; @@ -306,7 +357,8 @@ export class Lv2Plugin implements Deserializable { comment: string = ""; ports: Port[] = Port.EmptyPorts; port_groups: PortGroup[] = []; - fileProperties: UiFileProperty[] = []; + fileProperties: UiFileProperty[] = []; + frequencyPlots: UiFrequencyPlot[] = []; uiPortNotifications: UiPropertyNotification[] = []; } @@ -375,7 +427,19 @@ export enum PluginType { WaveshaperPlugin = "WaveshaperPlugin", // psuedo plugin type for the Amps node of the filter dialog. - PiPedalAmpsNode = "PiPedalAmpsNode" + PiPedalAmpsNode = "PiPedalAmpsNode", + + // pseudo plugin types for splitter. + SplitA = "SplitA", + SplitB = "SplitB", //"img/fx_split_b.svg"; + SplitMix = "SplitMix", //SplitMix; //"img/fx_dial.svg"; + SplitLR = "SplitLR", //"img/fx_dial.svg"; + + + + // psuedo plugin type for misc icons. + ErrorPlugin = "ErrorPlugin", + Terminal = "Terminal", //"img/fx_terminal.svg"; } @@ -656,6 +720,8 @@ export class UiPlugin implements Deserializable { { this.uri = input.uri; this.name = input.name; + this.brand = input.brand ? input.brand: ""; + this.label = input.label? input.label: this.name; this.plugin_type = input.plugin_type as PluginType; this.plugin_display_type = input.plugin_display_type; this.author_name = input.author_name; @@ -673,6 +739,12 @@ export class UiPlugin implements Deserializable { } else { this.fileProperties = []; } + if (input.frequencyPlots) + { + this.frequencyPlots = UiFrequencyPlot.deserialize_array(input.frequencyPlots) + } else { + this.frequencyPlots = []; + } this.is_vst3 = input.is_vst3; return this; @@ -726,6 +798,8 @@ export class UiPlugin implements Deserializable { uri: string = ""; name: string = ""; + brand: string = ""; + label: string = ""; plugin_type: PluginType = PluginType.InvalidPlugin; plugin_display_type: string = ""; author_name: string = ""; @@ -737,7 +811,8 @@ export class UiPlugin implements Deserializable { description: string = ""; controls: UiControl[] = []; port_groups: PortGroup[] = []; - fileProperties: UiFileProperty[] = []; + fileProperties: UiFileProperty[] = []; + frequencyPlots: UiFrequencyPlot[] = []; is_vst3 : boolean = false; } diff --git a/react/src/MainPage.tsx b/react/src/MainPage.tsx index baee5ab..6dd2b4c 100644 --- a/react/src/MainPage.tsx +++ b/react/src/MainPage.tsx @@ -45,7 +45,9 @@ import PluginInfoDialog from './PluginInfoDialog'; import { GetControlView } from './ControlViewFactory'; import MidiBindingsDialog from './MidiBindingsDialog'; import PluginPresetSelector from './PluginPresetSelector'; - +import {ReactComponent as OldDeleteIcon} from "./svg/old_delete_outline_24dp.svg"; +import {ReactComponent as MidiIcon} from "./svg/ic_midi.svg"; +import isDarkMode from './DarkMode'; const SPLIT_CONTROLBAR_THRESHHOLD = 650; @@ -461,7 +463,7 @@ export const MainPage =
{ this.onAddClick(e) }} size="large"> - + { this.onDeletePedal(pedalboardItem?.instanceId ?? -1) }} size="large"> - Delete +
@@ -493,7 +495,8 @@ export const MainPage = onClick={this.onLoadClick} disabled={this.state.selectedPedal === -1 || (!canLoad) || (this.getSelectedPedalboardItem()?.isSplit() ?? true)} startIcon={} - style={{ textTransform: "none" }} + style={{ textTransform: "none", + background: (isDarkMode() ? "#6750A4": undefined) }} > Load @@ -502,7 +505,7 @@ export const MainPage = { this.handleMidiConfiguration(instanceId); }} size="large"> - Midi configuration +
diff --git a/react/src/PedalboardView.tsx b/react/src/PedalboardView.tsx index 4ff409d..212f3f4 100644 --- a/react/src/PedalboardView.tsx +++ b/react/src/PedalboardView.tsx @@ -26,13 +26,15 @@ import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel'; import { PluginType } from './Lv2Plugin'; import ButtonBase from '@mui/material/ButtonBase'; import Typography from '@mui/material/Typography'; -import { SelectIcon } from './PluginIcon'; +import PluginIcon, {SelectIconUri} from './PluginIcon'; import { SelectHoverBackground } from './SelectHoverBackground'; import SvgPathBuilder from './SvgPathBuilder'; import Draggable from './Draggable' import Rect from './Rect'; import {PiPedalStateError} from './PiPedalError'; -import Utility from './Utility' +import Utility from './Utility'; +import isDarkMode from './DarkMode'; + import { Pedalboard, PedalboardItem, PedalboardSplitItem, SplitType } from './Pedalboard'; @@ -44,8 +46,9 @@ const END_CONTROL = Pedalboard.END_CONTROL; const START_PEDALBOARD_ITEM_URI = Pedalboard.START_PEDALBOARD_ITEM_URI; const END_PEDALBOARD_ITEM_URI = Pedalboard.END_PEDALBOARD_ITEM_URI; -const ENABLED_CONNECTOR_COLOR = "#666"; -const DISABLED_CONNECTOR_COLOR = "#CCC"; +const ENABLED_CONNECTOR_COLOR = isDarkMode() ? "#CCC": "#666"; +const DISABLED_CONNECTOR_COLOR = isDarkMode() ? "#666": "#CCC"; + const CELL_WIDTH: number = 96; @@ -129,14 +132,14 @@ const pedalboardStyles = (theme: Theme) => createStyles({ alignItems: "center", justifyContent: "center", - background: theme.palette.background.paper, + background: theme.palette.background.default, marginLeft: (CELL_WIDTH - FRAME_SIZE) / 2, marginRight: (CELL_WIDTH - FRAME_SIZE) / 2, marginTop: (CELL_HEIGHT - FRAME_SIZE) / 2, marginBottom: (CELL_HEIGHT - FRAME_SIZE) / 2, width: FRAME_SIZE, height: FRAME_SIZE, - border: "1pt #666 solid", + border: isDarkMode()? "1pt #AAA solid": "1pt #666 solid", borderRadius: 6 }, borderlessIconFrame: { @@ -237,6 +240,7 @@ class PedalLayout { static Start(): PedalLayout { let t: PedalLayout = new PedalLayout(); t.uri = START_PEDALBOARD_ITEM_URI; + t.pluginType = PluginType.Terminal; t.iconUrl = TERMINAL_ICON_URL; t.numberOfInputs = 0; t.numberOfOutputs = 2; @@ -244,7 +248,7 @@ class PedalLayout { } static End(): PedalLayout { let t: PedalLayout = new PedalLayout(); - t.pluginType = PluginType.UtilityPlugin; + t.pluginType = PluginType.Terminal; t.uri = END_PEDALBOARD_ITEM_URI; t.iconUrl = TERMINAL_ICON_URL; t.numberOfInputs = 2; @@ -272,7 +276,7 @@ class PedalLayout { } else if (pedalItem.isEmpty()) { - this.pluginType = PluginType.UtilityPlugin; + this.pluginType = PluginType.None; this.iconUrl = EMPTY_ICON_URL; this.numberOfInputs = 2; this.numberOfOutputs = 2; @@ -293,13 +297,13 @@ class PedalLayout { let uiPlugin = model.getUiPlugin(pedalItem.uri); if (uiPlugin != null) { this.pluginType = uiPlugin.plugin_type; - this.iconUrl = SelectIcon(uiPlugin.plugin_type,uiPlugin.uri); - this.name = uiPlugin.name; + this.iconUrl = SelectIconUri(uiPlugin.plugin_type); + this.name = uiPlugin.label; this.numberOfInputs = Math.max(uiPlugin.audio_inputs,2); this.numberOfOutputs = Math.max(uiPlugin.audio_outputs,2); } else { // default to empty plugin. - this.pluginType = PluginType.UtilityPlugin; + this.pluginType = PluginType.ErrorPlugin; this.name = pedalItem.pluginName??"#error"; this.iconUrl = ERROR_ICON_URL; this.numberOfInputs = 2; @@ -525,22 +529,22 @@ const PedalboardView = } } - getSplitterIcon(layoutItem: PedalLayout) { + getSplitterIcon(layoutItem: PedalLayout): PluginType { if (layoutItem.pedalItem === undefined) { throw new Error("Invalid splitter"); } let split = layoutItem.pedalItem as PedalboardSplitItem; if (split.getSplitType() === SplitType.Ab) { if (split.isASelected()) { - return "img/fx_split_a.svg"; + return PluginType.SplitA; } else { - return "img/fx_split_b.svg"; + return PluginType.SplitB; } } else if (split.getSplitType() === SplitType.Mix) { - return "img/fx_dial.svg"; + return PluginType.SplitMix; //"img/fx_dial.svg"; } else { - return "img/fx_lr.svg"; + return PluginType.SplitLR; //"img/fx_lr.svg"; } } @@ -675,7 +679,7 @@ const PedalboardView = event.preventDefault(); event.stopPropagation(); - if (!Utility.isTouchDevice()) { + if (!Utility.needsZoomedControls()) { if (this.props.onDoubleClick && instanceId) { this.props.onDoubleClick(instanceId); } @@ -903,7 +907,7 @@ const PedalboardView = throw new PiPedalStateError("scroll container not found."); } - pedalButton(instanceId: number, iconUrl: string, draggable: boolean, enabled: boolean,hasBorder: boolean = true): ReactNode { + pedalButton(instanceId: number, iconType: PluginType, draggable: boolean, enabled: boolean,hasBorder: boolean = true): ReactNode { let classes = this.props.classes; return (
{ e.preventDefault(); }}> @@ -917,8 +921,7 @@ const PedalboardView = this.getScrollContainer()} onDragEnd={(x, y) => { this.onDragEnd(instanceId, x, y) }} > - Pedal + @@ -982,7 +985,7 @@ const PedalboardView = result.push(
- {this.pedalButton(START_CONTROL, item.iconUrl, false,true,false)} + {this.pedalButton(START_CONTROL, item.pluginType,false,true,false)}
); break; @@ -990,7 +993,7 @@ const PedalboardView = result.push(
- {this.pedalButton(END_CONTROL, item.iconUrl, false,true,false)} + {this.pedalButton(END_CONTROL, item.pluginType, false,true,false)}
); break; @@ -1011,11 +1014,11 @@ const PedalboardView = }}> {item.pedalItem?.pluginName} + >{item.name}
) result.push(
- {this.pedalButton(item.pedalItem?.instanceId ?? -1, item.iconUrl, !item.isEmpty(), item.pedalItem?.isEnabled ?? false)} + {this.pedalButton(item.pedalItem?.instanceId ?? -1, item.pluginType, !item.isEmpty(), item.pedalItem?.isEnabled ?? false)}
); diff --git a/react/src/PiPedalModel.tsx b/react/src/PiPedalModel.tsx index a4e0c79..c25b27d 100644 --- a/react/src/PiPedalModel.tsx +++ b/react/src/PiPedalModel.tsx @@ -38,7 +38,12 @@ import GovernorSettings from './GovernorSettings'; import WifiChannel from './WifiChannel'; import AlsaDeviceInfo from './AlsaDeviceInfo'; import { AndroidHostInterface, FakeAndroidHost } from './AndroidHost'; +import isDarkMode, {setDarkMode} from './DarkMode'; +export enum ColorTheme { + Light, + Dark +}; export enum State { Loading, @@ -470,8 +475,11 @@ export class PiPedalModel //implements PiPedalModel let value = body as number; this._setInputVolume(value,false); } else if (message === "onLv2StateChanged") { - let instanceId = body as number; - this.onLv2StateChanged(instanceId); + + let instanceId = body.instanceId as number; + let state = body.state as [boolean,any]; + + this.onLv2StateChanged(instanceId,state); } else if (message === "onVst3ControlChanged") { let controlChangedBody = body as Vst3ControlChangedBody; this._setVst3PedalboardControlValue( @@ -665,6 +673,10 @@ export class PiPedalModel //implements PiPedalModel this.getWebSocket().request("hello") .then(clientId => { this.clientId = clientId; + return this.getWebSocket().request("plugins"); + }) + .then(data => { + this.ui_plugins.set(UiPlugin.deserialize_array(data)); return this.getWebSocket().request("currentPedalboard"); }) .then(data => { @@ -1116,7 +1128,9 @@ export class PiPedalModel //implements PiPedalModel } } - private onLv2StateChanged(instanceId: number): void { + private onLv2StateChanged(instanceId: number, state: [boolean,any]): void { + let item = this.pedalboard.get().getItem(instanceId); + item.lv2State = state; for (let item of this.stateChangedListeners) { if (item.instanceId === instanceId) { item.onStateChanged(instanceId); @@ -2531,6 +2545,27 @@ export class PiPedalModel //implements PiPedalModel return nullCast(this.webSocket).request("newPreset"); } + getTheme(): ColorTheme { + return isDarkMode() ? ColorTheme.Dark: ColorTheme.Light; + } + + setTheme(value: ColorTheme) { + if (this.getTheme() !== value) + { + setDarkMode(value === ColorTheme.Dark); + this.reloadPage(); + } + } + + reloadRequested: boolean = false; + + reloadPage() { + this.reloadRequested = true; + // eslint-disable-next-line no-restricted-globals + location.reload(); + } + + }; let instance: PiPedalModel | undefined = undefined; diff --git a/react/src/PluginControl.tsx b/react/src/PluginControl.tsx index 38feddd..de6e453 100644 --- a/react/src/PluginControl.tsx +++ b/react/src/PluginControl.tsx @@ -1,4 +1,4 @@ - // Copyright (c) 2022 Robin Davies +// Copyright (c) 2022 Robin Davies // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in @@ -30,7 +30,7 @@ import Switch from '@mui/material/Switch'; import Utility, {nullCast} from './Utility'; import MenuItem from '@mui/material/MenuItem'; import {PiPedalModel,PiPedalModelFactory} from './PiPedalModel'; - +import {ReactComponent as DialIcon} from './svg/fx_dial.svg'; const MIN_ANGLE = -135; const MAX_ANGLE = 135; @@ -72,8 +72,8 @@ const styles = (theme: Theme) => createStyles({ right: 0, bottom: 4, textAlign: "center", - background: "white", - color: "#666", + background: theme.mainBackground, + color: theme.palette.text.secondary, // zIndex: -1, } }); @@ -98,7 +98,7 @@ const PluginControl = { frameRef: React.RefObject; - imgRef: React.RefObject; + imgRef: React.RefObject; inputRef: React.RefObject; selectRef: React.RefObject; @@ -137,7 +137,7 @@ const PluginControl = this.onInputKeyPress = this.onInputKeyPress.bind(this); } isTouchDevice(): boolean { - return Utility.isTouchDevice(); + return Utility.needsZoomedControls(); } showZoomedControl() @@ -243,7 +243,7 @@ const PluginControl = e.preventDefault(); } - isValidPointer(e: PointerEvent): boolean { + isValidPointer(e: PointerEvent): boolean { if (e.pointerType === "mouse") { return e.button === 0; } else if (e.pointerType === "pen") { @@ -257,9 +257,9 @@ const PluginControl = touchDown: boolean = false; touchIdentifier: number = -1; - onTouchStart(e: TouchEvent) { + onTouchStart(e: TouchEvent) { } - onTouchMove(e: TouchEvent) { + onTouchMove(e: TouchEvent) { // e.preventDefault(); e.stopPropagation(); // cancels scroll!!! @@ -282,7 +282,7 @@ const PluginControl = pointerId: number = 0; pointerType: string = ""; - isCapturedPointer(e: PointerEvent): boolean { + isCapturedPointer(e: PointerEvent): boolean { return this.mouseDown && e.pointerId === this.pointerId && e.pointerType === this.pointerType; @@ -294,9 +294,9 @@ const PluginControl = dRange: number = 0; pointersDown: number = 0; - captureElement?: HTMLElement = undefined; + captureElement?: SVGSVGElement = undefined; - onPointerDown(e: PointerEvent): void { + onPointerDown(e: PointerEvent): void { if (!this.mouseDown && this.isValidPointer(e)) { e.preventDefault(); e.stopPropagation(); @@ -351,7 +351,7 @@ const PluginControl = } - onPointerLostCapture(e: PointerEvent) { + onPointerLostCapture(e: PointerEvent) { if (this.isCapturedPointer(e)) { --this.pointersDown; @@ -361,7 +361,7 @@ const PluginControl = } - updateRange(e: PointerEvent): number { + updateRange(e: PointerEvent): number { let ultraHigh = false; let high = false; @@ -396,7 +396,7 @@ const PluginControl = this.lastX = e.clientX; return this.dRange; } - onPointerUp(e: PointerEvent) { + onPointerUp(e: PointerEvent) { if (this.isCapturedPointer(e)) { --this.pointersDown; @@ -414,7 +414,7 @@ const PluginControl = } } - releaseCapture(e: PointerEvent) + releaseCapture(e: PointerEvent) { let img = this.imgRef.current; @@ -437,7 +437,7 @@ const PluginControl = } - onPointerMove(e: PointerEvent): void { + onPointerMove(e: PointerEvent): void { if (this.isCapturedPointer(e)) { e.preventDefault(); let dRange = this.updateRange(e) @@ -547,7 +547,7 @@ const PluginControl = ); } else { return ( - this.handleChange(e, extra)} - onClose={(e) => this.handleSelectClose(e)} - displayEmpty - value={presets.selectedInstanceId === 0? '' : presets.selectedInstanceId} - inputProps={{ - classes: { icon: classes.icon }, - 'aria-label': "Select preset" - } - } - > - { - presets.presets.map((preset) => { - return ( - - {(presets.presetChanged && preset.instanceId === presets.selectedInstanceId) - ? (preset.name + "*") - : (preset.name) - } - - ); - }) - } - - ) : ( - { this.showPresetDialog(true); }} - > -
-
- - {this.state.presets.getSelectedText()} - -
-
-
- -
- -
- - )} +
-
+
this.handlePresetMenuClick(e)} - size="large"> - + size="large" + > + createStyles({ position: "absolute", width: "100%", height: "100%", - background: "#000000", - opacity: 0.1 + background: palette.action.hover, + //opacity: palette.action.hoverOpacity }, selected: { position: "absolute", width: "100%", height: "100%", - background: palette.action.active, - opacity: 0.3 + background: palette.action.selected, + //opacity: palette.action.selectedOpacity } }); diff --git a/react/src/SelectThemeDialog.tsx b/react/src/SelectThemeDialog.tsx new file mode 100644 index 0000000..92df59f --- /dev/null +++ b/react/src/SelectThemeDialog.tsx @@ -0,0 +1,85 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +// Copyright (c) 2023 Robin Davies +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import { useState } from 'react'; +import Button from '@mui/material/Button'; +import DialogTitle from '@mui/material/DialogTitle'; +import DialogContent from '@mui/material/DialogContent'; +import DialogActions from '@mui/material/DialogActions'; +import Dialog from '@mui/material/Dialog'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import FormControl from '@mui/material/FormControl'; + +import RadioGroup from '@mui/material/RadioGroup'; +import Radio from '@mui/material/Radio'; +import {ColorTheme} from './PiPedalModel'; + + + +export interface SelectThemesDialogProps { + open: boolean; + defaultTheme: ColorTheme; + onClose: () => void; + onOk: (value: ColorTheme) => void; +} + +function SelectThemesDialog(props: SelectThemesDialogProps) { + //const classes = useStyles(); + const { onClose, defaultTheme, open,onOk } = props; + const [ selectedTheme, setSelectedTheme ] = useState(defaultTheme); + const handleChange = (event: React.ChangeEvent) => { + let value = ((event.target as HTMLInputElement).value) === '0' ? ColorTheme.Light: ColorTheme.Dark; + setSelectedTheme(value); + }; + const handleClose = (): void => { + onClose(); + }; + const handleOk = (): void => { + onOk(selectedTheme) + }; + + + return ( + + Theme + + + + } label="Light" /> + } label="Dark" /> + + + + + + + + + ); +} + +export default SelectThemesDialog; diff --git a/react/src/SettingsDialog.tsx b/react/src/SettingsDialog.tsx index 024e910..ef31072 100644 --- a/react/src/SettingsDialog.tsx +++ b/react/src/SettingsDialog.tsx @@ -23,7 +23,7 @@ import OkCancelDialog from './OkCancelDialog'; import ListSelectDialog from './ListSelectDialog'; import IconButton from '@mui/material/IconButton'; import Typography from '@mui/material/Typography'; -import { PiPedalModel, PiPedalModelFactory, State } from './PiPedalModel'; +import { PiPedalModel, PiPedalModelFactory, State, ColorTheme } from './PiPedalModel'; import ButtonBase from "@mui/material/ButtonBase"; import AppBar from '@mui/material/AppBar'; import Button from '@mui/material/Button'; @@ -45,6 +45,7 @@ import DialogEx from './DialogEx' import GovernorSettings from './GovernorSettings'; import { AlsaMidiDeviceInfo } from './AlsaMidiDeviceInfo'; import SystemMidiBindingsDialog from './SystemMidiBindingsDialog'; +import SelectThemeDialog from './SelectThemeDialog'; import Slide, { SlideProps } from '@mui/material/Slide'; import { createStyles, Theme } from '@mui/material/styles'; @@ -79,6 +80,7 @@ interface SettingsDialogState { showInputSelectDialog: boolean; showOutputSelectDialog: boolean; showMidiSelectDialog: boolean; + showThemeSelectDialog: boolean; showJackServerSettingsDialog: boolean; shuttingDown: boolean; restarting: boolean; @@ -98,6 +100,11 @@ const styles = (theme: Theme) => createStyles({ marginLeft: theme.spacing(2), flex: 1, }, + cpuStatusColor: { + color: theme.palette.text.secondary, + opacity: 0.7 + + }, sectionHead: { marginLeft: 24, marginRight: 24, @@ -179,6 +186,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })( showInputSelectDialog: false, showOutputSelectDialog: false, showMidiSelectDialog: false, + showThemeSelectDialog: false, showJackServerSettingsDialog: false, shuttingDown: false, restarting: false, @@ -379,6 +387,11 @@ const SettingsDialog = withStyles(styles, { withTheme: true })( showMidiSelectDialog: true }) } + handleThemeSelection() { + this.setState({ + showThemeSelectDialog: true + }); + } handleMidiMessageSettings() { this.setState({ showSystemMidiBindingsDialog: true @@ -548,36 +561,36 @@ const SettingsDialog = withStyles(styles, { withTheme: true })( }} >
- {this.props.onboarding && - ( -
- - Select and configure an audio device. You may optionally configure MIDI inputs, and set up a Wi-Fi Direct Hotspot now as well. - - - Access and modify these settings later by selecting the Settings menu item on the main menu. - - -
- )} + {this.props.onboarding && + ( +
+ + Select and configure an audio device. You may optionally configure MIDI inputs, and set up a Wi-Fi Direct Hotspot now as well. + + + Access and modify these settings later by selecting the Settings menu item on the main menu. + + +
+ )}
- STATUS + STATUS {(!isConfigValid) ? ( -
+
Status: Not configured. {(!this.props.onboarding) && ( - Governor: + Governor: )}
) : ( -
+
{JackHostStatus.getDisplayView("", this.state.jackStatus)} - { (!this.props.onboarding) && JackHostStatus.getCpuInfo("Governor:\u00A0", this.state.jackStatus)} + {(!this.props.onboarding) && JackHostStatus.getCpuInfo("Governor:\u00A0", this.state.jackStatus)}
) } @@ -652,7 +665,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
System MIDI Bindings - +
@@ -708,7 +721,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
- { (!this.props.onboarding) ? ( + {(!this.props.onboarding) ? (
SYSTEM @@ -730,7 +743,23 @@ const SettingsDialog = withStyles(styles, { withTheme: true })( { this.setState({ showStatusMonitorDialog: true }); }} > + onClick={() => { this.handleThemeSelection(); }} > + +
+ + Color Theme + + { this.model.getTheme() === ColorTheme.Dark ? "Dark" :"Light"} + +
+
+ + + { + this.model.setShowStatusMonitor(!this.state.showStatusMonitor) + }} >
Show status monitor on main screen.
+
- ): ( + ) : (
- -
- + +
+
) - } + } @@ -844,6 +876,20 @@ const SettingsDialog = withStyles(styles, { withTheme: true })( ) } + { + (this.state.showThemeSelectDialog) && + ( + { this.setState({showThemeSelectDialog: false});} } + onOk={(selectedTheme: ColorTheme) => { + this.model.setTheme(selectedTheme); + this.setState({showThemeSelectDialog: false}); + }} + defaultTheme={this.model.getTheme()} + /> + + ) + } this.setState({ showWifiConfigDialog: false })} onOk={(wifiConfigSettings) => this.handleApplyWifiConfig(wifiConfigSettings)} @@ -864,8 +910,8 @@ const SettingsDialog = withStyles(styles, { withTheme: true })( /> { this.setState({showSystemMidiBindingsDialog: false});}} - /> + onClose={() => { this.setState({ showSystemMidiBindingsDialog: false }); }} + /> ); diff --git a/react/src/SplitControlView.tsx b/react/src/SplitControlView.tsx index b8fbf01..45a560b 100644 --- a/react/src/SplitControlView.tsx +++ b/react/src/SplitControlView.tsx @@ -52,7 +52,7 @@ const styles = (theme: Theme) => createStyles({ paddingLeft: 12, paddingRight: 12, left: 0, - background: "white", + background: theme.mainBackground, zIndex: 3 }, @@ -61,7 +61,7 @@ const styles = (theme: Theme) => createStyles({ right: 0, marginRight: 22, paddingLeft: 12, - background: "white", + background: theme.mainBackground, zIndex: 3 }, @@ -70,7 +70,7 @@ const styles = (theme: Theme) => createStyles({ right: 0, paddingRight: 22, paddingLeft: 12, - background: "white", + background: theme.mainBackground, zIndex: 3 }, diff --git a/react/src/TemporaryDrawer.tsx b/react/src/TemporaryDrawer.tsx index 98b01c3..9e82237 100644 --- a/react/src/TemporaryDrawer.tsx +++ b/react/src/TemporaryDrawer.tsx @@ -22,11 +22,11 @@ import { WithStyles } from '@mui/styles'; import withStyles from '@mui/styles/withStyles'; import createStyles from '@mui/styles/createStyles'; -import Toolbar from '@mui/material/Toolbar'; import IconButton from '@mui/material/Toolbar'; import Drawer from '@mui/material/Drawer'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import { Theme } from '@mui/material/styles'; +import isDarkMode from './DarkMode'; const drawerStyles = (theme: Theme) => { @@ -40,7 +40,7 @@ const drawerStyles = (theme: Theme) => { drawer_header: { color: theme.palette.primary.main, - background: 'white', + background: (isDarkMode()? theme.palette.background.default: 'white'), } })}; @@ -98,13 +98,13 @@ export const TemporaryDrawer = withStyles(drawerStyles)( onClick={() => { this.fireClose(); }} onKeyDown={() => { this.fireClose(); }} > - - +
+ + - + +
{this.props.children}
diff --git a/react/src/ToobFrequencyResponseView.tsx b/react/src/ToobFrequencyResponseView.tsx index b007efa..0df3cbe 100644 --- a/react/src/ToobFrequencyResponseView.tsx +++ b/react/src/ToobFrequencyResponseView.tsx @@ -25,11 +25,11 @@ import createStyles from '@mui/styles/createStyles'; import withStyles from '@mui/styles/withStyles'; import { PiPedalModelFactory, PiPedalModel, State } from "./PiPedalModel"; -import { StandardItemSize } from './PluginControlView'; import Utility from './Utility'; import SvgPathBuilder from './SvgPathBuilder'; +const StandardItemSize = { width: 80, height: 110 }; const FREQUENCY_RESPONSE_VECTOR_URI = "http://two-play.com/plugins/toob#frequencyResponseVector"; @@ -47,13 +47,15 @@ const styles = (theme: Theme) => createStyles({ interface ToobFrequencyResponseProps extends WithStyles { instanceId: number; - maxDb?: number; - minDb?: number; - minFrequency?: number; - maxFrequency?: number; + propertyName?: string; + width?: number; } interface ToobFrequencyResponseState { + minDb: number; + maxDb: number; + minF: number; + maxF: number; path: string; } @@ -70,7 +72,11 @@ const ToobFrequencyResponseView = super(props); this.model = PiPedalModelFactory.getInstance(); this.state = { - path: "" + path: "", + minDb: -30, + maxDb: 5, + minF: 30, + maxF: 20000 }; this.pathRef = React.createRef(); @@ -146,24 +152,61 @@ const ToobFrequencyResponseView = onFrequencyResponseUpdated(data: number[]) { if (!this.mounted) return; let pathBuilder = new SvgPathBuilder(); + + let minF = data[0]; + let maxF = data[1]; + let dbMin = data[2]; + let dbMax = data[3]; + let logMin = Math.log(minF); + let logMax = Math.log(maxF); + + let toX_ = (frequency: number): number => { + var logV = Math.log(frequency); + return (this.xMax - this.xMin) * (logV - logMin) / (logMax - logMin) + this.xMin; + + }; + let toY_ = (value: number): number => { + + var db; + if (value < this.MIN_DB_AF) { + db = -192.0; + } else { + db = 20 * Math.log10(value); + } + var y = (db - dbMin) / (dbMax - dbMin) * (this.yMax - this.yMin) + this.yMin; + return y; + }; + + + if (data.length > 2) { - pathBuilder.moveTo(this.toX(data[0]), this.toY(data[1])) - for (let i = 2; i < data.length; i += 2) { - pathBuilder.lineTo(this.toX(data[i]), this.toY(data[i + 1])); + pathBuilder.moveTo(toX_(data[4]), toY_(data[5])) + for (let i = 6; i < data.length; i += 2) { + pathBuilder.lineTo(toX_(data[i]), toY_(data[i + 1])); } } this.currentPath = pathBuilder.toString(); - this.setState({ path: this.currentPath }); + this.setState({ + minF: data[0], + maxF: data[1], + maxDb: data[2], + minDb: data[3], + path: this.currentPath + }); } + private getPropertyUri() + { + return this.props.propertyName? this.props.propertyName: FREQUENCY_RESPONSE_VECTOR_URI; + } updateAllWaveShapes() { if (this.requestOutstanding) { // throttling. this.requestDeferred = true; return; } this.requestOutstanding = true; - this.model.getPatchProperty(this.props.instanceId, FREQUENCY_RESPONSE_VECTOR_URI) + this.model.getPatchProperty(this.props.instanceId, this.getPropertyUri()) .then((json) => { if (json && json.otype_ === "Vector" && json.vtype_ === "Float") { this.onFrequencyResponseUpdated(json.value as number[]); @@ -237,11 +280,11 @@ const ToobFrequencyResponseView = render() { // deliberately reversed to flip up and down. - this.dbMax = this.props.minDb ?? this.dbMinDefault; - this.dbMin = this.props.maxDb ?? this.dbMaxDefault; + this.dbMax = this.state.minDb; + this.dbMin = this.state.maxDb; - this.fMin = this.props.minFrequency ?? 30; - this.fMax = this.props.maxFrequency ?? 20000; + this.fMin = this.state.minF; + this.fMax = this.state.maxF; this.logMin = Math.log(this.fMin); this.logMax = Math.log(this.fMax); diff --git a/react/src/ToobMLView.tsx b/react/src/ToobMLView.tsx index 2798758..f5fe4e1 100644 --- a/react/src/ToobMLView.tsx +++ b/react/src/ToobMLView.tsx @@ -109,7 +109,7 @@ const ToobMLView = { let group = controls[4] as ControlGroup; group.controls.splice(0,0, - ( ) + ( ) ); let gainControl: React.ReactElement = controls[2] as React.ReactElement; diff --git a/react/src/ToobToneStackView.tsx b/react/src/ToobToneStackView.tsx index f66e21d..41eea1d 100644 --- a/react/src/ToobToneStackView.tsx +++ b/react/src/ToobToneStackView.tsx @@ -92,7 +92,7 @@ const ToobToneStackView = { controls.splice(0,0, ( ) + />) ); } else { controls.splice(0,0, diff --git a/react/src/UploadFileDialog.tsx b/react/src/UploadFileDialog.tsx index cfb8444..ab9c24d 100644 --- a/react/src/UploadFileDialog.tsx +++ b/react/src/UploadFileDialog.tsx @@ -206,7 +206,7 @@ export default class UploadFileDialog extends ResizeResponsiveComponent window.innerHeight; } - static isTouchDevice(): boolean { + static needsZoomedControls(): boolean { if (!this.isLandscape()) return false; if (window.innerHeight > 500) return false; - + if (window.innerHeight > 300) return true; return (('ontouchstart' in window) && ((navigator.maxTouchPoints??0) > 0) ); } diff --git a/react/src/ZoomedDial.tsx b/react/src/ZoomedDial.tsx index 97e436b..d4bfbea 100644 --- a/react/src/ZoomedDial.tsx +++ b/react/src/ZoomedDial.tsx @@ -22,6 +22,7 @@ import { WithStyles } from '@mui/styles'; import withStyles from '@mui/styles/withStyles'; import createStyles from '@mui/styles/createStyles'; import { PiPedalModel, PiPedalModelFactory, ZoomedControlInfo } from './PiPedalModel'; +import {ReactComponent as DialIcon} from './svg/fx_dial.svg'; const SELECTED_OPACITY = 0.8; const DEFAULT_OPACITY = 0.6; @@ -34,6 +35,12 @@ const DEAD_ZONE_SIZE = 5; const styles = (theme: Theme) => createStyles({ + dialIcon: { + overscrollBehavior: "none", touchAction: "none", + fill: theme.palette.text.primary, + opacity: DEFAULT_OPACITY, + + } }); @@ -56,7 +63,7 @@ const ZoomedDial = withStyles(styles, { withTheme: true })( class extends React.Component { model: PiPedalModel = PiPedalModelFactory.getInstance(); - imgRef: React.RefObject = React.createRef(); + imgRef: React.RefObject = React.createRef(); constructor(props: ZoomedDialProps) { super(props); @@ -75,7 +82,7 @@ const ZoomedDial = withStyles(styles, { withTheme: true })( } - onPointerUp(e: PointerEvent) { + onPointerUp(e: PointerEvent) { if (this.isCapturedPointer(e)) { --this.pointersDown; @@ -94,7 +101,7 @@ const ZoomedDial = withStyles(styles, { withTheme: true })( } - releaseCapture(e: PointerEvent) + releaseCapture(e: PointerEvent) { let img = this.imgRef.current; @@ -118,7 +125,7 @@ const ZoomedDial = withStyles(styles, { withTheme: true })( } - onPointerLostCapture(e: PointerEvent) { + onPointerLostCapture(e: PointerEvent) { if (this.isCapturedPointer(e)) { --this.pointersDown; @@ -128,10 +135,10 @@ const ZoomedDial = withStyles(styles, { withTheme: true })( } - onTouchStart(e: TouchEvent) { + onTouchStart(e: TouchEvent) { //must be defined to get onTouchMove } - onTouchMove(e: TouchEvent) { + onTouchMove(e: TouchEvent) { // e.preventDefault(); e.stopPropagation(); // cancels scroll!!! @@ -150,7 +157,7 @@ const ZoomedDial = withStyles(styles, { withTheme: true })( currentAngle: number = 0; lastPointerAngle: number = 0; - isValidPointer(e: PointerEvent): boolean { + isValidPointer(e: PointerEvent): boolean { if (e.pointerType === "mouse") { return e.button === 0; } else if (e.pointerType === "pen") { @@ -202,7 +209,7 @@ const ZoomedDial = withStyles(styles, { withTheme: true })( return 0; } - pointerToAngle(e: PointerEvent): number { + pointerToAngle(e: PointerEvent): number { if (this.imgRef.current) { let img = this.imgRef.current; @@ -220,10 +227,10 @@ const ZoomedDial = withStyles(styles, { withTheme: true })( } return NaN; } - captureElement?: HTMLElement; + captureElement?: SVGSVGElement; pointersDown: number = 0; - onPointerDown(e: PointerEvent): void { + onPointerDown(e: PointerEvent): void { if (!this.mouseDown && this.isValidPointer(e)) { e.preventDefault(); e.stopPropagation(); @@ -262,7 +269,7 @@ const ZoomedDial = withStyles(styles, { withTheme: true })( } } - isCapturedPointer(e: PointerEvent): boolean { + isCapturedPointer(e: PointerEvent): boolean { return this.mouseDown && e.pointerId === this.pointerId && e.pointerType === this.pointerType; @@ -271,7 +278,7 @@ const ZoomedDial = withStyles(styles, { withTheme: true })( - onPointerMove(e: PointerEvent): void { + onPointerMove(e: PointerEvent): void { if (this.isCapturedPointer(e)) { e.preventDefault(); this.updateAngle(e) @@ -286,7 +293,7 @@ const ZoomedDial = withStyles(styles, { withTheme: true })( } - updateAngle(e: PointerEvent): void { + updateAngle(e: PointerEvent): void { let angle: number = this.pointerToAngle(e); if (!isNaN(angle)) { if (!isNaN(this.lastPointerAngle)) { @@ -352,14 +359,13 @@ const ZoomedDial = withStyles(styles, { withTheme: true })( render() { + let classes = this.props.classes; return ( - { this.props.onDialogClose() }} onAbort={() => { this.props.onDialogClose() }} > -
+ + + + + + + + \ No newline at end of file diff --git a/react/public/img/file_download_black_24dp.svg b/react/src/svg/file_download_black_24dp.svg similarity index 100% rename from react/public/img/file_download_black_24dp.svg rename to react/src/svg/file_download_black_24dp.svg diff --git a/react/public/img/file_upload_black_24dp.svg b/react/src/svg/file_upload_black_24dp.svg similarity index 100% rename from react/public/img/file_upload_black_24dp.svg rename to react/src/svg/file_upload_black_24dp.svg diff --git a/react/src/svg/fx_amplifier.svg b/react/src/svg/fx_amplifier.svg new file mode 100644 index 0000000..14fea41 --- /dev/null +++ b/react/src/svg/fx_amplifier.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + diff --git a/react/src/svg/fx_analyzer.svg b/react/src/svg/fx_analyzer.svg new file mode 100644 index 0000000..54be993 --- /dev/null +++ b/react/src/svg/fx_analyzer.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/react/src/svg/fx_chorus.svg b/react/src/svg/fx_chorus.svg new file mode 100644 index 0000000..301a39f --- /dev/null +++ b/react/src/svg/fx_chorus.svg @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/react/src/svg/fx_compressor.svg b/react/src/svg/fx_compressor.svg new file mode 100644 index 0000000..f6d7029 --- /dev/null +++ b/react/src/svg/fx_compressor.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + diff --git a/react/src/svg/fx_constant.svg b/react/src/svg/fx_constant.svg new file mode 100644 index 0000000..321a890 --- /dev/null +++ b/react/src/svg/fx_constant.svg @@ -0,0 +1,28 @@ + + + + + + + fx + + k + + + + + diff --git a/react/src/svg/fx_converter.svg b/react/src/svg/fx_converter.svg new file mode 100644 index 0000000..ade942c --- /dev/null +++ b/react/src/svg/fx_converter.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + diff --git a/react/src/svg/fx_delay.svg b/react/src/svg/fx_delay.svg new file mode 100644 index 0000000..b5e29ca --- /dev/null +++ b/react/src/svg/fx_delay.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + diff --git a/react/src/svg/fx_dial.svg b/react/src/svg/fx_dial.svg new file mode 100644 index 0000000..4152f63 --- /dev/null +++ b/react/src/svg/fx_dial.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/react/src/svg/fx_distortion.svg b/react/src/svg/fx_distortion.svg new file mode 100644 index 0000000..1ad8b16 --- /dev/null +++ b/react/src/svg/fx_distortion.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + diff --git a/react/src/svg/fx_empty.svg b/react/src/svg/fx_empty.svg new file mode 100644 index 0000000..e6f1eb5 --- /dev/null +++ b/react/src/svg/fx_empty.svg @@ -0,0 +1,6 @@ + + + + + diff --git a/react/src/svg/fx_eq.svg b/react/src/svg/fx_eq.svg new file mode 100644 index 0000000..61d6873 --- /dev/null +++ b/react/src/svg/fx_eq.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + diff --git a/react/src/svg/fx_error.svg b/react/src/svg/fx_error.svg new file mode 100644 index 0000000..37f8f1f --- /dev/null +++ b/react/src/svg/fx_error.svg @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/react/src/svg/fx_filter.svg b/react/src/svg/fx_filter.svg new file mode 100644 index 0000000..e294879 --- /dev/null +++ b/react/src/svg/fx_filter.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + diff --git a/react/src/svg/fx_filter_hp.svg b/react/src/svg/fx_filter_hp.svg new file mode 100755 index 0000000..d695af5 --- /dev/null +++ b/react/src/svg/fx_filter_hp.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + diff --git a/react/src/svg/fx_flanger.svg b/react/src/svg/fx_flanger.svg new file mode 100644 index 0000000..e281aef --- /dev/null +++ b/react/src/svg/fx_flanger.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + diff --git a/react/src/svg/fx_flanger2.svg b/react/src/svg/fx_flanger2.svg new file mode 100644 index 0000000..ffed761 --- /dev/null +++ b/react/src/svg/fx_flanger2.svg @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/react/src/svg/fx_function.svg b/react/src/svg/fx_function.svg new file mode 100644 index 0000000..fb59658 --- /dev/null +++ b/react/src/svg/fx_function.svg @@ -0,0 +1,41 @@ + + + + + + + + fx + + + + + diff --git a/react/src/svg/fx_gate.svg b/react/src/svg/fx_gate.svg new file mode 100644 index 0000000..8f2462a --- /dev/null +++ b/react/src/svg/fx_gate.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + diff --git a/react/src/svg/fx_generator.svg b/react/src/svg/fx_generator.svg new file mode 100644 index 0000000..b77ef00 --- /dev/null +++ b/react/src/svg/fx_generator.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + diff --git a/react/src/svg/fx_instrument.svg b/react/src/svg/fx_instrument.svg new file mode 100644 index 0000000..7627d61 --- /dev/null +++ b/react/src/svg/fx_instrument.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/react/src/svg/fx_limiter.svg b/react/src/svg/fx_limiter.svg new file mode 100644 index 0000000..2c465c7 --- /dev/null +++ b/react/src/svg/fx_limiter.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + diff --git a/react/src/svg/fx_lr.svg b/react/src/svg/fx_lr.svg new file mode 100644 index 0000000..6f178d2 --- /dev/null +++ b/react/src/svg/fx_lr.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + diff --git a/react/src/svg/fx_mixer.svg b/react/src/svg/fx_mixer.svg new file mode 100644 index 0000000..891fd3b --- /dev/null +++ b/react/src/svg/fx_mixer.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/react/src/svg/fx_modulator.svg b/react/src/svg/fx_modulator.svg new file mode 100644 index 0000000..f1bba64 --- /dev/null +++ b/react/src/svg/fx_modulator.svg @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/react/src/svg/fx_oscillator.svg b/react/src/svg/fx_oscillator.svg new file mode 100644 index 0000000..b034041 --- /dev/null +++ b/react/src/svg/fx_oscillator.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + diff --git a/react/src/svg/fx_parametric_eq.svg b/react/src/svg/fx_parametric_eq.svg new file mode 100755 index 0000000..c1a0305 --- /dev/null +++ b/react/src/svg/fx_parametric_eq.svg @@ -0,0 +1,15 @@ + + + + + + + + diff --git a/react/src/svg/fx_phaser.svg b/react/src/svg/fx_phaser.svg new file mode 100644 index 0000000..2653f16 --- /dev/null +++ b/react/src/svg/fx_phaser.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + diff --git a/react/src/svg/fx_pitch.svg b/react/src/svg/fx_pitch.svg new file mode 100644 index 0000000..88b9517 --- /dev/null +++ b/react/src/svg/fx_pitch.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/react/src/svg/fx_plugin.svg b/react/src/svg/fx_plugin.svg new file mode 100644 index 0000000..dc524a1 --- /dev/null +++ b/react/src/svg/fx_plugin.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + diff --git a/react/src/svg/fx_reverb.svg b/react/src/svg/fx_reverb.svg new file mode 100644 index 0000000..55f836f --- /dev/null +++ b/react/src/svg/fx_reverb.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/react/src/svg/fx_simulator.svg b/react/src/svg/fx_simulator.svg new file mode 100644 index 0000000..981bda0 --- /dev/null +++ b/react/src/svg/fx_simulator.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + diff --git a/react/src/svg/fx_spatial.svg b/react/src/svg/fx_spatial.svg new file mode 100644 index 0000000..ae27920 --- /dev/null +++ b/react/src/svg/fx_spatial.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/react/src/svg/fx_spectral.svg b/react/src/svg/fx_spectral.svg new file mode 100644 index 0000000..dadd1a1 --- /dev/null +++ b/react/src/svg/fx_spectral.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/react/src/svg/fx_split_a.svg b/react/src/svg/fx_split_a.svg new file mode 100644 index 0000000..8ecc0ee --- /dev/null +++ b/react/src/svg/fx_split_a.svg @@ -0,0 +1,18 @@ + + + + + + + A + + + + + + + + diff --git a/react/src/svg/fx_split_b.svg b/react/src/svg/fx_split_b.svg new file mode 100644 index 0000000..c0cbae6 --- /dev/null +++ b/react/src/svg/fx_split_b.svg @@ -0,0 +1,22 @@ + + + + + + + B + + + + + + + + diff --git a/react/src/svg/fx_terminal.svg b/react/src/svg/fx_terminal.svg new file mode 100644 index 0000000..b2f746a --- /dev/null +++ b/react/src/svg/fx_terminal.svg @@ -0,0 +1,9 @@ + + + + + + + diff --git a/react/src/svg/fx_utility.svg b/react/src/svg/fx_utility.svg new file mode 100644 index 0000000..f7b40b8 --- /dev/null +++ b/react/src/svg/fx_utility.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + diff --git a/react/src/svg/ic_edit_banks.svg b/react/src/svg/ic_edit_banks.svg new file mode 100644 index 0000000..7d7be94 --- /dev/null +++ b/react/src/svg/ic_edit_banks.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + diff --git a/react/public/img/help_outline_black_24dp.svg b/react/src/svg/ic_help_outline.svg similarity index 100% rename from react/public/img/help_outline_black_24dp.svg rename to react/src/svg/ic_help_outline.svg diff --git a/react/public/img/ic_midi.svg b/react/src/svg/ic_midi.svg similarity index 100% rename from react/public/img/ic_midi.svg rename to react/src/svg/ic_midi.svg diff --git a/react/public/img/ic_pluginpreset.svg b/react/src/svg/ic_pluginpreset.svg similarity index 68% rename from react/public/img/ic_pluginpreset.svg rename to react/src/svg/ic_pluginpreset.svg index 10e261a..5557cad 100644 --- a/react/public/img/ic_pluginpreset.svg +++ b/react/src/svg/ic_pluginpreset.svg @@ -7,8 +7,8 @@ H4.5h11h0.01c1.1,0,1.99-0.9,1.99-2V11L17.493,10.993z M15.5,12v9h-11v-9V7h5h1h5V12z"/> - - - + + + diff --git a/react/public/img/ic_pluginpreset2.svg b/react/src/svg/ic_pluginpreset2.svg similarity index 63% rename from react/public/img/ic_pluginpreset2.svg rename to react/src/svg/ic_pluginpreset2.svg index 49578ce..a5f0977 100755 --- a/react/public/img/ic_pluginpreset2.svg +++ b/react/src/svg/ic_pluginpreset2.svg @@ -5,8 +5,10 @@ width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve"> - - - + + diff --git a/react/src/svg/ic_presets.svg b/react/src/svg/ic_presets.svg new file mode 100644 index 0000000..5f3d7d5 --- /dev/null +++ b/react/src/svg/ic_presets.svg @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/react/public/img/save_bank_as.svg b/react/src/svg/ic_save_bank_as.svg similarity index 100% rename from react/public/img/save_bank_as.svg rename to react/src/svg/ic_save_bank_as.svg diff --git a/react/src/svg/ic_settings.svg b/react/src/svg/ic_settings.svg new file mode 100644 index 0000000..4165162 --- /dev/null +++ b/react/src/svg/ic_settings.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/react/src/svg/old_delete_outline_24dp.svg b/react/src/svg/old_delete_outline_24dp.svg new file mode 100644 index 0000000..24f7b41 --- /dev/null +++ b/react/src/svg/old_delete_outline_24dp.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/react/tmp.txt b/react/tmp.txt new file mode 100644 index 0000000..da32ca9 --- /dev/null +++ b/react/tmp.txt @@ -0,0 +1,45 @@ +src/svg/ +src/svg/fx_constant.svg +src/svg/fx_distortion.svg +src/svg/fx_oscillator.svg +src/svg/fx_filter.svg +src/svg/fx_function.svg +src/svg/fx_generator.svg +src/svg/fx_compressor.svg +src/svg/file_download_black_24dp.svg +src/svg/fx_filter_hp.svg +src/svg/fx_plugin.svg +src/svg/fx_error.svg +src/svg/fx_gate.svg +src/svg/ic_midi.svg +src/svg/ic_pluginpreset.svg +src/svg/fx_split_a.svg +src/svg/fx_utility.svg +src/svg/ic_pluginpreset2.svg +src/svg/fx_parametric_eq.svg +src/svg/fx_terminal.svg +src/svg/ic_presets.svg +src/svg/fx_flanger.svg +src/svg/fx_analyzer.svg +src/svg/fx_delay.svg +src/svg/fx_spectral.svg +src/svg/fx_chorus.svg +src/svg/fx_simulator.svg +src/svg/fx_mixer.svg +src/svg/fx_eq.svg +src/svg/fx_modulator.svg +src/svg/file_upload_black_24dp.svg +src/svg/fx_pitch.svg +src/svg/fx_phaser.svg +src/svg/fx_amplifier.svg +src/svg/fx_lr.svg +src/svg/fx_instrument.svg +src/svg/fx_reverb.svg +src/svg/fx_limiter.svg +src/svg/fx_empty.svg +src/svg/fx_dial.svg +src/svg/fx_flanger2.svg +src/svg/fx_split_b.svg +src/svg/fx_spatial.svg +src/svg/fx_converter.svg +src/svg/old_delete_outline_24dp.svg diff --git a/src/ConfigMain.cpp b/src/ConfigMain.cpp index 1cb8d6c..84cfadb 100644 --- a/src/ConfigMain.cpp +++ b/src/ConfigMain.cpp @@ -991,7 +991,7 @@ int main(int argc, char **argv) parser.AddOption("--help", &help); parser.AddOption("--prefix", &prefixOption); parser.AddOption("--port", &portOption); - parser.AddOption("--enable-ap", &enable_ap); + parser.AddOption("--enable-legacy-ap", &enable_ap); parser.AddOption("--disable-ap", &disable_ap); parser.AddOption("--enable-p2p", &enable_p2p); parser.AddOption("--disable-p2p", &disable_p2p); diff --git a/src/FileBrowserFilesFeature.cpp b/src/FileBrowserFilesFeature.cpp index 45e3991..9894c5a 100644 --- a/src/FileBrowserFilesFeature.cpp +++ b/src/FileBrowserFilesFeature.cpp @@ -237,7 +237,7 @@ char *FileBrowserFilesFeature::MapPath(const char *path,const char*resourcePathB } catch (const std::exception&e) { LogError(e.what()); - return ""; + return strdup(""); } } diff --git a/src/PiPedalModel.cpp b/src/PiPedalModel.cpp index a415a39..cf209d5 100644 --- a/src/PiPedalModel.cpp +++ b/src/PiPedalModel.cpp @@ -378,6 +378,8 @@ void PiPedalModel::OnNotifyMaybeLv2StateChanged(uint64_t instanceId) IPiPedalModelSubscriber **t = new IPiPedalModelSubscriber *[this->subscribers.size()]; + + Lv2PluginState newState = item->lv2State(); for (size_t i = 0; i < subscribers.size(); ++i) { t[i] = this->subscribers[i]; @@ -386,7 +388,7 @@ void PiPedalModel::OnNotifyMaybeLv2StateChanged(uint64_t instanceId) { for (size_t i = 0; i < n; ++i) { - t[i]->OnLv2StateChanged(instanceId); + t[i]->OnLv2StateChanged(instanceId,newState); } } delete[] t; diff --git a/src/PiPedalModel.hpp b/src/PiPedalModel.hpp index eb82847..cbe74fb 100644 --- a/src/PiPedalModel.hpp +++ b/src/PiPedalModel.hpp @@ -55,7 +55,7 @@ namespace pipedal virtual void OnInputVolumeChanged(float value) = 0; virtual void OnOutputVolumeChanged(float value) = 0; - virtual void OnLv2StateChanged(int64_t pedalItemId) = 0; + virtual void OnLv2StateChanged(int64_t pedalItemId,const Lv2PluginState&newState ) = 0; virtual void OnVst3ControlChanged(int64_t clientId, int64_t pedalItemId, const std::string &symbol, float value, const std::string &state) = 0; virtual void OnPedalboardChanged(int64_t clientId, const Pedalboard &pedalboard) = 0; virtual void OnPresetsChanged(int64_t clientId, const PresetIndex &presets) = 0; diff --git a/src/PiPedalSocket.cpp b/src/PiPedalSocket.cpp index 4008796..8f43dbb 100644 --- a/src/PiPedalSocket.cpp +++ b/src/PiPedalSocket.cpp @@ -57,6 +57,21 @@ JSON_MAP_REFERENCE(GetPatchPropertyBody, instanceId) JSON_MAP_REFERENCE(GetPatchPropertyBody, propertyUri) JSON_MAP_END() + +class Lv2StateChangedBody { +public: + uint64_t instanceId_; + Lv2PluginState state_; + DECLARE_JSON_MAP(Lv2StateChangedBody); +}; + +JSON_MAP_BEGIN(Lv2StateChangedBody) +JSON_MAP_REFERENCE(Lv2StateChangedBody, instanceId) +JSON_MAP_REFERENCE(Lv2StateChangedBody, state) +JSON_MAP_END() + + + class SetPatchPropertyBody { public: uint64_t instanceId_; @@ -1493,9 +1508,10 @@ protected: } private: - virtual void OnLv2StateChanged(int64_t instanceId) + virtual void OnLv2StateChanged(int64_t instanceId, const Lv2PluginState &state) { - Send("onLv2StateChanged",instanceId); + Lv2StateChangedBody message { (uint64_t)instanceId, state}; + Send("onLv2StateChanged",message); } virtual void OnErrorMessage(const std::string&message) diff --git a/src/PiPedalUI.cpp b/src/PiPedalUI.cpp index 4764f78..923f1aa 100644 --- a/src/PiPedalUI.cpp +++ b/src/PiPedalUI.cpp @@ -26,14 +26,16 @@ #include "PluginHost.hpp" #include "ss.hpp" #include "MimeTypes.hpp" +#include "AutoLilvNode.hpp" using namespace pipedal; + PiPedalUI::PiPedalUI(PluginHost *pHost, const LilvNode *uiNode, const std::filesystem::path &resourcePath) { auto pWorld = pHost->getWorld(); - LilvNodes *fileNodes = lilv_world_find_nodes(pWorld, uiNode, pHost->lilvUris.pipedalUI__fileProperties, nullptr); + AutoLilvNodes fileNodes = lilv_world_find_nodes(pWorld, uiNode, pHost->lilvUris.pipedalUI__fileProperties, nullptr); LILV_FOREACH(nodes, i, fileNodes) { const LilvNode *fileNode = lilv_nodes_get(fileNodes, i); @@ -47,8 +49,23 @@ PiPedalUI::PiPedalUI(PluginHost *pHost, const LilvNode *uiNode, const std::files pHost->LogWarning(SS("Failed to read pipedalui::fileProperties. " << e.what())); } } + AutoLilvNodes frequencyPlotNodes = lilv_world_find_nodes(pWorld, uiNode, pHost->lilvUris.pipedalUI__frequencyPlot, nullptr); + LILV_FOREACH(nodes, i, frequencyPlotNodes) + { + const LilvNode *frequencyPlotNode = lilv_nodes_get(frequencyPlotNodes, i); + try + { + UiFrequencyPlot::ptr frequencyPlotUI = + std::make_shared(pHost, frequencyPlotNode, resourcePath); + this->frequencyPlots_.push_back(std::move(frequencyPlotUI)); + } + catch (const std::exception &e) + { + pHost->LogWarning(SS("Failed to read pipedalui::frequencyPlots. " << e.what())); + } + } - LilvNodes *portNotifications = lilv_world_find_nodes(pWorld, uiNode, pHost->lilvUris.ui__portNotification, nullptr); + AutoLilvNodes portNotifications = lilv_world_find_nodes(pWorld, uiNode, pHost->lilvUris.ui__portNotification, nullptr); LILV_FOREACH(nodes, i, portNotifications) { const LilvNode *portNotificationNode = lilv_nodes_get(portNotifications, i); @@ -62,8 +79,6 @@ PiPedalUI::PiPedalUI(PluginHost *pHost, const LilvNode *uiNode, const std::files pHost->LogWarning(SS("Failed to read ui:portNotifications. " << e.what())); } } - - lilv_nodes_free(fileNodes); } UiFileType::UiFileType(PluginHost *pHost, const LilvNode *node) @@ -330,7 +345,15 @@ UiFileProperty::UiFileProperty(const std::string &name, const std::string &patch directory_(directory) { } -PiPedalUI::PiPedalUI(std::vector &&fileProperties) +PiPedalUI::PiPedalUI( + std::vector &&fileProperties, + std::vector&& frequencyPlots) +{ + this->fileProperties_ = std::move(fileProperties); + this->frequencyPlots_ = std::move(frequencyPlots); +} +PiPedalUI::PiPedalUI( + std::vector &&fileProperties) { this->fileProperties_ = std::move(fileProperties); } @@ -357,6 +380,66 @@ UiFileType::UiFileType(const std::string&label, const std::string &fileType) } } +static float GetFloat(LilvWorld *pWorld,const LilvNode*node,const LilvNode*property,float defaultValue) +{ + AutoLilvNode value = lilv_world_get( + pWorld, + node, + property, + nullptr); + return value.AsFloat(defaultValue); +} + + +UiFrequencyPlot::UiFrequencyPlot(PluginHost*pHost, const LilvNode*node, + const std::filesystem::path&resourcePath) +{ + auto pWorld = pHost->getWorld(); + + AutoLilvNode patchProperty = lilv_world_get( + pWorld, + node, + pHost->lilvUris.pipedalUI__patchProperty, + nullptr); + if (patchProperty) + { + this->patchProperty_ = patchProperty.AsUri(); + } + else + { + throw std::logic_error("PiPedal FileProperty is missing pipedalui:patchProperty value."); + } + + + AutoLilvNode index = lilv_world_get( + pWorld, + node, + pHost->lilvUris.lv2core__index, + nullptr); + if (index) + { + this->index_ = index.AsInt(-1); + } + else + { + this->index_ = -1; + } + + AutoLilvNode portGroup = lilv_world_get(pWorld,node,pHost->lilvUris.portgroups__group,nullptr); + if (portGroup) + { + this->portGroup_ = portGroup.AsUri(); + } + this->xLeft_ = GetFloat(pWorld,node,pHost->lilvUris.pipedalUI__xLeft,100); + this->xRight_ = GetFloat(pWorld,node,pHost->lilvUris.pipedalUI__xRight,22000); + this->yTop_ = GetFloat(pWorld,node,pHost->lilvUris.pipedalUI__yTop,5); + this->yBottom_ = GetFloat(pWorld,node,pHost->lilvUris.pipedalUI__yBottom,-35); + this->xLog_ = GetFloat(pWorld,node,pHost->lilvUris.pipedalUI__xLog,-35); + this->width_ = GetFloat(pWorld,node,pHost->lilvUris.pipedalUI__width,60); +} + + + JSON_MAP_BEGIN(UiPortNotification) JSON_MAP_REFERENCE(UiPortNotification, portIndex) JSON_MAP_REFERENCE(UiPortNotification, symbol) @@ -377,4 +460,17 @@ JSON_MAP_REFERENCE(UiFileProperty, directory) JSON_MAP_REFERENCE(UiFileProperty, patchProperty) JSON_MAP_REFERENCE(UiFileProperty, fileTypes) JSON_MAP_REFERENCE(UiFileProperty, portGroup) +JSON_MAP_END() + +JSON_MAP_BEGIN(UiFrequencyPlot) +JSON_MAP_REFERENCE(UiFrequencyPlot, patchProperty) +JSON_MAP_REFERENCE(UiFrequencyPlot, index) +JSON_MAP_REFERENCE(UiFrequencyPlot, portGroup) +JSON_MAP_REFERENCE(UiFrequencyPlot, xLeft) +JSON_MAP_REFERENCE(UiFrequencyPlot, xRight) +JSON_MAP_REFERENCE(UiFrequencyPlot, xLog) +JSON_MAP_REFERENCE(UiFrequencyPlot, yTop) +JSON_MAP_REFERENCE(UiFrequencyPlot, yBottom) +JSON_MAP_REFERENCE(UiFrequencyPlot, width) + JSON_MAP_END() \ No newline at end of file diff --git a/src/PiPedalUI.hpp b/src/PiPedalUI.hpp index 90886b8..dfafdcb 100644 --- a/src/PiPedalUI.hpp +++ b/src/PiPedalUI.hpp @@ -50,12 +50,20 @@ #define PIPEDAL_UI__outputPorts PIPEDAL_UI_PREFIX "outputPorts" #define PIPEDAL_UI__text PIPEDAL_UI_PREFIX "text" +#define PIPEDAL_UI__frequencyPlot PIPEDAL_UI_PREFIX "frequencyPlot" +#define PIPEDAL_UI__xLeft PIPEDAL_UI_PREFIX "xLeft" +#define PIPEDAL_UI__xRight PIPEDAL_UI_PREFIX "xRight" +#define PIPEDAL_UI__xLog PIPEDAL_UI_PREFIX "xLog" +#define PIPEDAL_UI__yTop PIPEDAL_UI_PREFIX "yTop" +#define PIPEDAL_UI__yBottom PIPEDAL_UI_PREFIX "yBottom" +#define PIPEDAL_UI__width PIPEDAL_UI_PREFIX "width" namespace pipedal { class PluginHost; + class UiFileType { private: std::string label_; @@ -126,18 +134,55 @@ namespace pipedal { public: DECLARE_JSON_MAP(UiFileProperty); }; + class UiFrequencyPlot { + private: + std::string patchProperty_; + std::int64_t index_ = -1; + std::string portGroup_; + float xLeft_ = 100; + float xRight_ = 22000; + float yTop_ = 5; + float yBottom_ = -30; + bool xLog_ = true; + float width_ = 60; + public: + using ptr = std::shared_ptr; + UiFrequencyPlot() { } + UiFrequencyPlot(PluginHost*pHost, const LilvNode*node, + const std::filesystem::path&resourcePath); + + const std::string &patchProperty() const { return patchProperty_; } + int64_t index() const { return index_; } + const std::string&portGroup() const { return portGroup_; } + float xLeft() const { return xLeft_; } + float xRight() const { return xRight_; } + float xLog() const { return xLog_; } + float yTop() const { return yTop_; } + float yBottom() const { return yBottom_; } + float width() const { return width_; } + + public: + DECLARE_JSON_MAP(UiFrequencyPlot); + }; class PiPedalUI { public: using ptr = std::shared_ptr; PiPedalUI(PluginHost*pHost, const LilvNode*uiNode, const std::filesystem::path&resourcePath); - - PiPedalUI(std::vector &&fileProperites); + PiPedalUI( + std::vector &&fileProperties, + std::vector &&frequencyPlots); + PiPedalUI( + std::vector &&fileProperties); const std::vector& fileProperties() const { return fileProperties_; } + const std::vector& frequencyPlots() const + { + return frequencyPlots_; + } const std::vector &portNotifications() const { return portNotifications_; } @@ -155,6 +200,7 @@ namespace pipedal { private: std::vector fileProperties_; + std::vector frequencyPlots_; std::vector portNotifications_; }; diff --git a/src/PiPedalVersion.cpp b/src/PiPedalVersion.cpp index 47d7b6c..54d3bb1 100644 --- a/src/PiPedalVersion.cpp +++ b/src/PiPedalVersion.cpp @@ -107,7 +107,7 @@ PiPedalVersion::PiPedalVersion(PiPedalModel&model) std::string p2pAddr = GetInterfaceIpv4Address("p2p-wlan0-0"); if (p2pAddr.length() != 0) { - this->webAddresses_.push_back(MakeWebAddress(p2pAddr,port)); + this->webAddresses_.push_back(MakeWebAddress(p2pAddr,port) + " (WiFi Direct)"); } diff --git a/src/PluginHost.cpp b/src/PluginHost.cpp index 62cb67c..f462451 100644 --- a/src/PluginHost.cpp +++ b/src/PluginHost.cpp @@ -145,6 +145,14 @@ void PluginHost::LilvUris::Initialize(LilvWorld *pWorld) pipedalUI__outputPorts = lilv_new_uri(pWorld, PIPEDAL_UI__outputPorts); pipedalUI__text = lilv_new_uri(pWorld, PIPEDAL_UI__text); + pipedalUI__frequencyPlot = lilv_new_uri(pWorld, PIPEDAL_UI__frequencyPlot); + pipedalUI__xLeft = lilv_new_uri(pWorld, PIPEDAL_UI__xLeft); + pipedalUI__xRight = lilv_new_uri(pWorld, PIPEDAL_UI__xRight); + pipedalUI__yTop = lilv_new_uri(pWorld, PIPEDAL_UI__yTop); + pipedalUI__yBottom = lilv_new_uri(pWorld, PIPEDAL_UI__yBottom); + pipedalUI__xLog = lilv_new_uri(pWorld, PIPEDAL_UI__xLog); + pipedalUI__width = lilv_new_uri(pWorld, PIPEDAL_UI__width); + ui__portNotification = lilv_new_uri(pWorld, LV2_UI__portNotification); ui__plugin = lilv_new_uri(pWorld, LV2_UI__plugin); ui__protocol = lilv_new_uri(pWorld, LV2_UI__protocol); @@ -154,6 +162,9 @@ void PluginHost::LilvUris::Initialize(LilvWorld *pWorld) lv2__symbol = lilv_new_uri(pWorld, LV2_CORE__symbol); lv2__port = lilv_new_uri(pWorld, LV2_CORE__port); + #define MOD_PREFIX "http://moddevices.com/ns/mod#" + mod__label = lilv_new_uri(pWorld,MOD_PREFIX "label"); + mod__brand = lilv_new_uri(pWorld,MOD_PREFIX "brand"); // ui:portNotification // [ // ui:portIndex 3; @@ -639,11 +650,25 @@ Lv2PluginInfo::Lv2PluginInfo(PluginHost *lv2Host, LilvWorld *pWorld, const LilvP this->has_factory_presets_ = HasFactoryPresets(lv2Host, pPlugin); + AutoLilvNode plugUri = lilv_plugin_get_uri(pPlugin); this->uri_ = nodeAsString(lilv_plugin_get_uri(pPlugin)); AutoLilvNode name = (lilv_plugin_get_name(pPlugin)); this->name_ = nodeAsString(name); + AutoLilvNode brand = lilv_world_get(pWorld,plugUri,lv2Host->lilvUris.mod__brand, nullptr); + this->brand_ = nodeAsString(brand); + + AutoLilvNode label = lilv_world_get(pWorld,plugUri,lv2Host->lilvUris.mod__label, nullptr); + this->label_ = nodeAsString(label); + if (label_.length() == 0) + { + this->label_ = name; + } else { + std::cout << this->label_ << std::endl; + } + + AutoLilvNode author_name = (lilv_plugin_get_author_name(pPlugin)); this->author_name_ = nodeAsString(author_name); @@ -1015,6 +1040,8 @@ bool PluginHost::is_a(const std::string &class_, const std::string &target_class Lv2PluginUiInfo::Lv2PluginUiInfo(PluginHost *pHost, const Lv2PluginInfo *plugin) : uri_(plugin->uri()), name_(plugin->name()), + brand_(plugin->brand()), + label_(plugin->label()), author_name_(plugin->author_name()), author_homepage_(plugin->author_homepage()), plugin_type_(uri_to_plugin_type(plugin->plugin_class())), @@ -1082,6 +1109,7 @@ Lv2PluginUiInfo::Lv2PluginUiInfo(PluginHost *pHost, const Lv2PluginInfo *plugin) if (piPedalUI) { this->fileProperties_ = piPedalUI->fileProperties(); + this->frequencyPlots_ = piPedalUI->frequencyPlots(); this->uiPortNotifications_ = piPedalUI->portNotifications(); } } @@ -1401,6 +1429,8 @@ json_map::storage_type Lv2PluginInfo::jmap{{ json_map::reference("bundle_path", &Lv2PluginInfo::bundle_path_), json_map::reference("uri", &Lv2PluginInfo::uri_), json_map::reference("name", &Lv2PluginInfo::name_), + json_map::reference("brand", &Lv2PluginInfo::brand_), + json_map::reference("label", &Lv2PluginInfo::label_), json_map::reference("plugin_class", &Lv2PluginInfo::plugin_class_), json_map::reference("supported_features", &Lv2PluginInfo::supported_features_), json_map::reference("required_features", &Lv2PluginInfo::required_features_), @@ -1466,6 +1496,8 @@ json_map::storage_type { json_map::reference("uri", &Lv2PluginUiInfo::uri_), json_map::reference("name", &Lv2PluginUiInfo::name_), + json_map::reference("brand", &Lv2PluginUiInfo::brand_), + json_map::reference("label", &Lv2PluginUiInfo::label_), json_map::enum_reference("plugin_type", &Lv2PluginUiInfo::plugin_type_, get_plugin_type_enum_converter()), json_map::reference("plugin_display_type", &Lv2PluginUiInfo::plugin_display_type_), json_map::reference("author_name", &Lv2PluginUiInfo::author_name_), @@ -1479,5 +1511,6 @@ json_map::storage_type json_map::reference("port_groups", &Lv2PluginUiInfo::port_groups_), json_map::reference("is_vst3", &Lv2PluginUiInfo::is_vst3_), json_map::reference("fileProperties", &Lv2PluginUiInfo::fileProperties_), + json_map::reference("frequencyPlots", &Lv2PluginUiInfo::frequencyPlots_), json_map::reference("uiPortNotifications", &Lv2PluginUiInfo::uiPortNotifications_), }}; diff --git a/src/PluginHost.hpp b/src/PluginHost.hpp index a089ced..2a6dd4f 100644 --- a/src/PluginHost.hpp +++ b/src/PluginHost.hpp @@ -346,6 +346,8 @@ namespace pipedal std::string uri_; std::string name_; std::string plugin_class_; + std::string brand_; + std::string label_; std::vector supported_features_; std::vector required_features_; std::vector optional_features_; @@ -367,6 +369,8 @@ namespace pipedal LV2_PROPERTY_GETSET(bundle_path) LV2_PROPERTY_GETSET(uri) LV2_PROPERTY_GETSET(name) + LV2_PROPERTY_GETSET(brand) + LV2_PROPERTY_GETSET(label) LV2_PROPERTY_GETSET(plugin_class) LV2_PROPERTY_GETSET(supported_features) LV2_PROPERTY_GETSET(required_features) @@ -574,6 +578,8 @@ namespace pipedal private: std::string uri_; std::string name_; + std::string brand_; + std::string label_; std::string author_name_; std::string author_homepage_; PluginType plugin_type_; @@ -588,11 +594,14 @@ namespace pipedal std::vector controls_; std::vector port_groups_; std::vector fileProperties_; + std::vector frequencyPlots_; std::vector uiPortNotifications_; public: LV2_PROPERTY_GETSET(uri) LV2_PROPERTY_GETSET(name) + LV2_PROPERTY_GETSET(brand) + LV2_PROPERTY_GETSET(label) LV2_PROPERTY_GETSET(author_name) LV2_PROPERTY_GETSET(author_homepage) LV2_PROPERTY_GETSET_SCALAR(plugin_type) @@ -606,6 +615,7 @@ namespace pipedal LV2_PROPERTY_GETSET(port_groups) LV2_PROPERTY_GETSET_SCALAR(is_vst3) LV2_PROPERTY_GETSET(fileProperties) + LV2_PROPERTY_GETSET(frequencyPlots) LV2_PROPERTY_GETSET(uiPortNotifications) static json_map::storage_type jmap; @@ -676,6 +686,15 @@ namespace pipedal AutoLilvNode pipedalUI__fileExtension; AutoLilvNode pipedalUI__mimeType; + AutoLilvNode pipedalUI__frequencyPlot; + AutoLilvNode pipedalUI__xLeft; + AutoLilvNode pipedalUI__xRight; + AutoLilvNode pipedalUI__xLog; + AutoLilvNode pipedalUI__yTop; + AutoLilvNode pipedalUI__yBottom; + AutoLilvNode pipedalUI__width; + + AutoLilvNode pipedalUI__outputPorts; AutoLilvNode pipedalUI__text; @@ -696,9 +715,14 @@ namespace pipedal AutoLilvNode lv2__symbol; AutoLilvNode lv2__port; + AutoLilvNode ui__FrequencyPlot; + AutoLilvNode patch__writable; AutoLilvNode patch__readable; + AutoLilvNode mod__brand; + AutoLilvNode mod__label; + AutoLilvNode dc__format; }; LilvUris lilvUris; diff --git a/src/Worker.cpp b/src/Worker.cpp index f3ec6c5..7e30685 100644 --- a/src/Worker.cpp +++ b/src/Worker.cpp @@ -190,7 +190,7 @@ void HostWorkerThread::ThreadProc() noexcept // run nice +2 (priority -2 on Windows) SetThreadName("lv2_worker"); errno = 0; - nice(2); + (void)nice(2); if (errno != 0) { std::cout << "Warning: Unable to run Lv2 schedule thread at nice +1" << std::endl;