This commit is contained in:
Robin E.R. Davies
2026-06-17 14:11:24 -04:00
85 changed files with 1702 additions and 437 deletions
+27 -9
View File
@@ -1,19 +1,37 @@
cmake_minimum_required(VERSION 3.16.0) cmake_minimum_required(VERSION 3.16.0)
project(pipedal project(pipedal
VERSION 2.0.105 VERSION 2.0.106
DESCRIPTION "PiPedal Guitar Effect Pedal For Raspberry Pi" DESCRIPTION "PiPedal Guitar Effect Pedal For Raspberry Pi"
HOMEPAGE_URL "https://rerdavies.github.io/pipedal" 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") option(PIPEDAL_DISABLE_COPYRIGHT_BUILD "Skip generation of copyright notices (use on non-Debian distros)" OFF)
set (PACKAGE_ARCHITECTURE ${DEBIAN_ARCHITECTURE}) option(PIPEDAL_EXCLUDE_TESTS "Exclude test targets from default build" OFF)
set (CMAKE_INSTALL_PREFIX "/usr/")
set(PIPEDAL_T3K_PUBLISHABLE_KEY "t3k_pub_bHrH8btdwXXTtxz5ryEU8sNLF-2TGRT9") 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 (LIBZIP_DO_INSTALL OFF CACHE BOOL "Disable libzip install" FORCE)
set (SQLITECPP_INSTALL OFF CACHE BOOL "Disable SQLiteCpp 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)" "*.a;sqlite3.h;$(CPACK_SOURCE_IGNORE_FILES)"
) )
set (PIPEDAL_EXCLUDE_TESTS false)
include(CTest) include(CTest)
enable_testing() enable_testing()
@@ -58,8 +74,10 @@ install (
install ( install (
DIRECTORY ${VITE_BUILD_DIRECTORY} DESTINATION /etc/pipedal/react DIRECTORY ${VITE_BUILD_DIRECTORY} DESTINATION /etc/pipedal/react
) )
install (FILES ${PROJECT_SOURCE_DIR}/build/src/notices.txt if(NOT PIPEDAL_DISABLE_COPYRIGHT_BUILD)
DESTINATION /etc/pipedal/react/var/) install (FILES ${PROJECT_SOURCE_DIR}/build/src/notices.txt
DESTINATION /etc/pipedal/react/var/)
endif()
install ( install (
DIRECTORY ${PROJECT_SOURCE_DIR}/src/templates DIRECTORY ${PROJECT_SOURCE_DIR}/src/templates
+1 -1
View File
@@ -97,7 +97,7 @@ add_library(PiPedalCommon STATIC
ss.hpp ss.hpp
) )
target_include_directories(PiPedalCommon PUBLIC "include" ${LIBNL3_INCLUDE_DIRS}) 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
) )
+1
View File
@@ -3,6 +3,7 @@
#include <sdbus-c++/IConnection.h> #include <sdbus-c++/IConnection.h>
#include <poll.h> #include <poll.h>
#include <sys/eventfd.h> #include <sys/eventfd.h>
#include <unistd.h>
#include "ss.hpp" #include "ss.hpp"
#include <chrono> #include <chrono>
#include <cassert> #include <cassert>
+2 -2
View File
@@ -9,11 +9,11 @@
<img src="https://img.shields.io/github/downloads/rerdavies/pipedal/total?color=%23808080&link=https%3A%2F%2Frerdavies.github.io%2Fpipedal%2Fdownload.html"/> <img src="https://img.shields.io/github/downloads/rerdavies/pipedal/total?color=%23808080&link=https%3A%2F%2Frerdavies.github.io%2Fpipedal%2Fdownload.html"/>
Download:&nbsp;<a href='https://rerdavies.github.io/pipedal/download.html'>v2.0.105</a> Download:&nbsp;<a href='https://rerdavies.github.io/pipedal/download.html'>v2.0.106</a>
Website:&nbsp;[https://rerdavies.github.io/pipedal](https://rerdavies.github.io/pipedal). Website:&nbsp;[https://rerdavies.github.io/pipedal](https://rerdavies.github.io/pipedal).
Documentation:&nbsp;[https://rerdavies.github.io/pipedal/Documentation.html](https://rerdavies.github.io/pipedal/Documentation.html). Documentation:&nbsp;[https://rerdavies.github.io/pipedal/Documentation.html](https://rerdavies.github.io/pipedal/Documentation.html).
#### Announcing PiPedal 2.0 (2.0.105)&mdash;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)&mdash;a major update to PiPedal, including exciting new features. See the Pipedal website [documentation](https://rerdavies.github.io/pipedal/PiPedal.html) for more information.
&nbsp; &nbsp;
+22 -1
View File
@@ -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. 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. Run the following commands to install dependent libraries required by the PiPedal build.
Ubuntu:
sudo apt update sudo apt update
sudo apt upgrade 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 \ libsdbus-c++-dev libzip-dev google-perftools \
libgoogle-perftools-dev \ libgoogle-perftools-dev \
libpipewire-0.3-dev libbz2-dev libssl-dev librsvg2-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 ### Installing Sources
+7 -7
View File
@@ -13,18 +13,18 @@ page_icon: img/Install4.jpg
Download the most recent Debian (.deb) package for your platform: 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) - [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.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.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.105](https://github.com/rerdavies/pipedal/releases/download/v2.0.105/pipedal_2.0.105_amd64.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 update
sudo apt upgrade sudo apt upgrade
cd ~/Downloads 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. 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: 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. 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 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. 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.
+1 -1
View File
@@ -11,7 +11,7 @@
<a href="https://rerdavies.github.io/pipedal/download2"><img src="https://img.shields.io/badge/Download-008060" /></a> <a href="https://rerdavies.github.io/pipedal/download2"><img src="https://img.shields.io/badge/Download-008060" /></a>
<a href="https://rerdavies.github.io/pipedal/Documentation"><img src="https://img.shields.io/badge/Documentation-0060d0"/></a> <a href="https://rerdavies.github.io/pipedal/Documentation"><img src="https://img.shields.io/badge/Documentation-0060d0"/></a>
_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)._ 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. Use your Raspberry Pi as a guitar effects pedal. Configure and control PiPedal with your phone or tablet.
+18
View File
@@ -1,5 +1,23 @@
# Release Notes # 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 ## PiPedal 2.0.105-Release
Bug fix: Bug fix:
+5 -5
View File
@@ -1,13 +1,13 @@
## Download ## Download
# Download PiPedal 2.0.105-alpha # Download PiPedal 2.0.106-alpha
Download the most recent Debian (.deb) package for your platform: 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) - [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.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.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.105 Alpha](https://github.com/rerdavies/pipedal/releases/download/v2.0.105/pipedal_2.0.105_amd64.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 Install the package by running
@@ -15,7 +15,7 @@ Install the package by running
``` ```
sudo apt update sudo apt update
cd ~/Downloads 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 # or ... _amd64.deb as appropriate for your platform
``` ```
The message about missing permissions given by `apt` is The message about missing permissions given by `apt` is
+1 -1
View File
@@ -9,7 +9,7 @@
<a href="https://rerdavies.github.io/pipedal/download"><img src="https://img.shields.io/badge/Download-008060" /></a> <a href="https://rerdavies.github.io/pipedal/download"><img src="https://img.shields.io/badge/Download-008060" /></a>
<a href="https://rerdavies.github.io/pipedal/Documentation"><img src="https://img.shields.io/badge/Documentation-0060d0"/></a> <a href="https://rerdavies.github.io/pipedal/Documentation"><img src="https://img.shields.io/badge/Documentation-0060d0"/></a>
_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)._ 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. 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.
+1 -1
View File
@@ -93,7 +93,7 @@ cabir:impulseFile3
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
rdfs:comment """ rdfs:comment """
TooB Cab IR is a convolution-based guitar cabinet impulse response simulator. TooB Cab IR is a convolution-based guitar cabinet impulse response simulator.
+1 -1
View File
@@ -50,7 +50,7 @@ toob:frequencyResponseVector
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
mod:brand "TooB"; mod:brand "TooB";
mod:label "Cab Simulator"; mod:label "Cab Simulator";
@@ -53,7 +53,7 @@ toobimpulse:impulseFile
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
rdfs:comment """ rdfs:comment """
Toob Convolution Reverb Stereo uses convolution reverb impulse/response files in order to produce highly Toob Convolution Reverb Stereo uses convolution reverb impulse/response files in order to produce highly
@@ -51,7 +51,7 @@ toobimpulse:impulseFile
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
rdfs:comment """ rdfs:comment """
+1 -1
View File
@@ -66,7 +66,7 @@ inputStage:filterGroup
doap:license <https://two-play.com/TooB/licenses/isc> ; doap:license <https://two-play.com/TooB/licenses/isc> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
mod:brand "TooB"; mod:brand "TooB";
mod:label "Input"; mod:label "Input";
+1 -1
View File
@@ -68,7 +68,7 @@ pstage:stage3
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
mod:brand "TooB"; mod:brand "TooB";
mod:label "Power Stage"; mod:label "Power Stage";
+1 -1
View File
@@ -57,7 +57,7 @@
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
rdfs:comment "TooB spectrum analyzer" ; rdfs:comment "TooB spectrum analyzer" ;
mod:brand "TooB"; mod:brand "TooB";
+1 -1
View File
@@ -57,7 +57,7 @@ tonestack:eqGroup
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
uiext:ui <http://two-play.com/plugins/toob-tone-stack-ui>; uiext:ui <http://two-play.com/plugins/toob-tone-stack-ui>;
+1 -1
View File
@@ -52,7 +52,7 @@ toob:frequencyResponseVector
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
uiext:ui <http://two-play.com/plugins/toob-three-band-eq-ui>; uiext:ui <http://two-play.com/plugins/toob-three-band-eq-ui>;
@@ -52,7 +52,7 @@ toob:frequencyResponseVector
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
uiext:ui <http://two-play.com/plugins/toob-three-band-eq-stereo-ui>; uiext:ui <http://two-play.com/plugins/toob-three-band-eq-stereo-ui>;
Binary file not shown.
+1 -1
View File
@@ -40,7 +40,7 @@
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
rdfs:comment """ rdfs:comment """
Emulation of a Boss CE-2 Chorus. Emulation of a Boss CE-2 Chorus.
+1 -1
View File
@@ -41,7 +41,7 @@
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
rdfs:comment """ rdfs:comment """
A straightforward no-frills digital delay. A straightforward no-frills digital delay.
+1 -1
View File
@@ -41,7 +41,7 @@
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
rdfs:comment """ rdfs:comment """
Emulation of a Boss BF-2 Flanger, based on circuit analysis and simulation of the original. Emulation of a Boss BF-2 Flanger, based on circuit analysis and simulation of the original.
@@ -41,7 +41,7 @@
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
rdfs:comment """ rdfs:comment """
Digital emulation of a Boss BF-2 Flanger. Digital emulation of a Boss BF-2 Flanger.
+1 -1
View File
@@ -41,7 +41,7 @@
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
rdfs:comment """ 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. Toob Freeverb is an Lv2 implementation of the famous Freeverb reverb effect. FreeVerb delivers a well-balanced reverb with very little tonal coloration.
+1 -1
View File
@@ -41,7 +41,7 @@
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
rdfs:comment """ rdfs:comment """
A 7 octave graphic equalizer, with frequencies chosen to be useful for EQ-ing guitar signals. A 7 octave graphic equalizer, with frequencies chosen to be useful for EQ-ing guitar signals.
""" ; """ ;
+1 -1
View File
@@ -92,7 +92,7 @@ myprefix:output_group
doap:license <https://opensource.org/license/mit/> ; doap:license <https://opensource.org/license/mit/> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 3 ; lv2:minorVersion 3 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
ui:ui <http://two-play.com/plugins/toob-looper-four-ui>; ui:ui <http://two-play.com/plugins/toob-looper-four-ui>;
+1 -1
View File
@@ -78,7 +78,7 @@ myprefix:output_group
doap:license <https://opensource.org/license/mit/> ; doap:license <https://opensource.org/license/mit/> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 3 ; lv2:minorVersion 3 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
ui:ui <http://two-play.com/plugins/toob-looper-one-ui>; ui:ui <http://two-play.com/plugins/toob-looper-one-ui>;
+1 -1
View File
@@ -67,7 +67,7 @@ toobml:sagGroup
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
rdfs:comment """ rdfs:comment """
The TooB ML Amplifier plugin provides emulation of a variety of amplifiers and overdrive pedals that are implemented 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. using neural-network-based machine learning models of real amplifiers.
+1 -1
View File
@@ -40,7 +40,7 @@
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
rdfs:comment """ rdfs:comment """
Remix a stereo input signal. Remix a stereo input signal.
+457
View File
@@ -0,0 +1,457 @@
@prefix doap: <http://usefulinc.com/ns/doap#> .
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix units: <http://lv2plug.in/ns/extensions/units#> .
@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
@prefix midi: <http://lv2plug.in/ns/ext/midi#> .
@prefix epp: <http://lv2plug.in/ns/ext/port-props#> .
@prefix uiext: <http://lv2plug.in/ns/extensions/ui#> .
@prefix idpy: <http://harrisonconsoles.com/lv2/inlinedisplay#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix mod: <http://moddevices.com/ns/mod#> .
@prefix param: <http://lv2plug.in/ns/ext/parameters#> .
@prefix work: <http://lv2plug.in/ns/ext/worker#> .
@prefix pg: <http://lv2plug.in/ns/ext/port-groups#> .
@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix state: <http://lv2plug.in/ns/ext/state#> .
@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix toobMultiEcho: <http://two-play.com/plugins/toob-multi-echo#> .
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" .
<http://two-play.com/rerdavies#me>
a foaf:Person ;
foaf:name "Robin Davies" ;
foaf:mbox <mailto:rerdavies@gmail.com> ;
foaf:homepage <https:##github.com/sponsors/rerdavies> .
<http://two-play.com/plugins/toob-multi-echo>
a lv2:Plugin ,
lv2:DelayPlugin ;
doap:name "TooB Multi-Tap Delay" ,
"Toob Multi-Tap Delay"@en-gb-gb
;
uiext:ui <http://two-play.com/plugins/toob-multi-echo-ui> ;
doap:license <https:##rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ;
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 <http://lv2plug.in/ns/ext/port-props#logarithmic>;
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 <http://lv2plug.in/ns/ext/port-props#logarithmic>;
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 <http://lv2plug.in/ns/ext/port-props#logarithmic>;
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 <http://lv2plug.in/ns/ext/port-props#logarithmic>;
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"
#]
.
<http://two-play.com/plugins/toob-multi-echo-ui>
a uiext:X11UI ;
lv2:binary <ToobAmpUI.so>;
lv2:extensionData uiext::idle ;
lv2:extensionData uiext:resize ;
lv2:extensionData uiext:idleInterface;
lv2:requiredFeature uiext:idleInterface ;
.
@@ -0,0 +1,453 @@
@prefix doap: <http://usefulinc.com/ns/doap#> .
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix units: <http://lv2plug.in/ns/extensions/units#> .
@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
@prefix midi: <http://lv2plug.in/ns/ext/midi#> .
@prefix epp: <http://lv2plug.in/ns/ext/port-props#> .
@prefix uiext: <http://lv2plug.in/ns/extensions/ui#> .
@prefix idpy: <http://harrisonconsoles.com/lv2/inlinedisplay#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix mod: <http://moddevices.com/ns/mod#> .
@prefix param: <http://lv2plug.in/ns/ext/parameters#> .
@prefix work: <http://lv2plug.in/ns/ext/worker#> .
@prefix pg: <http://lv2plug.in/ns/ext/port-groups#> .
@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix state: <http://lv2plug.in/ns/ext/state#> .
@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix toobMultiEcho: <http://two-play.com/plugins/toob-multi-echo-stereo#> .
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" .
<http://two-play.com/rerdavies#me>
a foaf:Person ;
foaf:name "Robin Davies" ;
foaf:mbox <mailto:rerdavies@gmail.com> ;
foaf:homepage <https:##github.com/sponsors/rerdavies> .
<http://two-play.com/plugins/toob-multi-echo-stereo>
a lv2:Plugin ,
lv2:DelayPlugin ;
doap:name "TooB Multi-Tap Delay (Stereo)" ,
"Toob Multi-Tap Delay (Stereo)"@en-gb-gb
;
uiext:ui <http://two-play.com/plugins/toob-multi-echo-stereo-ui> ;
doap:license <https:##rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ;
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 <http://lv2plug.in/ns/ext/port-props#logarithmic>;
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 <http://lv2plug.in/ns/ext/port-props#logarithmic>;
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 <http://lv2plug.in/ns/ext/port-props#logarithmic>;
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 <http://lv2plug.in/ns/ext/port-props#logarithmic>;
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"
]
.
<http://two-play.com/plugins/toob-multi-echo-stereo-ui>
a uiext:X11UI ;
lv2:binary <ToobAmpUI.so>;
lv2:extensionData uiext::idle ;
lv2:extensionData uiext:resize ;
lv2:extensionData uiext:idleInterface;
lv2:requiredFeature uiext:idleInterface ;
.
@@ -74,7 +74,7 @@ toobNam:calibrationGroup
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
rdfs:comment """ 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 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 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
+1 -1
View File
@@ -54,7 +54,7 @@ noisegate:envelope_group
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
rdfs:comment """ rdfs:comment """
A noise gate is an audio processing tool that controls the volume of an audio signal 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. by allowing it to pass through only when it exceeds a set threshold.
+1 -1
View File
@@ -87,7 +87,7 @@ parametric_eq:hfGroup
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
uiext:ui <http://two-play.com/plugins/toob-parametric-eq-ui>; uiext:ui <http://two-play.com/plugins/toob-parametric-eq-ui>;
@@ -87,7 +87,7 @@ toob-parametric-eq-stereo:hfGroup
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
uiext:ui <http://two-play.com/plugins/toob-parametric-eq-stereo-ui>; uiext:ui <http://two-play.com/plugins/toob-parametric-eq-stereo-ui>;
+1 -1
View File
@@ -40,7 +40,7 @@
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
rdfs:comment """ rdfs:comment """
A loose emulation of an MXR® Phase 90 Phaser. A loose emulation of an MXR® Phase 90 Phaser.
+1 -1
View File
@@ -60,7 +60,7 @@ toobPlayer:seek
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
rdfs:comment """ rdfs:comment """
Play audio files. Audio files can optionally be loooped by pressing the "Set loop" button. Play audio files. Audio files can optionally be loooped by pressing the "Set loop" button.
+1 -1
View File
@@ -51,7 +51,7 @@ recordPrefix:audioFile
doap:license <https://opensource.org/license/mit/> ; doap:license <https://opensource.org/license/mit/> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 3 ; lv2:minorVersion 3 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
ui:ui <http://two-play.com/plugins/toob-record-mono-ui>; ui:ui <http://two-play.com/plugins/toob-record-mono-ui>;
+1 -1
View File
@@ -88,7 +88,7 @@ myprefix:loop3_group
doap:license <https://opensource.org/license/mit/> ; doap:license <https://opensource.org/license/mit/> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 3 ; lv2:minorVersion 3 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
ui:ui <http://two-play.com/plugins/toob-record-stereo-ui>; ui:ui <http://two-play.com/plugins/toob-record-stereo-ui>;
+1 -1
View File
@@ -43,7 +43,7 @@
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
rdfs:comment """ rdfs:comment """
TooB Tone is a simple tone control. For more flexible EQ plugins, see TooB 3 Band Eq, or TooB Parameteric EQ. TooB Tone is a simple tone control. For more flexible EQ plugins, see TooB 3 Band Eq, or TooB Parameteric EQ.
+1 -1
View File
@@ -43,7 +43,7 @@
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
rdfs:comment """ rdfs:comment """
TooB Tone is a simple tone control. For more flexible EQ plugins, see TooB 3 Band Eq, or TooB Parameteric EQ. TooB Tone is a simple tone control. For more flexible EQ plugins, see TooB 3 Band Eq, or TooB Parameteric EQ.
+1 -1
View File
@@ -41,7 +41,7 @@
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
rdfs:comment """ rdfs:comment """
A tremolo effect, equivalent to "vibrato" on vintage Fender tube amps. A tremolo effect, equivalent to "vibrato" on vintage Fender tube amps.
+1 -1
View File
@@ -41,7 +41,7 @@
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
rdfs:comment """ rdfs:comment """
A tremolo effect, equivalent to "vibrato" on vintage Fender tube amps. A tremolo effect, equivalent to "vibrato" on vintage Fender tube amps.
+1 -1
View File
@@ -44,7 +44,7 @@
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
rdfs:comment """ rdfs:comment """
TooB Tuner is a chromatic guitar tuner. TooB Tuner is a chromatic guitar tuner.
""" ; """ ;
+1 -1
View File
@@ -39,7 +39,7 @@
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ; doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ; doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ; lv2:minorVersion 0 ;
lv2:microVersion 80 ; lv2:microVersion 81 ;
rdfs:comment """ rdfs:comment """
Volume control. Volume control.
Binary file not shown.
Binary file not shown.
+11
View File
@@ -4,6 +4,7 @@
@prefix tpset: <http://two-play.com/plugins/preset#> . @prefix tpset: <http://two-play.com/plugins/preset#> .
<http://two-play.com/plugins/toob-tone-stack> a lv2:Plugin ; <http://two-play.com/plugins/toob-tone-stack> a lv2:Plugin ;
lv2:binary <ToobAmp.so> ; lv2:binary <ToobAmp.so> ;
rdfs:seeAlso <ToneStack.ttl> . rdfs:seeAlso <ToneStack.ttl> .
@@ -114,6 +115,16 @@
lv2:binary <ToobAmp.so> ; lv2:binary <ToobAmp.so> ;
rdfs:seeAlso <ToobMix.ttl> . rdfs:seeAlso <ToobMix.ttl> .
<http://two-play.com/plugins/toob-multi-echo> a lv2:Plugin ;
lv2:binary <ToobAmp.so> ;
rdfs:seeAlso <ToobMultiEcho.ttl> .
<http://two-play.com/plugins/toob-multi-echo-stereo> a lv2:Plugin ;
lv2:binary <ToobAmp.so> ;
rdfs:seeAlso <ToobMultiEchoStereo.ttl> .
<http://two-play.com/plugins/toob-tremolo> a lv2:Plugin ; <http://two-play.com/plugins/toob-tremolo> a lv2:Plugin ;
lv2:binary <ToobAmp.so> ; lv2:binary <ToobAmp.so> ;
rdfs:seeAlso <ToobTremolo.ttl> . rdfs:seeAlso <ToobTremolo.ttl> .
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 25 KiB

+16 -3
View File
@@ -1,7 +1,20 @@
#!/usr/bin/bash #!/usr/bin/bash
rm -rf build/lv2_x64 rm -rf build/lv2_x64
mkdir build/lv2_x64 mkdir -p build/lv2_x64/pkg
mkdir build/lv2_x64/pkg # Extract .deb using ar + tar (portable, no dpkg-deb needed)
dpkg-deb -x lv2/x86_64/toobamp_*_amd64.deb build/lv2_x64/pkg 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"
+6 -1
View File
@@ -1,8 +1,10 @@
cmake_minimum_required(VERSION 3.16.0) cmake_minimum_required(VERSION 3.16.0)
# Prevent installation of SQLite built components. (we use the .a library files directly) # 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) 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_OSSFUZZ "Build fuzzers for ossfuzz" OFF)
option(BUILD_EXAMPLES "Build examples" OFF) option(BUILD_EXAMPLES "Build examples" OFF)
option(BUILD_DOC "Build documentation" 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") add_subdirectory("libzip")
+3
View File
@@ -2,6 +2,7 @@
* MIT License * MIT License
* *
* Copyright (c) Robin E.R. Davies * Copyright (c) Robin E.R. Davies
* Copyright (c) Roberto Figliè
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of * 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 * this software and associated documentation files (the "Software"), to deal in
@@ -1681,6 +1682,7 @@ namespace pipedal
AlsaMidiMessage message; AlsaMidiMessage message;
midiEventCount = 0; midiEventCount = 0;
midiEventMemoryIndex = 0;
auto alsaSequener = this->alsaSequencer; // take an addref auto alsaSequener = this->alsaSequencer; // take an addref
if (!alsaSequener) if (!alsaSequener)
{ {
@@ -1772,6 +1774,7 @@ namespace pipedal
break; break;
} }
this->midiEventCount = 0; this->midiEventCount = 0;
this->midiEventMemoryIndex = 0;
// snd_pcm_wait(captureHandle, 1); // snd_pcm_wait(captureHandle, 1);
ssize_t framesToRead = bufferSize; ssize_t framesToRead = bufferSize;
+1
View File
@@ -28,6 +28,7 @@
#include <mutex> #include <mutex>
#include <iostream> #include <iostream>
#include <algorithm> #include <algorithm>
#include <unistd.h>
#include "AlsaDriver.hpp" #include "AlsaDriver.hpp"
#include "ChannelRouterSettings.hpp" #include "ChannelRouterSettings.hpp"
+28
View File
@@ -516,6 +516,8 @@ private:
SystemMidiBinding snapshot5MidiBinding; SystemMidiBinding snapshot5MidiBinding;
SystemMidiBinding snapshot6MidiBinding; SystemMidiBinding snapshot6MidiBinding;
SystemMidiBinding nextSnapshotMidiBinding;
SystemMidiBinding prevSnapshotMidiBinding;
SystemMidiBinding startHotspotMidiBinding; SystemMidiBinding startHotspotMidiBinding;
SystemMidiBinding stopHotspotMidiBinding; SystemMidiBinding stopHotspotMidiBinding;
SystemMidiBinding rebootMidiBinding; SystemMidiBinding rebootMidiBinding;
@@ -1015,6 +1017,18 @@ private:
midiProgramChangePending = true; midiProgramChangePending = true;
this->realtimeWriter.OnNextMidiProgram(++(this->midiProgramChangeId), -1); 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)) else if (shutdownMidiBinding.IsTriggered(event))
{ {
@@ -1737,6 +1751,12 @@ public:
hostReader.read(&request); hostReader.read(&request);
pNotifyCallbacks->OnNotifyNextMidiBank(request); pNotifyCallbacks->OnNotifyNextMidiBank(request);
} }
else if (command == RingBufferCommand::NextMidiSnapshot)
{
RealtimeNextMidiProgramRequest request;
hostReader.read(&request);
pNotifyCallbacks->OnNotifyNextMidiSnapshot(request);
}
else if (command == RingBufferCommand::RealtimeMidiEvent) else if (command == RingBufferCommand::RealtimeMidiEvent)
{ {
@@ -2423,6 +2443,14 @@ void AudioHostImpl::SetSystemMidiBindings(const std::vector<MidiBinding> &bindin
{ {
this->snapshot6MidiBinding.SetBinding(*i); this->snapshot6MidiBinding.SetBinding(*i);
} }
else if (i->symbol() == "nextSnapshot")
{
this->nextSnapshotMidiBinding.SetBinding(*i);
}
else if (i->symbol() == "prevSnapshot")
{
this->prevSnapshotMidiBinding.SetBinding(*i);
}
else else
{ {
Lv2Log::error(SS("Invalid system midi binding: " << i->symbol())); Lv2Log::error(SS("Invalid system midi binding: " << i->symbol()));
+1
View File
@@ -177,6 +177,7 @@ namespace pipedal
virtual void OnNotifyMidiProgramChange(RealtimeMidiProgramRequest &midiProgramRequest) = 0; virtual void OnNotifyMidiProgramChange(RealtimeMidiProgramRequest &midiProgramRequest) = 0;
virtual void OnNotifyNextMidiProgram(const RealtimeNextMidiProgramRequest &request) = 0; virtual void OnNotifyNextMidiProgram(const RealtimeNextMidiProgramRequest &request) = 0;
virtual void OnNotifyNextMidiBank(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 OnNotifyLv2RealtimeError(int64_t instanceId, const std::string &error) = 0;
virtual void OnNotifyMidiRealtimeEvent(RealtimeMidiEventType eventType) = 0; virtual void OnNotifyMidiRealtimeEvent(RealtimeMidiEventType eventType) = 0;
virtual void OnNotifyMidiRealtimeSnapshotRequest(int32_t snapshotIndex,int64_t snapshotRequestId) = 0; virtual void OnNotifyMidiRealtimeSnapshotRequest(int32_t snapshotIndex,int64_t snapshotRequestId) = 0;
+1
View File
@@ -19,6 +19,7 @@
#include "AuxIn.hpp" #include "AuxIn.hpp"
#include <unistd.h>
using namespace pipedal; using namespace pipedal;
int main(int argc, char**argv) { int main(int argc, char**argv) {
+63 -9
View File
@@ -22,6 +22,38 @@ set(CXX_STANDARD 20)
find_package(PkgConfig REQUIRED) 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 <catch/catch.hpp> 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 <catch2/catch_all.hpp>\n"
)
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/catch")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/catch/catch.hpp"
"#pragma once\n"
"#include <catch2/catch_all.hpp>\n"
)
# Add the build directory to the global include paths so that
# both #include "catch.hpp" (quote) and #include <catch/catch.hpp> (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) pkg_check_modules(PipeWire REQUIRED libpipewire-0.3)
include(FetchContent) include(FetchContent)
@@ -155,15 +187,27 @@ else()
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-multichar -Wno-psabi") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-multichar -Wno-psabi")
endif() endif()
execute_process( COMMAND dpkg --print-architecture COMMAND tr -d '\n' OUTPUT_VARIABLE DEBIAN_ARCHITECTURE ) # Determine Debian-compatible architecture name (portable across distros)
message(STATUS DEBIAN_ARCHITECTURE="${DEBIAN_ARCHITECTURE}") 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}") add_compile_definitions(DEBIAN_ARCHITECTURE="${DEBIAN_ARCHITECTURE}")
if (CMAKE_BUILD_TYPE MATCHES Debug) if (CMAKE_BUILD_TYPE MATCHES Debug)
message(STATUS "Debug build") 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 " ) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g -DDEBUG " )
if (USE_SANITIZE) if (USE_SANITIZE)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -static-libasan " ) 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_link_libraries(pipedaltest PRIVATE ${PIPEDAL_LIBS} ${ICU_LIBRARIES})
target_include_directories(pipedaltest PRIVATE ${PIPEDAL_INCLUDES}) 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}) set_target_properties(pipedaltest PROPERTIES EXCLUDE_FROM_ALL ${PIPEDAL_EXCLUDE_TESTS})
if (NOT GITHUB_ACTIONS) # Google perftools are not available on Github action servers. 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_link_libraries(jsonTest PRIVATE PiPedalCommon)
target_include_directories(jsonTest PRIVATE ${PIPEDAL_INCLUDES}) 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}) 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") set (BOOST_COPYRIGHT_DIR "libboost1.71-dev")
elseif(EXISTS "/usr/share/doc/libboost1.67-dev") elseif(EXISTS "/usr/share/doc/libboost1.67-dev")
set (BOOST_COPYRIGHT_DIR "libboost1.67-dev") set (BOOST_COPYRIGHT_DIR "libboost1.67-dev")
else() # else()
message(FATAL_ERROR "Boost libary version has changed. Please update me.") # message(FATAL_ERROR "Boost libary version has changed. Please update me.")
endif() endif()
@@ -871,10 +923,12 @@ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Updating copyright notices." COMMENT "Updating copyright notices."
) )
add_custom_target ( if(NOT PIPEDAL_DISABLE_COPYRIGHT_BUILD)
CopyrightBuild ALL add_custom_target (
DEPENDS ${REACT_NOTICES_FILE} CopyrightBuild ALL
) DEPENDS ${REACT_NOTICES_FILE}
)
endif()
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.31) if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.31)
cmake_policy(SET CMP0177 NEW) cmake_policy(SET CMP0177 NEW)
+1
View File
@@ -1154,6 +1154,7 @@ void Install(const fs::path &programPrefix, const std::string endpointAddress)
// Add to audio groups. // Add to audio groups.
sysExec(USERMOD_BIN " -a -G " AUDIO_SERVICE_GROUP_NAME " " SERVICE_ACCOUNT_NAME); sysExec(USERMOD_BIN " -a -G " AUDIO_SERVICE_GROUP_NAME " " SERVICE_ACCOUNT_NAME);
// add to netdev group // add to netdev group
sysExec(GROUPADD_BIN " -f " NETDEV_GROUP_NAME);
sysExec(USERMOD_BIN " -a -G " NETDEV_GROUP_NAME " " SERVICE_ACCOUNT_NAME); sysExec(USERMOD_BIN " -a -G " NETDEV_GROUP_NAME " " SERVICE_ACCOUNT_NAME);
try try
+1
View File
@@ -21,6 +21,7 @@
#include "util.hpp" #include "util.hpp"
#include <stdexcept> #include <stdexcept>
#include <iostream> #include <iostream>
#include <mutex>
#include "ss.hpp" #include "ss.hpp"
#include "MimeTypes.hpp" #include "MimeTypes.hpp"
+86
View File
@@ -1181,6 +1181,92 @@ void PiPedalModel::NextPreset(Direction direction)
LoadPreset(-1, index.presets()[currentPresetIndex].instanceId()); LoadPreset(-1, index.presets()[currentPresetIndex].instanceId());
} }
void PiPedalModel::NextSnapshot(Direction direction)
{
std::lock_guard<std::recursive_mutex> 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<std::recursive_mutex> 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) void PiPedalModel::OnNotifyNextMidiProgram(const RealtimeNextMidiProgramRequest &request)
{ {
std::lock_guard<std::recursive_mutex> guard{mutex}; std::lock_guard<std::recursive_mutex> guard{mutex};
+3
View File
@@ -273,6 +273,7 @@ namespace pipedal
virtual void OnNotifyMidiProgramChange(RealtimeMidiProgramRequest &midiProgramRequest) override; virtual void OnNotifyMidiProgramChange(RealtimeMidiProgramRequest &midiProgramRequest) override;
virtual void OnNotifyNextMidiProgram(const RealtimeNextMidiProgramRequest &request) override; virtual void OnNotifyNextMidiProgram(const RealtimeNextMidiProgramRequest &request) override;
virtual void OnNotifyNextMidiBank(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; virtual void OnNotifyLv2RealtimeError(int64_t instanceId, const std::string &error) override;
PostHandle networkChangingDelayHandle = 0; PostHandle networkChangingDelayHandle = 0;
@@ -311,6 +312,8 @@ namespace pipedal
void PreviousBank() { NextBank(Direction::Decrease); } void PreviousBank() { NextBank(Direction::Decrease); }
void NextPreset(Direction direction = Direction::Increase); void NextPreset(Direction direction = Direction::Increase);
void PreviousPreset() { NextPreset(Direction::Decrease); } void PreviousPreset() { NextPreset(Direction::Decrease); }
void NextSnapshot(Direction direction = Direction::Increase);
void PreviousSnapshot() { NextSnapshot(Direction::Decrease); }
int64_t DownloadModelsFromTone3000( int64_t DownloadModelsFromTone3000(
const std::string &responseuri, const std::string &responseuri,
+6
View File
@@ -83,6 +83,7 @@ namespace pipedal
NextMidiProgram, NextMidiProgram,
NextMidiBank, NextMidiBank,
NextMidiSnapshot,
Lv2StateChanged, Lv2StateChanged,
MaybeLv2StateChanged, MaybeLv2StateChanged,
@@ -444,6 +445,11 @@ namespace pipedal
RealtimeNextMidiProgramRequest msg{requestId : requestId, direction : direction}; RealtimeNextMidiProgramRequest msg{requestId : requestId, direction : direction};
write(RingBufferCommand::NextMidiBank, msg); 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) void SetControlValue(int effectIndex, int controlIndex, float value)
{ {
+3
View File
@@ -2213,6 +2213,9 @@ std::vector<MidiBinding> Storage::GetSystemMidiBindings()
result.push_back(MidiBinding::SystemBinding("snapshot5")); result.push_back(MidiBinding::SystemBinding("snapshot5"));
result.push_back(MidiBinding::SystemBinding("snapshot6")); 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("stopHotspot"));
result.push_back(MidiBinding::SystemBinding("startHotspot")); result.push_back(MidiBinding::SystemBinding("startHotspot"));
result.push_back(MidiBinding::SystemBinding("shutdown")); result.push_back(MidiBinding::SystemBinding("shutdown"));
+1
View File
@@ -55,6 +55,7 @@
#include <atomic> #include <atomic>
#include "BootConfig.hpp" #include "BootConfig.hpp"
#include <array> #include <array>
#include <unistd.h>
#include "CommandLineParser.hpp" #include "CommandLineParser.hpp"
#include "PrettyPrinter.hpp" #include "PrettyPrinter.hpp"
#include "SysExec.hpp" #include "SysExec.hpp"
+1 -1
View File
@@ -293,7 +293,7 @@ add_custom_command(
src/pipedal/IconColorDropdownButton.tsx src/pipedal/IconColorDropdownButton.tsx
src/pipedal/PluginOutputControl.tsx src/pipedal/PluginOutputControl.tsx
src/pipedal/PluginPresetsDialog.tsx src/pipedal/PluginPresetsDialog.tsx
src/pipedal/JackServerSettingsDialog.tsx src/pipedal/AudioDeviceDialog.tsx
src/pipedal/TextInfo_RewriteT3kUrls.ts src/pipedal/TextInfo_RewriteT3kUrls.ts
src/pipedal/MidiBindingsDialog.tsx src/pipedal/MidiBindingsDialog.tsx
src/pipedal/DialogStack.tsx src/pipedal/DialogStack.tsx
+8
View File
@@ -83,6 +83,14 @@ export default class AlsaDeviceInfo {
} }
return result; 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; cardId: number = -1;
id: string = ""; id: string = "";
@@ -67,7 +67,7 @@ function sortDevices(devices: AlsaDeviceInfo[]): AlsaDeviceInfo[] {
let ca = category(a); let ca = category(a);
let cb = category(b); let cb = category(b);
if (ca !== cb) return ca - cb; if (ca !== cb) return ca - cb;
return a.name.localeCompare(b.name); return a.shortDeviceName().localeCompare(b.shortDeviceName());
}); });
return copy; return copy;
} }
@@ -91,7 +91,7 @@ enum WarningDialogType {
Apply, Apply,
Ok, Ok,
}; };
interface JackServerSettingsDialogState { interface AudioDeviceDialogState {
latencyText: string; latencyText: string;
jackServerSettings: JackServerSettings; jackServerSettings: JackServerSettings;
jackHostStatus?: JackHostStatus; jackHostStatus?: JackHostStatus;
@@ -117,7 +117,7 @@ const styles = (theme: Theme) =>
whiteSpace: "nowrap" whiteSpace: "nowrap"
} }
}); });
export interface JackServerSettingsDialogProps extends WithStyles<typeof styles> { export interface AudioDeviceDialogProps extends WithStyles<typeof styles> {
open: boolean; open: boolean;
jackServerSettings: JackServerSettings; jackServerSettings: JackServerSettings;
onClose: () => void; onClose: () => void;
@@ -287,12 +287,12 @@ function isOkEnabled(jackServerSettings: JackServerSettings, alsaDevices?: AlsaD
} }
const JackServerSettingsDialog = withStyles( const AudioDeviceDialog = withStyles(
class extends ResizeResponsiveComponent<JackServerSettingsDialogProps, JackServerSettingsDialogState> { class extends ResizeResponsiveComponent<AudioDeviceDialogProps, AudioDeviceDialogState> {
model: PiPedalModel; model: PiPedalModel;
constructor(props: JackServerSettingsDialogProps) { constructor(props: AudioDeviceDialogProps) {
super(props); super(props);
this.model = PiPedalModelFactory.getInstance(); this.model = PiPedalModelFactory.getInstance();
@@ -456,7 +456,7 @@ const JackServerSettingsDialog = withStyles(
} }
componentDidUpdate(oldProps: JackServerSettingsDialogProps) { componentDidUpdate(oldProps: AudioDeviceDialogProps) {
this.updateActive(); this.updateActive();
} }
@@ -584,7 +584,7 @@ const JackServerSettingsDialog = withStyles(
if (!this.state.alsaDevices) return deviceId; if (!this.state.alsaDevices) return deviceId;
for (let i = 0; i < this.state.alsaDevices.length; ++i) { for (let i = 0; i < this.state.alsaDevices.length; ++i) {
if (this.state.alsaDevices[i].id === deviceId) { if (this.state.alsaDevices[i].id === deviceId) {
return this.state.alsaDevices[i].name; return this.state.alsaDevices[i].shortDeviceName();
} }
} }
return deviceId; return deviceId;
@@ -673,7 +673,7 @@ const JackServerSettingsDialog = withStyles(
<MenuItem key={d.id} disabled={d.captureBusy} <MenuItem key={d.id} disabled={d.captureBusy}
value={d.id} value={d.id}
style={{ opacity: d.captureBusy ? 0.3 : 1 }} style={{ opacity: d.captureBusy ? 0.3 : 1 }}
>{d.name}</MenuItem> >{d.shortDeviceName()}</MenuItem>
))} ))}
</Select> </Select>
</FormControl> </FormControl>
@@ -695,7 +695,7 @@ const JackServerSettingsDialog = withStyles(
<MenuItem key={d.id} value={d.id} <MenuItem key={d.id} value={d.id}
style={{ opacity: d.playbackBusy ? 0.3 : 1.0 }} style={{ opacity: d.playbackBusy ? 0.3 : 1.0 }}
disabled={d.playbackBusy} disabled={d.playbackBusy}
>{d.name}</MenuItem> >{d.shortDeviceName()}</MenuItem>
))} ))}
</Select> </Select>
</FormControl> </FormControl>
@@ -819,4 +819,4 @@ const JackServerSettingsDialog = withStyles(
}, },
styles); styles);
export default JackServerSettingsDialog; export default AudioDeviceDialog;
+2
View File
@@ -251,6 +251,7 @@ const Draggable =
if (this.pointerType !== "touch") { if (this.pointerType !== "touch") {
this.dragTarget.style.transform = "scale(" + SELECT_SCALE + ")"; this.dragTarget.style.transform = "scale(" + SELECT_SCALE + ")";
this.dragTarget.style.zIndex = "3"; this.dragTarget.style.zIndex = "3";
this.dragTarget.style.position = "absolute";
} }
} }
} }
@@ -259,6 +260,7 @@ const Draggable =
element.style.transform = ""; element.style.transform = "";
element.style.zIndex = this.savedIndex; element.style.zIndex = this.savedIndex;
element.style.opacity = this.savedOpacity; element.style.opacity = this.savedOpacity;
element.style.position = "relative";
} }
onPointerCancel(e: PointerEvent<HTMLDivElement>) { onPointerCancel(e: PointerEvent<HTMLDivElement>) {
+90 -57
View File
@@ -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 // 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 // this software and associated documentation files (the "Software"), to deal in
@@ -835,15 +836,44 @@ export class UiControl implements Deserializable<UiControl> {
valueToRange(value: number): number { valueToRange(value: number): number {
if (this.toggled_property) return value === 0 ? 0 : 1; if (this.toggled_property) return value === 0 ? 0 : 1;
if (this.integer_property || this.enumeration_property) { if (this.integer_property || this.enumeration_property) {
value = Math.round(value); 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 > 1) range = 1;
if (range < 0) range = 0; if (range < 0) range = 0;
return range; return range;
} }
@@ -852,17 +882,67 @@ export class UiControl implements Deserializable<UiControl> {
if (range > 1) range = 1; if (range > 1) range = 1;
if (this.toggled_property) return range === 0 ? 0 : 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.min_value === this.max_value) {
if (this.integer_property || this.enumeration_property) { value = this.min_value;
value = Math.round(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; return value;
} }
clampValue(value: number): number { clampValue(value: number): number {
return this.rangeToValue(this.valueToRange(value)); 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 { formatDisplayValue(value: number): string {
if (this.integer_property) { if (this.integer_property) {
value = Math.round(value); value = Math.round(value);
@@ -893,56 +973,9 @@ export class UiControl implements Deserializable<UiControl> {
return semitone12TETValue(value); return semitone12TETValue(value);
} }
let text = this.formatShortValue(value); return this.formatShortValue(value) + this.getDisplayUnits();
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;
} }
formatShortValue(value: number): string { formatShortValue(value: number): string {
if (this.enumeration_property) { if (this.enumeration_property) {
for (let i = 0; i < this.scale_points.length; ++i) { for (let i = 0; i < this.scale_points.length; ++i) {
+40 -10
View File
@@ -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 // 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 // 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 { createStyles } from './WithStyles';
import Snackbar from '@mui/material/Snackbar'; import Snackbar from '@mui/material/Snackbar';
import { UiPlugin } from './Lv2Plugin'; import { UiPlugin, UiControl } from './Lv2Plugin';
import MenuItem from '@mui/material/MenuItem'; import MenuItem from '@mui/material/MenuItem';
import Select from '@mui/material/Select'; import Select from '@mui/material/Select';
@@ -96,6 +97,7 @@ interface MidiBindingViewProps extends WithStyles<typeof styles> {
midiBinding: MidiBinding; midiBinding: MidiBinding;
midiControlType: MidiControlType; midiControlType: MidiControlType;
canDoTapTempo: boolean; canDoTapTempo: boolean;
uiControl?: UiControl;
onChange: (instanceId: number, newBinding: MidiBinding) => void; onChange: (instanceId: number, newBinding: MidiBinding) => void;
} }
@@ -188,15 +190,28 @@ const MidiBindingView =
this.props.onChange(this.props.instanceId, newBinding); this.props.onChange(this.props.instanceId, newBinding);
} }
handleMinChange(value: number): void { handleMinChange(value: number): void {
if (!this.props.uiControl) return;
let newBinding = this.props.midiBinding.clone(); let newBinding = this.props.midiBinding.clone();
newBinding.minValue = value; newBinding.minValue = this.props.uiControl.valueToRange(value) ;
this.props.onChange(this.props.instanceId, newBinding); this.props.onChange(this.props.instanceId, newBinding);
} }
handleMaxChange(value: number): void { handleMaxChange(value: number): void {
if (!this.props.uiControl) return;
let newBinding = this.props.midiBinding.clone(); 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); 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 { handleCtlMinChange(value: number): void {
let newBinding = this.props.midiBinding.clone(); let newBinding = this.props.midiBinding.clone();
newBinding.minControlValue = Math.round(value); 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 (
<div style={{ display: "flex", flexFlow: "row wrap", alignItems: "center" }}> <div style={{ display: "flex", flexFlow: "row wrap", alignItems: "center" }}>
<div className={classes.controlDiv}> <div className={classes.controlDiv}>
<Typography noWrap display="inline">Min Val:&nbsp;</Typography> <Typography noWrap display="inline">Min Val:&nbsp;</Typography>
<NumericInput value={midiBinding.minValue} ariaLabel='min' <NumericInput value={this.props.uiControl?.rangeToValue(midiBinding.minValue) ?? 0} ariaLabel='min'
min={0} max={1} onChange={(value) => { this.handleMinChange(value); }} min={displayMin} max={displayMax} integer={isInteger}
onChange={(value) => { this.handleMinChange(value); }}
/> />
{unit !== "" && (
<Typography noWrap display="inline">&nbsp;{unit.trim()}</Typography>
)}
</div> </div>
<div className={classes.controlDiv}> <div className={classes.controlDiv}>
<Typography noWrap display="inline">Max Val:&nbsp;</Typography> <Typography noWrap display="inline">Max Val:&nbsp;</Typography>
<NumericInput value={midiBinding.maxValue} ariaLabel='max' <NumericInput value={this.props.uiControl?.rangeToValue(midiBinding.maxValue) ?? 0} ariaLabel='max'
min={0} max={1} onChange={(value) => { this.handleMaxChange(value); }} /> min={displayMin} max={displayMax} integer={isInteger}
onChange={(value) => { this.handleMaxChange(value); }} />
{unit !== "" && (
<Typography noWrap display="inline">&nbsp;{unit.trim()}</Typography>
)}
</div> </div>
</div> </div>
) );
})()
} }
{ {
canRotaryScale && ( canRotaryScale && (
+2
View File
@@ -214,6 +214,7 @@ export const MidiBindingDialog =
midiControlType={ getMidiControlType(plugin, "__bypass") midiControlType={ getMidiControlType(plugin, "__bypass")
} }
canDoTapTempo={false } canDoTapTempo={false }
uiControl={plugin.getControl("__bypass")}
onChange={(instanceId: number, newItem: MidiBinding) => this.handleItemChanged(instanceId, newItem)} onChange={(instanceId: number, newItem: MidiBinding) => this.handleItemChanged(instanceId, newItem)}
/> />
</td> </td>
@@ -258,6 +259,7 @@ export const MidiBindingDialog =
midiControlType={getMidiControlType(plugin, symbol)} midiControlType={getMidiControlType(plugin, symbol)}
midiBinding={item.getMidiBinding(symbol)} midiBinding={item.getMidiBinding(symbol)}
canDoTapTempo={canDoTapTempo(plugin, symbol)} canDoTapTempo={canDoTapTempo(plugin, symbol)}
uiControl={control}
onChange={(instanceId: number, newItem: MidiBinding) => this.handleItemChanged(instanceId, newItem)} onChange={(instanceId: number, newItem: MidiBinding) => this.handleItemChanged(instanceId, newItem)}
/> />
</td> </td>
+8 -103
View File
@@ -862,7 +862,7 @@ class FilmstripControl implements ModGuiControl {
console.error("pipedal: Invalid mod-widget-rotation value: " + rotationAttr); console.error("pipedal: Invalid mod-widget-rotation value: " + rotationAttr);
return; 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)`; this.frameElement.style.transform = `rotate(${rotation * range - rotation / 2}deg)`;
return; return;
@@ -926,13 +926,13 @@ class FilmstripControl implements ModGuiControl {
let pluginControl = this.props.pluginControl; let pluginControl = this.props.pluginControl;
let value = this.isPointerDown ? this.pointerDownValue : this.value; let value = this.isPointerDown ? this.pointerDownValue : this.value;
value = this.clampValue(value); value = this.props.pluginControl.clampValue(value);
let isHorizontalStrip = let isHorizontalStrip =
this.filmstripInfo.frameWidth / this.filmstripInfo.frameHeight this.filmstripInfo.frameWidth / this.filmstripInfo.frameHeight
< this.filmstripInfo.width / this.filmstripInfo.height; < this.filmstripInfo.width / this.filmstripInfo.height;
if (isHorizontalStrip) { if (isHorizontalStrip) {
let range = this.valueToRange(value); let range = this.props.pluginControl.valueToRange(value);
if (range < 0) { if (range < 0) {
range = 0; range = 0;
@@ -1045,101 +1045,6 @@ class FilmstripControl implements ModGuiControl {
this.pointerDownValue = this.value; 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) { handlePointerMove(event: PointerEvent) {
if (!this.frameElement) { if (!this.frameElement) {
return; // Not mounted yet return; // Not mounted yet
@@ -1167,14 +1072,14 @@ class FilmstripControl implements ModGuiControl {
let dRange = -dy / rate; let dRange = -dy / rate;
let range = this.valueToRange(this.pointerDownValue); let range = this.props.pluginControl.valueToRange(this.pointerDownValue);
range += dRange; range += dRange;
if (range < 0) range = 0; if (range < 0) range = 0;
if (range > 1) range = 1; if (range > 1) range = 1;
let newValue = this.rangeToValue(range); let newValue = this.props.pluginControl.rangeToValue(range);
this.pointerDownValue = newValue; this.pointerDownValue = newValue;
this.setControlValue(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) { handlePointerUp(event: PointerEvent) {
let index = this.pointerIds.indexOf(event.pointerId); let index = this.pointerIds.indexOf(event.pointerId);
@@ -1229,11 +1134,11 @@ class FilmstripControl implements ModGuiControl {
} }
let dRange = event.deltaY / rate; let dRange = event.deltaY / rate;
let range = this.valueToRange(this.value); let range = this.props.pluginControl.valueToRange(this.value);
range += dRange; range += dRange;
if (range < 0) range = 0; if (range < 0) range = 0;
if (range > 1) range = 1; if (range > 1) range = 1;
let newValue = this.rangeToValue(range); let newValue = this.props.pluginControl.rangeToValue(range);
this.setControlValue(newValue); this.setControlValue(newValue);
this.props.onValueChanged(this.props.instanceId, this.props.pluginControl.symbol, newValue); this.props.onValueChanged(this.props.instanceId, this.props.pluginControl.symbol, newValue);
} }
+45 -2
View File
@@ -59,7 +59,13 @@ export class ControlValue implements Deserializable<ControlValue> {
} }
export class PedalboardItem implements Deserializable<PedalboardItem> { export class PedalboardItem implements Deserializable<PedalboardItem> {
clone() { clone(): PedalboardItem {
if (this.isSplit())
{
let result = new PedalboardSplitItem();
result.deserialize(this);
return result as PedalboardItem;;
}
return new PedalboardItem().deserialize(this); return new PedalboardItem().deserialize(this);
} }
deserializePedalboardItem(input: any): PedalboardItem { deserializePedalboardItem(input: any): PedalboardItem {
@@ -144,6 +150,23 @@ export class PedalboardItem implements Deserializable<PedalboardItem> {
} }
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 { getControlValue(key: string): number {
for (let i = 0; i < this.controlValues.length; ++i) { for (let i = 0; i < this.controlValues.length; ++i) {
let v = this.controlValues[i]; let v = this.controlValues[i];
@@ -753,7 +776,20 @@ export class Pedalboard implements Deserializable<Pedalboard> {
} }
return false; 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) replaceItem(instanceId: number, newItem: PedalboardItem)
{ {
newItem.instanceId = ++this.nextInstanceId; newItem.instanceId = ++this.nextInstanceId;
@@ -763,6 +799,13 @@ export class Pedalboard implements Deserializable<Pedalboard> {
{ {
throw new PiPedalArgumentError("instanceId not found."); 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; return newItem.instanceId;
} }
private _addItem(items: PedalboardItem[], newItem: PedalboardItem, instanceId: number, append: boolean) private _addItem(items: PedalboardItem[], newItem: PedalboardItem, instanceId: number, append: boolean)
+93 -45
View File
@@ -147,24 +147,36 @@ const pedalboardStyles = (theme: Theme) => createStyles({
top: -6, top: -6,
fill: theme.palette.text.secondary fill: theme.palette.text.secondary
}), }),
iconFrame: css({
display: "flex", pedalButton: css({
alignItems: "center",
justifyContent: "center",
position: "relative", position: "relative",
background: theme.palette.background.default,
marginLeft: (CELL_WIDTH - FRAME_SIZE) / 2, marginLeft: (CELL_WIDTH - FRAME_SIZE) / 2,
marginRight: (CELL_WIDTH - FRAME_SIZE) / 2, marginRight: (CELL_WIDTH - FRAME_SIZE) / 2,
marginTop: (CELL_HEIGHT - FRAME_SIZE) / 2, marginTop: (CELL_HEIGHT - FRAME_SIZE) / 2,
marginBottom: (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, width: FRAME_SIZE,
height: FRAME_SIZE, height: FRAME_SIZE,
borderColor: "#777", borderColor: "#777",
borderWidth: 2, borderWidth: 2,
borderStyle: "solid", borderStyle: "solid",
padding: 1, overflow: "hidden",
padding: 0,
borderRadius: 8 borderRadius: 8
}), }),
selectedIconFrame: css({ selectedIconFrame: css({
@@ -173,12 +185,7 @@ const pedalboardStyles = (theme: Theme) => createStyles({
alignItems: "center", alignItems: "center",
justifyContent: "center", justifyContent: "center",
position: "relative", position: "relative",
background: theme.palette.background.paper,
background: theme.palette.background.default,
marginLeft: (CELL_WIDTH - FRAME_SIZE) / 2,
marginRight: (CELL_WIDTH - FRAME_SIZE) / 2,
marginTop: (CELL_HEIGHT - FRAME_SIZE) / 2,
marginBottom: (CELL_HEIGHT - FRAME_SIZE) / 2,
width: FRAME_SIZE, width: FRAME_SIZE,
height: FRAME_SIZE, height: FRAME_SIZE,
borderColor: theme.palette.primary.main, borderColor: theme.palette.primary.main,
@@ -195,10 +202,6 @@ const pedalboardStyles = (theme: Theme) => createStyles({
justifyContent: "center", justifyContent: "center",
background: "transparent", 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, width: FRAME_SIZE,
height: FRAME_SIZE, height: FRAME_SIZE,
border: "0px #666 solid", border: "0px #666 solid",
@@ -458,6 +461,20 @@ const PedalboardView =
// touchyMove. :-/ // 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) { onDragEnd(instanceId: number, clientX: number, clientY: number) {
if (!this.props.enableStructureEditing) { if (!this.props.enableStructureEditing) {
return; return;
@@ -483,10 +500,18 @@ const PedalboardView =
if (item.isSplitter() && item.pedalItem) { if (item.isSplitter() && item.pedalItem) {
if (item.bounds.contains(clientX, clientY)) { if (item.bounds.contains(clientX, clientY)) {
if (clientX < item.bounds.x + CELL_WIDTH / 2) { if (clientX < item.bounds.x + CELL_WIDTH / 2) {
if (this.isSplitterChild(item.pedalItem, instanceId))
{
return;
}
this.model.movePedalboardItemBefore(instanceId, item.pedalItem.instanceId); this.model.movePedalboardItemBefore(instanceId, item.pedalItem.instanceId);
this.setSelection(instanceId); this.setSelection(instanceId);
return; return;
} else if (clientX > item.bounds.right - CELL_WIDTH / 2) { } else if (clientX > item.bounds.right - CELL_WIDTH / 2) {
if (this.isSplitterChild(item.pedalItem, instanceId))
{
return;
}
this.model.movePedalboardItemAfter(instanceId, item.pedalItem.instanceId); this.model.movePedalboardItemAfter(instanceId, item.pedalItem.instanceId);
this.setSelection(instanceId); this.setSelection(instanceId);
return; return;
@@ -501,6 +526,11 @@ const PedalboardView =
if (clientX < item.topChildren[0].bounds.x) { if (clientX < item.topChildren[0].bounds.x) {
let topPedalItem = item.topChildren[0].pedalItem; let topPedalItem = item.topChildren[0].pedalItem;
if (topPedalItem) { if (topPedalItem) {
if (this.isSplitterChild(topPedalItem, instanceId))
{
return;
}
this.model.movePedalboardItemBefore(instanceId, topPedalItem.instanceId); this.model.movePedalboardItemBefore(instanceId, topPedalItem.instanceId);
this.setSelection(instanceId); this.setSelection(instanceId);
return; return;
@@ -509,6 +539,9 @@ const PedalboardView =
let lastTop = item.topChildren[item.topChildren.length - 1]; let lastTop = item.topChildren[item.topChildren.length - 1];
if (clientX >= lastTop.bounds.right && clientX < item.bounds.right - CELL_WIDTH / 2) { if (clientX >= lastTop.bounds.right && clientX < item.bounds.right - CELL_WIDTH / 2) {
if (lastTop.pedalItem) { if (lastTop.pedalItem) {
if (this.isSplitterChild(lastTop.pedalItem, instanceId)) {
return;
}
this.model.movePedalboardItemAfter(instanceId, lastTop.pedalItem.instanceId); this.model.movePedalboardItemAfter(instanceId, lastTop.pedalItem.instanceId);
this.setSelection(instanceId); this.setSelection(instanceId);
return; return;
@@ -522,6 +555,9 @@ const PedalboardView =
if (clientX < item.bottomChildren[0].bounds.x) { if (clientX < item.bottomChildren[0].bounds.x) {
let bottomPedalItem = item.bottomChildren[0].pedalItem; let bottomPedalItem = item.bottomChildren[0].pedalItem;
if (bottomPedalItem) { if (bottomPedalItem) {
if (this.isSplitterChild(bottomPedalItem, instanceId)) {
return;
}
this.model.movePedalboardItemBefore(instanceId, bottomPedalItem.instanceId); this.model.movePedalboardItemBefore(instanceId, bottomPedalItem.instanceId);
this.setSelection(instanceId); this.setSelection(instanceId);
return; return;
@@ -530,6 +566,9 @@ const PedalboardView =
let lastBottom = item.bottomChildren[item.bottomChildren.length - 1]; let lastBottom = item.bottomChildren[item.bottomChildren.length - 1];
if (clientX >= lastBottom.bounds.right && clientX < item.bounds.right - CELL_WIDTH / 2) { if (clientX >= lastBottom.bounds.right && clientX < item.bounds.right - CELL_WIDTH / 2) {
if (lastBottom.pedalItem) { if (lastBottom.pedalItem) {
if (this.isSplitterChild(lastBottom.pedalItem, instanceId)) {
return;
}
this.model.movePedalboardItemAfter(instanceId, lastBottom.pedalItem.instanceId); this.model.movePedalboardItemAfter(instanceId, lastBottom.pedalItem.instanceId);
this.setSelection(instanceId); this.setSelection(instanceId);
return; return;
@@ -552,10 +591,22 @@ const PedalboardView =
if (item.pedalItem) { if (item.pedalItem) {
let margin = (CELL_WIDTH - FRAME_SIZE) / 2; let margin = (CELL_WIDTH - FRAME_SIZE) / 2;
if (clientX < item.bounds.x + margin) { if (clientX < item.bounds.x + margin) {
if (this.isSplitterChild(item.pedalItem,instanceId))
{
return;
}
this.model.movePedalboardItemBefore(instanceId, item.pedalItem.instanceId); this.model.movePedalboardItemBefore(instanceId, item.pedalItem.instanceId);
} else if (clientX > item.bounds.right - margin) { } else if (clientX > item.bounds.right - margin) {
if (this.isSplitterChild(item.pedalItem,instanceId))
{
return;
}
this.model.movePedalboardItemAfter(instanceId, item.pedalItem.instanceId); this.model.movePedalboardItemAfter(instanceId, item.pedalItem.instanceId);
} else { } else {
if (this.isSplitterChild(item.pedalItem,instanceId))
{
return;
}
this.model.movePedalboardItem(instanceId, item.pedalItem.instanceId); this.model.movePedalboardItem(instanceId, item.pedalItem.instanceId);
} }
this.setSelection(instanceId); this.setSelection(instanceId);
@@ -1036,39 +1087,35 @@ const PedalboardView =
} }
return ( return (
<div className={frameStyle} onContextMenu={(e) => { e.preventDefault(); }} <div style={{ width: "100%", height: "100%" }}>
> <ButtonBase className={classes.pedalButton}
{/* {!enabled && (
<div className={classes.midiConnectorDecoration} >
<CloseIcon style={{ width: 16, height: 16, opacity: 0.6, fill: this.props.theme.palette.text.secondary }} />
</div>
)} */}
<ButtonBase style={{ width: "100%", height: "100%" }}
onClick={(e) => { this.onItemClick(e, instanceId); }} onClick={(e) => { this.onItemClick(e, instanceId); }}
onDoubleClick={(e: SyntheticEvent) => { this.onItemDoubleClick(e, instanceId); }} onDoubleClick={(e: SyntheticEvent) => { this.onItemDoubleClick(e, instanceId); }}
onContextMenu={(e: SyntheticEvent) => { this.onItemLongClick(e, instanceId); }} onContextMenu={(e: SyntheticEvent) => { this.onItemLongClick(e, instanceId); }}
> >
<SelectHoverBackground selected={instanceId === this.props.selectedId} showHover={true} <div className={frameStyle} style={{ position: "absolute" }} onContextMenu={(e) => { e.preventDefault(); }}
clipChildren={true}
> >
<Draggable draggable={draggable && (this.props.enableStructureEditing)} getScrollContainer={() => this.getScrollContainer()} <SelectHoverBackground selected={instanceId === this.props.selectedId} showHover={true}
onDragEnd={(x, y) => { this.onDragEnd(instanceId, x, y) }} clipChildren={false}
style={{ opacity: enabled ? 0.99 : 0.3 }}
> >
<div id="childIcon" style={{ position: "relative", display: "flex", justifyContent: "center", alignItems: "center" }} > </SelectHoverBackground>
<PluginIcon pluginType={iconType} </div>
size={24} <Draggable draggable={draggable && (this.props.enableStructureEditing)} getScrollContainer={() => this.getScrollContainer()}
color={getIconColor(iconColor)} onDragEnd={(x, y) => { this.onDragEnd(instanceId, x, y) }}
pluginMissing={pluginNotFound} style={{ opacity: enabled ? 0.99 : 0.3 }}
/>
</div>
</Draggable> >
</SelectHoverBackground> <div id="childIcon" style={{ position: "relative", display: "flex", justifyContent: "center", alignItems: "center" }} >
<PluginIcon pluginType={iconType}
size={24}
color={getIconColor(iconColor)}
pluginMissing={pluginNotFound}
/>
</div>
</Draggable>
</ButtonBase> </ButtonBase>
</div> </div>
); );
@@ -1146,7 +1193,7 @@ const PedalboardView =
result.push(<div key={this.renderKey++} className={classes.splitItem} style={{ left: item.bounds.x, top: item.bounds.y, width: item.bounds.width }} > result.push(<div key={this.renderKey++} className={classes.splitItem} style={{ left: item.bounds.x, top: item.bounds.y, width: item.bounds.width }} >
<div className={classes.splitStart} > <div className={classes.splitStart} >
{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)}
</div> </div>
</div>); </div>);
@@ -1157,9 +1204,10 @@ const PedalboardView =
position: "absolute", left: item.bounds.x, width: CELL_WIDTH, top: item.bounds.bottom - 12, paddingLeft: 2, paddingRight: 2 position: "absolute", left: item.bounds.x, width: CELL_WIDTH, top: item.bounds.bottom - 12, paddingLeft: 2, paddingRight: 2
}}> }}>
<Typography variant="caption" display="block" noWrap={true} <Typography variant="caption" display="block" noWrap={true}
style={{ width: CELL_WIDTH - 4, textAlign: "center", flex: "0 1 auto", style={{
opacity: item.pedalItem?.isEnabled??true ? 1.0 : 0.4 width: CELL_WIDTH - 4, textAlign: "center", flex: "0 1 auto",
}} opacity: item.pedalItem?.isEnabled ?? true ? 1.0 : 0.4
}}
>{item.name}</Typography> >{item.name}</Typography>
</div> </div>
) )
+3 -1
View File
@@ -602,7 +602,9 @@ export class PiPedalModel //implements PiPedalModel
( (
[ [
MidiBinding.systemBinding("prevProgram"), MidiBinding.systemBinding("prevProgram"),
MidiBinding.systemBinding("nextProgram") MidiBinding.systemBinding("nextProgram"),
MidiBinding.systemBinding("prevSnapshot"),
MidiBinding.systemBinding("nextSnapshot")
] ]
); );
zoomedUiControl: ObservableProperty<ZoomedControlInfo | undefined> = new ObservableProperty<ZoomedControlInfo | undefined>(undefined); zoomedUiControl: ObservableProperty<ZoomedControlInfo | undefined> = new ObservableProperty<ZoomedControlInfo | undefined>(undefined);
+12 -106
View File
@@ -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 // 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 // this software and associated documentation files (the "Software"), to deal in
@@ -203,7 +204,6 @@ const CustomPluginControl =
</div> </div>
); );
return (<div />);
} }
}, },
@@ -342,8 +342,9 @@ const PluginControl =
result = this.currentValue; // reset the value! result = this.currentValue; // reset the value!
} }
// clamp and quantize. // clamp and quantize.
let range = this.valueToRange(result); let range = this.props.uiControl?.valueToRange(result) ?? 0;
result = this.rangeToValue(range); result = this.props.uiControl?.rangeToValue(range) ?? 0;
let displayVal = this.props.uiControl?.formatShortValue(result) ?? ""; let displayVal = this.props.uiControl?.formatShortValue(result) ?? "";
if (event.currentTarget) { if (event.currentTarget) {
event.currentTarget.value = displayVal; event.currentTarget.value = displayVal;
@@ -682,8 +683,8 @@ const PluginControl =
this.setState({ previewValue: undefined }); this.setState({ previewValue: undefined });
} }
previewInputValue(value: number, commitValue: boolean) { previewInputValue(value: number, commitValue: boolean) {
let range = this.valueToRange(value); let range = this.props.uiControl?.valueToRange(value) ?? 0;
value = this.rangeToValue(range); value = this.props.uiControl?.rangeToValue(range) ?? 0;
let imgElement = this.imgRef.current let imgElement = this.imgRef.current
if (imgElement) { if (imgElement) {
@@ -712,13 +713,13 @@ const PluginControl =
} }
previewRange(dRange: number, commitValue: boolean): void { 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 > 1) range = 1;
if (range < 0) range = 0; if (range < 0) range = 0;
let value = this.rangeToValue(range); let value = this.props.uiControl?.rangeToValue(range) ?? 0;
// apply value quantization and clipping. // apply value quantization and clipping.
range = this.valueToRange(value); range = this.props.uiControl?.valueToRange(value) ?? 0;
if (this.props.uiControl?.isGraphicEq()) { if (this.props.uiControl?.isGraphicEq()) {
let imgElement = this.imgRef.current let imgElement = this.imgRef.current
@@ -835,89 +836,6 @@ const PluginControl =
if (!uiControl) return ""; if (!uiControl) return "";
return uiControl.formatDisplayValue(value); 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 { rangeToRotationTransform(range: number): string {
@@ -926,22 +844,10 @@ const PluginControl =
} }
getEqPosition(): number { getEqPosition(): number {
let range = 0; return this.props.uiControl?.valueToRange(this.props.value) ?? 0;
let uiControl = this.props.uiControl;
if (uiControl) {
let value = this.props.value;
range = this.valueToRange(value);
}
return range;
} }
getRotationTransform(): string { getRotationTransform(): string {
let range = 0; let range = this.props.uiControl?.valueToRange(this.props.value) ?? 0;
let uiControl = this.props.uiControl;
if (uiControl) {
let value = this.props.value;
range = this.valueToRange(value);
}
return this.rangeToRotationTransform(range); return this.rangeToRotationTransform(range);
} }
+22 -23
View File
@@ -114,7 +114,7 @@ const PluginPresetSelector =
hasPresets(pedalboardItem: PedalboardItem | null): boolean { hasPresets(pedalboardItem: PedalboardItem | null): boolean {
if (pedalboardItem === null) return false; if (pedalboardItem === null) return false;
if (!pedalboardItem.uri) return false; if (!pedalboardItem.uri) return false;
if (pedalboardItem.isStart() || pedalboardItem.isEnd() if (pedalboardItem.isStart() || pedalboardItem.isEnd()
|| pedalboardItem.isEmpty() || pedalboardItem.isSplit()) { || pedalboardItem.isEmpty() || pedalboardItem.isSplit()) {
return false; return false;
} }
@@ -123,8 +123,8 @@ const PluginPresetSelector =
isVisible(pedalboardItem: PedalboardItem | null): boolean { isVisible(pedalboardItem: PedalboardItem | null): boolean {
if (pedalboardItem === null) return false; if (pedalboardItem === null) return false;
if (!pedalboardItem.uri) return false; if (!pedalboardItem.uri) return false;
if (pedalboardItem.isStart() || pedalboardItem.isEnd() if (pedalboardItem.isStart() || pedalboardItem.isEnd()
|| pedalboardItem.isSplit()) { ) {
return false; return false;
} }
return true; return true;
@@ -197,7 +197,7 @@ const PluginPresetSelector =
loadPresets(): void { loadPresets(): void {
if (this.props.pedalboardItem === null) return; 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; let captureUri: string = this.props.pedalboardItem.uri;
this.model.getPluginPresets(captureUri) this.model.getPluginPresets(captureUri)
.then((presets: PluginUiPresets) => { .then((presets: PluginUiPresets) => {
@@ -235,20 +235,18 @@ const PluginPresetSelector =
currentPedalboard?: PedalboardItem; currentPedalboard?: PedalboardItem;
componentDidUpdate( componentDidUpdate(
prevProps: Readonly<PluginPresetSelectorProps>, prevProps: Readonly<PluginPresetSelectorProps>,
prevState: Readonly<PluginPresetSelectorState>, prevState: Readonly<PluginPresetSelectorState>,
snapshot?: any): void snapshot?: any): void {
{
if (this.props.pedalboardItem !== prevProps.pedalboardItem) { if (this.props.pedalboardItem !== prevProps.pedalboardItem) {
this.setState({ this.setState({
isVisible: this.isVisible(this.props.pedalboardItem), 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) {
if (this.props.pedalboardItem.isEmpty()) if (this.props.pedalboardItem.isEmpty()) {
{
this.setState({ presets: new PluginUiPresets() }); this.setState({ presets: new PluginUiPresets() });
} else } else {
{
this.loadPresets(); this.loadPresets();
} }
} }
@@ -272,7 +270,8 @@ const PluginPresetSelector =
} }
isMenuCopyPluginEnabled(): boolean { isMenuCopyPluginEnabled(): boolean {
return this.state.hasPresets; if (this.props.pedalboardItem === null) return false;
return !this.props.pedalboardItem.isEmpty();
} }
handleMenuCopyPlugin(): void { handleMenuCopyPlugin(): void {
this.handlePresetsMenuClose(); this.handlePresetsMenuClose();
@@ -340,7 +339,7 @@ const PluginPresetSelector =
} }
buildMenuItems() { buildMenuItems() {
let result : React.ReactNode[] = []; let result: React.ReactNode[] = [];
if (this.state.hasPresets) { if (this.state.hasPresets) {
result.push((<MenuItem key="menuSaveAs" onClick={(e) => this.handlePluginPresetsMenuSaveAs(e)}>Save plugin preset...</MenuItem>)); result.push((<MenuItem key="menuSaveAs" onClick={(e) => this.handlePluginPresetsMenuSaveAs(e)}>Save plugin preset...</MenuItem>));
result.push((<MenuItem key="menuEdit" onClick={(e) => this.handleMenuEditPluginPresets()}>Manage plugin presets...</MenuItem>)); result.push((<MenuItem key="menuEdit" onClick={(e) => this.handleMenuEditPluginPresets()}>Manage plugin presets...</MenuItem>));
@@ -355,7 +354,7 @@ const PluginPresetSelector =
<PluginPresetIcon className={this.props.classes?.pluginMenuIcon ?? ""} /> <PluginPresetIcon className={this.props.classes?.pluginMenuIcon ?? ""} />
{preset.label}</MenuItem> {preset.label}</MenuItem>
)); ));
hasPresets = true; hasPresets = true;
} }
if (hasPresets) { if (hasPresets) {
@@ -402,12 +401,12 @@ const PluginPresetSelector =
} }
</Menu> </Menu>
{this.state.hasPresets && this.state.showPresetsDialog && ( {this.state.hasPresets && this.state.showPresetsDialog && (
<PluginPresetsDialog <PluginPresetsDialog
instanceId={this.props.instanceId} instanceId={this.props.instanceId}
presets={this.state.presets} presets={this.state.presets}
show={this.state.showPresetsDialog} show={this.state.showPresetsDialog}
isEditDialog={this.state.showEditPresetsDialog} isEditDialog={this.state.showEditPresetsDialog}
onDialogClose={() => this.handleDialogClose()} /> onDialogClose={() => this.handleDialogClose()} />
)} )}
<RenameDialog open={this.state.renameDialogOpen} <RenameDialog open={this.state.renameDialogOpen}
title="Rename" title="Rename"
+2 -2
View File
@@ -40,7 +40,7 @@ import SelectChannelsDialog from './SelectChannelsDialog';
import SelectMidiChannelsDialog from './SelectMidiChannelsDialog'; import SelectMidiChannelsDialog from './SelectMidiChannelsDialog';
import SelectHoverBackground from './SelectHoverBackground'; import SelectHoverBackground from './SelectHoverBackground';
import JackServerSettings from './JackServerSettings'; import JackServerSettings from './JackServerSettings';
import JackServerSettingsDialog from './JackServerSettingsDialog'; import AudioDeviceDialog from './AudioDeviceDialog';
import JackHostStatus from './JackHostStatus'; import JackHostStatus from './JackHostStatus';
import WifiConfigSettings from './WifiConfigSettings'; import WifiConfigSettings from './WifiConfigSettings';
import WifiDirectConfigSettings from './WifiDirectConfigSettings'; import WifiDirectConfigSettings from './WifiDirectConfigSettings';
@@ -711,7 +711,7 @@ const SettingsDialog = withStyles(
)} )}
{this.state.showJackServerSettingsDialog && ( {this.state.showJackServerSettingsDialog && (
<JackServerSettingsDialog <AudioDeviceDialog
open={this.state.showJackServerSettingsDialog} open={this.state.showJackServerSettingsDialog}
jackServerSettings={this.state.jackServerSettings} jackServerSettings={this.state.jackServerSettings}
onClose={() => this.setState({ showJackServerSettingsDialog: false })} onClose={() => this.setState({ showJackServerSettingsDialog: false })}
@@ -159,6 +159,11 @@ export const SystemMidiBindingDialog =
} }
else if (item.symbol === "snapshot6") { else if (item.symbol === "snapshot6") {
displayName = "Snapshot 6"; displayName = "Snapshot 6";
}
else if (item.symbol === "nextSnapshot") {
displayName = "Next Snapshot";
} else if (item.symbol === "prevSnapshot") {
displayName = "Previous Snapshot";
} else if (item.symbol === "startHotspot") { } else if (item.symbol === "startHotspot") {
displayName = "Enable Hotspot"; displayName = "Enable Hotspot";
} else if (item.symbol === "stopHotspot") { } else if (item.symbol === "stopHotspot") {
+1 -1
View File
@@ -20,7 +20,7 @@ export default defineConfig({
target: 'http://localhost:8080', target: 'http://localhost:8080',
changeOrigin: false, changeOrigin: false,
}, },
'^/var/(?!config\\.json$).*': { '^/var/.*': {
target: 'http://localhost:8080', target: 'http://localhost:8080',
changeOrigin: false, changeOrigin: false,
}, },