Tap Tempo, ToobAmp v1.1.72
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 """
|
||||
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
] , [
|
||||
|
||||
@@ -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.
@@ -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.
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
""" ;
|
||||
|
||||
@@ -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>;
|
||||
|
||||
|
||||
@@ -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>;
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>;
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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>;
|
||||
|
||||
|
||||
@@ -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>;
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
""" ;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
+101
-7
@@ -194,7 +194,9 @@ std::vector<float *> Lv2Pedalboard::PrepareItems(
|
||||
pLv2Effect->SetAudioSidechainBuffer(i, this->pedalboardInputBuffers[0]);
|
||||
}
|
||||
}
|
||||
} else if (item.sideChainInputId() != -1) {
|
||||
}
|
||||
else if (item.sideChainInputId() != -1)
|
||||
{
|
||||
IEffect *pSideChainInput = GetEffect(item.sideChainInputId());
|
||||
|
||||
if (pSideChainInput)
|
||||
@@ -211,7 +213,9 @@ std::vector<float *> Lv2Pedalboard::PrepareItems(
|
||||
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;
|
||||
@@ -922,3 +939,80 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
+19
-7
@@ -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)
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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"}}, //
|
||||
|
||||
|
||||
|
||||
@@ -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 {
|
||||
@@ -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,7 +932,7 @@ 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:
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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}
|
||||
>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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); }}
|
||||
|
||||
@@ -42,6 +42,7 @@ import PlayArrowIcon from '@mui/icons-material/PlayArrow';
|
||||
import StopIcon from '@mui/icons-material/Stop';
|
||||
import FiberManualRecordIcon from '@mui/icons-material/FiberManualRecord';
|
||||
import GraphicEqCtl, { UpdateGraphicEqPath } from './GraphicEqCtl';
|
||||
import Units from './Units';
|
||||
|
||||
const MIN_ANGLE = -135;
|
||||
const MAX_ANGLE = 135;
|
||||
@@ -499,6 +500,8 @@ const PluginControl =
|
||||
}
|
||||
private tapStartMs: number = 0;
|
||||
|
||||
|
||||
|
||||
onPointerUp(e: PointerEvent) {
|
||||
|
||||
if (this.isCapturedPointer(e)) {
|
||||
|
||||
Reference in New Issue
Block a user