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 196d80c..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 @@ -133,9 +134,9 @@ add_custom_command( public/img/fx_distortion.svg public/img/Pi-Logo-3.png public/img/fx_oscillator.svg - public/img/save_bank_as_white.svg - public/img/edit_banks_white.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 @@ -150,7 +151,7 @@ add_custom_command( public/img/cc-by.png public/img/fx_split_a.svg public/img/fx_utility.svg - public/img/help_outline_white_24dp.svg + src/svg/ic_help_outline.svg public/img/ic_navigate_before.svg public/img/ic_drawer_2.svg public/img/fx_parametric_eq.svg @@ -161,10 +162,8 @@ 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 @@ -173,7 +172,6 @@ add_custom_command( 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 @@ -183,14 +181,11 @@ add_custom_command( 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 public/img/fx_spatial.svg public/img/fx_converter.svg - public/img/settings_white_24dp.svg - public/img/drive_file_rename_outline_white_24dp.svg public/img/vst.svg public/img/vst.png public/sample_lv2_plugins.json 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/drive_file_rename_outline_white_24dp.svg b/react/public/img/drive_file_rename_outline_white_24dp.svg deleted file mode 100644 index d1525a7..0000000 --- a/react/public/img/drive_file_rename_outline_white_24dp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/react/public/img/help_outline_white_24dp.svg b/react/public/img/help_outline_white_24dp.svg deleted file mode 100644 index 9c1a3b8..0000000 --- a/react/public/img/help_outline_white_24dp.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/react/public/img/save_bank_as_white.svg b/react/public/img/save_bank_as_white.svg deleted file mode 100644 index 41e5755..0000000 --- a/react/public/img/save_bank_as_white.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - diff --git a/react/public/index.html b/react/public/index.html index 3562313..87fec91 100644 --- a/react/public/index.html +++ b/react/public/index.html @@ -1,26 +1,24 @@ - - - - - - - - - - - - - - PiPedal - - + + @@ -52,4 +66,4 @@ To create a production bundle, use `npm run build` or `yarn build`. --> - + \ No newline at end of file diff --git a/react/src/App.tsx b/react/src/App.tsx index 3acc70c..9efc4dd 100644 --- a/react/src/App.tsx +++ b/react/src/App.tsx @@ -25,10 +25,12 @@ import isDarkMode from './DarkMode'; declare module '@mui/material/styles' { interface Theme { - mainBackground: string; + mainBackground: React.CSSProperties['color']; + toolbarColor: React.CSSProperties['color']; } interface ThemeOptions { - mainBackground?: string; + mainBackground?: React.CSSProperties['color']; + toolbarColor?: React.CSSProperties['color']; } } @@ -50,13 +52,14 @@ const theme = createTheme( palette: { mode: 'dark', primary: { - main: "#6750A4" // #5B5690 #60529A #5C5694 + main: '#A770E4'// #6750A4" // #5B5690 #60529A #5C5694 }, secondary: { main: "#FF6060" }, }, - mainBackground: "#222" + mainBackground: "#222", + toolbarColor: '#FFFFFF' } : { @@ -69,7 +72,9 @@ const theme = createTheme( } }, - mainBackground: "#FFFFFF" + mainBackground: "#FFFFFF", + toolbarColor: '#FFFFFF' + } ); diff --git a/react/src/AppThemed.css b/react/src/AppThemed.css index 51f7afa..e007307 100644 --- a/react/src/AppThemed.css +++ b/react/src/AppThemed.css @@ -16,8 +16,3 @@ div { min-width: 0; } -@media (prefers-color-scheme: dark) { - :root { - color-scheme: dark; - } -} \ No newline at end of file diff --git a/react/src/AppThemed.tsx b/react/src/AppThemed.tsx index 2a0a81b..aeb2ffa 100644 --- a/react/src/AppThemed.tsx +++ b/react/src/AppThemed.tsx @@ -62,14 +62,27 @@ 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({ - menuIcon: { - fill: "green" + "&": { // :root + colorScheme: (isDarkMode()? "dark": "light") }, - appBar: { + 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)" @@ -499,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?"; } @@ -652,6 +665,7 @@ render() { return (
{(!this.state.tinyToolBar) ? ( - - + + { this.showDrawer() }} size="large"> - +
@@ -683,11 +698,12 @@ render() { { this.toggleFullScreen(); }} + color="inherit" size="large"> {this.state.isFullScreen ? ( - + ) : ( - + )} @@ -701,6 +717,7 @@ render() { style={{ position: "absolute", left: 12, top: 8, zIndex: 2 }} aria-label="menu" onClick={() => { this.showDrawer() }} + color="inherit" size="large"> @@ -708,6 +725,7 @@ render() { { this.toggleFullScreen(); }} size="large"> {this.state.isFullScreen ? ( @@ -756,26 +774,20 @@ render() { { this.handleDrawerRenameBank() }}> - - + + { this.handleDrawerSaveBankAs() }} > - + - { this.handleDrawerManageBanks(); }}> + { this.handleDrawerManageBanks(); }}> - + @@ -784,23 +796,19 @@ render() { { this.handleDrawerSettingsClick() }}> - + { this.handleDrawerAboutClick() }}> - + { this.handleDrawerDonationClick() }}> - - + + diff --git a/react/src/DarkMode.tsx b/react/src/DarkMode.tsx index 06b6c05..4bf6189 100644 --- a/react/src/DarkMode.tsx +++ b/react/src/DarkMode.tsx @@ -19,8 +19,10 @@ export default function isDarkMode(): boolean { - if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { - return true; - } - return false; + 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/LoadPluginDialog.tsx b/react/src/LoadPluginDialog.tsx index 98cdba2..f818115 100644 --- a/react/src/LoadPluginDialog.tsx +++ b/react/src/LoadPluginDialog.tsx @@ -602,7 +602,7 @@ export const LoadPluginDialog =
- +
diff --git a/react/src/Lv2Plugin.tsx b/react/src/Lv2Plugin.tsx index 39f4ec9..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[] = []; } @@ -668,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; @@ -685,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; @@ -738,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 = ""; @@ -749,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 a81ecec..6dd2b4c 100644 --- a/react/src/MainPage.tsx +++ b/react/src/MainPage.tsx @@ -47,7 +47,7 @@ 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; @@ -463,7 +463,7 @@ export const MainPage =
{ this.onAddClick(e) }} size="large"> - + } - style={{ textTransform: "none" }} + style={{ textTransform: "none", + background: (isDarkMode() ? "#6750A4": undefined) }} > Load diff --git a/react/src/PedalboardView.tsx b/react/src/PedalboardView.tsx index f2e3b2e..212f3f4 100644 --- a/react/src/PedalboardView.tsx +++ b/react/src/PedalboardView.tsx @@ -298,7 +298,7 @@ class PedalLayout { if (uiPlugin != null) { this.pluginType = uiPlugin.plugin_type; this.iconUrl = SelectIconUri(uiPlugin.plugin_type); - this.name = uiPlugin.name; + this.name = uiPlugin.label; this.numberOfInputs = Math.max(uiPlugin.audio_inputs,2); this.numberOfOutputs = Math.max(uiPlugin.audio_outputs,2); } else { @@ -679,7 +679,7 @@ const PedalboardView = event.preventDefault(); event.stopPropagation(); - if (!Utility.isTouchDevice()) { + if (!Utility.needsZoomedControls()) { if (this.props.onDoubleClick && instanceId) { this.props.onDoubleClick(instanceId); } @@ -1014,7 +1014,7 @@ const PedalboardView = }}> {item.pedalItem?.pluginName} + >{item.name}
) result.push(
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 6b4237c..de6e453 100644 --- a/react/src/PluginControl.tsx +++ b/react/src/PluginControl.tsx @@ -137,7 +137,7 @@ const PluginControl = this.onInputKeyPress = this.onInputKeyPress.bind(this); } isTouchDevice(): boolean { - return Utility.isTouchDevice(); + return Utility.needsZoomedControls(); } showZoomedControl() @@ -547,7 +547,7 @@ const PluginControl = ); } else { return ( - this.handleChange(e, extra)} onClose={(e) => this.handleSelectClose(e)} displayEmpty @@ -336,12 +336,13 @@ const PresetSelector = }
-
+
this.handlePresetMenuClick(e)} - size="large"> - + size="large" + > + 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 c0b376b..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; @@ -184,6 +186,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })( showInputSelectDialog: false, showOutputSelectDialog: false, showMidiSelectDialog: false, + showThemeSelectDialog: false, showJackServerSettingsDialog: false, shuttingDown: false, restarting: false, @@ -384,6 +387,11 @@ const SettingsDialog = withStyles(styles, { withTheme: true })( showMidiSelectDialog: true }) } + handleThemeSelection() { + this.setState({ + showThemeSelectDialog: true + }); + } handleMidiMessageSettings() { this.setState({ showSystemMidiBindingsDialog: true @@ -553,22 +561,22 @@ 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) ? ( @@ -582,7 +590,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })( (
{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)}
) } @@ -657,7 +665,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
System MIDI Bindings - +
@@ -713,7 +721,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
- { (!this.props.onboarding) ? ( + {(!this.props.onboarding) ? (
SYSTEM @@ -735,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.
+
- ): ( + ) : (
- -
- + +
+
) - } + } @@ -849,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)} @@ -869,8 +910,8 @@ const SettingsDialog = withStyles(styles, { withTheme: true })( /> { this.setState({showSystemMidiBindingsDialog: false});}} - /> + onClose={() => { this.setState({ showSystemMidiBindingsDialog: false }); }} + /> ); 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/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/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/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/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;