diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c64939..d7baae4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,19 +1,37 @@ cmake_minimum_required(VERSION 3.16.0) project(pipedal - VERSION 2.0.105 + VERSION 2.0.106 DESCRIPTION "PiPedal Guitar Effect Pedal For Raspberry Pi" HOMEPAGE_URL "https://rerdavies.github.io/pipedal" ) -execute_process( COMMAND dpkg --print-architecture COMMAND tr -d '\n' OUTPUT_VARIABLE DEBIAN_ARCHITECTURE ) +set (DISPLAY_VERSION "PiPedal v2.0.106-Release") -set (DISPLAY_VERSION "PiPedal v2.0.105-Release") -set (PACKAGE_ARCHITECTURE ${DEBIAN_ARCHITECTURE}) -set (CMAKE_INSTALL_PREFIX "/usr/") +option(PIPEDAL_DISABLE_COPYRIGHT_BUILD "Skip generation of copyright notices (use on non-Debian distros)" OFF) +option(PIPEDAL_EXCLUDE_TESTS "Exclude test targets from default build" OFF) set(PIPEDAL_T3K_PUBLISHABLE_KEY "t3k_pub_bHrH8btdwXXTtxz5ryEU8sNLF-2TGRT9") +# Determine Debian-compatible architecture name (portable across distros) +if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") + set(DEBIAN_ARCHITECTURE "amd64") +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64") + set(DEBIAN_ARCHITECTURE "arm64") +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "armv7l") + set(DEBIAN_ARCHITECTURE "armhf") +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "i386|i686") + set(DEBIAN_ARCHITECTURE "i386") +else() + execute_process(COMMAND uname -m COMMAND tr -d '\n' OUTPUT_VARIABLE UNAME_M) + message(FATAL_ERROR "Cannot determine Debian architecture from CMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR} (uname -m: ${UNAME_M})") +endif() +message(STATUS "DEBIAN_ARCHITECTURE=${DEBIAN_ARCHITECTURE}") + +set (PACKAGE_ARCHITECTURE ${DEBIAN_ARCHITECTURE}) +set (CMAKE_INSTALL_PREFIX "/usr/") + + set (LIBZIP_DO_INSTALL OFF CACHE BOOL "Disable libzip install" FORCE) set (SQLITECPP_INSTALL OFF CACHE BOOL "Disable SQLiteCpp install" FORCE) @@ -24,8 +42,6 @@ set(CPACK_SOURCE_IGNORE_FILES "*.a;sqlite3.h;$(CPACK_SOURCE_IGNORE_FILES)" ) - -set (PIPEDAL_EXCLUDE_TESTS false) include(CTest) enable_testing() @@ -58,8 +74,10 @@ install ( install ( DIRECTORY ${VITE_BUILD_DIRECTORY} DESTINATION /etc/pipedal/react ) -install (FILES ${PROJECT_SOURCE_DIR}/build/src/notices.txt - DESTINATION /etc/pipedal/react/var/) +if(NOT PIPEDAL_DISABLE_COPYRIGHT_BUILD) + install (FILES ${PROJECT_SOURCE_DIR}/build/src/notices.txt + DESTINATION /etc/pipedal/react/var/) +endif() install ( DIRECTORY ${PROJECT_SOURCE_DIR}/src/templates diff --git a/PiPedalCommon/src/CMakeLists.txt b/PiPedalCommon/src/CMakeLists.txt index 6829869..40a58c8 100644 --- a/PiPedalCommon/src/CMakeLists.txt +++ b/PiPedalCommon/src/CMakeLists.txt @@ -97,7 +97,7 @@ add_library(PiPedalCommon STATIC ss.hpp ) target_include_directories(PiPedalCommon PUBLIC "include" ${LIBNL3_INCLUDE_DIRS}) -target_link_libraries(PiPedalCommon PUBLIC ${LIBNL3_LIBRARIES} sdbus-c++-objlib +target_link_libraries(PiPedalCommon PUBLIC ${LIBNL3_LIBRARIES} sdbus-c++-objlib asound ) diff --git a/PiPedalCommon/src/DBusDispatcher.cpp b/PiPedalCommon/src/DBusDispatcher.cpp index e440552..d80dfec 100644 --- a/PiPedalCommon/src/DBusDispatcher.cpp +++ b/PiPedalCommon/src/DBusDispatcher.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "ss.hpp" #include #include diff --git a/README.md b/README.md index 10f5c19..1c56325 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,11 @@ -Download: v2.0.105 +Download: v2.0.106 Website: [https://rerdavies.github.io/pipedal](https://rerdavies.github.io/pipedal). Documentation: [https://rerdavies.github.io/pipedal/Documentation.html](https://rerdavies.github.io/pipedal/Documentation.html). -#### Announcing PiPedal 2.0 (2.0.105)—a major update to PiPedal, including exciting new features. See the Pipedal website [documentation](https://rerdavies.github.io/pipedal/PiPedal.html) for more information. +#### Announcing PiPedal 2.0 (2.0.106)—a major update to PiPedal, including exciting new features. See the Pipedal website [documentation](https://rerdavies.github.io/pipedal/PiPedal.html) for more information.   diff --git a/docs/BuildPrerequisites.md b/docs/BuildPrerequisites.md index 1b6257f..35c5418 100644 --- a/docs/BuildPrerequisites.md +++ b/docs/BuildPrerequisites.md @@ -17,7 +17,8 @@ If your distribution doesn't provide a suitable version of nodejs, please refer to the `node.js` website for instructions on [how to install the latest version of `node.js`](https://nodejs.org/en/download) directly. Run the following commands to install dependent libraries required by the PiPedal build. - + +Ubuntu: sudo apt update sudo apt upgrade @@ -27,6 +28,26 @@ Run the following commands to install dependent libraries required by the PiPeda libsdbus-c++-dev libzip-dev google-perftools \ libgoogle-perftools-dev \ libpipewire-0.3-dev libbz2-dev libssl-dev librsvg2-dev + +Arch Linux (alpha status): + + sudo pacman -S --needed lilv boost systemd catch2 alsa-lib \ + util-linux avahi networkmanager icu libzip gperftools \ + pipewire bzip2 openssl librsvg sdbus-cpp + + There is one AUR package: + sudo paru -S authbind + +Note: in the Arch Linux build, copyright notices generation won't work, so it needs to be disabled with a cmake argument. Add this to your .vscode/settings.json file: + +```json +{ + "cmake.configureSettings": { + "PIPEDAL_DISABLE_COPYRIGHT_BUILD": "ON" + }, + ... +} +``` ### Installing Sources diff --git a/docs/Installing.md b/docs/Installing.md index 2689565..f0a8234 100644 --- a/docs/Installing.md +++ b/docs/Installing.md @@ -13,18 +13,18 @@ page_icon: img/Install4.jpg Download the most recent Debian (.deb) package for your platform: -- [Raspberry Pi OS bookworm (aarch64) v2.0.105](https://github.com/rerdavies/pipedal/releases/download/v2.0.105/pipedal_2.0.105_arm64.deb) -- [Ubuntu 24.04 through 25.04 (aarch64) v2.0.105](https://github.com/rerdavies/pipedal/releases/download/v2.0.105/pipedal_2.0.105_arm64.deb) -- [Ubuntu 24.04 through 25.04 (amd64) v2.0.105](https://github.com/rerdavies/pipedal/releases/download/v2.0.105/pipedal_2.0.105_amd64.deb) +- [Raspberry Pi OS bookworm (aarch64) v2.0.106](https://github.com/rerdavies/pipedal/releases/download/v2.0.106/pipedal_2.0.106_arm64.deb) +- [Ubuntu 24.04 through 25.04 (aarch64) v2.0.106](https://github.com/rerdavies/pipedal/releases/download/v2.0.106/pipedal_2.0.106_arm64.deb) +- [Ubuntu 24.04 through 25.04 (amd64) v2.0.106](https://github.com/rerdavies/pipedal/releases/download/v2.0.106/pipedal_2.0.106_amd64.deb) -Version 2.0.105 has been tested on Raspberry Pi OS bookworm, Ubuntu 24.04 (amd64), Ubuntu 24.10 (aarch64), and Ubuntu 25.04 (aarch64). Download the appropriate package for your platform, and install using the following procedure: +Version 2.0.106 has been tested on Raspberry Pi OS bookworm, Ubuntu 24.04 (amd64), Ubuntu 24.10 (aarch64), and Ubuntu 25.04 (aarch64). Download the appropriate package for your platform, and install using the following procedure: ``` sudo apt update sudo apt upgrade cd ~/Downloads - sudo apt-get install ./pipedal_2.0.105_arm64.deb + sudo apt-get install ./pipedal_2.0.106_arm64.deb ``` You MUST use `apt-get`. `apt` will not install downloaded packages; and `dpkg -i` will not install dependencies. @@ -62,7 +62,7 @@ Use the following command to copy the downloaded package to the server from Linu Adjust `username`, `server_address` and the actual name of the Debian package you downloaded as needed: ``` -scp Downloads/pipedal_2.0.105_arm64.deb username@server_address:/home/username/ +scp Downloads/pipedal_2.0.106_arm64.deb username@server_address:/home/username/ ``` This will copy the downloaded package on your desktop computer to your home directory on the PiPedal server computer. @@ -70,7 +70,7 @@ Once you have copied the package to the server, you can SSH into the server and ``` ssh username@server_address -sudo apt-get install ./pipedal_2.0.105_arm64.deb +sudo apt-get install ./pipedal_2.0.106_arm64.deb ``` The PiPedal package installer will print out the port number that the PiPedal web server is listening on. It defaults to port 80, but if you have another web server already running on port 80, it will select the next available port. Ubuntu default installs have Apache Server running on port 80, so the PiPedal web server will default to port 81 on Ubuntu. diff --git a/docs/PiPedal2.md b/docs/PiPedal2.md index 2647682..99dd98d 100644 --- a/docs/PiPedal2.md +++ b/docs/PiPedal2.md @@ -11,7 +11,7 @@ -_To download PiPedal v2.0.105-alpha, click [*here*](download.md). +_To download PiPedal v2.0.106-alpha, click [*here*](download.md). To view PiPedal documentation, click [*here*](Documentation.md)._ Use your Raspberry Pi as a guitar effects pedal. Configure and control PiPedal with your phone or tablet. diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index bad51f4..142a819 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -1,5 +1,23 @@ # Release Notes +## PiPedal 2.0.106-Release + +New Features +- New TooB Multi-Tap Delay plugin. +- Drag and copy/paste Split nodes (and all their children) to new locations in the pedalboard. This allows you to easily rearrange the structure of your pedalboard. +- Range mapping controls in MIDI bindings now use the units of the target control, instead of 0..1 ranges. Provided by Fulgenzio Ruiz Rubio. +- New system MIDI bindings for Next/Previous Snapshot. Provided by Fulgenzio Ruiz Rubio. + +Minor Features: +- Alpha release of PiPedal build procedure for ARCH Linux. +- Fix compilation warnings/errors when compiling with GCC 16. +- Better display names for ALSA USB audio devices in the UI. + +Bug fixes: + +- Incorrect rendering when dragging plugins. +- MIDI hangups when using MIDI footpedals (or other high-rate CC messages). Provided by onirob. + ## PiPedal 2.0.105-Release Bug fix: diff --git a/docs/download.md b/docs/download.md index 85aacbb..4887c61 100644 --- a/docs/download.md +++ b/docs/download.md @@ -1,13 +1,13 @@ ## Download -# Download PiPedal 2.0.105-alpha +# Download PiPedal 2.0.106-alpha Download the most recent Debian (.deb) package for your platform: -- [Raspberry Pi OS bookworm (aarch64) v2.0.105 Alpha](https://github.com/rerdavies/pipedal/releases/download/v2.0.105/pipedal_2.0.105_arm64.deb) -- [Ubuntu 24.x, 25.04 (aarch64) v2.0.105 Alpha](https://github.com/rerdavies/pipedal/releases/download/v2.0.105/pipedal_2.0.105_arm64.deb) -- [Ubuntu 24.x, 25.04 (amd64/x86_64) v2.0.105 Alpha](https://github.com/rerdavies/pipedal/releases/download/v2.0.105/pipedal_2.0.105_amd64.deb) +- [Raspberry Pi OS bookworm (aarch64) v2.0.106 Alpha](https://github.com/rerdavies/pipedal/releases/download/v2.0.106/pipedal_2.0.106_arm64.deb) +- [Ubuntu 24.x, 25.04 (aarch64) v2.0.106 Alpha](https://github.com/rerdavies/pipedal/releases/download/v2.0.106/pipedal_2.0.106_arm64.deb) +- [Ubuntu 24.x, 25.04 (amd64/x86_64) v2.0.106 Alpha](https://github.com/rerdavies/pipedal/releases/download/v2.0.106/pipedal_2.0.106_amd64.deb) Install the package by running @@ -15,7 +15,7 @@ Install the package by running ``` sudo apt update cd ~/Downloads - sudo apt install ./pipedal_2.0.105_arm64.deb + sudo apt install ./pipedal_2.0.106_arm64.deb # or ... _amd64.deb as appropriate for your platform ``` The message about missing permissions given by `apt` is diff --git a/docs/index.md b/docs/index.md index d32f038..471857a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -9,7 +9,7 @@ -_To download PiPedal v2.0.105 Release, click [*here*](download.md). +_To download PiPedal v2.0.106 Release, click [*here*](download.md). To view PiPedal documentation, click [*here*](Documentation.md)._ Use your Raspberry Pi, or Ubuntu amd/x86-64 computer as a guitar effects pedal. Configure and control PiPedal remotedly, with your phone or tablet, or via a web browser. diff --git a/lv2/aarch64/ToobAmp.lv2/CabIR.ttl b/lv2/aarch64/ToobAmp.lv2/CabIR.ttl index 0124787..2dc36d8 100644 --- a/lv2/aarch64/ToobAmp.lv2/CabIR.ttl +++ b/lv2/aarch64/ToobAmp.lv2/CabIR.ttl @@ -93,7 +93,7 @@ cabir:impulseFile3 doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; rdfs:comment """ TooB Cab IR is a convolution-based guitar cabinet impulse response simulator. diff --git a/lv2/aarch64/ToobAmp.lv2/CabSim.ttl b/lv2/aarch64/ToobAmp.lv2/CabSim.ttl index e22b251..823f2d8 100644 --- a/lv2/aarch64/ToobAmp.lv2/CabSim.ttl +++ b/lv2/aarch64/ToobAmp.lv2/CabSim.ttl @@ -50,7 +50,7 @@ toob:frequencyResponseVector doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; mod:brand "TooB"; mod:label "Cab Simulator"; diff --git a/lv2/aarch64/ToobAmp.lv2/ConvolutionReverb.ttl b/lv2/aarch64/ToobAmp.lv2/ConvolutionReverb.ttl index f2577f2..658f273 100644 --- a/lv2/aarch64/ToobAmp.lv2/ConvolutionReverb.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ConvolutionReverb.ttl @@ -53,7 +53,7 @@ toobimpulse:impulseFile doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; rdfs:comment """ Toob Convolution Reverb Stereo uses convolution reverb impulse/response files in order to produce highly diff --git a/lv2/aarch64/ToobAmp.lv2/ConvolutionReverbStereo.ttl b/lv2/aarch64/ToobAmp.lv2/ConvolutionReverbStereo.ttl index 5050654..77a502e 100644 --- a/lv2/aarch64/ToobAmp.lv2/ConvolutionReverbStereo.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ConvolutionReverbStereo.ttl @@ -51,7 +51,7 @@ toobimpulse:impulseFile doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; rdfs:comment """ diff --git a/lv2/aarch64/ToobAmp.lv2/InputStage.ttl b/lv2/aarch64/ToobAmp.lv2/InputStage.ttl index e0c909a..a1a9296 100644 --- a/lv2/aarch64/ToobAmp.lv2/InputStage.ttl +++ b/lv2/aarch64/ToobAmp.lv2/InputStage.ttl @@ -66,7 +66,7 @@ inputStage:filterGroup doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; mod:brand "TooB"; mod:label "Input"; diff --git a/lv2/aarch64/ToobAmp.lv2/PowerStage2.ttl b/lv2/aarch64/ToobAmp.lv2/PowerStage2.ttl index 4de7bf7..eb762a3 100644 --- a/lv2/aarch64/ToobAmp.lv2/PowerStage2.ttl +++ b/lv2/aarch64/ToobAmp.lv2/PowerStage2.ttl @@ -68,7 +68,7 @@ pstage:stage3 doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; mod:brand "TooB"; mod:label "Power Stage"; diff --git a/lv2/aarch64/ToobAmp.lv2/SpectrumAnalyzer.ttl b/lv2/aarch64/ToobAmp.lv2/SpectrumAnalyzer.ttl index 7684809..d5196b5 100644 --- a/lv2/aarch64/ToobAmp.lv2/SpectrumAnalyzer.ttl +++ b/lv2/aarch64/ToobAmp.lv2/SpectrumAnalyzer.ttl @@ -57,7 +57,7 @@ doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; rdfs:comment "TooB spectrum analyzer" ; mod:brand "TooB"; diff --git a/lv2/aarch64/ToobAmp.lv2/ToneStack.ttl b/lv2/aarch64/ToobAmp.lv2/ToneStack.ttl index 74a4cce..857f23e 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToneStack.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToneStack.ttl @@ -57,7 +57,7 @@ tonestack:eqGroup doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; uiext:ui ; diff --git a/lv2/aarch64/ToobAmp.lv2/Toob3BandEq.ttl b/lv2/aarch64/ToobAmp.lv2/Toob3BandEq.ttl index 6ec40fb..944ea54 100644 --- a/lv2/aarch64/ToobAmp.lv2/Toob3BandEq.ttl +++ b/lv2/aarch64/ToobAmp.lv2/Toob3BandEq.ttl @@ -52,7 +52,7 @@ toob:frequencyResponseVector doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; uiext:ui ; diff --git a/lv2/aarch64/ToobAmp.lv2/Toob3BandEqStereo.ttl b/lv2/aarch64/ToobAmp.lv2/Toob3BandEqStereo.ttl index c96940f..b2e0c60 100644 --- a/lv2/aarch64/ToobAmp.lv2/Toob3BandEqStereo.ttl +++ b/lv2/aarch64/ToobAmp.lv2/Toob3BandEqStereo.ttl @@ -52,7 +52,7 @@ toob:frequencyResponseVector doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; uiext:ui ; diff --git a/lv2/aarch64/ToobAmp.lv2/ToobAmpUI.so b/lv2/aarch64/ToobAmp.lv2/ToobAmpUI.so index 5f8a863..6d87301 100755 Binary files a/lv2/aarch64/ToobAmp.lv2/ToobAmpUI.so and b/lv2/aarch64/ToobAmp.lv2/ToobAmpUI.so differ diff --git a/lv2/aarch64/ToobAmp.lv2/ToobChorus.ttl b/lv2/aarch64/ToobAmp.lv2/ToobChorus.ttl index 551dba1..8fc263f 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobChorus.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobChorus.ttl @@ -40,7 +40,7 @@ doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; rdfs:comment """ Emulation of a Boss CE-2 Chorus. diff --git a/lv2/aarch64/ToobAmp.lv2/ToobDelay.ttl b/lv2/aarch64/ToobAmp.lv2/ToobDelay.ttl index ec29d80..104a907 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobDelay.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobDelay.ttl @@ -41,7 +41,7 @@ doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; rdfs:comment """ A straightforward no-frills digital delay. diff --git a/lv2/aarch64/ToobAmp.lv2/ToobFlanger.ttl b/lv2/aarch64/ToobAmp.lv2/ToobFlanger.ttl index 07516d0..ffcca04 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobFlanger.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobFlanger.ttl @@ -41,7 +41,7 @@ doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; rdfs:comment """ Emulation of a Boss BF-2 Flanger, based on circuit analysis and simulation of the original. diff --git a/lv2/aarch64/ToobAmp.lv2/ToobFlangerStereo.ttl b/lv2/aarch64/ToobAmp.lv2/ToobFlangerStereo.ttl index 37c049e..7811605 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobFlangerStereo.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobFlangerStereo.ttl @@ -41,7 +41,7 @@ doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; rdfs:comment """ Digital emulation of a Boss BF-2 Flanger. diff --git a/lv2/aarch64/ToobAmp.lv2/ToobFreeverb.ttl b/lv2/aarch64/ToobAmp.lv2/ToobFreeverb.ttl index f41b379..d1b5abc 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobFreeverb.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobFreeverb.ttl @@ -41,7 +41,7 @@ doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; rdfs:comment """ Toob Freeverb is an Lv2 implementation of the famous Freeverb reverb effect. FreeVerb delivers a well-balanced reverb with very little tonal coloration. diff --git a/lv2/aarch64/ToobAmp.lv2/ToobGraphicEq.ttl b/lv2/aarch64/ToobAmp.lv2/ToobGraphicEq.ttl index 08d0b26..1f28cd7 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobGraphicEq.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobGraphicEq.ttl @@ -41,7 +41,7 @@ doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; rdfs:comment """ A 7 octave graphic equalizer, with frequencies chosen to be useful for EQ-ing guitar signals. """ ; diff --git a/lv2/aarch64/ToobAmp.lv2/ToobLooperFour.ttl b/lv2/aarch64/ToobAmp.lv2/ToobLooperFour.ttl index 56c5a2d..d93dddb 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobLooperFour.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobLooperFour.ttl @@ -92,7 +92,7 @@ myprefix:output_group doap:license ; doap:maintainer ; lv2:minorVersion 3 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; ui:ui ; diff --git a/lv2/aarch64/ToobAmp.lv2/ToobLooperOne.ttl b/lv2/aarch64/ToobAmp.lv2/ToobLooperOne.ttl index 8a56f2d..228a0ab 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobLooperOne.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobLooperOne.ttl @@ -78,7 +78,7 @@ myprefix:output_group doap:license ; doap:maintainer ; lv2:minorVersion 3 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; ui:ui ; diff --git a/lv2/aarch64/ToobAmp.lv2/ToobML.ttl b/lv2/aarch64/ToobAmp.lv2/ToobML.ttl index 8ef52ef..fdb6284 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobML.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobML.ttl @@ -67,7 +67,7 @@ toobml:sagGroup doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; rdfs:comment """ The TooB ML Amplifier plugin provides emulation of a variety of amplifiers and overdrive pedals that are implemented using neural-network-based machine learning models of real amplifiers. diff --git a/lv2/aarch64/ToobAmp.lv2/ToobMix.ttl b/lv2/aarch64/ToobAmp.lv2/ToobMix.ttl index 4714bc8..d699d4c 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobMix.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobMix.ttl @@ -40,7 +40,7 @@ doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; rdfs:comment """ Remix a stereo input signal. diff --git a/lv2/aarch64/ToobAmp.lv2/ToobMultiEcho.ttl b/lv2/aarch64/ToobAmp.lv2/ToobMultiEcho.ttl new file mode 100644 index 0000000..cc3798e --- /dev/null +++ b/lv2/aarch64/ToobAmp.lv2/ToobMultiEcho.ttl @@ -0,0 +1,457 @@ +@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 atom: . +@prefix patch: . +@prefix rdfs: . +@prefix state: . +@prefix urid: . +@prefix xsd: . +@prefix toobMultiEcho: . + +toobMultiEcho:tap1 + a param:ControlGroup , + pg:InputGroup ; + lv2:name "Tap 1" ; + lv2:symbol "tap1" . + +toobMultiEcho:tap2 + a param:ControlGroup , + pg:InputGroup ; + lv2:name "Tap 2" ; + lv2:symbol "tap2" . + +toobMultiEcho:tap3 + a param:ControlGroup , + pg:InputGroup ; + lv2:name "Tap 3" ; + lv2:symbol "tap3" . + +toobMultiEcho:tap4 + a param:ControlGroup , + pg:InputGroup ; + lv2:name "Tap 4" ; + lv2:symbol "tap4" . + + + + a foaf:Person ; + foaf:name "Robin Davies" ; + foaf:mbox ; + foaf:homepage . + + + a lv2:Plugin , + lv2:DelayPlugin ; + doap:name "TooB Multi-Tap Delay" , + "Toob Multi-Tap Delay"@en-gb + ; + uiext:ui ; + doap:license ; + doap:maintainer ; + lv2:minorVersion 0 ; + lv2:microVersion 81 ; + rdfs:comment """ +A 4-tap Echo/Delay effect. + +""" ; + + mod:brand "TooB"; + mod:label "Multi-Tap Delay"; + lv2:optionalFeature lv2:hardRTCapable; + + lv2:port + [ + a lv2:InputPort , + lv2:ControlPort ; + lv2:index 0 ; + lv2:symbol "bypass" ; + lv2:name "Bypass" ; + lv2:default 1.0 ; + lv2:minimum 0.0 ; + lv2:maximum 1.0 ; + lv2:designation lv2:enabled; + lv2:portProperty lv2:toggled; + ], + #### tap1 ############################################################## + [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap1 ; + lv2:index 1; + lv2:symbol "delay1" ; + lv2:name "Delay"; + lv2:default 340 ; + lv2:minimum 5; + lv2:maximum 3000; + units:unit units:ms; + lv2:portProperty ; + rdfs:comment "Delay in ms"; + + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap1 ; + lv2:index 2 ; + lv2:symbol "level1" ; + lv2:name "Level"; + + lv2:default 27.0 ; + lv2:minimum 0.0 ; + lv2:maximum 100.0 ; + units:unit units:pc; + rdfs:comment "Attenuation of the first echo"; + lv2:scalePoint [ + rdfs:label "Off" ; + rdf:value 0.0 + ] + ], + + [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap1 ; + lv2:index 3 ; + lv2:symbol "feedback1" ; + lv2:name "Feedback"; + + + lv2:default 39.0 ; + lv2:minimum 0.0 ; + lv2:maximum 100.0 ; + units:unit units:pc; + rdfs:comment "Attenuation of subequent echoes"; + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap1 ; + lv2:index 4 ; + lv2:symbol "pan1" ; + lv2:name "Pan"; + lv2:portProperty epp:notOnGUI; + + lv2:default 0.0 ; + lv2:minimum -1.0 ; + lv2:maximum 1.0 ; + rdfs:comment "Pan (tap 1)"; + ], + + + #### tap2 ############################################################## + [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap2 ; + lv2:index 5; + lv2:symbol "delay2" ; + lv2:name "Delay"; + lv2:default 111 ; + lv2:minimum 5; + lv2:maximum 3000; + units:unit units:ms; + lv2:portProperty ; + rdfs:comment "Delay in ms"; + + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap2 ; + lv2:index 6 ; + lv2:symbol "level2" ; + lv2:name "Level"; + + lv2:default 0.0 ; + lv2:minimum 0.0 ; + lv2:maximum 100.0 ; + units:unit units:pc; + rdfs:comment "Attenuation of the first echo"; + lv2:scalePoint [ + rdfs:label "Off" ; + rdf:value 0.0 + ] + + ], [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap2 ; + lv2:index 7 ; + lv2:symbol "feedback2" ; + lv2:name "Feedback"; + + + lv2:default 0.0 ; + lv2:minimum 0.0 ; + lv2:maximum 100.0 ; + units:unit units:pc; + rdfs:comment "Attenuation of subequent echoes"; + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap2 ; + lv2:index 8 ; + lv2:symbol "pan2" ; + lv2:name "Pan"; + lv2:portProperty epp:notOnGUI; + + lv2:default 0.0 ; + lv2:minimum -1.0 ; + lv2:maximum 1.0 ; + rdfs:comment "Pan (tap 1)"; + ], + + #### tap3 ############################################################## + [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap3 ; + lv2:index 9; + lv2:symbol "delay3" ; + lv2:name "Delay"; + lv2:default 23 ; + lv2:minimum 5; + lv2:maximum 3000; + units:unit units:ms; + lv2:portProperty ; + rdfs:comment "Delay in ms"; + + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap3 ; + lv2:index 10 ; + lv2:symbol "level3" ; + lv2:name "Level"; + + lv2:default 0.0 ; + lv2:minimum 0.0 ; + lv2:maximum 100.0 ; + units:unit units:pc; + rdfs:comment "Attenuation of the first echo"; + lv2:scalePoint [ + rdfs:label "Off" ; + rdf:value 0.0 + ] + + ], [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap3 ; + lv2:index 11 ; + lv2:symbol "feedback3" ; + lv2:name "Feedback"; + + + lv2:default 0.0 ; + lv2:minimum 0.0 ; + lv2:maximum 100.0 ; + units:unit units:pc; + rdfs:comment "Attenuation of subequent echoes"; + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap3 ; + lv2:index 12 ; + lv2:symbol "pan3" ; + lv2:name "Pan"; + lv2:portProperty epp:notOnGUI; + + lv2:default 0.0 ; + lv2:minimum -1.0 ; + lv2:maximum 1.0 ; + rdfs:comment "Pan (tap 1)"; + ], + + #### tap4 ############################################################## + [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap4 ; + lv2:index 13; + lv2:symbol "delay4" ; + lv2:name "Delay"; + lv2:default 40 ; + lv2:minimum 5; + lv2:maximum 3000; + units:unit units:ms; + lv2:portProperty ; + rdfs:comment "Delay in ms"; + + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap4 ; + lv2:index 14 ; + lv2:symbol "level4" ; + lv2:name "Level"; + + lv2:default 0.0 ; + lv2:minimum 0.0 ; + lv2:maximum 100.0 ; + units:unit units:pc; + rdfs:comment "Attenuation of the first echo"; + lv2:scalePoint [ + rdfs:label "Off" ; + rdf:value 0.0 + ] + + ], [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap4 ; + lv2:index 15 ; + lv2:symbol "feedback4" ; + lv2:name "Feedback"; + + + lv2:default 50.0 ; + lv2:minimum 0.0 ; + lv2:maximum 100.0 ; + units:unit units:pc; + rdfs:comment "Attenuation of subequent echoes"; + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap4 ; + lv2:index 16 ; + lv2:symbol "pan4" ; + lv2:name "Pan"; + lv2:portProperty epp:notOnGUI; + + lv2:default 0.0 ; + lv2:minimum -1.0 ; + lv2:maximum 1.0 ; + rdfs:comment "Pan (tap 1)"; + ], + ######### Mix ################################ + + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 17 ; + lv2:symbol "direct" ; + lv2:name "Direct"; + + lv2:default 100.0 ; + lv2:minimum 0.0 ; + lv2:maximum 200.0 ; + units:unit units:pc; + rdfs:comment "Attenuation of the first echo"; + lv2:scalePoint [ + rdfs:label "Off" ; + rdf:value 0.0 + ] + + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 18; + lv2:symbol "master" ; + lv2:name "Master"; + lv2:default 0.0 ; + lv2:minimum -40.0; + lv2:maximum 40.0; + units:unit units:db; + rdfs:comment "Master volume"; + + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 19 ; + lv2:symbol "tails" ; + lv2:name "Tails"; + + lv2:default 1 ; + lv2:minimum 0; + lv2:maximum 1 ; + lv2:portProperty lv2:toggled; + + rdfs:comment "Enable echo tails when bypassed" ; + ], + + ############# Audio I/O ####################### + [ + a lv2:AudioPort , + lv2:InputPort ; + lv2:index 20 ; + lv2:symbol "inl" ; + lv2:name "In L" + ], + [ + a lv2:AudioPort , + lv2:OutputPort ; + lv2:index 21 ; + lv2:symbol "outl" ; + lv2:name "Out L" + ] + #,[ + # a lv2:AudioPort , + # lv2:InputPort ; + # lv2:index 22 ; + # lv2:symbol "inr" ; + # lv2:name "In R" + #], + #[ + # a lv2:AudioPort , + # lv2:OutputPort ; + # lv2:index 23 ; + # lv2:symbol "outr" ; + # lv2:name "OutR" + #] + + . + + + a uiext:X11UI ; + lv2:binary ; + + lv2:extensionData uiext::idle ; + lv2:extensionData uiext:resize ; + lv2:extensionData uiext:idleInterface; + lv2:requiredFeature uiext:idleInterface ; + . + + + diff --git a/lv2/aarch64/ToobAmp.lv2/ToobMultiEchoStereo.ttl b/lv2/aarch64/ToobAmp.lv2/ToobMultiEchoStereo.ttl new file mode 100644 index 0000000..3889327 --- /dev/null +++ b/lv2/aarch64/ToobAmp.lv2/ToobMultiEchoStereo.ttl @@ -0,0 +1,453 @@ +@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 atom: . +@prefix patch: . +@prefix rdfs: . +@prefix state: . +@prefix urid: . +@prefix xsd: . +@prefix toobMultiEcho: . + +toobMultiEcho:tap1 + a param:ControlGroup , + pg:InputGroup ; + lv2:name "Tap 1" ; + lv2:symbol "tap1" . + +toobMultiEcho:tap2 + a param:ControlGroup , + pg:InputGroup ; + lv2:name "Tap 2" ; + lv2:symbol "tap2" . + +toobMultiEcho:tap3 + a param:ControlGroup , + pg:InputGroup ; + lv2:name "Tap 3" ; + lv2:symbol "tap3" . + +toobMultiEcho:tap4 + a param:ControlGroup , + pg:InputGroup ; + lv2:name "Tap 4" ; + lv2:symbol "tap4" . + + + + a foaf:Person ; + foaf:name "Robin Davies" ; + foaf:mbox ; + foaf:homepage . + + + a lv2:Plugin , + lv2:DelayPlugin ; + doap:name "TooB Multi-Tap Delay (Stereo)" , + "Toob Multi-Tap Delay (Stereo)"@en-gb + ; + uiext:ui ; + doap:license ; + doap:maintainer ; + lv2:minorVersion 0 ; + lv2:microVersion 81 ; + rdfs:comment """ +A 4-tap Echo/Delay effect. + +""" ; + + mod:brand "TooB"; + mod:label "Multi-Tap Delay"; + lv2:optionalFeature lv2:hardRTCapable; + + lv2:port + [ + a lv2:InputPort , + lv2:ControlPort ; + lv2:index 0 ; + lv2:symbol "bypass" ; + lv2:name "Bypass" ; + lv2:default 1.0 ; + lv2:minimum 0.0 ; + lv2:maximum 1.0 ; + lv2:designation lv2:enabled; + lv2:portProperty lv2:toggled; + ], + #### tap1 ############################################################## + [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap1 ; + lv2:index 1; + lv2:symbol "delay1" ; + lv2:name "Delay"; + lv2:default 340 ; + lv2:minimum 5; + lv2:maximum 3000; + units:unit units:ms; + lv2:portProperty ; + rdfs:comment "Delay in ms"; + + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap1 ; + lv2:index 2 ; + lv2:symbol "level1" ; + lv2:name "Level"; + + lv2:default 27.0 ; + lv2:minimum 0.0 ; + lv2:maximum 100.0 ; + units:unit units:pc; + rdfs:comment "Attenuation of the first echo"; + lv2:scalePoint [ + rdfs:label "Off" ; + rdf:value 0.0 + ] + ], + + [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap1 ; + lv2:index 3 ; + lv2:symbol "feedback1" ; + lv2:name "Feedback"; + + + lv2:default 39.0 ; + lv2:minimum 0.0 ; + lv2:maximum 100.0 ; + units:unit units:pc; + rdfs:comment "Attenuation of subequent echoes"; + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap1 ; + lv2:index 4 ; + lv2:symbol "pan1" ; + lv2:name "Pan"; + + lv2:default 0.0 ; + lv2:minimum -1.0 ; + lv2:maximum 1.0 ; + rdfs:comment "Pan (tap 1)"; + ], + + + #### tap2 ############################################################## + [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap2 ; + lv2:index 5; + lv2:symbol "delay2" ; + lv2:name "Delay"; + lv2:default 111 ; + lv2:minimum 5; + lv2:maximum 3000; + units:unit units:ms; + lv2:portProperty ; + rdfs:comment "Delay in ms"; + + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap2 ; + lv2:index 6 ; + lv2:symbol "level2" ; + lv2:name "Level"; + + lv2:default 0.0 ; + lv2:minimum 0.0 ; + lv2:maximum 100.0 ; + units:unit units:pc; + rdfs:comment "Attenuation of the first echo"; + lv2:scalePoint [ + rdfs:label "Off" ; + rdf:value 0.0 + ] + + ], [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap2 ; + lv2:index 7 ; + lv2:symbol "feedback2" ; + lv2:name "Feedback"; + + + lv2:default 0.0 ; + lv2:minimum 0.0 ; + lv2:maximum 100.0 ; + units:unit units:pc; + rdfs:comment "Attenuation of subequent echoes"; + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap2 ; + lv2:index 8 ; + lv2:symbol "pan2" ; + lv2:name "Pan"; + + lv2:default 0.0 ; + lv2:minimum -1.0 ; + lv2:maximum 1.0 ; + rdfs:comment "Pan (tap 1)"; + ], + + #### tap3 ############################################################## + [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap3 ; + lv2:index 9; + lv2:symbol "delay3" ; + lv2:name "Delay"; + lv2:default 23 ; + lv2:minimum 5; + lv2:maximum 3000; + units:unit units:ms; + lv2:portProperty ; + rdfs:comment "Delay in ms"; + + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap3 ; + lv2:index 10 ; + lv2:symbol "level3" ; + lv2:name "Level"; + + lv2:default 0.0 ; + lv2:minimum 0.0 ; + lv2:maximum 100.0 ; + units:unit units:pc; + rdfs:comment "Attenuation of the first echo"; + lv2:scalePoint [ + rdfs:label "Off" ; + rdf:value 0.0 + ] + + ], [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap3 ; + lv2:index 11 ; + lv2:symbol "feedback3" ; + lv2:name "Feedback"; + + + lv2:default 0.0 ; + lv2:minimum 0.0 ; + lv2:maximum 100.0 ; + units:unit units:pc; + rdfs:comment "Attenuation of subequent echoes"; + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap3 ; + lv2:index 12 ; + lv2:symbol "pan3" ; + lv2:name "Pan"; + + lv2:default 0.0 ; + lv2:minimum -1.0 ; + lv2:maximum 1.0 ; + rdfs:comment "Pan (tap 1)"; + ], + + #### tap4 ############################################################## + [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap4 ; + lv2:index 13; + lv2:symbol "delay4" ; + lv2:name "Delay"; + lv2:default 40 ; + lv2:minimum 5; + lv2:maximum 3000; + units:unit units:ms; + lv2:portProperty ; + rdfs:comment "Delay in ms"; + + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap4 ; + lv2:index 14 ; + lv2:symbol "level4" ; + lv2:name "Level"; + + lv2:default 0.0 ; + lv2:minimum 0.0 ; + lv2:maximum 100.0 ; + units:unit units:pc; + rdfs:comment "Attenuation of the first echo"; + lv2:scalePoint [ + rdfs:label "Off" ; + rdf:value 0.0 + ] + + ], [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap4 ; + lv2:index 15 ; + lv2:symbol "feedback4" ; + lv2:name "Feedback"; + + + lv2:default 50.0 ; + lv2:minimum 0.0 ; + lv2:maximum 100.0 ; + units:unit units:pc; + rdfs:comment "Attenuation of subequent echoes"; + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + pg:group toobMultiEcho:tap4 ; + lv2:index 16 ; + lv2:symbol "pan4" ; + lv2:name "Pan"; + + lv2:default 0.0 ; + lv2:minimum -1.0 ; + lv2:maximum 1.0 ; + rdfs:comment "Pan (tap 1)"; + ], + ######### Mix ################################ + + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 17 ; + lv2:symbol "direct" ; + lv2:name "Direct"; + + lv2:default 100.0 ; + lv2:minimum 0.0 ; + lv2:maximum 200.0 ; + units:unit units:pc; + rdfs:comment "Attenuation of the first echo"; + lv2:scalePoint [ + rdfs:label "Off" ; + rdf:value 0.0 + ] + + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 18; + lv2:symbol "master" ; + lv2:name "Master"; + lv2:default 0.0 ; + lv2:minimum -40.0; + lv2:maximum 40.0; + units:unit units:db; + rdfs:comment "Master volume"; + + ], + [ + a lv2:InputPort , + lv2:ControlPort ; + + lv2:index 19 ; + lv2:symbol "tails" ; + lv2:name "Tails"; + + lv2:default 1 ; + lv2:minimum 0; + lv2:maximum 1 ; + lv2:portProperty lv2:toggled; + + rdfs:comment "Enable echo tails when bypassed" ; + ], + + ############# Audio I/O ####################### + [ + a lv2:AudioPort , + lv2:InputPort ; + lv2:index 20 ; + lv2:symbol "inl" ; + lv2:name "In L" + ], + [ + a lv2:AudioPort , + lv2:OutputPort ; + lv2:index 21 ; + lv2:symbol "outl" ; + lv2:name "Out L" + ] + ,[ + a lv2:AudioPort , + lv2:InputPort ; + lv2:index 22 ; + lv2:symbol "inr" ; + lv2:name "In R" + ], + [ + a lv2:AudioPort , + lv2:OutputPort ; + lv2:index 23 ; + lv2:symbol "outr" ; + lv2:name "OutR" + ] + + . + + + a uiext:X11UI ; + lv2:binary ; + + lv2:extensionData uiext::idle ; + lv2:extensionData uiext:resize ; + lv2:extensionData uiext:idleInterface; + lv2:requiredFeature uiext:idleInterface ; + . + + + diff --git a/lv2/aarch64/ToobAmp.lv2/ToobNeuralAmpModeler.ttl b/lv2/aarch64/ToobAmp.lv2/ToobNeuralAmpModeler.ttl index ff12aa1..25cc3c3 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobNeuralAmpModeler.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobNeuralAmpModeler.ttl @@ -74,7 +74,7 @@ toobNam:calibrationGroup doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; rdfs:comment """ TooB Neural Amp Modeler is a neural network based Amp Simulator. It uses .nam model files that are generated by training nueral networks on audio recordings of actual guitar amps, effects pedals and other equipment. .nam files contain data from the trained models, which can be loaded into diff --git a/lv2/aarch64/ToobAmp.lv2/ToobNoiseGate.ttl b/lv2/aarch64/ToobAmp.lv2/ToobNoiseGate.ttl index 2024656..f52416e 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobNoiseGate.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobNoiseGate.ttl @@ -54,7 +54,7 @@ noisegate:envelope_group doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; rdfs:comment """ A noise gate is an audio processing tool that controls the volume of an audio signal by allowing it to pass through only when it exceeds a set threshold. diff --git a/lv2/aarch64/ToobAmp.lv2/ToobParametricEq.ttl b/lv2/aarch64/ToobAmp.lv2/ToobParametricEq.ttl index 5ff2042..4a67d6b 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobParametricEq.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobParametricEq.ttl @@ -87,7 +87,7 @@ parametric_eq:hfGroup doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; uiext:ui ; diff --git a/lv2/aarch64/ToobAmp.lv2/ToobParametricEqStereo.ttl b/lv2/aarch64/ToobAmp.lv2/ToobParametricEqStereo.ttl index b118a92..1a57bcf 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobParametricEqStereo.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobParametricEqStereo.ttl @@ -87,7 +87,7 @@ toob-parametric-eq-stereo:hfGroup doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; uiext:ui ; diff --git a/lv2/aarch64/ToobAmp.lv2/ToobPhaser.ttl b/lv2/aarch64/ToobAmp.lv2/ToobPhaser.ttl index 83ae2bb..be63a31 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobPhaser.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobPhaser.ttl @@ -40,7 +40,7 @@ doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; rdfs:comment """ A loose emulation of an MXR® Phase 90 Phaser. diff --git a/lv2/aarch64/ToobAmp.lv2/ToobPlayer.ttl b/lv2/aarch64/ToobAmp.lv2/ToobPlayer.ttl index 775d431..664a385 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobPlayer.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobPlayer.ttl @@ -60,7 +60,7 @@ toobPlayer:seek doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; rdfs:comment """ Play audio files. Audio files can optionally be loooped by pressing the "Set loop" button. diff --git a/lv2/aarch64/ToobAmp.lv2/ToobRecordMono.ttl b/lv2/aarch64/ToobAmp.lv2/ToobRecordMono.ttl index 4a061b7..07d8195 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobRecordMono.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobRecordMono.ttl @@ -51,7 +51,7 @@ recordPrefix:audioFile doap:license ; doap:maintainer ; lv2:minorVersion 3 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; ui:ui ; diff --git a/lv2/aarch64/ToobAmp.lv2/ToobRecordStereo.ttl b/lv2/aarch64/ToobAmp.lv2/ToobRecordStereo.ttl index dde2ce7..f8ba5f2 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobRecordStereo.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobRecordStereo.ttl @@ -88,7 +88,7 @@ myprefix:loop3_group doap:license ; doap:maintainer ; lv2:minorVersion 3 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; ui:ui ; diff --git a/lv2/aarch64/ToobAmp.lv2/ToobTone.ttl b/lv2/aarch64/ToobAmp.lv2/ToobTone.ttl index f064d56..a43e712 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobTone.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobTone.ttl @@ -43,7 +43,7 @@ doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; rdfs:comment """ TooB Tone is a simple tone control. For more flexible EQ plugins, see TooB 3 Band Eq, or TooB Parameteric EQ. diff --git a/lv2/aarch64/ToobAmp.lv2/ToobToneStereo.ttl b/lv2/aarch64/ToobAmp.lv2/ToobToneStereo.ttl index d25f85d..d02404a 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobToneStereo.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobToneStereo.ttl @@ -43,7 +43,7 @@ doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; rdfs:comment """ TooB Tone is a simple tone control. For more flexible EQ plugins, see TooB 3 Band Eq, or TooB Parameteric EQ. diff --git a/lv2/aarch64/ToobAmp.lv2/ToobTremolo.ttl b/lv2/aarch64/ToobAmp.lv2/ToobTremolo.ttl index f5d7564..8f9e628 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobTremolo.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobTremolo.ttl @@ -41,7 +41,7 @@ doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; rdfs:comment """ A tremolo effect, equivalent to "vibrato" on vintage Fender tube amps. diff --git a/lv2/aarch64/ToobAmp.lv2/ToobTremoloMono.ttl b/lv2/aarch64/ToobAmp.lv2/ToobTremoloMono.ttl index c1e06c8..e1e50ce 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobTremoloMono.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobTremoloMono.ttl @@ -41,7 +41,7 @@ doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; rdfs:comment """ A tremolo effect, equivalent to "vibrato" on vintage Fender tube amps. diff --git a/lv2/aarch64/ToobAmp.lv2/ToobTuner.ttl b/lv2/aarch64/ToobAmp.lv2/ToobTuner.ttl index 2592592..9018e6f 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobTuner.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobTuner.ttl @@ -44,7 +44,7 @@ doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; rdfs:comment """ TooB Tuner is a chromatic guitar tuner. """ ; diff --git a/lv2/aarch64/ToobAmp.lv2/ToobVolume.ttl b/lv2/aarch64/ToobAmp.lv2/ToobVolume.ttl index 9c140d1..18decbe 100644 --- a/lv2/aarch64/ToobAmp.lv2/ToobVolume.ttl +++ b/lv2/aarch64/ToobAmp.lv2/ToobVolume.ttl @@ -39,7 +39,7 @@ doap:license ; doap:maintainer ; lv2:minorVersion 0 ; - lv2:microVersion 80 ; + lv2:microVersion 81 ; rdfs:comment """ Volume control. diff --git a/lv2/aarch64/ToobAmp.lv2/bin/ToobAmp-a72.so b/lv2/aarch64/ToobAmp.lv2/bin/ToobAmp-a72.so index cfcb39e..5405e8e 100755 Binary files a/lv2/aarch64/ToobAmp.lv2/bin/ToobAmp-a72.so and b/lv2/aarch64/ToobAmp.lv2/bin/ToobAmp-a72.so differ diff --git a/lv2/aarch64/ToobAmp.lv2/bin/ToobAmp-a76.so b/lv2/aarch64/ToobAmp.lv2/bin/ToobAmp-a76.so index 2378475..cc5bdb6 100755 Binary files a/lv2/aarch64/ToobAmp.lv2/bin/ToobAmp-a76.so and b/lv2/aarch64/ToobAmp.lv2/bin/ToobAmp-a76.so differ diff --git a/lv2/aarch64/ToobAmp.lv2/manifest.ttl b/lv2/aarch64/ToobAmp.lv2/manifest.ttl index 0c33b70..276f20f 100644 --- a/lv2/aarch64/ToobAmp.lv2/manifest.ttl +++ b/lv2/aarch64/ToobAmp.lv2/manifest.ttl @@ -4,6 +4,7 @@ @prefix tpset: . + a lv2:Plugin ; lv2:binary ; rdfs:seeAlso . @@ -114,6 +115,16 @@ lv2:binary ; rdfs:seeAlso . + a lv2:Plugin ; + lv2:binary ; + rdfs:seeAlso . + + a lv2:Plugin ; + lv2:binary ; + rdfs:seeAlso . + + + a lv2:Plugin ; lv2:binary ; rdfs:seeAlso . diff --git a/lv2/aarch64/ToobAmp.lv2/resources/ToobMultiEchoLogo.svg b/lv2/aarch64/ToobAmp.lv2/resources/ToobMultiEchoLogo.svg new file mode 100644 index 0000000..ea086f4 --- /dev/null +++ b/lv2/aarch64/ToobAmp.lv2/resources/ToobMultiEchoLogo.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + diff --git a/lv2/aarch64/ToobAmp.lv2/resources/ToobMultiEchoStereoLogo.svg b/lv2/aarch64/ToobAmp.lv2/resources/ToobMultiEchoStereoLogo.svg new file mode 100644 index 0000000..801268c --- /dev/null +++ b/lv2/aarch64/ToobAmp.lv2/resources/ToobMultiEchoStereoLogo.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + diff --git a/lv2/x86_64/toobamp_1.3.80_amd64.deb b/lv2/x86_64/toobamp_1.3.81_amd64.deb similarity index 64% rename from lv2/x86_64/toobamp_1.3.80_amd64.deb rename to lv2/x86_64/toobamp_1.3.81_amd64.deb index 4bc7e27..74b691a 100644 Binary files a/lv2/x86_64/toobamp_1.3.80_amd64.deb and b/lv2/x86_64/toobamp_1.3.81_amd64.deb differ diff --git a/lv2/x86_64/unpackArtifact.sh b/lv2/x86_64/unpackArtifact.sh index a01412f..68f101d 100755 --- a/lv2/x86_64/unpackArtifact.sh +++ b/lv2/x86_64/unpackArtifact.sh @@ -1,7 +1,20 @@ #!/usr/bin/bash rm -rf build/lv2_x64 -mkdir build/lv2_x64 +mkdir -p build/lv2_x64/pkg -mkdir build/lv2_x64/pkg -dpkg-deb -x lv2/x86_64/toobamp_*_amd64.deb build/lv2_x64/pkg \ No newline at end of file +# Extract .deb using ar + tar (portable, no dpkg-deb needed) +cd build/lv2_x64 +ar x ../../lv2/x86_64/toobamp_*_amd64.deb +if [ -f data.tar.zst ]; then + tar --zstd -xf data.tar.zst -C pkg +elif [ -f data.tar.xz ]; then + tar -xJf data.tar.xz -C pkg +elif [ -f data.tar.gz ]; then + tar -xzf data.tar.gz -C pkg +else + echo "Error: cannot find data.tar.* in the .deb package" + exit 1 +fi +rm -f control.tar.* data.tar.* debian-binary +cd "$OLDPWD" \ No newline at end of file diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt index 69fcf98..6512745 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -1,8 +1,10 @@ cmake_minimum_required(VERSION 3.16.0) # Prevent installation of SQLite built components. (we use the .a library files directly) +option(SQLITECPP_INSTALL "Enables the install target." OFF) +option(BUILD_SHARED_LIBS "Build shared libraries (DLLs)." OFF) -add_subdirectory("SQLiteCpp" EXCLUDE_FROM_ALL) +add_subdirectory("SQLiteCpp") set(SDBUSCPP_BUILD_CODEGEN off) @@ -25,5 +27,8 @@ option(BUILD_REGRESS "Build regression tests" OFF) option(BUILD_OSSFUZZ "Build fuzzers for ossfuzz" OFF) option(BUILD_EXAMPLES "Build examples" OFF) option(BUILD_DOC "Build documentation" OFF) + +option(LIBZIP_DO_INSTALL "Install libzip and the related files" OFF) +option(BUILD_SHARED_LIBS "Build shared libraries (DLLs)." OFF) add_subdirectory("libzip") diff --git a/modules/websocketpp b/modules/websocketpp index 508c9b5..e6df114 160000 --- a/modules/websocketpp +++ b/modules/websocketpp @@ -1 +1 @@ -Subproject commit 508c9b5e404df2249e6f0590bc12bd9912889ba4 +Subproject commit e6df114a1a1ba83038a0c5a71985c0e294190fb2 diff --git a/src/AlsaDriver.cpp b/src/AlsaDriver.cpp index d49a0b3..32f3f58 100644 --- a/src/AlsaDriver.cpp +++ b/src/AlsaDriver.cpp @@ -2,6 +2,7 @@ * MIT License * * Copyright (c) Robin E.R. Davies + * Copyright (c) Roberto Figliè * * 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 @@ -1681,6 +1682,7 @@ namespace pipedal AlsaMidiMessage message; midiEventCount = 0; + midiEventMemoryIndex = 0; auto alsaSequener = this->alsaSequencer; // take an addref if (!alsaSequener) { @@ -1772,6 +1774,7 @@ namespace pipedal break; } this->midiEventCount = 0; + this->midiEventMemoryIndex = 0; // snd_pcm_wait(captureHandle, 1); ssize_t framesToRead = bufferSize; diff --git a/src/AlsaDriverTest.cpp b/src/AlsaDriverTest.cpp index 938ce44..ac3e78f 100644 --- a/src/AlsaDriverTest.cpp +++ b/src/AlsaDriverTest.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include "AlsaDriver.hpp" #include "ChannelRouterSettings.hpp" diff --git a/src/AudioHost.cpp b/src/AudioHost.cpp index 92d618a..84d23f7 100644 --- a/src/AudioHost.cpp +++ b/src/AudioHost.cpp @@ -516,6 +516,8 @@ private: SystemMidiBinding snapshot5MidiBinding; SystemMidiBinding snapshot6MidiBinding; + SystemMidiBinding nextSnapshotMidiBinding; + SystemMidiBinding prevSnapshotMidiBinding; SystemMidiBinding startHotspotMidiBinding; SystemMidiBinding stopHotspotMidiBinding; SystemMidiBinding rebootMidiBinding; @@ -1015,6 +1017,18 @@ private: midiProgramChangePending = true; this->realtimeWriter.OnNextMidiProgram(++(this->midiProgramChangeId), -1); } + else if (nextSnapshotMidiBinding.IsTriggered(event)) + { + this->deferredMidiMessageCount = 0; // we can discard previous control changes. + midiProgramChangePending = true; + this->realtimeWriter.OnNextMidiSnapshot(++(this->midiProgramChangeId), 1); + } + else if (prevSnapshotMidiBinding.IsTriggered(event)) + { + this->deferredMidiMessageCount = 0; // we can discard previous control changes. + midiProgramChangePending = true; + this->realtimeWriter.OnNextMidiSnapshot(++(this->midiProgramChangeId), -1); + } else if (shutdownMidiBinding.IsTriggered(event)) { @@ -1737,6 +1751,12 @@ public: hostReader.read(&request); pNotifyCallbacks->OnNotifyNextMidiBank(request); } + else if (command == RingBufferCommand::NextMidiSnapshot) + { + RealtimeNextMidiProgramRequest request; + hostReader.read(&request); + pNotifyCallbacks->OnNotifyNextMidiSnapshot(request); + } else if (command == RingBufferCommand::RealtimeMidiEvent) { @@ -2423,6 +2443,14 @@ void AudioHostImpl::SetSystemMidiBindings(const std::vector &bindin { this->snapshot6MidiBinding.SetBinding(*i); } + else if (i->symbol() == "nextSnapshot") + { + this->nextSnapshotMidiBinding.SetBinding(*i); + } + else if (i->symbol() == "prevSnapshot") + { + this->prevSnapshotMidiBinding.SetBinding(*i); + } else { Lv2Log::error(SS("Invalid system midi binding: " << i->symbol())); diff --git a/src/AudioHost.hpp b/src/AudioHost.hpp index bc39c0f..a37dc13 100644 --- a/src/AudioHost.hpp +++ b/src/AudioHost.hpp @@ -177,6 +177,7 @@ namespace pipedal virtual void OnNotifyMidiProgramChange(RealtimeMidiProgramRequest &midiProgramRequest) = 0; virtual void OnNotifyNextMidiProgram(const RealtimeNextMidiProgramRequest &request) = 0; virtual void OnNotifyNextMidiBank(const RealtimeNextMidiProgramRequest &request) = 0; + virtual void OnNotifyNextMidiSnapshot(const RealtimeNextMidiProgramRequest &request) = 0; virtual void OnNotifyLv2RealtimeError(int64_t instanceId, const std::string &error) = 0; virtual void OnNotifyMidiRealtimeEvent(RealtimeMidiEventType eventType) = 0; virtual void OnNotifyMidiRealtimeSnapshotRequest(int32_t snapshotIndex,int64_t snapshotRequestId) = 0; diff --git a/src/AuxInTest.cpp b/src/AuxInTest.cpp index 3fbfd76..a729ac8 100644 --- a/src/AuxInTest.cpp +++ b/src/AuxInTest.cpp @@ -19,6 +19,7 @@ #include "AuxIn.hpp" +#include using namespace pipedal; int main(int argc, char**argv) { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f9f02d6..441677f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -22,6 +22,38 @@ set(CXX_STANDARD 20) find_package(PkgConfig REQUIRED) +find_package(Catch2 QUIET) + +# Catch2 Arch and Debian13 provide catch2 v3, with multi-headers under catch2 +# Catch2 Debian12 provides catch2 v2, with a single header. +# When Catch2 v3 is found via CMake, generate compatibility wrappers so +# old-style #include "catch.hpp" and #include still work. +if(Catch2_FOUND) + if(Catch2_VERSION VERSION_GREATER_EQUAL "3.0.0") # Generate catch.hpp wrapper in the build directory. + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/catch.hpp" + "#pragma once\n" + "#include \n" + ) + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/catch") + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/catch/catch.hpp" + "#pragma once\n" + "#include \n" + ) + # Add the build directory to the global include paths so that + # both #include "catch.hpp" (quote) and #include (angle) + # can find the compatibility wrappers. + include_directories(${CMAKE_CURRENT_BINARY_DIR}) + message(STATUS "Catch2 v3 found. Generated compatibility wrappers in ${CMAKE_CURRENT_BINARY_DIR}") + elseif(Catch2_VERSION VERSION_GREATER_EQUAL "2.0.0") # Generate catch.hpp wrapper in the build directory. + # Just use the system version directly. + message(STATUS "Catch2 v2 found. Using system catch2/catch.hpp directly.") + else() + message(ERROR "Catch2: Unsupported Catch2 version: ${Catch2_VERSION}. Expecting Catch2 v2 or v3.") + endif() +else() + message(ERROR "Catch2 CMake package not found. Expecting system catch.hpp (Catch2 v2 style).") +endif() + pkg_check_modules(PipeWire REQUIRED libpipewire-0.3) include(FetchContent) @@ -155,15 +187,27 @@ else() set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-multichar -Wno-psabi") endif() -execute_process( COMMAND dpkg --print-architecture COMMAND tr -d '\n' OUTPUT_VARIABLE DEBIAN_ARCHITECTURE ) -message(STATUS DEBIAN_ARCHITECTURE="${DEBIAN_ARCHITECTURE}") +# Determine Debian-compatible architecture name (portable across distros) +if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") + set(DEBIAN_ARCHITECTURE "amd64") +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64") + set(DEBIAN_ARCHITECTURE "arm64") +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "armv7l") + set(DEBIAN_ARCHITECTURE "armhf") +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "i386|i686") + set(DEBIAN_ARCHITECTURE "i386") +else() + execute_process(COMMAND uname -m COMMAND tr -d '\n' OUTPUT_VARIABLE UNAME_M) + message(FATAL_ERROR "Cannot determine Debian architecture from CMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR} (uname -m: ${UNAME_M})") +endif() +message(STATUS "DEBIAN_ARCHITECTURE=${DEBIAN_ARCHITECTURE}") add_compile_definitions(DEBIAN_ARCHITECTURE="${DEBIAN_ARCHITECTURE}") if (CMAKE_BUILD_TYPE MATCHES Debug) message(STATUS "Debug build") - # Must not -D_GLIBCXX_DEBUG, since it conflict with SQLiteCpp.lol + # Must not -D_GLIBCXX_DEBUG, since it conflict with SQLiteCpp set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g -DDEBUG " ) if (USE_SANITIZE) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -static-libasan " ) @@ -460,6 +504,10 @@ add_executable(pipedaltest target_link_libraries(pipedaltest PRIVATE ${PIPEDAL_LIBS} ${ICU_LIBRARIES}) target_include_directories(pipedaltest PRIVATE ${PIPEDAL_INCLUDES}) +if(Catch2_FOUND) + target_link_libraries(pipedaltest PRIVATE Catch2::Catch2 Catch2::Catch2WithMain) +endif() + set_target_properties(pipedaltest PROPERTIES EXCLUDE_FROM_ALL ${PIPEDAL_EXCLUDE_TESTS}) if (NOT GITHUB_ACTIONS) # Google perftools are not available on Github action servers. @@ -490,6 +538,10 @@ add_executable(jsonTest target_link_libraries(jsonTest PRIVATE PiPedalCommon) target_include_directories(jsonTest PRIVATE ${PIPEDAL_INCLUDES}) +if(Catch2_FOUND) + target_link_libraries(jsonTest PRIVATE Catch2::Catch2 Catch2::Catch2WithMain) +endif() + set_target_properties(jsonTest PROPERTIES EXCLUDE_FROM_ALL ${PIPEDAL_EXCLUDE_TESTS}) @@ -856,8 +908,8 @@ elseif(EXISTS "/usr/share/doc/libboost1.71-dev") set (BOOST_COPYRIGHT_DIR "libboost1.71-dev") elseif(EXISTS "/usr/share/doc/libboost1.67-dev") set (BOOST_COPYRIGHT_DIR "libboost1.67-dev") -else() - message(FATAL_ERROR "Boost libary version has changed. Please update me.") +# else() +# message(FATAL_ERROR "Boost libary version has changed. Please update me.") endif() @@ -871,10 +923,12 @@ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Updating copyright notices." ) -add_custom_target ( - CopyrightBuild ALL - DEPENDS ${REACT_NOTICES_FILE} -) +if(NOT PIPEDAL_DISABLE_COPYRIGHT_BUILD) + add_custom_target ( + CopyrightBuild ALL + DEPENDS ${REACT_NOTICES_FILE} + ) +endif() if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.31) cmake_policy(SET CMP0177 NEW) diff --git a/src/ConfigMain.cpp b/src/ConfigMain.cpp index 2b48792..8b704c7 100644 --- a/src/ConfigMain.cpp +++ b/src/ConfigMain.cpp @@ -1154,6 +1154,7 @@ void Install(const fs::path &programPrefix, const std::string endpointAddress) // Add to audio groups. sysExec(USERMOD_BIN " -a -G " AUDIO_SERVICE_GROUP_NAME " " SERVICE_ACCOUNT_NAME); // add to netdev group + sysExec(GROUPADD_BIN " -f " NETDEV_GROUP_NAME); sysExec(USERMOD_BIN " -a -G " NETDEV_GROUP_NAME " " SERVICE_ACCOUNT_NAME); try diff --git a/src/ModFileTypes.cpp b/src/ModFileTypes.cpp index 500a9e9..6c48823 100644 --- a/src/ModFileTypes.cpp +++ b/src/ModFileTypes.cpp @@ -21,6 +21,7 @@ #include "util.hpp" #include #include +#include #include "ss.hpp" #include "MimeTypes.hpp" diff --git a/src/PiPedalModel.cpp b/src/PiPedalModel.cpp index 9d02874..e758af0 100644 --- a/src/PiPedalModel.cpp +++ b/src/PiPedalModel.cpp @@ -1181,6 +1181,92 @@ void PiPedalModel::NextPreset(Direction direction) LoadPreset(-1, index.presets()[currentPresetIndex].instanceId()); } +void PiPedalModel::NextSnapshot(Direction direction) +{ + std::lock_guard guard{mutex}; + + auto &snapshots = this->pedalboard.snapshots(); + if (snapshots.size() == 0) + { + return; + } + int64_t currentSnapshot = this->pedalboard.selectedSnapshot(); + int64_t nextSnapshot = -1; + + if (direction == Direction::Increase) + { + for (int64_t i = currentSnapshot + 1; i < (int64_t)snapshots.size(); ++i) + { + if (snapshots[i]) + { + nextSnapshot = i; + break; + } + } + if (nextSnapshot == -1) + { + for (int64_t i = 0; i <= currentSnapshot && i < (int64_t)snapshots.size(); ++i) + { + if (snapshots[i]) + { + nextSnapshot = i; + break; + } + } + } + } + else + { + for (int64_t i = currentSnapshot - 1; i >= 0; --i) + { + if (snapshots[i]) + { + nextSnapshot = i; + break; + } + } + if (nextSnapshot == -1) + { + for (int64_t i = (int64_t)snapshots.size() - 1; i > currentSnapshot; --i) + { + if (snapshots[i]) + { + nextSnapshot = i; + break; + } + } + } + } + if (nextSnapshot != -1 && nextSnapshot != currentSnapshot) + { + SetSnapshot(nextSnapshot); + } +} + +void PiPedalModel::OnNotifyNextMidiSnapshot(const RealtimeNextMidiProgramRequest &request) +{ + std::lock_guard guard{mutex}; + try + { + if (request.direction >= 0) + { + NextSnapshot(); + } + else + { + PreviousSnapshot(); + } + } + catch (std::exception &e) + { + Lv2Log::error(e.what()); + } + if (this->audioHost) + { + this->audioHost->AckMidiProgramRequest(request.requestId); + } +} + void PiPedalModel::OnNotifyNextMidiProgram(const RealtimeNextMidiProgramRequest &request) { std::lock_guard guard{mutex}; diff --git a/src/PiPedalModel.hpp b/src/PiPedalModel.hpp index 2a53848..c8f9d7a 100644 --- a/src/PiPedalModel.hpp +++ b/src/PiPedalModel.hpp @@ -273,6 +273,7 @@ namespace pipedal virtual void OnNotifyMidiProgramChange(RealtimeMidiProgramRequest &midiProgramRequest) override; virtual void OnNotifyNextMidiProgram(const RealtimeNextMidiProgramRequest &request) override; virtual void OnNotifyNextMidiBank(const RealtimeNextMidiProgramRequest &request) override; + virtual void OnNotifyNextMidiSnapshot(const RealtimeNextMidiProgramRequest &request) override; virtual void OnNotifyLv2RealtimeError(int64_t instanceId, const std::string &error) override; PostHandle networkChangingDelayHandle = 0; @@ -311,6 +312,8 @@ namespace pipedal void PreviousBank() { NextBank(Direction::Decrease); } void NextPreset(Direction direction = Direction::Increase); void PreviousPreset() { NextPreset(Direction::Decrease); } + void NextSnapshot(Direction direction = Direction::Increase); + void PreviousSnapshot() { NextSnapshot(Direction::Decrease); } int64_t DownloadModelsFromTone3000( const std::string &responseuri, diff --git a/src/RingBufferReader.hpp b/src/RingBufferReader.hpp index 9855958..dbbabf8 100644 --- a/src/RingBufferReader.hpp +++ b/src/RingBufferReader.hpp @@ -83,6 +83,7 @@ namespace pipedal NextMidiProgram, NextMidiBank, + NextMidiSnapshot, Lv2StateChanged, MaybeLv2StateChanged, @@ -444,6 +445,11 @@ namespace pipedal RealtimeNextMidiProgramRequest msg{requestId : requestId, direction : direction}; write(RingBufferCommand::NextMidiBank, msg); } + void OnNextMidiSnapshot(int64_t requestId, int32_t direction) + { + RealtimeNextMidiProgramRequest msg{requestId : requestId, direction : direction}; + write(RingBufferCommand::NextMidiSnapshot, msg); + } void SetControlValue(int effectIndex, int controlIndex, float value) { diff --git a/src/Storage.cpp b/src/Storage.cpp index 7b8c981..3a3b68f 100644 --- a/src/Storage.cpp +++ b/src/Storage.cpp @@ -2213,6 +2213,9 @@ std::vector Storage::GetSystemMidiBindings() result.push_back(MidiBinding::SystemBinding("snapshot5")); result.push_back(MidiBinding::SystemBinding("snapshot6")); + result.push_back(MidiBinding::SystemBinding("prevSnapshot")); + result.push_back(MidiBinding::SystemBinding("nextSnapshot")); + result.push_back(MidiBinding::SystemBinding("stopHotspot")); result.push_back(MidiBinding::SystemBinding("startHotspot")); result.push_back(MidiBinding::SystemBinding("shutdown")); diff --git a/src/kconfigMain.cpp b/src/kconfigMain.cpp index f8d1b5a..1365a9e 100644 --- a/src/kconfigMain.cpp +++ b/src/kconfigMain.cpp @@ -55,6 +55,7 @@ #include #include "BootConfig.hpp" #include +#include #include "CommandLineParser.hpp" #include "PrettyPrinter.hpp" #include "SysExec.hpp" diff --git a/vite/CMakeLists.txt b/vite/CMakeLists.txt index ced3c85..4cb0b61 100644 --- a/vite/CMakeLists.txt +++ b/vite/CMakeLists.txt @@ -293,7 +293,7 @@ add_custom_command( src/pipedal/IconColorDropdownButton.tsx src/pipedal/PluginOutputControl.tsx src/pipedal/PluginPresetsDialog.tsx - src/pipedal/JackServerSettingsDialog.tsx + src/pipedal/AudioDeviceDialog.tsx src/pipedal/TextInfo_RewriteT3kUrls.ts src/pipedal/MidiBindingsDialog.tsx src/pipedal/DialogStack.tsx diff --git a/vite/src/pipedal/AlsaDeviceInfo.tsx b/vite/src/pipedal/AlsaDeviceInfo.tsx index 2620eba..b59d136 100644 --- a/vite/src/pipedal/AlsaDeviceInfo.tsx +++ b/vite/src/pipedal/AlsaDeviceInfo.tsx @@ -83,6 +83,14 @@ export default class AlsaDeviceInfo { } return result; } + shortDeviceName() : string { + // The best device names for USB devices are buried in the long name, so we try to extract them if we can. + let pos = this.longName.indexOf(" at usb-"); + if (pos > 0) { + return this.longName.substring(0, pos); + } + return this.name; + } cardId: number = -1; id: string = ""; diff --git a/vite/src/pipedal/JackServerSettingsDialog.tsx b/vite/src/pipedal/AudioDeviceDialog.tsx similarity index 98% rename from vite/src/pipedal/JackServerSettingsDialog.tsx rename to vite/src/pipedal/AudioDeviceDialog.tsx index eae2e81..d96151b 100644 --- a/vite/src/pipedal/JackServerSettingsDialog.tsx +++ b/vite/src/pipedal/AudioDeviceDialog.tsx @@ -67,7 +67,7 @@ function sortDevices(devices: AlsaDeviceInfo[]): AlsaDeviceInfo[] { let ca = category(a); let cb = category(b); if (ca !== cb) return ca - cb; - return a.name.localeCompare(b.name); + return a.shortDeviceName().localeCompare(b.shortDeviceName()); }); return copy; } @@ -91,7 +91,7 @@ enum WarningDialogType { Apply, Ok, }; -interface JackServerSettingsDialogState { +interface AudioDeviceDialogState { latencyText: string; jackServerSettings: JackServerSettings; jackHostStatus?: JackHostStatus; @@ -117,7 +117,7 @@ const styles = (theme: Theme) => whiteSpace: "nowrap" } }); -export interface JackServerSettingsDialogProps extends WithStyles { +export interface AudioDeviceDialogProps extends WithStyles { open: boolean; jackServerSettings: JackServerSettings; onClose: () => void; @@ -287,12 +287,12 @@ function isOkEnabled(jackServerSettings: JackServerSettings, alsaDevices?: AlsaD } -const JackServerSettingsDialog = withStyles( - class extends ResizeResponsiveComponent { +const AudioDeviceDialog = withStyles( + class extends ResizeResponsiveComponent { model: PiPedalModel; - constructor(props: JackServerSettingsDialogProps) { + constructor(props: AudioDeviceDialogProps) { super(props); this.model = PiPedalModelFactory.getInstance(); @@ -456,7 +456,7 @@ const JackServerSettingsDialog = withStyles( } - componentDidUpdate(oldProps: JackServerSettingsDialogProps) { + componentDidUpdate(oldProps: AudioDeviceDialogProps) { this.updateActive(); } @@ -584,7 +584,7 @@ const JackServerSettingsDialog = withStyles( if (!this.state.alsaDevices) return deviceId; for (let i = 0; i < this.state.alsaDevices.length; ++i) { if (this.state.alsaDevices[i].id === deviceId) { - return this.state.alsaDevices[i].name; + return this.state.alsaDevices[i].shortDeviceName(); } } return deviceId; @@ -673,7 +673,7 @@ const JackServerSettingsDialog = withStyles( {d.name} + >{d.shortDeviceName()} ))} @@ -695,7 +695,7 @@ const JackServerSettingsDialog = withStyles( {d.name} + >{d.shortDeviceName()} ))} @@ -819,4 +819,4 @@ const JackServerSettingsDialog = withStyles( }, styles); -export default JackServerSettingsDialog; +export default AudioDeviceDialog; diff --git a/vite/src/pipedal/Draggable.tsx b/vite/src/pipedal/Draggable.tsx index 7f2f553..d7887b1 100644 --- a/vite/src/pipedal/Draggable.tsx +++ b/vite/src/pipedal/Draggable.tsx @@ -251,6 +251,7 @@ const Draggable = if (this.pointerType !== "touch") { this.dragTarget.style.transform = "scale(" + SELECT_SCALE + ")"; this.dragTarget.style.zIndex = "3"; + this.dragTarget.style.position = "absolute"; } } } @@ -259,6 +260,7 @@ const Draggable = element.style.transform = ""; element.style.zIndex = this.savedIndex; element.style.opacity = this.savedOpacity; + element.style.position = "relative"; } onPointerCancel(e: PointerEvent) { diff --git a/vite/src/pipedal/Lv2Plugin.tsx b/vite/src/pipedal/Lv2Plugin.tsx index d45e4b4..e7297b6 100644 --- a/vite/src/pipedal/Lv2Plugin.tsx +++ b/vite/src/pipedal/Lv2Plugin.tsx @@ -1,4 +1,5 @@ -// Copyright (c) Robin E.R. Davies +// Copyright (c) 2022 Robin Davies +// Copyright (c) Fulgencio Ruiz Rubio. // // 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 @@ -835,15 +836,44 @@ export class UiControl implements Deserializable { valueToRange(value: number): number { if (this.toggled_property) return value === 0 ? 0 : 1; - if (this.integer_property || this.enumeration_property) { value = Math.round(value); } - let range = (value - this.min_value) / (this.max_value - this.min_value); + let range: number; + if (this.is_logarithmic) { + let minValue = this.min_value; + if (minValue === 0) // LSP plugins do this. + { + minValue = 0.0001; + } + range = Math.log(value / minValue) / Math.log(this.max_value / minValue); + if (!isFinite(range)) { + if (range < 0) { + range = 0; + } else { + range = 1.0; + } + } else if (isNaN(range)) { + range = 0; + } + if (this.range_steps > 1) { + range = Math.round(range * (this.range_steps - 1)) / (this.range_steps - 1); + } + } else { + range = (value - this.min_value) / (this.max_value - this.min_value); + if (!isFinite(range)) { + if (range < 0) { + range = 0; + } else { + range = 1.0; + } + } else if (isNaN(range)) { + range = 0; + } + } + if (range > 1) range = 1; if (range < 0) range = 0; - - return range; } @@ -852,17 +882,67 @@ export class UiControl implements Deserializable { if (range > 1) range = 1; if (this.toggled_property) return range === 0 ? 0 : 1; + if (this.range_steps > 1) { + range = Math.round(range * (this.range_steps - 1)) / (this.range_steps - 1); + } + + let value: number; - let value = range * (this.max_value - this.min_value) + this.min_value; - if (this.integer_property || this.enumeration_property) { - value = Math.round(value); + if (this.min_value === this.max_value) { + value = this.min_value; + } else { + if (this.is_logarithmic) { + let minValue = this.min_value; + if (minValue === 0) // LSP controls. + { + minValue = 0.0001; + } + value = minValue * Math.pow(this.max_value / minValue, range); + if (!isFinite(value)) { + value = this.max_value; + } else if (isNaN(value)) { + value = this.min_value; + } + } else { + value = range * (this.max_value - this.min_value) + this.min_value; + } + if (this.integer_property) { + value = Math.round(value); + } } return value; } + clampValue(value: number): number { return this.rangeToValue(this.valueToRange(value)); } + getDisplayUnits(): string { + if (this.custom_units !== "") { + return this.custom_units.replace("%f", ""); + } + switch (this.units) { + case Units.bar: return "bar"; + case Units.beat: return "beats"; + case Units.bpm: return "bpm"; + case Units.cent: return "cents"; + case Units.cm: return "cm"; + case Units.db: return "dB"; + case Units.hz: return "Hz"; + case Units.khz: return "kHz"; + case Units.km: return "km"; + case Units.m: return "m"; + case Units.mhz: return "MHz"; + case Units.midiNote: return "MIDI note"; + case Units.min: return "min"; + case Units.ms: return "ms"; + case Units.pc: return "%"; + case Units.s: return "s"; + case Units.semitone12TET: return "st"; + default: return ""; + } + } + formatDisplayValue(value: number): string { if (this.integer_property) { value = Math.round(value); @@ -893,56 +973,9 @@ export class UiControl implements Deserializable { return semitone12TETValue(value); } - let text = this.formatShortValue(value); - - switch (this.units) { - case Units.bpm: - text += "bpm"; - break; - case Units.cent: - text += "cents"; - break; - case Units.cm: - text += "cm"; - break; - case Units.db: - text += "dB"; - break; - case Units.hz: - text += "Hz"; - break; - case Units.khz: - text += "kHz"; - break; - case Units.km: - text += "km"; - break; - case Units.m: - text += "m"; - break; - case Units.mhz: - text += "MHz"; - break; - case Units.min: - text += "min"; - break; - case Units.ms: - text += "ms"; - break; - case Units.pc: - text += "%"; - break; - case Units.unknown: - if (this.custom_units !== "") { - text = this.custom_units.replace("%f", text); - } - break; - default: - break; - - } - return text; + return this.formatShortValue(value) + this.getDisplayUnits(); } + formatShortValue(value: number): string { if (this.enumeration_property) { for (let i = 0; i < this.scale_points.length; ++i) { diff --git a/vite/src/pipedal/MidiBindingView.tsx b/vite/src/pipedal/MidiBindingView.tsx index f7b72a2..e8372f2 100644 --- a/vite/src/pipedal/MidiBindingView.tsx +++ b/vite/src/pipedal/MidiBindingView.tsx @@ -1,4 +1,5 @@ -// Copyright (c) Robin E.R. Davies +// Copyright (c) 2022 Robin Davies +// Copyright (c) Fulgencio Ruiz Rubio. // // 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 @@ -25,7 +26,7 @@ import { withStyles } from "tss-react/mui"; import { createStyles } from './WithStyles'; import Snackbar from '@mui/material/Snackbar'; -import { UiPlugin } from './Lv2Plugin'; +import { UiPlugin, UiControl } from './Lv2Plugin'; import MenuItem from '@mui/material/MenuItem'; import Select from '@mui/material/Select'; @@ -96,6 +97,7 @@ interface MidiBindingViewProps extends WithStyles { midiBinding: MidiBinding; midiControlType: MidiControlType; canDoTapTempo: boolean; + uiControl?: UiControl; onChange: (instanceId: number, newBinding: MidiBinding) => void; } @@ -188,15 +190,28 @@ const MidiBindingView = this.props.onChange(this.props.instanceId, newBinding); } handleMinChange(value: number): void { + if (!this.props.uiControl) return; let newBinding = this.props.midiBinding.clone(); - newBinding.minValue = value; + newBinding.minValue = this.props.uiControl.valueToRange(value) ; this.props.onChange(this.props.instanceId, newBinding); } handleMaxChange(value: number): void { + if (!this.props.uiControl) return; let newBinding = this.props.midiBinding.clone(); - newBinding.maxValue = value; + newBinding.maxValue = this.props.uiControl.valueToRange(value); + console.log(value, newBinding.maxValue) this.props.onChange(this.props.instanceId, newBinding); } + + private getDisplayUnit(): string { + let control = this.props.uiControl; + if (!control) return ""; + if (control.custom_units !== "") { + return control.custom_units.replace("%f", ""); + } + + return ` ${control.getDisplayUnits()}` + } handleCtlMinChange(value: number): void { let newBinding = this.props.midiBinding.clone(); newBinding.minControlValue = Math.round(value); @@ -638,21 +653,36 @@ const MidiBindingView = ) } { - showLinearRange && ( + showLinearRange && (() => { + let uiControl = this.props.uiControl; + let displayMin = uiControl ? uiControl.min_value : 0; + let displayMax = uiControl ? uiControl.max_value : 1; + let unit = this.getDisplayUnit(); + let isInteger = uiControl?.integer_property ?? false; + return (
Min Val:  - { this.handleMinChange(value); }} + { this.handleMinChange(value); }} /> + {unit !== "" && ( +  {unit.trim()} + )}
Max Val:  - { this.handleMaxChange(value); }} /> + { this.handleMaxChange(value); }} /> + {unit !== "" && ( +  {unit.trim()} + )}
- ) + ); + })() } { canRotaryScale && ( diff --git a/vite/src/pipedal/MidiBindingsDialog.tsx b/vite/src/pipedal/MidiBindingsDialog.tsx index e746d55..750343e 100644 --- a/vite/src/pipedal/MidiBindingsDialog.tsx +++ b/vite/src/pipedal/MidiBindingsDialog.tsx @@ -214,6 +214,7 @@ export const MidiBindingDialog = midiControlType={ getMidiControlType(plugin, "__bypass") } canDoTapTempo={false } + uiControl={plugin.getControl("__bypass")} onChange={(instanceId: number, newItem: MidiBinding) => this.handleItemChanged(instanceId, newItem)} /> @@ -258,6 +259,7 @@ export const MidiBindingDialog = midiControlType={getMidiControlType(plugin, symbol)} midiBinding={item.getMidiBinding(symbol)} canDoTapTempo={canDoTapTempo(plugin, symbol)} + uiControl={control} onChange={(instanceId: number, newItem: MidiBinding) => this.handleItemChanged(instanceId, newItem)} /> diff --git a/vite/src/pipedal/ModGuiHost.tsx b/vite/src/pipedal/ModGuiHost.tsx index d123916..92d89cb 100644 --- a/vite/src/pipedal/ModGuiHost.tsx +++ b/vite/src/pipedal/ModGuiHost.tsx @@ -862,7 +862,7 @@ class FilmstripControl implements ModGuiControl { console.error("pipedal: Invalid mod-widget-rotation value: " + rotationAttr); return; } - let range = this.valueToRange(this.value); + let range = this.props.pluginControl.valueToRange(this.value); this.frameElement.style.transform = `rotate(${rotation * range - rotation / 2}deg)`; return; @@ -926,13 +926,13 @@ class FilmstripControl implements ModGuiControl { let pluginControl = this.props.pluginControl; let value = this.isPointerDown ? this.pointerDownValue : this.value; - value = this.clampValue(value); + value = this.props.pluginControl.clampValue(value); let isHorizontalStrip = this.filmstripInfo.frameWidth / this.filmstripInfo.frameHeight < this.filmstripInfo.width / this.filmstripInfo.height; if (isHorizontalStrip) { - let range = this.valueToRange(value); + let range = this.props.pluginControl.valueToRange(value); if (range < 0) { range = 0; @@ -1045,101 +1045,6 @@ class FilmstripControl implements ModGuiControl { this.pointerDownValue = this.value; } - - valueToRange(value: number): number { - let uiControl = this.props.pluginControl; - if (uiControl) { - let range: number; - if (uiControl.is_logarithmic) { - let minValue = uiControl.min_value; - if (minValue === 0) // LSP plugins do this. - { - minValue = 0.0001; - } - range = Math.log(value / minValue) / Math.log(uiControl.max_value / minValue); - if (!isFinite(range)) { - if (range < 0) { - range = 0; - } else { - range = 1.0; - } - } else if (isNaN(range)) { - range = 0; - } - } else { - range = (value - uiControl.min_value) / (uiControl.max_value - uiControl.min_value); - if (!isFinite(range)) { - if (range < 0) { - range = 0; - } else { - range = 1.0; - } - } else if (isNaN(range)) { - range = 0; - } - } - - if (range > 1) range = 1; - if (range < 0) range = 0; - return range; - } - return 0; - } - rangeToValue(range: number): number { - if (range < 0) range = 0; - if (range > 1) range = 1; - let uiControl = this.props.pluginControl; - if (uiControl) { - let value: number; - if (uiControl.min_value === uiControl.max_value) { - value = uiControl.min_value; - } else { - if (uiControl.is_logarithmic) { - let minValue = uiControl.min_value; - if (minValue === 0) // LSP controls. - { - minValue = 0.0001; - } - value = minValue * Math.pow(uiControl.max_value / minValue, range); - if (!isFinite(value)) { - value = uiControl.max_value; - } else if (isNaN(value)) { - value = uiControl.min_value; - } - } else { - value = range * (uiControl.max_value - uiControl.min_value) + uiControl.min_value; - } - } - return value; - } - return 0; - } - - clampValue(value: number): number { - let control = this.props.pluginControl; - if (control.enumeration_property) { - value = control.clampSelectValue(value); - } else if (control.toggled_property) { - if (value < (control.min_value + control.max_value) / 2) { - value = control.min_value; - } else { - value = control.max_value; - } - } else if (control.integer_property) { - value = Math.round(value); - } else if (control.range_steps != 0) { - let step = (control.max_value - control.min_value) / control.range_steps; - value = Math.round((value - control.min_value) / step) * step + control.min_value; - } - if (value < control.min_value) { - value = control.min_value; - } - if (value > control.max_value) { - value = control.max_value; - } - return value; - } - handlePointerMove(event: PointerEvent) { if (!this.frameElement) { return; // Not mounted yet @@ -1167,14 +1072,14 @@ class FilmstripControl implements ModGuiControl { let dRange = -dy / rate; - let range = this.valueToRange(this.pointerDownValue); + let range = this.props.pluginControl.valueToRange(this.pointerDownValue); range += dRange; if (range < 0) range = 0; if (range > 1) range = 1; - let newValue = this.rangeToValue(range); + let newValue = this.props.pluginControl.rangeToValue(range); this.pointerDownValue = newValue; this.setControlValue(newValue); - this.props.onValueChanged(this.props.instanceId, this.props.pluginControl.symbol, this.clampValue(newValue)); + this.props.onValueChanged(this.props.instanceId, this.props.pluginControl.symbol, this.props.pluginControl.clampValue(newValue)); } handlePointerUp(event: PointerEvent) { let index = this.pointerIds.indexOf(event.pointerId); @@ -1229,11 +1134,11 @@ class FilmstripControl implements ModGuiControl { } let dRange = event.deltaY / rate; - let range = this.valueToRange(this.value); + let range = this.props.pluginControl.valueToRange(this.value); range += dRange; if (range < 0) range = 0; if (range > 1) range = 1; - let newValue = this.rangeToValue(range); + let newValue = this.props.pluginControl.rangeToValue(range); this.setControlValue(newValue); this.props.onValueChanged(this.props.instanceId, this.props.pluginControl.symbol, newValue); } diff --git a/vite/src/pipedal/Pedalboard.tsx b/vite/src/pipedal/Pedalboard.tsx index b6d83b5..85fd1e6 100644 --- a/vite/src/pipedal/Pedalboard.tsx +++ b/vite/src/pipedal/Pedalboard.tsx @@ -59,7 +59,13 @@ export class ControlValue implements Deserializable { } export class PedalboardItem implements Deserializable { - clone() { + clone(): PedalboardItem { + if (this.isSplit()) + { + let result = new PedalboardSplitItem(); + result.deserialize(this); + return result as PedalboardItem;; + } return new PedalboardItem().deserialize(this); } deserializePedalboardItem(input: any): PedalboardItem { @@ -144,6 +150,23 @@ export class PedalboardItem implements Deserializable { } + isChild(instanceId: number): boolean { + if (this.instanceId === instanceId) return true; + if (this.isSplit()) + { + let splitItem = this as unknown as PedalboardSplitItem; + for (let topItem of splitItem.topChain) + { + if (topItem.isChild(instanceId)) return true; + } + for (let bottomItem of splitItem.bottomChain) + { + if (bottomItem.isChild(instanceId)) return true; + } + } + return false; + } + getControlValue(key: string): number { for (let i = 0; i < this.controlValues.length; ++i) { let v = this.controlValues[i]; @@ -753,7 +776,20 @@ export class Pedalboard implements Deserializable { } return false; } - + + assignNewInstanceIds(items: PedalboardItem[]) + { + for (let i = 0; i < items.length; ++i) + { + items[i].instanceId = ++this.nextInstanceId; + if (items[i].isSplit()) + { + let splitItem = items[i] as PedalboardSplitItem; + this.assignNewInstanceIds(splitItem.topChain); + this.assignNewInstanceIds(splitItem.bottomChain); + } + } + } replaceItem(instanceId: number, newItem: PedalboardItem) { newItem.instanceId = ++this.nextInstanceId; @@ -763,6 +799,13 @@ export class Pedalboard implements Deserializable { { throw new PiPedalArgumentError("instanceId not found."); } + if (newItem.isSplit()) + { + // Generate new instance ids for all children, to avoid conflicts. + let splitItem = newItem as PedalboardSplitItem; + this.assignNewInstanceIds(splitItem.topChain); + this.assignNewInstanceIds(splitItem.bottomChain); + } return newItem.instanceId; } private _addItem(items: PedalboardItem[], newItem: PedalboardItem, instanceId: number, append: boolean) diff --git a/vite/src/pipedal/PedalboardView.tsx b/vite/src/pipedal/PedalboardView.tsx index 5eca3f7..aeed9b2 100644 --- a/vite/src/pipedal/PedalboardView.tsx +++ b/vite/src/pipedal/PedalboardView.tsx @@ -147,24 +147,36 @@ const pedalboardStyles = (theme: Theme) => createStyles({ top: -6, fill: theme.palette.text.secondary }), - iconFrame: css({ - display: "flex", - alignItems: "center", - justifyContent: "center", + pedalButton: css({ + position: "relative", - 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, + padding: 0, + borderRadius: 8 + }), + + iconFrame: css({ + + display: "flex", + alignItems: "center", + justifyContent: "center", + position: "relative", + background: theme.palette.background.paper, + width: FRAME_SIZE, height: FRAME_SIZE, borderColor: "#777", borderWidth: 2, borderStyle: "solid", - padding: 1, + overflow: "hidden", + padding: 0, borderRadius: 8 }), selectedIconFrame: css({ @@ -173,12 +185,7 @@ const pedalboardStyles = (theme: Theme) => createStyles({ alignItems: "center", justifyContent: "center", position: "relative", - - 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, + background: theme.palette.background.paper, width: FRAME_SIZE, height: FRAME_SIZE, borderColor: theme.palette.primary.main, @@ -195,10 +202,6 @@ const pedalboardStyles = (theme: Theme) => createStyles({ justifyContent: "center", background: "transparent", - 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: "0px #666 solid", @@ -458,6 +461,20 @@ const PedalboardView = // touchyMove. :-/ } + isSplitterChild(item: PedalboardItem, splitterInstanceId: number) { + if (item.instanceId === splitterInstanceId) { + return true; + } + let pedalboard: Pedalboard | undefined = this.state.pedalboard; + if (!pedalboard) return false; + + let splitter = pedalboard.maybeGetItem(splitterInstanceId); + if (splitter === null) { + return false; + } + return splitter.isChild(item.instanceId); + } + onDragEnd(instanceId: number, clientX: number, clientY: number) { if (!this.props.enableStructureEditing) { return; @@ -483,10 +500,18 @@ const PedalboardView = if (item.isSplitter() && item.pedalItem) { if (item.bounds.contains(clientX, clientY)) { if (clientX < item.bounds.x + CELL_WIDTH / 2) { + if (this.isSplitterChild(item.pedalItem, instanceId)) + { + return; + } this.model.movePedalboardItemBefore(instanceId, item.pedalItem.instanceId); this.setSelection(instanceId); return; } else if (clientX > item.bounds.right - CELL_WIDTH / 2) { + if (this.isSplitterChild(item.pedalItem, instanceId)) + { + return; + } this.model.movePedalboardItemAfter(instanceId, item.pedalItem.instanceId); this.setSelection(instanceId); return; @@ -501,6 +526,11 @@ const PedalboardView = if (clientX < item.topChildren[0].bounds.x) { let topPedalItem = item.topChildren[0].pedalItem; if (topPedalItem) { + if (this.isSplitterChild(topPedalItem, instanceId)) + { + return; + } + this.model.movePedalboardItemBefore(instanceId, topPedalItem.instanceId); this.setSelection(instanceId); return; @@ -509,6 +539,9 @@ const PedalboardView = let lastTop = item.topChildren[item.topChildren.length - 1]; if (clientX >= lastTop.bounds.right && clientX < item.bounds.right - CELL_WIDTH / 2) { if (lastTop.pedalItem) { + if (this.isSplitterChild(lastTop.pedalItem, instanceId)) { + return; + } this.model.movePedalboardItemAfter(instanceId, lastTop.pedalItem.instanceId); this.setSelection(instanceId); return; @@ -522,6 +555,9 @@ const PedalboardView = if (clientX < item.bottomChildren[0].bounds.x) { let bottomPedalItem = item.bottomChildren[0].pedalItem; if (bottomPedalItem) { + if (this.isSplitterChild(bottomPedalItem, instanceId)) { + return; + } this.model.movePedalboardItemBefore(instanceId, bottomPedalItem.instanceId); this.setSelection(instanceId); return; @@ -530,6 +566,9 @@ const PedalboardView = let lastBottom = item.bottomChildren[item.bottomChildren.length - 1]; if (clientX >= lastBottom.bounds.right && clientX < item.bounds.right - CELL_WIDTH / 2) { if (lastBottom.pedalItem) { + if (this.isSplitterChild(lastBottom.pedalItem, instanceId)) { + return; + } this.model.movePedalboardItemAfter(instanceId, lastBottom.pedalItem.instanceId); this.setSelection(instanceId); return; @@ -552,10 +591,22 @@ const PedalboardView = if (item.pedalItem) { let margin = (CELL_WIDTH - FRAME_SIZE) / 2; if (clientX < item.bounds.x + margin) { + if (this.isSplitterChild(item.pedalItem,instanceId)) + { + return; + } this.model.movePedalboardItemBefore(instanceId, item.pedalItem.instanceId); } else if (clientX > item.bounds.right - margin) { + if (this.isSplitterChild(item.pedalItem,instanceId)) + { + return; + } this.model.movePedalboardItemAfter(instanceId, item.pedalItem.instanceId); } else { + if (this.isSplitterChild(item.pedalItem,instanceId)) + { + return; + } this.model.movePedalboardItem(instanceId, item.pedalItem.instanceId); } this.setSelection(instanceId); @@ -1036,39 +1087,35 @@ const PedalboardView = } return ( -
{ e.preventDefault(); }} - > - {/* {!enabled && ( -
- -
- - )} */} - - + { this.onItemClick(e, instanceId); }} onDoubleClick={(e: SyntheticEvent) => { this.onItemDoubleClick(e, instanceId); }} onContextMenu={(e: SyntheticEvent) => { this.onItemLongClick(e, instanceId); }} > - { e.preventDefault(); }} > - this.getScrollContainer()} - onDragEnd={(x, y) => { this.onDragEnd(instanceId, x, y) }} - style={{ opacity: enabled ? 0.99 : 0.3 }} - + -
- -
+
+
+ this.getScrollContainer()} + onDragEnd={(x, y) => { this.onDragEnd(instanceId, x, y) }} + style={{ opacity: enabled ? 0.99 : 0.3 }} - - + > +
+ +
+ + + ); @@ -1146,7 +1193,7 @@ const PedalboardView = result.push(
- {this.pedalButton(item.pedalItem?.instanceId ?? -1, this.getSplitterIcon(item), "", false, true, true, false, false)} + {this.pedalButton(item.pedalItem?.instanceId ?? -1, this.getSplitterIcon(item), "", true, true, true, false, false)}
); @@ -1157,9 +1204,10 @@ const PedalboardView = position: "absolute", left: item.bounds.x, width: CELL_WIDTH, top: item.bounds.bottom - 12, paddingLeft: 2, paddingRight: 2 }}> {item.name} ) diff --git a/vite/src/pipedal/PiPedalModel.tsx b/vite/src/pipedal/PiPedalModel.tsx index f5ba352..d356d89 100644 --- a/vite/src/pipedal/PiPedalModel.tsx +++ b/vite/src/pipedal/PiPedalModel.tsx @@ -602,7 +602,9 @@ export class PiPedalModel //implements PiPedalModel ( [ MidiBinding.systemBinding("prevProgram"), - MidiBinding.systemBinding("nextProgram") + MidiBinding.systemBinding("nextProgram"), + MidiBinding.systemBinding("prevSnapshot"), + MidiBinding.systemBinding("nextSnapshot") ] ); zoomedUiControl: ObservableProperty = new ObservableProperty(undefined); diff --git a/vite/src/pipedal/PluginControl.tsx b/vite/src/pipedal/PluginControl.tsx index 826010a..f7de1ec 100644 --- a/vite/src/pipedal/PluginControl.tsx +++ b/vite/src/pipedal/PluginControl.tsx @@ -1,4 +1,5 @@ -// Copyright (c) Robin E.R. Davies +// Copyright (c) 2022 Robin Davies +// Copyright (c) Fulgencio Ruiz Rubio. // // 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 @@ -203,7 +204,6 @@ const CustomPluginControl = ); - return (
); } }, @@ -342,8 +342,9 @@ const PluginControl = result = this.currentValue; // reset the value! } // clamp and quantize. - let range = this.valueToRange(result); - result = this.rangeToValue(range); + let range = this.props.uiControl?.valueToRange(result) ?? 0; + result = this.props.uiControl?.rangeToValue(range) ?? 0; + let displayVal = this.props.uiControl?.formatShortValue(result) ?? ""; if (event.currentTarget) { event.currentTarget.value = displayVal; @@ -682,8 +683,8 @@ const PluginControl = this.setState({ previewValue: undefined }); } previewInputValue(value: number, commitValue: boolean) { - let range = this.valueToRange(value); - value = this.rangeToValue(range); + let range = this.props.uiControl?.valueToRange(value) ?? 0; + value = this.props.uiControl?.rangeToValue(range) ?? 0; let imgElement = this.imgRef.current if (imgElement) { @@ -712,13 +713,13 @@ const PluginControl = } previewRange(dRange: number, commitValue: boolean): void { - let range = this.valueToRange(this.currentValue) + dRange; + let range = (this.props.uiControl?.valueToRange(this.currentValue) ?? 0) + dRange; if (range > 1) range = 1; if (range < 0) range = 0; - let value = this.rangeToValue(range); + let value = this.props.uiControl?.rangeToValue(range) ?? 0; // apply value quantization and clipping. - range = this.valueToRange(value); + range = this.props.uiControl?.valueToRange(value) ?? 0; if (this.props.uiControl?.isGraphicEq()) { let imgElement = this.imgRef.current @@ -835,89 +836,6 @@ const PluginControl = if (!uiControl) return ""; return uiControl.formatDisplayValue(value); - - - } - - valueToRange(value: number): number { - let uiControl = this.props.uiControl; - if (uiControl) { - if (uiControl.integer_property) { - value = Math.round(value); - } - let range: number; - if (uiControl.is_logarithmic) { - let minValue = uiControl.min_value; - if (minValue === 0) // LSP plugins do this. - { - minValue = 0.0001; - } - range = Math.log(value / minValue) / Math.log(uiControl.max_value / minValue); - if (!isFinite(range)) { - if (range < 0) { - range = 0; - } else { - range = 1.0; - } - } else if (isNaN(range)) { - range = 0; - } - if (uiControl.range_steps > 1) { - range = Math.round(range * (uiControl.range_steps - 1)) / (uiControl.range_steps - 1); - } - } else { - range = (value - uiControl.min_value) / (uiControl.max_value - uiControl.min_value); - if (!isFinite(range)) { - if (range < 0) { - range = 0; - } else { - range = 1.0; - } - } else if (isNaN(range)) { - range = 0; - } - } - - if (range > 1) range = 1; - if (range < 0) range = 0; - return range; - } - return 0; - } - rangeToValue(range: number): number { - if (range < 0) range = 0; - if (range > 1) range = 1; - let uiControl = this.props.uiControl; - if (uiControl) { - if (uiControl.range_steps > 1) { - range = Math.round(range * (uiControl.range_steps - 1)) / (uiControl.range_steps - 1); - } - let value: number; - if (uiControl.min_value === uiControl.max_value) { - value = uiControl.min_value; - } else { - if (uiControl.is_logarithmic) { - let minValue = uiControl.min_value; - if (minValue === 0) // LSP controls. - { - minValue = 0.0001; - } - value = minValue * Math.pow(uiControl.max_value / minValue, range); - if (!isFinite(value)) { - value = uiControl.max_value; - } else if (isNaN(value)) { - value = uiControl.min_value; - } - } else { - value = range * (uiControl.max_value - uiControl.min_value) + uiControl.min_value; - } - if (uiControl.integer_property) { - value = Math.round(value); - } - } - return value; - } - return 0; } rangeToRotationTransform(range: number): string { @@ -926,22 +844,10 @@ const PluginControl = } getEqPosition(): number { - let range = 0; - let uiControl = this.props.uiControl; - if (uiControl) { - let value = this.props.value; - range = this.valueToRange(value); - } - return range; - + return this.props.uiControl?.valueToRange(this.props.value) ?? 0; } getRotationTransform(): string { - let range = 0; - let uiControl = this.props.uiControl; - if (uiControl) { - let value = this.props.value; - range = this.valueToRange(value); - } + let range = this.props.uiControl?.valueToRange(this.props.value) ?? 0; return this.rangeToRotationTransform(range); } diff --git a/vite/src/pipedal/PluginPresetSelector.tsx b/vite/src/pipedal/PluginPresetSelector.tsx index 698651c..0365f34 100644 --- a/vite/src/pipedal/PluginPresetSelector.tsx +++ b/vite/src/pipedal/PluginPresetSelector.tsx @@ -114,7 +114,7 @@ const PluginPresetSelector = hasPresets(pedalboardItem: PedalboardItem | null): boolean { if (pedalboardItem === null) return false; if (!pedalboardItem.uri) return false; - if (pedalboardItem.isStart() || pedalboardItem.isEnd() + if (pedalboardItem.isStart() || pedalboardItem.isEnd() || pedalboardItem.isEmpty() || pedalboardItem.isSplit()) { return false; } @@ -123,8 +123,8 @@ const PluginPresetSelector = isVisible(pedalboardItem: PedalboardItem | null): boolean { if (pedalboardItem === null) return false; if (!pedalboardItem.uri) return false; - if (pedalboardItem.isStart() || pedalboardItem.isEnd() - || pedalboardItem.isSplit()) { + if (pedalboardItem.isStart() || pedalboardItem.isEnd() + ) { return false; } return true; @@ -197,7 +197,7 @@ const PluginPresetSelector = loadPresets(): void { if (this.props.pedalboardItem === null) return; - if (!this.hasPresets(this.props.pedalboardItem)) return; + if (!this.hasPresets(this.props.pedalboardItem)) return; let captureUri: string = this.props.pedalboardItem.uri; this.model.getPluginPresets(captureUri) .then((presets: PluginUiPresets) => { @@ -235,20 +235,18 @@ const PluginPresetSelector = currentPedalboard?: PedalboardItem; componentDidUpdate( - prevProps: Readonly, - prevState: Readonly, - snapshot?: any): void - { + prevProps: Readonly, + prevState: Readonly, + snapshot?: any): void { if (this.props.pedalboardItem !== prevProps.pedalboardItem) { - this.setState({ + this.setState({ isVisible: this.isVisible(this.props.pedalboardItem), - hasPresets: this.hasPresets(this.props.pedalboardItem) }); + hasPresets: this.hasPresets(this.props.pedalboardItem) + }); if (this.props.pedalboardItem) { - if (this.props.pedalboardItem.isEmpty()) - { + if (this.props.pedalboardItem.isEmpty()) { this.setState({ presets: new PluginUiPresets() }); - } else - { + } else { this.loadPresets(); } } @@ -272,7 +270,8 @@ const PluginPresetSelector = } isMenuCopyPluginEnabled(): boolean { - return this.state.hasPresets; + if (this.props.pedalboardItem === null) return false; + return !this.props.pedalboardItem.isEmpty(); } handleMenuCopyPlugin(): void { this.handlePresetsMenuClose(); @@ -340,7 +339,7 @@ const PluginPresetSelector = } buildMenuItems() { - let result : React.ReactNode[] = []; + let result: React.ReactNode[] = []; if (this.state.hasPresets) { result.push(( this.handlePluginPresetsMenuSaveAs(e)}>Save plugin preset...)); result.push(( this.handleMenuEditPluginPresets()}>Manage plugin presets...)); @@ -355,7 +354,7 @@ const PluginPresetSelector = {preset.label} - )); + )); hasPresets = true; } if (hasPresets) { @@ -402,12 +401,12 @@ const PluginPresetSelector = } {this.state.hasPresets && this.state.showPresetsDialog && ( - this.handleDialogClose()} /> + this.handleDialogClose()} /> )} this.setState({ showJackServerSettingsDialog: false })} diff --git a/vite/src/pipedal/SystemMidiBindingsDialog.tsx b/vite/src/pipedal/SystemMidiBindingsDialog.tsx index d9aef17..b272884 100644 --- a/vite/src/pipedal/SystemMidiBindingsDialog.tsx +++ b/vite/src/pipedal/SystemMidiBindingsDialog.tsx @@ -159,6 +159,11 @@ export const SystemMidiBindingDialog = } else if (item.symbol === "snapshot6") { displayName = "Snapshot 6"; + } + else if (item.symbol === "nextSnapshot") { + displayName = "Next Snapshot"; + } else if (item.symbol === "prevSnapshot") { + displayName = "Previous Snapshot"; } else if (item.symbol === "startHotspot") { displayName = "Enable Hotspot"; } else if (item.symbol === "stopHotspot") { diff --git a/vite/vite.config.ts b/vite/vite.config.ts index d8da56c..1b4b930 100644 --- a/vite/vite.config.ts +++ b/vite/vite.config.ts @@ -20,7 +20,7 @@ export default defineConfig({ target: 'http://localhost:8080', changeOrigin: false, }, - '^/var/(?!config\\.json$).*': { + '^/var/.*': { target: 'http://localhost:8080', changeOrigin: false, },