Merge pull request #435 from rerdavies/dev

v1.5.95 dev merge
This commit is contained in:
Robin Davies
2026-01-12 09:30:04 -05:00
committed by GitHub
58 changed files with 433 additions and 110 deletions
+2 -2
View File
@@ -1,13 +1,13 @@
cmake_minimum_required(VERSION 3.16.0)
project(pipedal
VERSION 1.5.94
VERSION 1.5.95
DESCRIPTION "PiPedal Guitar Effect Pedal For Raspberry Pi"
HOMEPAGE_URL "https://rerdavies.github.io/pipedal"
)
execute_process( COMMAND dpkg --print-architecture COMMAND tr -d '\n' OUTPUT_VARIABLE DEBIAN_ARCHITECTURE )
set (DISPLAY_VERSION "PiPedal v1.5.94-Experimental")
set (DISPLAY_VERSION "PiPedal v1.5.95-Experimental")
set (PACKAGE_ARCHITECTURE ${DEBIAN_ARCHITECTURE})
set (CMAKE_INSTALL_PREFIX "/usr/")
+3
View File
@@ -704,6 +704,9 @@ namespace pipedal
queueId = -1;
throw std::runtime_error(SS("Failed to set port timestamping: " << snd_strerror(rc)));
}
// Flush the output buffer to start the queue and apply port settings
snd_seq_drain_output(seqHandle);
}
return queueId;
+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"/>
Download:&nbsp;<a href='https://rerdavies.github.io/pipedal/download.html'>v1.5.94</a>
Download:&nbsp;<a href='https://rerdavies.github.io/pipedal/download.html'>v1.5.95</a>
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).
#### NEW version 1.5.94 Release. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details. TooB NAM calibration, manipulation of i/r files in Toob Convolution Reverb, new Parametric EQ and 3 Band EQ plugins, and MORE!
#### NEW version 1.5.95 Release. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details. TooB NAM calibration, manipulation of i/r files in Toob Convolution Reverb, new Parametric EQ and 3 Band EQ plugins, and MORE!
&nbsp;
+5 -1
View File
@@ -4,7 +4,11 @@
set -e
# clean build
rm -rf build
if [ "$1" != "--continue" ]; then
rm -rf build
fi
# configure for release
mkdir -p build
+5 -5
View File
@@ -13,18 +13,18 @@ page_icon: img/Install4.jpg
Download the most recent Debian (.deb) package for your platform:
- [Raspberry Pi OS bookworm (aarch64) v1.5.94](https://github.com/rerdavies/pipedal/releases/download/v1.5.94/pipedal_1.5.94_arm64.deb)
- [Ubuntu 24.04 through 25.04 (aarch64) v1.5.94](https://github.com/rerdavies/pipedal/releases/download/v1.5.94/pipedal_1.5.94_arm64.deb)
- [Ubuntu 24.04 through 25.04 (amd64) v1.5.94](https://github.com/rerdavies/pipedal/releases/download/v1.5.94/pipedal_1.5.94_amd64.deb)
- [Raspberry Pi OS bookworm (aarch64) v1.5.95](https://github.com/rerdavies/pipedal/releases/download/v1.5.95/pipedal_1.5.95_arm64.deb)
- [Ubuntu 24.04 through 25.04 (aarch64) v1.5.95](https://github.com/rerdavies/pipedal/releases/download/v1.5.95/pipedal_1.5.95_arm64.deb)
- [Ubuntu 24.04 through 25.04 (amd64) v1.5.95](https://github.com/rerdavies/pipedal/releases/download/v1.5.95/pipedal_1.5.95_amd64.deb)
Version 1.5.94 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 1.5.95 has been tested on Raspberry Pi OS bookworm, Ubuntu 24.04 (amd64), Ubuntu 24.10 (aarch64), and Ubuntu 25.04 (aarch64). Download the appropriate package for your platform, and install using the following procedure:
```
sudo apt update
sudo apt upgrade
cd ~/Downloads
sudo apt-get install ./pipedal_1.5.94_arm64.deb
sudo apt-get install ./pipedal_1.5.95_arm64.deb
```
You MUST use `apt-get`. `apt` will not install downloaded packages; and `dpkg -i` will not install dependencies.
+17 -1
View File
@@ -1,5 +1,21 @@
# Release Notes
## PiPedal 1.5.95 Beta
Usability tweaks that have been blocked by a pending major feature, that needed to be released to satisfy commitments made to users. Now pushed out for general use.
### Major features
- **Tap Tempo MIDI Bindings.** Use MIDI events to set time-based controls for any plugin using tap tempo. The Tap Tempo MIDI binding is only visible for controls that have been declared as having units of seconds, milliseconds, Hz, or BPM by their owning plugin, and also have value ranges that are suitable for tap tempo controls.<br/>
To use Tap Tempo, open the _Midi Bindings_ dialog, and scroll to find a suitable control. If the control supports Tap Tempo, you will find a _Tap Tempo_ entry in the dropdown that controls the event type.<br/>
Consecutive MIDI Note ON events are measured, and the target control will be set to match the measured time between events, as long as the time fits within valid value ranges for the control (again, as declared by the owning plugin).<br/>
Tap Tempo is only implemented for MIDI bindings, because the HTML event system used in the PiPedal UI is not responsive enough to implement usable tap-tempo control in the UI.
### Minor Features
- **Mix Plugin Phase Controls**. The TooB Mix plugin has new controls that allow independent phase inversion of left and right input channels.
- The maximum delay for the TooB Delay plugin has been increased from 1.2 seconds to 3 seconds, in order to make it suitable for use with the new Tap Tempo feature.
## PiPedal 1.5.94 Beta
A minor release that allows use of plugins with audio sidechains.
@@ -8,7 +24,7 @@ A minor release that allows use of plugins with audio sidechains.
- **Sidechains**. You can now load plugins that have sidechain inputs. This allows you to use compressor and gate plugins, particularly, that often have sidechains, but also enables a broad class of dynamics processing plugins that would not be loadable in previous versions of PiPedal. Plugins that have sidechain inputs will show a dropdown control in their control view which allows you to select the source of the sidechain input. The source plugin for the sidechain must appear earlier in the signal chain.
Although sidechain processing is occasionally useful in guitar effect pedalboards, the more important part of this feature is that it allows you to load plugins that were rejected by previous versions of PiPedal because they had optional sidechain inputs.
Although sidechain processing is occasionally useful in guitar effect pedalboards, the more important part of this feature is that it allows you to load third-part plugins (compressors, particularly) that were ignored by previous versions of PiPedal because they had optional sidechain inputs.
## PiPedal 1.5.93 Release
+1 -1
View File
@@ -16,7 +16,7 @@ You can create up to six snapshots for any given preset. A snapshot differs from
- Loading a snapshot is much faster than loading a preset, because new plugins don't have to be created.
- You can configure PiPedal to switch between snapshots in response to MIDI messages from a MIDI foot controller or other device.`
Snapshots are useful if you have a USB or MIDI foot controller. You can configure PiPedal to switch between snapshots when you step on buttons on your foot controller. To configure MIDI bindings for snapshots, select <b><i>Settings</i></b> from the main menu, and tap on <b><i>System MIDI bindings</i></b>.
Snapshots are particularly useful if you have a USB or MIDI foot controller. You can configure PiPedal to switch between snapshots when you step on buttons on your foot controller. To configure MIDI bindings for snapshots, select <b><i>Settings</i></b> from the main menu, and tap on <b><i>System MIDI bindings</i></b>.
But snapshots are equally useful when you are using PiPedal's <b><i>Performance View</i></b>. To access the <b><i>Performance View</i></b> click on the <b><i>Performance View</i></b> menu item in the main PiPedal menu.
+4 -4
View File
@@ -4,9 +4,9 @@
Download the most recent Debian (.deb) package for your platform:
- [Raspberry Pi OS bookworm (aarch64) v1.5.94 Beta](https://github.com/rerdavies/pipedal/releases/download/v1.5.94/pipedal_1.5.94_arm64.deb)
- [Ubuntu 24.x, 25.04 (aarch64) v1.5.94 Alpha](https://github.com/rerdavies/pipedal/releases/download/v1.5.94/pipedal_1.5.94_arm64.deb)
- [Ubuntu 24.x, 25.04 (amd64) v1.5.94 Alpha](https://github.com/rerdavies/pipedal/releases/download/v1.5.94/pipedal_1.5.94_amd64.deb)
- [Raspberry Pi OS bookworm (aarch64) v1.5.95 Beta](https://github.com/rerdavies/pipedal/releases/download/v1.5.95/pipedal_1.5.95_arm64.deb)
- [Ubuntu 24.x, 25.04 (aarch64) v1.5.95 Alpha](https://github.com/rerdavies/pipedal/releases/download/v1.5.95/pipedal_1.5.95_arm64.deb)
- [Ubuntu 24.x, 25.04 (amd64) v1.5.95 Alpha](https://github.com/rerdavies/pipedal/releases/download/v1.5.95/pipedal_1.5.95_amd64.deb)
Install the package by running
@@ -14,7 +14,7 @@ Install the package by running
```
sudo apt update
cd ~/Downloads
sudo apt-get install ./pipedal_1.5.94_arm64.deb
sudo apt-get install ./pipedal_1.5.95_arm64.deb
```
You MUST use `apt-get` to install the package. `apt install` will NOT install the package correctly. The message about missing permissions given by `apt-get` is
expected, and can be safely ignored.
+1 -1
View File
@@ -93,7 +93,7 @@ cabir:impulseFile3
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
rdfs:comment """
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:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
mod:brand "TooB";
mod:label "TooB CabSim";
@@ -53,7 +53,7 @@ toobimpulse:impulseFile
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
rdfs:comment """
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:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
rdfs:comment """
+1 -1
View File
@@ -66,7 +66,7 @@ inputStage:filterGroup
doap:license <https://two-play.com/TooB/licenses/isc> ;
doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
mod:brand "TooB";
mod:label "TooB Input";
+1 -1
View File
@@ -68,7 +68,7 @@ pstage:stage3
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
mod:brand "TooB";
mod:label "Power Stage";
+1 -1
View File
@@ -57,7 +57,7 @@
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
rdfs:comment "TooB spectrum analyzer" ;
mod:brand "TooB";
+2 -2
View File
@@ -57,7 +57,7 @@ tonestack:eqGroup
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
uiext:ui <http://two-play.com/plugins/toob-tone-stack-ui>;
@@ -121,7 +121,7 @@ tone stack used in Polytone and HiWatt amps.
lv2:maximum 2.0;
lv2:portProperty lv2:enumeration ;
lv2:scalePoint [
lv2:scalePoint [
rdfs:label "Bassman" ;
rdf:value 0.0
] , [
+1 -1
View File
@@ -52,7 +52,7 @@ toob:frequencyResponseVector
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
uiext:ui <http://two-play.com/plugins/toob-three-band-eq-ui>;
Binary file not shown.
+1 -1
View File
@@ -40,7 +40,7 @@
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
rdfs:comment """
Emulation of a Boss CE-2 Chorus.
+2 -2
View File
@@ -41,7 +41,7 @@
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
rdfs:comment """
A straightforward no-frills digital delay.
@@ -61,7 +61,7 @@ A straightforward no-frills digital delay.
lv2:name "Delay";
lv2:default 340 ;
lv2:minimum 5;
lv2:maximum 1200;
lv2:maximum 3000;
units:unit units:ms;
lv2:portProperty <http://lv2plug.in/ns/ext/port-props#logarithmic>;
rdfs:comment "Delay in ms";
+1 -1
View File
@@ -41,7 +41,7 @@
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
rdfs:comment """
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:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
rdfs:comment """
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:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
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.
+1 -1
View File
@@ -41,7 +41,7 @@
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
rdfs:comment """
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:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 2 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
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:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 2 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
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:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
rdfs:comment """
The TooB ML Amplifier plugin provides emulation of a variety of amplifiers and overdrive pedals that are implemented
using neural-network-based machine learning models of real amplifiers.
+49 -5
View File
@@ -40,7 +40,7 @@
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
rdfs:comment """
Remix a stereo input signal.
@@ -115,31 +115,75 @@ Remix a stereo input signal.
lv2:maximum 1.0;
rdfs:comment "Right Pan";
],
[
a lv2:InputPort ,
lv2:ControlPort ;
lv2:index 4;
lv2:symbol "phaseL" ;
lv2:name "Phase L";
lv2:default 1.0 ;
lv2:minimum 0.0;
lv2:maximum 1.0;
lv2:portProperty lv2:enumeration ;
lv2:scalePoint [
rdfs:label "Inverted" ;
rdf:value 0.0
] , [
rdfs:label "Normal" ;
rdf:value 1.0
];
rdfs:comment "Left phase (pre-mix)";
],
[
a lv2:InputPort ,
lv2:ControlPort ;
lv2:index 5;
lv2:symbol "phaseR" ;
lv2:name "Phase R";
lv2:default 1.0 ;
lv2:minimum 0.0;
lv2:maximum 1.0;
lv2:portProperty lv2:enumeration ;
lv2:scalePoint [
rdfs:label "Inverted" ;
rdf:value 0.0
] , [
rdfs:label "Normal" ;
rdf:value 1.0
];
rdfs:comment "Right phase (pre-mix)";
],
[
a lv2:AudioPort ,
lv2:InputPort ;
lv2:index 4 ;
lv2:index 6 ;
lv2:symbol "inl" ;
lv2:name "In L"
],
[
a lv2:AudioPort ,
lv2:InputPort ;
lv2:index 5 ;
lv2:index 7 ;
lv2:symbol "inr" ;
lv2:name "In R"
],
[
a lv2:AudioPort ,
lv2:OutputPort ;
lv2:index 6 ;
lv2:index 8 ;
lv2:symbol "outl" ;
lv2:name "Out L"
],
[
a lv2:AudioPort ,
lv2:OutputPort ;
lv2:index 7 ;
lv2:index 9 ;
lv2:symbol "outr" ;
lv2:name "OutR"
]
@@ -72,7 +72,7 @@ toobNam:calibrationGroup
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
rdfs:comment """
TooB Neural Amp Modeler is a neural network based Amp Simulator. It uses .nam model files that are generated by training nueral networks
on audio recordings of actual guitar amps, effects pedals and other equipment. .nam files contain data from the trained models, which can be loaded into
+1 -1
View File
@@ -54,7 +54,7 @@ noisegate:envelope_group
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
rdfs:comment """
A noise gate is an audio processing tool that controls the volume of an audio signal
by allowing it to pass through only when it exceeds a set threshold.
+1 -1
View File
@@ -87,7 +87,7 @@ parametric_eq:hfGroup
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
uiext:ui <http://two-play.com/plugins/toob-parametric-eq-ui>;
+1 -1
View File
@@ -40,7 +40,7 @@
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
rdfs:comment """
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:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
rdfs:comment """
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:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 2 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
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:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 2 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
ui:ui <http://two-play.com/plugins/toob-record-stereo-ui>;
+1 -1
View File
@@ -41,7 +41,7 @@
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
rdfs:comment """
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:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
rdfs:comment """
A tremolo effect, equivalent to "vibrato" on vintage Fender tube amps.
+1 -1
View File
@@ -40,7 +40,7 @@
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
rdfs:comment """
TooB Tuner is a chromatic guitar tuner.
""" ;
+1 -1
View File
@@ -39,7 +39,7 @@
doap:license <https://rerdavies.github.io/pipedal/LicenseToobAmp> ;
doap:maintainer <http://two-play.com/rerdavies#me> ;
lv2:minorVersion 0 ;
lv2:microVersion 71 ;
lv2:microVersion 72 ;
rdfs:comment """
Volume control.
Binary file not shown.
Binary file not shown.
+2 -1
View File
@@ -1686,7 +1686,8 @@ namespace pipedal
continue;
}
MidiEvent *pEvent = midiEvents.data() + midiEventCount++;
pEvent->time = audioFrame;
pEvent->timeStamp = MidiTimestamp(message.realtime_sec, message.realtime_nsec);
pEvent->frame = audioFrame;
pEvent->size = messageSize;
pEvent->buffer = midiEventMemory.data() + midiEventMemoryIndex;
+1 -9
View File
@@ -26,6 +26,7 @@
#include "JackConfiguration.hpp"
#include <functional>
#include "AlsaSequencer.hpp"
#include "MidiEvent.hpp"
@@ -35,15 +36,6 @@ namespace pipedal {
using ProcessCallback = std::function<void (size_t)>;
struct MidiEvent
{
uint32_t time; /**< Sample frame at which event is valid */
uint32_t size; /**< Number of bytes of data in \a buffer */
uint8_t *buffer; /**< Raw MIDI data */
};
class AudioDriverHost {
public:
virtual void OnProcess(size_t nFrames) = 0;
+4 -2
View File
@@ -365,6 +365,7 @@ bool SystemMidiBinding::IsMatch(const MidiEvent &event)
switch (currentBinding.bindingType())
{
case BINDING_TYPE_NOTE:
case BINDING_TYPE_TAP_TEMPO:
{
if (event.size != 3)
return false;
@@ -926,7 +927,8 @@ private:
{
// eventBufferWriter.writeMidiEvent(iterator, 0, event.size, event.buffer);
this->realtimeActivePedalboard->OnMidiMessage(event.size, event.buffer, this, fnMidiValueChanged);
this->realtimeActivePedalboard->OnMidiMessage(
event, this, fnMidiValueChanged);
if (listenForMidiEvent)
{
if (event.size >= 3)
@@ -1077,7 +1079,7 @@ private:
int8_t messageCount = deferredMidiMessages[i++];
event.size = messageCount;
event.buffer = deferredMidiMessages + i;
event.time = 0;
event.frame = 0;
ProcessMidiEvent(eventBufferWriter, iterator, event);
+1 -1
View File
@@ -264,7 +264,7 @@ namespace pipedal
continue;
}
MidiEvent *pEvent = midiEvents.data() + midiEventCount++;
pEvent->time = audioFrame;
pEvent->frame = audioFrame;
pEvent->size = messageSize;
pEvent->buffer = midiEventMemory.data() + midiEventMemoryIndex;
+104 -10
View File
@@ -188,13 +188,15 @@ std::vector<float *> Lv2Pedalboard::PrepareItems(
{
pLv2Effect->SetAudioSidechainBuffer(i, this->pedalboardInputBuffers[i]);
}
else
else
{
// just use the first output buffer for all sidechain inputs.
pLv2Effect->SetAudioSidechainBuffer(i, this->pedalboardInputBuffers[0]);
}
}
} else if (item.sideChainInputId() != -1) {
}
else if (item.sideChainInputId() != -1)
{
IEffect *pSideChainInput = GetEffect(item.sideChainInputId());
if (pSideChainInput)
@@ -205,13 +207,15 @@ std::vector<float *> Lv2Pedalboard::PrepareItems(
{
pLv2Effect->SetAudioSidechainBuffer(i, pSideChainInput->GetAudioOutputBuffer(i));
}
else
else
{
// just use the first output buffer for all sidechain inputs.
pLv2Effect->SetAudioSidechainBuffer(i, pSideChainInput->GetAudioOutputBuffer(0));
}
}
} else {
}
else
{
throw std::runtime_error("Internal error: Sidechain input IEffect not found.");
}
}
@@ -418,11 +422,14 @@ void Lv2Pedalboard::PrepareMidiMap(const PedalboardItem &pedalboardItem)
{
mapping.mappingType = MidiControlType::Select;
}
else if (binding.bindingType() == BINDING_TYPE_TAP_TEMPO) {
mapping.mappingType = MidiControlType::TapTempo;
}
else
{
mapping.mappingType = MidiControlType::Dial;
}
if (binding.bindingType() == BINDING_TYPE_NOTE)
if (binding.bindingType() == BINDING_TYPE_NOTE || binding.bindingType() == BINDING_TYPE_TAP_TEMPO)
{
mapping.key = 0x9000 | binding.note(); // i.e. midi note on.
}
@@ -539,6 +546,8 @@ bool Lv2Pedalboard::Run(float **inputBuffers, float **outputBuffers, uint32_t sa
outputBuffers[c][i] = this->pedalboardOutputBuffers[c][i] * volume;
}
}
this->currentFrameOffset += samples;
return true;
}
@@ -715,14 +724,17 @@ void Lv2Pedalboard::GatherPatchProperties(RealtimePatchPropertyRequest *pParamet
}
}
void Lv2Pedalboard::OnMidiMessage(size_t size, uint8_t *message,
void *callbackHandle,
MidiCallbackFn *pfnCallback)
void Lv2Pedalboard::OnMidiMessage(
const MidiEvent&event,
void *callbackHandle,
MidiCallbackFn *pfnCallback)
{
if (midiMappings.size() == 0)
return;
size_t size = event.size;
const uint8_t *message = event.buffer;
if (size < 2)
return;
uint8_t cmd = message[0];
@@ -791,7 +803,7 @@ void Lv2Pedalboard::OnMidiMessage(size_t size, uint8_t *message,
for (int i = min; i < midiMappings.size(); ++i)
{
auto &mapping = midiMappings[i];
MidiMapping &mapping = midiMappings[i];
if (mapping.key != searchKey)
break;
@@ -914,6 +926,11 @@ void Lv2Pedalboard::OnMidiMessage(size_t size, uint8_t *message,
}
break;
}
case MidiControlType::TapTempo:
{
handleTapTempo(value, event.timeStamp, mapping, callbackHandle, pfnCallback);
break;
}
case MidiControlType::None:
default:
break;
@@ -921,4 +938,81 @@ void Lv2Pedalboard::OnMidiMessage(size_t size, uint8_t *message,
}
}
}
}
}
void Lv2Pedalboard::handleTapTempo(uint8_t value, const MidiTimestamp& timestamp, MidiMapping &mapping, void *callbackHandle, MidiCallbackFn *pfnSetControlCallback)
{
if (value != 0) // only on note on
{
if (!mapping.lastTapTimestamp.isEmpty())
{
double seconds = timestamp.timeDiff(mapping.lastTapTimestamp);
if (seconds > 60.0/450.0) // debounce check. (~= 450bpm)
{
auto units = mapping.pPortInfo->units();
float controlValue = -1;
switch (units)
{
case Units::bpm:
{
controlValue = 60.0f / (float)seconds;
break;
}
case Units::hz:
{
controlValue = 1.0f / (float)seconds;
break;
}
case Units::s:
{
controlValue = (float)(seconds);
break;
}
case Units::ms:
{
controlValue = (float)(seconds * 1000.0);
break;
}
default:
{
controlValue = -1;
}
}
if (mapping.pPortInfo->min_value() < mapping.pPortInfo->max_value())
{
if (controlValue < mapping.pPortInfo->min_value())
{
controlValue = -1;
}
else if (controlValue > mapping.pPortInfo->max_value())
{
controlValue = -1;
}
}
else
{
if (controlValue > mapping.pPortInfo->min_value())
{
controlValue = -1;
}
else if (controlValue < mapping.pPortInfo->max_value())
{
controlValue = -1;
}
}
if (controlValue != -1)
{
IEffect *pEffect = this->realtimeEffects[mapping.effectIndex];
if (pEffect->IsLv2Effect())
{
Lv2Effect *pLv2Effect = dynamic_cast<Lv2Effect *>(pEffect);
pEffect->SetControl(mapping.controlIndex, controlValue);
pfnSetControlCallback(callbackHandle, mapping.instanceId, mapping.pPortInfo->index(), controlValue);
}
}
}
}
mapping.lastTapTimestamp = timestamp;
}
}
+20 -8
View File
@@ -19,6 +19,7 @@
#pragma once
#include "Pedalboard.hpp"
#include "MidiEvent.hpp"
#include "PluginHost.hpp"
#include "Lv2Effect.hpp"
#include "BufferPool.hpp"
@@ -49,8 +50,9 @@ namespace pipedal
class Lv2Pedalboard
{
private:
IHost *pHost = nullptr;
size_t currentFrameOffset = 0;
DbDezipper inputVolume;
DbDezipper outputVolume;
@@ -82,7 +84,8 @@ namespace pipedal
Dial,
Toggle,
Trigger,
MomentarySwitch
MomentarySwitch,
TapTempo
};
class MidiMapping
{
@@ -93,6 +96,7 @@ namespace pipedal
int effectIndex = -1;
int controlIndex = -1;
int key; // key to the note or control. internal use only.
MidiTimestamp lastTapTimestamp;
bool hasLastValue = false;
bool lastValueIncreasing = false;
float lastValue = 0;
@@ -119,13 +123,11 @@ namespace pipedal
Lv2Pedalboard() {}
~Lv2Pedalboard() {}
void Prepare(IHost *pHost, Pedalboard &pedalboard, Lv2PedalboardErrorList &errorList, ExistingEffectMap *existingEffects = nullptr);
std::vector<IEffect *> &GetEffects() { return realtimeEffects; }
std::vector<std::shared_ptr<IEffect>> &GetSharedEffectList() { return effects; }
int GetIndexOfInstanceId(uint64_t instanceId)
{
for (int i = 0; i < this->realtimeEffects.size(); ++i)
@@ -149,7 +151,7 @@ namespace pipedal
void Activate();
void Deactivate();
void UpdateAudioPorts();
bool Run(float **inputBuffers, float **outputBuffers, uint32_t samples, RealtimeRingBufferWriter *realtimeWriter);
void ResetAtomBuffers();
@@ -172,9 +174,19 @@ namespace pipedal
float GetControlOutputValue(int effectIndex, int portIndex);
typedef void(MidiCallbackFn)(void *data, uint64_t intanceId, int controlIndex, float value);
void OnMidiMessage(size_t size, uint8_t *data,
void *callbackHandle,
MidiCallbackFn *pfnCallback);
void OnMidiMessage(
const MidiEvent&message,
void *callbackHandle,
MidiCallbackFn *pfnCallback);
private:
void handleTapTempo(
uint8_t value,
const MidiTimestamp& timestamp,
MidiMapping &mapping,
void *callbackHandle,
MidiCallbackFn *pfnCallback);
};
} // namespace
+1
View File
@@ -53,6 +53,7 @@ class MapFeature;
const int BINDING_TYPE_NONE = 0;
const int BINDING_TYPE_NOTE = 1;
const int BINDING_TYPE_CONTROL = 2;
const int BINDING_TYPE_TAP_TEMPO = 3;
const int LINEAR_CONTROL_TYPE = 0;
const int CIRCULAR_CONTROL_TYPE = 1;
+68
View File
@@ -0,0 +1,68 @@
/*
* MIT License
*
* Copyright (c) 2026 Robin E. R. Davies
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#ifndef PIPEDAL_MidiEvent_HPP
#define PIPEDAL_MidiEvent_HPP
#include <cstdint>
namespace pipedal {
struct MidiTimestamp
{
uint64_t seconds;
uint32_t nanoseconds;
MidiTimestamp()
: seconds(0), nanoseconds(0)
{
}
MidiTimestamp(uint64_t sec, uint32_t nsec)
: seconds(sec), nanoseconds(nsec)
{
}
bool isEmpty() const {
return seconds == 0 && nanoseconds == 0;
}
double timeDiff(const MidiTimestamp &other) const
{
double secDiff = (double)(int64_t)(this->seconds - other.seconds);
double nsecDiff = (double)((int64_t)this->nanoseconds - (int64_t)other.nanoseconds) * 1e-9;
return secDiff + nsecDiff;
}
};
struct MidiEvent
{
MidiTimestamp timeStamp; /**< Real-time timestamp of the event */
uint32_t frame; /**< Sample frame at which event is valid */
uint32_t size; /**< Number of bytes of data in \a buffer */
uint8_t *buffer; /**< Raw MIDI data */
};
};
#endif
+1 -1
View File
@@ -50,7 +50,7 @@ static std::vector<ModFileTypes::ModDirectory> *CreateModDirectories()
// extensions observed in the field.
{"audio", "shared/audio", "Audio", {"audio/*"}}, // all audio files (Ratatouille)
{"nammodel", "NeuralAmpModels", "Neural Amp Models", {".nam" ,".aidax"}}, // Ratatouille, Mike's NAM.
{"aidadspmodel", "shared/aidaaix", "AIDA IAX Models", {".json", ".aidaiax"}}, // Ratatouille
{"aidadspmodel", "shared/aidaaix", "AIDA IAX Models", {".json", ".aidax",".aidaiax"}}, // Ratatouille
{"mlmodel", "ToobMlModels", "ML Models", {".json"}}, //
+44 -7
View File
@@ -35,6 +35,11 @@ function semitone12TETValue(value: number): string {
}
export const MIN_TAP_TEMPO_SECONDS = 0.2;
export const MAX_TAP_TEMPO_SECONDS = 1.0;
export const MIN_TAP_TEMPO_HZ = 0.1;
export const MAX_TAP_TEMPO_HZ = 2.0;
export class Port implements Deserializable<Port> {
deserialize(input: any): Port {
@@ -117,7 +122,7 @@ export class PortGroup {
symbol: string = "";
name: string = "";
sideChainof: string = "";
parent_group: string = "";
program_list_id: number = -1;
};
@@ -654,6 +659,10 @@ export class UiControl implements Deserializable<UiControl> {
this.controlType = ControlType.Trigger;
}
}
if (this.controlType === ControlType.Dial) {
if (this.canDoTapTempo()) {
}
}
return this;
}
@@ -714,6 +723,7 @@ export class UiControl implements Deserializable<UiControl> {
scale_points: ScalePoint[] = [];
port_group: string = "";
units: Units = Units.none;
can_tap_tempo: boolean = false;
comment: string = "";
is_bypass: boolean = false;
is_program_controller: boolean = true;
@@ -779,6 +789,34 @@ export class UiControl implements Deserializable<UiControl> {
return this.controlType === ControlType.Dial;
}
canDoTapTempo(): boolean {
if (this.controlType === ControlType.Dial) {
let min_value = this.min_value;
let max_value = this.max_value;
if (min_value > max_value) {
let t = min_value;
min_value = max_value;
max_value = t;
}
switch (this.units) {
case Units.s:
return min_value <= MIN_TAP_TEMPO_SECONDS && max_value >= MAX_TAP_TEMPO_SECONDS;
case Units.ms:
return min_value <= MIN_TAP_TEMPO_SECONDS*1000 && max_value >= MAX_TAP_TEMPO_SECONDS*1000;
case Units.bpm:
return true;
case Units.hz:
return min_value <= MIN_TAP_TEMPO_HZ && max_value >= MAX_TAP_TEMPO_HZ;
default:
return false;
}
}
return false;
}
isTuner(): boolean {
return this.controlType === ControlType.Tuner;
}
@@ -894,8 +932,8 @@ export class UiControl implements Deserializable<UiControl> {
break;
case Units.unknown:
if (this.custom_units !== "") {
text = this.custom_units.replace("%f",text);
}
text = this.custom_units.replace("%f", text);
}
break;
default:
break;
@@ -950,7 +988,7 @@ export class Lv2PatchPropertyInfo {
return result;
}
uri: string = "";
writable : boolean = false;
writable: boolean = false;
readable: boolean = false;
label: string = "";
index: number = -1;
@@ -1045,8 +1083,7 @@ export class UiPlugin implements Deserializable<UiPlugin> {
}
getFilePropertyByUri(patchPropertyUri: string): UiFileProperty | null {
for (let i = 0; i < this.fileProperties.length; ++i)
{
for (let i = 0; i < this.fileProperties.length; ++i) {
let fileProperty = this.fileProperties[i];
if (fileProperty.patchProperty === patchPropertyUri) {
return fileProperty;
@@ -1078,7 +1115,7 @@ export class UiPlugin implements Deserializable<UiPlugin> {
frequencyPlots: UiFrequencyPlot[] = [];
is_vst3: boolean = false;
modGui: ModGui | null = null; // null if no mod gui.
patchProperties: Lv2PatchPropertyInfo[] = [];
patchProperties: Lv2PatchPropertyInfo[] = [];
}
+1
View File
@@ -67,6 +67,7 @@ export default class MidiBinding {
static BINDING_TYPE_NONE: number = 0;
static BINDING_TYPE_NOTE: number = 1;
static BINDING_TYPE_CONTROL: number = 2;
static BINDING_TYPE_TAP_TEMPO: number = 3;
setBindingType(bindingType:number)
{
+37 -15
View File
@@ -57,6 +57,7 @@ export enum MidiControlType {
MomentarySwitch
}
export function getMidiControlType(uiPlugin: UiPlugin | undefined, symbol: string): MidiControlType {
if (symbol === "__bypass") {
return MidiControlType.Toggle;
@@ -87,16 +88,14 @@ export function getMidiControlType(uiPlugin: UiPlugin | undefined, symbol: strin
return MidiControlType.Dial;
}
export function canBindToNote(controlType: MidiControlType): boolean {
return controlType === MidiControlType.Toggle
|| controlType === MidiControlType.Trigger
|| controlType === MidiControlType.MomentarySwitch
}
interface MidiBindingViewProps extends WithStyles<typeof styles> {
instanceId: number;
midiBinding: MidiBinding;
midiControlType: MidiControlType;
canDoTapTempo: boolean;
onChange: (instanceId: number, newBinding: MidiBinding) => void;
}
@@ -131,6 +130,21 @@ const MidiBindingView =
};
}
canDoTapTempo(): boolean {
let controlType = this.props.midiControlType;
if (controlType == MidiControlType.Dial && this.props.canDoTapTempo) {
return true;
}
return false;
}
canBindToNote(): boolean {
let controlType = this.props.midiControlType;
return controlType === MidiControlType.Toggle
|| controlType === MidiControlType.Trigger
|| controlType === MidiControlType.MomentarySwitch
}
handleBindingTypeChange(e: any, extra: any) {
let newValue = parseInt(e.target.value);
let newBinding = this.props.midiBinding.clone();
@@ -203,7 +217,7 @@ const MidiBindingView =
generateMidiNoteSelects(): React.ReactNode[] {
let result: React.ReactNode[] = [];
for (let i = 0; i < 127; ++i) {
for (let i = 0; i <= 127; ++i) {
result.push(
<MenuItem key={"k" + i} value={i}>{i} {Utility.midiNoteName(i)}</MenuItem>
)
@@ -314,10 +328,13 @@ const MidiBindingView =
let binding = this.props.midiBinding;
let newBinding = binding.clone();
if (midiMessage.isNote()) {
if (!canBindToNote(this.props.midiControlType)) {
if (!this.canBindToNote() && !this.props.canDoTapTempo) {
return;
}
newBinding.bindingType = MidiBinding.BINDING_TYPE_NOTE
newBinding.bindingType =
this.props.canDoTapTempo ?
MidiBinding.BINDING_TYPE_TAP_TEMPO
: MidiBinding.BINDING_TYPE_NOTE
newBinding.note = midiMessage.cc1;
} else if (midiMessage.isControl()) {
newBinding.bindingType = MidiBinding.BINDING_TYPE_CONTROL
@@ -413,23 +430,28 @@ const MidiBindingView =
<div style={{ display: "flex", flexDirection: "row", flexWrap: "wrap", justifyContent: "left", alignItems: "center", minHeight: 48 }}>
<div className={classes.controlDiv} >
<Select variant="standard"
style={{ width: 80, }}
style={{ width: undefined, }}
onChange={(e, extra) => this.handleBindingTypeChange(e, extra)}
value={midiBinding.bindingType}
>
<MenuItem value={0}>None</MenuItem>
{(canBindToNote(this.props.midiControlType)) && (
<MenuItem value={1}>Note</MenuItem>
{(this.canBindToNote()) && (
<MenuItem key="1" value={1}>Note</MenuItem>
)}
<MenuItem key="2" value={2}>Control</MenuItem>
{(this.canDoTapTempo()) && (
<MenuItem key="3" value={3}>Tap Tempo</MenuItem>
)}
<MenuItem value={2}>Control</MenuItem>
</Select>
</div>
{
(midiBinding.bindingType === MidiBinding.BINDING_TYPE_NOTE) &&
(midiBinding.bindingType === MidiBinding.BINDING_TYPE_NOTE
|| midiBinding.bindingType === MidiBinding.BINDING_TYPE_TAP_TEMPO
) &&
(
<div className={classes.controlDiv2} >
<Select variant="standard"
style={{ width: 80 }}
style={{}}
onChange={(e, extra) => this.handleNoteChange(e, extra)}
value={midiBinding.note}
@@ -569,7 +591,7 @@ const MidiBindingView =
(
<div className={classes.controlDiv} >
<Select variant="standard"
style={{ width: 120 }}
style={{}}
onChange={(e, extra) => this.handleLinearControlTypeChange(e, extra)}
value={midiBinding.linearControlType}
>
+15 -1
View File
@@ -69,6 +69,17 @@ const styles = (theme: Theme) => createStyles({
}),
});
function canDoTapTempo(uiPlugin: UiPlugin | undefined, symbol: string): boolean {
if (!uiPlugin) return false;
let port = uiPlugin.getControl(symbol);
if (!port) return false;
if (port.canDoTapTempo()) {
return true;
}
return false;
}
function not_null<T>(value: T | null) {
if (!value) throw Error("Unexpected null value");
return value;
@@ -200,7 +211,9 @@ export const MidiBindingDialog =
</td>
<td className={classes.bindingTd}>
<MidiBindingView instanceId={item.instanceId} midiBinding={item.getMidiBinding("__bypass")}
midiControlType={ getMidiControlType(plugin, "__bypass") }
midiControlType={ getMidiControlType(plugin, "__bypass")
}
canDoTapTempo={false }
onChange={(instanceId: number, newItem: MidiBinding) => this.handleItemChanged(instanceId, newItem)}
/>
</td>
@@ -244,6 +257,7 @@ export const MidiBindingDialog =
<MidiBindingView instanceId={item.instanceId}
midiControlType={getMidiControlType(plugin, symbol)}
midiBinding={item.getMidiBinding(symbol)}
canDoTapTempo={canDoTapTempo(plugin, symbol)}
onChange={(instanceId: number, newItem: MidiBinding) => this.handleItemChanged(instanceId, newItem)}
/>
</td>
+11 -2
View File
@@ -37,6 +37,15 @@ function isMobileDevice() {
return (navigator.maxTouchPoints && navigator.maxTouchPoints > 1);
}
function defaultStep(min: number, max: number): number {
let range = Math.abs(max - min);
if (range <= 1) return 0.01;
if (range <= 10) return 0.1;
if (range <= 100) return 1;
if (range <= 1000) return 10;
return 100;
}
interface NumericInputProps extends WithStyles<typeof styles> {
ariaLabel: string;
value: number;
@@ -155,7 +164,7 @@ export const NumericInput =
render() {
//const classes = withStyles.getClasses(this.props);
return (<Input style={{ width: 50 }}
return (<Input style={{ width: 65 }}
inputProps={{
'aria-label': this.props.ariaLabel,
style: { textAlign: 'right' },
@@ -166,7 +175,7 @@ export const NumericInput =
autoCapitalize: "off",
min: this.props.min,
max: this.props.max,
step: this.props.step
step: this.props.step ? this.props.step: defaultStep(this.props.min, this.props.max)
}}
onFocus={(e) => {this.handleFocus(e);}}
onBlur= {(e) => { this.handleBlur(e); }}
+3 -1
View File
@@ -499,6 +499,8 @@ const PluginControl =
}
private tapStartMs: number = 0;
onPointerUp(e: PointerEvent) {
if (this.isCapturedPointer(e)) {
@@ -519,7 +521,7 @@ const PluginControl =
let ms = e.timeStamp - this.tapStartMs;
if (ms < 200) {
this.onPointerTap(e);
}
}
}
// prevent click from firing on other elements
// when the pointer goes up0.
@@ -34,6 +34,7 @@ function SystemMidiBindingView(props: SystemMidiBindingViewProps) {
return (
<MidiBindingView
instanceId={props.instanceId}
canDoTapTempo={false}
midiBinding={props.midiBinding}
onChange={props.onChange}
midiControlType={MidiControlType.Trigger}