Tap Tempo, ToobAmp v1.1.72

This commit is contained in:
Robin E. R. Davies
2026-01-10 10:41:20 -05:00
parent 6e1657933b
commit fdeb1fef5e
49 changed files with 397 additions and 94 deletions
+3
View File
@@ -704,6 +704,9 @@ namespace pipedal
queueId = -1; queueId = -1;
throw std::runtime_error(SS("Failed to set port timestamping: " << snd_strerror(rc))); 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; return queueId;
+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 71 ; lv2:microVersion 72 ;
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 71 ; lv2:microVersion 72 ;
mod:brand "TooB"; mod:brand "TooB";
mod:label "TooB CabSim"; mod:label "TooB CabSim";
@@ -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 71 ; lv2:microVersion 72 ;
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 71 ; lv2:microVersion 72 ;
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 71 ; lv2:microVersion 72 ;
mod:brand "TooB"; mod:brand "TooB";
mod:label "TooB Input"; mod:label "TooB 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 71 ; lv2:microVersion 72 ;
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 71 ; lv2:microVersion 72 ;
rdfs:comment "TooB spectrum analyzer" ; rdfs:comment "TooB spectrum analyzer" ;
mod:brand "TooB"; mod:brand "TooB";
+2 -2
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 71 ; lv2:microVersion 72 ;
uiext:ui <http://two-play.com/plugins/toob-tone-stack-ui>; 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:maximum 2.0;
lv2:portProperty lv2:enumeration ; lv2:portProperty lv2:enumeration ;
lv2:scalePoint [ lv2:scalePoint [
rdfs:label "Bassman" ; rdfs:label "Bassman" ;
rdf:value 0.0 rdf:value 0.0
] , [ ] , [
+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 71 ; lv2:microVersion 72 ;
uiext:ui <http://two-play.com/plugins/toob-three-band-eq-ui>; 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: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 71 ; lv2:microVersion 72 ;
rdfs:comment """ rdfs:comment """
Emulation of a Boss CE-2 Chorus. Emulation of a Boss CE-2 Chorus.
+2 -2
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 71 ; lv2:microVersion 72 ;
rdfs:comment """ rdfs:comment """
A straightforward no-frills digital delay. A straightforward no-frills digital delay.
@@ -61,7 +61,7 @@ A straightforward no-frills digital delay.
lv2:name "Delay"; lv2:name "Delay";
lv2:default 340 ; lv2:default 340 ;
lv2:minimum 5; lv2:minimum 5;
lv2:maximum 1200; lv2:maximum 3000;
units:unit units:ms; units:unit units:ms;
lv2:portProperty <http://lv2plug.in/ns/ext/port-props#logarithmic>; lv2:portProperty <http://lv2plug.in/ns/ext/port-props#logarithmic>;
rdfs:comment "Delay in ms"; rdfs:comment "Delay in ms";
+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 71 ; lv2:microVersion 72 ;
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 71 ; lv2:microVersion 72 ;
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 71 ; lv2:microVersion 72 ;
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 71 ; lv2:microVersion 72 ;
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 2 ; lv2:minorVersion 2 ;
lv2:microVersion 71 ; lv2:microVersion 72 ;
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 2 ; lv2:minorVersion 2 ;
lv2:microVersion 71 ; lv2:microVersion 72 ;
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 71 ; lv2:microVersion 72 ;
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.
+49 -5
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 71 ; lv2:microVersion 72 ;
rdfs:comment """ rdfs:comment """
Remix a stereo input signal. Remix a stereo input signal.
@@ -115,31 +115,75 @@ Remix a stereo input signal.
lv2:maximum 1.0; lv2:maximum 1.0;
rdfs:comment "Right Pan"; 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 , a lv2:AudioPort ,
lv2:InputPort ; lv2:InputPort ;
lv2:index 4 ; lv2:index 6 ;
lv2:symbol "inl" ; lv2:symbol "inl" ;
lv2:name "In L" lv2:name "In L"
], ],
[ [
a lv2:AudioPort , a lv2:AudioPort ,
lv2:InputPort ; lv2:InputPort ;
lv2:index 5 ; lv2:index 7 ;
lv2:symbol "inr" ; lv2:symbol "inr" ;
lv2:name "In R" lv2:name "In R"
], ],
[ [
a lv2:AudioPort , a lv2:AudioPort ,
lv2:OutputPort ; lv2:OutputPort ;
lv2:index 6 ; lv2:index 8 ;
lv2:symbol "outl" ; lv2:symbol "outl" ;
lv2:name "Out L" lv2:name "Out L"
], ],
[ [
a lv2:AudioPort , a lv2:AudioPort ,
lv2:OutputPort ; lv2:OutputPort ;
lv2:index 7 ; lv2:index 9 ;
lv2:symbol "outr" ; lv2:symbol "outr" ;
lv2:name "OutR" lv2:name "OutR"
] ]
@@ -72,7 +72,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 71 ; lv2:microVersion 72 ;
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 71 ; lv2:microVersion 72 ;
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 71 ; lv2:microVersion 72 ;
uiext:ui <http://two-play.com/plugins/toob-parametric-eq-ui>; 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: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 71 ; lv2:microVersion 72 ;
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 71 ; lv2:microVersion 72 ;
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 2 ; lv2:minorVersion 2 ;
lv2:microVersion 71 ; lv2:microVersion 72 ;
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 2 ; lv2:minorVersion 2 ;
lv2:microVersion 71 ; lv2:microVersion 72 ;
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
@@ -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 71 ; lv2:microVersion 72 ;
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 71 ; lv2:microVersion 72 ;
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
@@ -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 71 ; lv2:microVersion 72 ;
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 71 ; lv2:microVersion 72 ;
rdfs:comment """ rdfs:comment """
Volume control. Volume control.
Binary file not shown.
Binary file not shown.
+2 -1
View File
@@ -1686,7 +1686,8 @@ namespace pipedal
continue; continue;
} }
MidiEvent *pEvent = midiEvents.data() + midiEventCount++; MidiEvent *pEvent = midiEvents.data() + midiEventCount++;
pEvent->time = audioFrame; pEvent->timeStamp = MidiTimestamp(message.realtime_sec, message.realtime_nsec);
pEvent->frame = audioFrame;
pEvent->size = messageSize; pEvent->size = messageSize;
pEvent->buffer = midiEventMemory.data() + midiEventMemoryIndex; pEvent->buffer = midiEventMemory.data() + midiEventMemoryIndex;
+1 -9
View File
@@ -26,6 +26,7 @@
#include "JackConfiguration.hpp" #include "JackConfiguration.hpp"
#include <functional> #include <functional>
#include "AlsaSequencer.hpp" #include "AlsaSequencer.hpp"
#include "MidiEvent.hpp"
@@ -35,15 +36,6 @@ namespace pipedal {
using ProcessCallback = std::function<void (size_t)>; 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 { class AudioDriverHost {
public: public:
virtual void OnProcess(size_t nFrames) = 0; virtual void OnProcess(size_t nFrames) = 0;
+4 -2
View File
@@ -365,6 +365,7 @@ bool SystemMidiBinding::IsMatch(const MidiEvent &event)
switch (currentBinding.bindingType()) switch (currentBinding.bindingType())
{ {
case BINDING_TYPE_NOTE: case BINDING_TYPE_NOTE:
case BINDING_TYPE_TAP_TEMPO:
{ {
if (event.size != 3) if (event.size != 3)
return false; return false;
@@ -926,7 +927,8 @@ private:
{ {
// eventBufferWriter.writeMidiEvent(iterator, 0, event.size, event.buffer); // 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 (listenForMidiEvent)
{ {
if (event.size >= 3) if (event.size >= 3)
@@ -1077,7 +1079,7 @@ private:
int8_t messageCount = deferredMidiMessages[i++]; int8_t messageCount = deferredMidiMessages[i++];
event.size = messageCount; event.size = messageCount;
event.buffer = deferredMidiMessages + i; event.buffer = deferredMidiMessages + i;
event.time = 0; event.frame = 0;
ProcessMidiEvent(eventBufferWriter, iterator, event); ProcessMidiEvent(eventBufferWriter, iterator, event);
+1 -1
View File
@@ -264,7 +264,7 @@ namespace pipedal
continue; continue;
} }
MidiEvent *pEvent = midiEvents.data() + midiEventCount++; MidiEvent *pEvent = midiEvents.data() + midiEventCount++;
pEvent->time = audioFrame; pEvent->frame = audioFrame;
pEvent->size = messageSize; pEvent->size = messageSize;
pEvent->buffer = midiEventMemory.data() + midiEventMemoryIndex; pEvent->buffer = midiEventMemory.data() + midiEventMemoryIndex;
+104 -10
View File
@@ -188,13 +188,15 @@ std::vector<float *> Lv2Pedalboard::PrepareItems(
{ {
pLv2Effect->SetAudioSidechainBuffer(i, this->pedalboardInputBuffers[i]); pLv2Effect->SetAudioSidechainBuffer(i, this->pedalboardInputBuffers[i]);
} }
else else
{ {
// just use the first output buffer for all sidechain inputs. // just use the first output buffer for all sidechain inputs.
pLv2Effect->SetAudioSidechainBuffer(i, this->pedalboardInputBuffers[0]); pLv2Effect->SetAudioSidechainBuffer(i, this->pedalboardInputBuffers[0]);
} }
} }
} else if (item.sideChainInputId() != -1) { }
else if (item.sideChainInputId() != -1)
{
IEffect *pSideChainInput = GetEffect(item.sideChainInputId()); IEffect *pSideChainInput = GetEffect(item.sideChainInputId());
if (pSideChainInput) if (pSideChainInput)
@@ -205,13 +207,15 @@ std::vector<float *> Lv2Pedalboard::PrepareItems(
{ {
pLv2Effect->SetAudioSidechainBuffer(i, pSideChainInput->GetAudioOutputBuffer(i)); pLv2Effect->SetAudioSidechainBuffer(i, pSideChainInput->GetAudioOutputBuffer(i));
} }
else else
{ {
// just use the first output buffer for all sidechain inputs. // just use the first output buffer for all sidechain inputs.
pLv2Effect->SetAudioSidechainBuffer(i, pSideChainInput->GetAudioOutputBuffer(0)); pLv2Effect->SetAudioSidechainBuffer(i, pSideChainInput->GetAudioOutputBuffer(0));
} }
} }
} else { }
else
{
throw std::runtime_error("Internal error: Sidechain input IEffect not found."); 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; mapping.mappingType = MidiControlType::Select;
} }
else if (binding.bindingType() == BINDING_TYPE_TAP_TEMPO) {
mapping.mappingType = MidiControlType::TapTempo;
}
else else
{ {
mapping.mappingType = MidiControlType::Dial; 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. 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; outputBuffers[c][i] = this->pedalboardOutputBuffers[c][i] * volume;
} }
} }
this->currentFrameOffset += samples;
return true; return true;
} }
@@ -715,14 +724,17 @@ void Lv2Pedalboard::GatherPatchProperties(RealtimePatchPropertyRequest *pParamet
} }
} }
void Lv2Pedalboard::OnMidiMessage(size_t size, uint8_t *message, void Lv2Pedalboard::OnMidiMessage(
void *callbackHandle, const MidiEvent&event,
MidiCallbackFn *pfnCallback) void *callbackHandle,
MidiCallbackFn *pfnCallback)
{ {
if (midiMappings.size() == 0) if (midiMappings.size() == 0)
return; return;
size_t size = event.size;
const uint8_t *message = event.buffer;
if (size < 2) if (size < 2)
return; return;
uint8_t cmd = message[0]; 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) for (int i = min; i < midiMappings.size(); ++i)
{ {
auto &mapping = midiMappings[i]; MidiMapping &mapping = midiMappings[i];
if (mapping.key != searchKey) if (mapping.key != searchKey)
break; break;
@@ -914,6 +926,11 @@ void Lv2Pedalboard::OnMidiMessage(size_t size, uint8_t *message,
} }
break; break;
} }
case MidiControlType::TapTempo:
{
handleTapTempo(value, event.timeStamp, mapping, callbackHandle, pfnCallback);
break;
}
case MidiControlType::None: case MidiControlType::None:
default: default:
break; 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 #pragma once
#include "Pedalboard.hpp" #include "Pedalboard.hpp"
#include "MidiEvent.hpp"
#include "PluginHost.hpp" #include "PluginHost.hpp"
#include "Lv2Effect.hpp" #include "Lv2Effect.hpp"
#include "BufferPool.hpp" #include "BufferPool.hpp"
@@ -49,8 +50,9 @@ namespace pipedal
class Lv2Pedalboard class Lv2Pedalboard
{ {
private:
IHost *pHost = nullptr; IHost *pHost = nullptr;
size_t currentFrameOffset = 0;
DbDezipper inputVolume; DbDezipper inputVolume;
DbDezipper outputVolume; DbDezipper outputVolume;
@@ -82,7 +84,8 @@ namespace pipedal
Dial, Dial,
Toggle, Toggle,
Trigger, Trigger,
MomentarySwitch MomentarySwitch,
TapTempo
}; };
class MidiMapping class MidiMapping
{ {
@@ -93,6 +96,7 @@ namespace pipedal
int effectIndex = -1; int effectIndex = -1;
int controlIndex = -1; int controlIndex = -1;
int key; // key to the note or control. internal use only. int key; // key to the note or control. internal use only.
MidiTimestamp lastTapTimestamp;
bool hasLastValue = false; bool hasLastValue = false;
bool lastValueIncreasing = false; bool lastValueIncreasing = false;
float lastValue = 0; float lastValue = 0;
@@ -119,13 +123,11 @@ namespace pipedal
Lv2Pedalboard() {} Lv2Pedalboard() {}
~Lv2Pedalboard() {} ~Lv2Pedalboard() {}
void Prepare(IHost *pHost, Pedalboard &pedalboard, Lv2PedalboardErrorList &errorList, ExistingEffectMap *existingEffects = nullptr); void Prepare(IHost *pHost, Pedalboard &pedalboard, Lv2PedalboardErrorList &errorList, ExistingEffectMap *existingEffects = nullptr);
std::vector<IEffect *> &GetEffects() { return realtimeEffects; } std::vector<IEffect *> &GetEffects() { return realtimeEffects; }
std::vector<std::shared_ptr<IEffect>> &GetSharedEffectList() { return effects; } std::vector<std::shared_ptr<IEffect>> &GetSharedEffectList() { return effects; }
int GetIndexOfInstanceId(uint64_t instanceId) int GetIndexOfInstanceId(uint64_t instanceId)
{ {
for (int i = 0; i < this->realtimeEffects.size(); ++i) for (int i = 0; i < this->realtimeEffects.size(); ++i)
@@ -149,7 +151,7 @@ namespace pipedal
void Activate(); void Activate();
void Deactivate(); void Deactivate();
void UpdateAudioPorts(); void UpdateAudioPorts();
bool Run(float **inputBuffers, float **outputBuffers, uint32_t samples, RealtimeRingBufferWriter *realtimeWriter); bool Run(float **inputBuffers, float **outputBuffers, uint32_t samples, RealtimeRingBufferWriter *realtimeWriter);
void ResetAtomBuffers(); void ResetAtomBuffers();
@@ -172,9 +174,19 @@ namespace pipedal
float GetControlOutputValue(int effectIndex, int portIndex); float GetControlOutputValue(int effectIndex, int portIndex);
typedef void(MidiCallbackFn)(void *data, uint64_t intanceId, int controlIndex, float value); typedef void(MidiCallbackFn)(void *data, uint64_t intanceId, int controlIndex, float value);
void OnMidiMessage(size_t size, uint8_t *data, void OnMidiMessage(
void *callbackHandle, const MidiEvent&message,
MidiCallbackFn *pfnCallback); void *callbackHandle,
MidiCallbackFn *pfnCallback);
private:
void handleTapTempo(
uint8_t value,
const MidiTimestamp& timestamp,
MidiMapping &mapping,
void *callbackHandle,
MidiCallbackFn *pfnCallback);
}; };
} // namespace } // namespace
+1
View File
@@ -53,6 +53,7 @@ class MapFeature;
const int BINDING_TYPE_NONE = 0; const int BINDING_TYPE_NONE = 0;
const int BINDING_TYPE_NOTE = 1; const int BINDING_TYPE_NOTE = 1;
const int BINDING_TYPE_CONTROL = 2; const int BINDING_TYPE_CONTROL = 2;
const int BINDING_TYPE_TAP_TEMPO = 3;
const int LINEAR_CONTROL_TYPE = 0; const int LINEAR_CONTROL_TYPE = 0;
const int CIRCULAR_CONTROL_TYPE = 1; 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. // extensions observed in the field.
{"audio", "shared/audio", "Audio", {"audio/*"}}, // all audio files (Ratatouille) {"audio", "shared/audio", "Audio", {"audio/*"}}, // all audio files (Ratatouille)
{"nammodel", "NeuralAmpModels", "Neural Amp Models", {".nam" ,".aidax"}}, // Ratatouille, Mike's NAM. {"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"}}, // {"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> { export class Port implements Deserializable<Port> {
deserialize(input: any): Port { deserialize(input: any): Port {
@@ -117,7 +122,7 @@ export class PortGroup {
symbol: string = ""; symbol: string = "";
name: string = ""; name: string = "";
sideChainof: string = ""; sideChainof: string = "";
parent_group: string = ""; parent_group: string = "";
program_list_id: number = -1; program_list_id: number = -1;
}; };
@@ -654,6 +659,10 @@ export class UiControl implements Deserializable<UiControl> {
this.controlType = ControlType.Trigger; this.controlType = ControlType.Trigger;
} }
} }
if (this.controlType === ControlType.Dial) {
if (this.canDoTapTempo()) {
}
}
return this; return this;
} }
@@ -714,6 +723,7 @@ export class UiControl implements Deserializable<UiControl> {
scale_points: ScalePoint[] = []; scale_points: ScalePoint[] = [];
port_group: string = ""; port_group: string = "";
units: Units = Units.none; units: Units = Units.none;
can_tap_tempo: boolean = false;
comment: string = ""; comment: string = "";
is_bypass: boolean = false; is_bypass: boolean = false;
is_program_controller: boolean = true; is_program_controller: boolean = true;
@@ -779,6 +789,34 @@ export class UiControl implements Deserializable<UiControl> {
return this.controlType === ControlType.Dial; 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 { isTuner(): boolean {
return this.controlType === ControlType.Tuner; return this.controlType === ControlType.Tuner;
} }
@@ -894,8 +932,8 @@ export class UiControl implements Deserializable<UiControl> {
break; break;
case Units.unknown: case Units.unknown:
if (this.custom_units !== "") { if (this.custom_units !== "") {
text = this.custom_units.replace("%f",text); text = this.custom_units.replace("%f", text);
} }
break; break;
default: default:
break; break;
@@ -950,7 +988,7 @@ export class Lv2PatchPropertyInfo {
return result; return result;
} }
uri: string = ""; uri: string = "";
writable : boolean = false; writable: boolean = false;
readable: boolean = false; readable: boolean = false;
label: string = ""; label: string = "";
index: number = -1; index: number = -1;
@@ -1045,8 +1083,7 @@ export class UiPlugin implements Deserializable<UiPlugin> {
} }
getFilePropertyByUri(patchPropertyUri: string): UiFileProperty | null { 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]; let fileProperty = this.fileProperties[i];
if (fileProperty.patchProperty === patchPropertyUri) { if (fileProperty.patchProperty === patchPropertyUri) {
return fileProperty; return fileProperty;
@@ -1078,7 +1115,7 @@ export class UiPlugin implements Deserializable<UiPlugin> {
frequencyPlots: UiFrequencyPlot[] = []; frequencyPlots: UiFrequencyPlot[] = [];
is_vst3: boolean = false; is_vst3: boolean = false;
modGui: ModGui | null = null; // null if no mod gui. 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_NONE: number = 0;
static BINDING_TYPE_NOTE: number = 1; static BINDING_TYPE_NOTE: number = 1;
static BINDING_TYPE_CONTROL: number = 2; static BINDING_TYPE_CONTROL: number = 2;
static BINDING_TYPE_TAP_TEMPO: number = 3;
setBindingType(bindingType:number) setBindingType(bindingType:number)
{ {
+37 -15
View File
@@ -57,6 +57,7 @@ export enum MidiControlType {
MomentarySwitch MomentarySwitch
} }
export function getMidiControlType(uiPlugin: UiPlugin | undefined, symbol: string): MidiControlType { export function getMidiControlType(uiPlugin: UiPlugin | undefined, symbol: string): MidiControlType {
if (symbol === "__bypass") { if (symbol === "__bypass") {
return MidiControlType.Toggle; return MidiControlType.Toggle;
@@ -87,16 +88,14 @@ export function getMidiControlType(uiPlugin: UiPlugin | undefined, symbol: strin
return MidiControlType.Dial; 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> { interface MidiBindingViewProps extends WithStyles<typeof styles> {
instanceId: number; instanceId: number;
midiBinding: MidiBinding; midiBinding: MidiBinding;
midiControlType: MidiControlType; midiControlType: MidiControlType;
canDoTapTempo: boolean;
onChange: (instanceId: number, newBinding: MidiBinding) => void; 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) { handleBindingTypeChange(e: any, extra: any) {
let newValue = parseInt(e.target.value); let newValue = parseInt(e.target.value);
let newBinding = this.props.midiBinding.clone(); let newBinding = this.props.midiBinding.clone();
@@ -203,7 +217,7 @@ const MidiBindingView =
generateMidiNoteSelects(): React.ReactNode[] { generateMidiNoteSelects(): React.ReactNode[] {
let result: React.ReactNode[] = []; let result: React.ReactNode[] = [];
for (let i = 0; i < 127; ++i) { for (let i = 0; i <= 127; ++i) {
result.push( result.push(
<MenuItem key={"k" + i} value={i}>{i} {Utility.midiNoteName(i)}</MenuItem> <MenuItem key={"k" + i} value={i}>{i} {Utility.midiNoteName(i)}</MenuItem>
) )
@@ -314,10 +328,13 @@ const MidiBindingView =
let binding = this.props.midiBinding; let binding = this.props.midiBinding;
let newBinding = binding.clone(); let newBinding = binding.clone();
if (midiMessage.isNote()) { if (midiMessage.isNote()) {
if (!canBindToNote(this.props.midiControlType)) { if (!this.canBindToNote() && !this.props.canDoTapTempo) {
return; 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; newBinding.note = midiMessage.cc1;
} else if (midiMessage.isControl()) { } else if (midiMessage.isControl()) {
newBinding.bindingType = MidiBinding.BINDING_TYPE_CONTROL 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 style={{ display: "flex", flexDirection: "row", flexWrap: "wrap", justifyContent: "left", alignItems: "center", minHeight: 48 }}>
<div className={classes.controlDiv} > <div className={classes.controlDiv} >
<Select variant="standard" <Select variant="standard"
style={{ width: 80, }} style={{ width: undefined, }}
onChange={(e, extra) => this.handleBindingTypeChange(e, extra)} onChange={(e, extra) => this.handleBindingTypeChange(e, extra)}
value={midiBinding.bindingType} value={midiBinding.bindingType}
> >
<MenuItem value={0}>None</MenuItem> <MenuItem value={0}>None</MenuItem>
{(canBindToNote(this.props.midiControlType)) && ( {(this.canBindToNote()) && (
<MenuItem value={1}>Note</MenuItem> <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> </Select>
</div> </div>
{ {
(midiBinding.bindingType === MidiBinding.BINDING_TYPE_NOTE) && (midiBinding.bindingType === MidiBinding.BINDING_TYPE_NOTE
|| midiBinding.bindingType === MidiBinding.BINDING_TYPE_TAP_TEMPO
) &&
( (
<div className={classes.controlDiv2} > <div className={classes.controlDiv2} >
<Select variant="standard" <Select variant="standard"
style={{ width: 80 }} style={{}}
onChange={(e, extra) => this.handleNoteChange(e, extra)} onChange={(e, extra) => this.handleNoteChange(e, extra)}
value={midiBinding.note} value={midiBinding.note}
@@ -569,7 +591,7 @@ const MidiBindingView =
( (
<div className={classes.controlDiv} > <div className={classes.controlDiv} >
<Select variant="standard" <Select variant="standard"
style={{ width: 120 }} style={{}}
onChange={(e, extra) => this.handleLinearControlTypeChange(e, extra)} onChange={(e, extra) => this.handleLinearControlTypeChange(e, extra)}
value={midiBinding.linearControlType} 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) { function not_null<T>(value: T | null) {
if (!value) throw Error("Unexpected null value"); if (!value) throw Error("Unexpected null value");
return value; return value;
@@ -200,7 +211,9 @@ export const MidiBindingDialog =
</td> </td>
<td className={classes.bindingTd}> <td className={classes.bindingTd}>
<MidiBindingView instanceId={item.instanceId} midiBinding={item.getMidiBinding("__bypass")} <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)} onChange={(instanceId: number, newItem: MidiBinding) => this.handleItemChanged(instanceId, newItem)}
/> />
</td> </td>
@@ -244,6 +257,7 @@ export const MidiBindingDialog =
<MidiBindingView instanceId={item.instanceId} <MidiBindingView instanceId={item.instanceId}
midiControlType={getMidiControlType(plugin, symbol)} midiControlType={getMidiControlType(plugin, symbol)}
midiBinding={item.getMidiBinding(symbol)} midiBinding={item.getMidiBinding(symbol)}
canDoTapTempo={canDoTapTempo(plugin, symbol)}
onChange={(instanceId: number, newItem: MidiBinding) => this.handleItemChanged(instanceId, newItem)} onChange={(instanceId: number, newItem: MidiBinding) => this.handleItemChanged(instanceId, newItem)}
/> />
</td> </td>
+11 -2
View File
@@ -37,6 +37,15 @@ function isMobileDevice() {
return (navigator.maxTouchPoints && navigator.maxTouchPoints > 1); 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> { interface NumericInputProps extends WithStyles<typeof styles> {
ariaLabel: string; ariaLabel: string;
value: number; value: number;
@@ -155,7 +164,7 @@ export const NumericInput =
render() { render() {
//const classes = withStyles.getClasses(this.props); //const classes = withStyles.getClasses(this.props);
return (<Input style={{ width: 50 }} return (<Input style={{ width: 65 }}
inputProps={{ inputProps={{
'aria-label': this.props.ariaLabel, 'aria-label': this.props.ariaLabel,
style: { textAlign: 'right' }, style: { textAlign: 'right' },
@@ -166,7 +175,7 @@ export const NumericInput =
autoCapitalize: "off", autoCapitalize: "off",
min: this.props.min, min: this.props.min,
max: this.props.max, 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);}} onFocus={(e) => {this.handleFocus(e);}}
onBlur= {(e) => { this.handleBlur(e); }} onBlur= {(e) => { this.handleBlur(e); }}
+4 -1
View File
@@ -42,6 +42,7 @@ import PlayArrowIcon from '@mui/icons-material/PlayArrow';
import StopIcon from '@mui/icons-material/Stop'; import StopIcon from '@mui/icons-material/Stop';
import FiberManualRecordIcon from '@mui/icons-material/FiberManualRecord'; import FiberManualRecordIcon from '@mui/icons-material/FiberManualRecord';
import GraphicEqCtl, { UpdateGraphicEqPath } from './GraphicEqCtl'; import GraphicEqCtl, { UpdateGraphicEqPath } from './GraphicEqCtl';
import Units from './Units';
const MIN_ANGLE = -135; const MIN_ANGLE = -135;
const MAX_ANGLE = 135; const MAX_ANGLE = 135;
@@ -499,6 +500,8 @@ const PluginControl =
} }
private tapStartMs: number = 0; private tapStartMs: number = 0;
onPointerUp(e: PointerEvent) { onPointerUp(e: PointerEvent) {
if (this.isCapturedPointer(e)) { if (this.isCapturedPointer(e)) {
@@ -519,7 +522,7 @@ const PluginControl =
let ms = e.timeStamp - this.tapStartMs; let ms = e.timeStamp - this.tapStartMs;
if (ms < 200) { if (ms < 200) {
this.onPointerTap(e); this.onPointerTap(e);
} }
} }
// prevent click from firing on other elements // prevent click from firing on other elements
// when the pointer goes up0. // when the pointer goes up0.