From b07f590b49ebd276667073bafcc04b16d275c137 Mon Sep 17 00:00:00 2001 From: "Robin E. R. Davies" Date: Fri, 6 Dec 2024 04:20:39 -0500 Subject: [PATCH] Trigger MIDI bindings. --- react/src/MidiBinding.tsx | 17 ++- react/src/MidiBindingView.tsx | 213 +++++++++++++++++++++++----------- react/src/PiPedalModel.tsx | 4 + react/src/PluginControl.tsx | 13 ++- src/Lv2Effect.cpp | 12 +- src/Lv2Effect.hpp | 1 + src/Lv2Pedalboard.cpp | 151 +++++++++++++++++++----- src/Lv2Pedalboard.hpp | 16 +-- src/MidiBinding.hpp | 25 +++- todo.txt | 31 ++++- 10 files changed, 362 insertions(+), 121 deletions(-) diff --git a/react/src/MidiBinding.tsx b/react/src/MidiBinding.tsx index 766d5b1..53e7c18 100644 --- a/react/src/MidiBinding.tsx +++ b/react/src/MidiBinding.tsx @@ -43,8 +43,8 @@ export default class MidiBinding { result.push(new MidiBinding().deserialize(input[i])); } return result; - } + clone(): MidiBinding { return new MidiBinding().deserialize(this);} equals(other: MidiBinding) : boolean { @@ -62,6 +62,12 @@ export default class MidiBinding { static BINDING_TYPE_NOTE: number = 1; static BINDING_TYPE_CONTROL: number = 2; + setBindingType(bindingType:number) + { + this.bindingType = bindingType; + } + + symbol: string = ""; bindingType: number = MidiBinding.BINDING_TYPE_NONE; @@ -76,9 +82,14 @@ export default class MidiBinding { linearControlType: number = MidiBinding.LINEAR_CONTROL_TYPE; - static LATCH_CONTROL_TYPE: number = 0; + static TRIGGER_ON_RISING_EDGE: number = 0; + static TOGGLE_ON_RISING_EDGE: number = 0; static MOMENTARY_CONTROL_TYPE: number = 1; + static TOGGLE_ON_VALUE: number = 1; + static TOGGLE_ON_ANY: number = 2; + static TRIGGER_ON_ANY: number = 2; - switchControlType: number = MidiBinding.LATCH_CONTROL_TYPE; + + switchControlType: number = MidiBinding.TRIGGER_ON_RISING_EDGE; }; \ No newline at end of file diff --git a/react/src/MidiBindingView.tsx b/react/src/MidiBindingView.tsx index 2a9c8fc..cab3a34 100644 --- a/react/src/MidiBindingView.tsx +++ b/react/src/MidiBindingView.tsx @@ -23,25 +23,36 @@ import { Theme } from '@mui/material/styles'; import { WithStyles } from '@mui/styles'; import withStyles from '@mui/styles/withStyles'; import createStyles from '@mui/styles/createStyles'; +import { UiPlugin } from './Lv2Plugin'; + import MenuItem from '@mui/material/MenuItem'; import Select from '@mui/material/Select'; import MidiBinding from './MidiBinding'; -import Utility, { nullCast } from './Utility'; +import Utility from './Utility'; import Typography from '@mui/material/Typography'; import MicNoneOutlinedIcon from '@mui/icons-material/MicNoneOutlined'; import MicOutlinedIcon from '@mui/icons-material/MicOutlined'; import IconButton from '@mui/material/IconButton'; import NumericInput from './NumericInput'; -import { UiPlugin } from './Lv2Plugin'; const styles = (theme: Theme) => createStyles({ - controlDiv: { flex: "0 0 auto", marginRight: 12,verticalAlign: "center", height: 48, paddingTop: 8, paddingBottom: 8 }, - controlDiv2: { flex: "0 0 auto", marginRight: 12,verticalAlign: "center", - height: 48, paddingTop: 0, paddingBottom: 0, whiteSpace: "nowrap" } + controlDiv: { flex: "0 0 auto", marginRight: 12, verticalAlign: "center", height: 48, paddingTop: 8, paddingBottom: 8 }, + controlDiv2: { + flex: "0 0 auto", marginRight: 12, verticalAlign: "center", + height: 48, paddingTop: 0, paddingBottom: 0, whiteSpace: "nowrap" + } }); +enum MidiControlType { + None, + Toggle, + Trigger, + Dial, + Select, +} + interface MidiBindingViewProps extends WithStyles { instanceId: number; listen: boolean; @@ -53,6 +64,7 @@ interface MidiBindingViewProps extends WithStyles { interface MidiBindingViewState { + midiControlType: MidiControlType; } @@ -68,37 +80,50 @@ const MidiBindingView = super(props); this.model = PiPedalModelFactory.getInstance(); this.state = { + midiControlType: this.getControlType() }; } - handleTypeChange(e: any, extra: any) { + handleBindingTypeChange(e: any, extra: any) { let newValue = parseInt(e.target.value); let newBinding = this.props.midiBinding.clone(); - newBinding.bindingType = newValue; + newBinding.setBindingType(newValue); + this.validateSwitchControlType(newBinding); this.props.onChange(this.props.instanceId, newBinding); } handleNoteChange(e: any, extra: any) { let newValue = parseInt(e.target.value); let newBinding = this.props.midiBinding.clone(); newBinding.note = newValue; + this.validateSwitchControlType(newBinding); this.props.onChange(this.props.instanceId, newBinding); } handleControlChange(e: any, extra: any) { let newValue = parseInt(e.target.value); let newBinding = this.props.midiBinding.clone(); newBinding.control = newValue; + this.validateSwitchControlType(newBinding); this.props.onChange(this.props.instanceId, newBinding); } handleLatchControlTypeChange(e: any, extra: any) { let newValue = parseInt(e.target.value); let newBinding = this.props.midiBinding.clone(); newBinding.switchControlType = newValue; + this.validateSwitchControlType(newBinding); + this.props.onChange(this.props.instanceId, newBinding); + } + handleTriggerControlTypeChange(e: any, extra: any) { + let newValue = parseInt(e.target.value); + let newBinding = this.props.midiBinding.clone(); + newBinding.switchControlType = newValue; + this.validateSwitchControlType(newBinding); this.props.onChange(this.props.instanceId, newBinding); } handleLinearControlTypeChange(e: any, extra: any) { let newValue = parseInt(e.target.value); let newBinding = this.props.midiBinding.clone(); newBinding.linearControlType = newValue; + this.validateSwitchControlType(newBinding); this.props.onChange(this.props.instanceId, newBinding); } handleMinChange(value: number): void { @@ -118,7 +143,7 @@ const MidiBindingView = } - generateMidiSelects(): React.ReactNode[] { + generateMidiNoteSelects(): React.ReactNode[] { let result: React.ReactNode[] = []; for (let i = 0; i < 127; ++i) { @@ -129,14 +154,50 @@ const MidiBindingView = return result; } + + validateSwitchControlType(midiBinding: MidiBinding) { + // :-( + + let controlType = this.state.midiControlType; + if (controlType === MidiControlType.Toggle + && midiBinding.bindingType === MidiBinding.BINDING_TYPE_NOTE) { + if (midiBinding.switchControlType !== MidiBinding.TRIGGER_ON_RISING_EDGE // Toggle on Note On. + && midiBinding.switchControlType !== MidiBinding.MOMENTARY_CONTROL_TYPE // Note on/Note off. + ) { + midiBinding.switchControlType = MidiBinding.TRIGGER_ON_RISING_EDGE; + } + } + + } generateControlSelects(): React.ReactNode[] { return Utility.validMidiControllers.map((control) => ( {control.displayName} - ) + ) ); } + getControlType(): MidiControlType { + + if (this.props.midiBinding.symbol === "__bypass") { + return MidiControlType.Toggle; + } + if (!this.props.uiPlugin) return MidiControlType.None; + + let port = this.props.uiPlugin.getControl(this.props.midiBinding.symbol); + + if (!port) return MidiControlType.None; + if (port.trigger_property) { + return MidiControlType.Trigger; + } + if (port.isAbToggle() || port.isOnOffSwitch()) { + return MidiControlType.Toggle; + } + if (port.isSelect()) { + return MidiControlType.Select; + } + return MidiControlType.Dial; + } render() { let classes = this.props.classes; @@ -145,37 +206,30 @@ const MidiBindingView = if (!uiPlugin) { return (
); } + let controlType = this.state.midiControlType; + + let showLinearRange = + controlType === MidiControlType.Dial && + midiBinding.bindingType === MidiBinding.BINDING_TYPE_CONTROL + && midiBinding.linearControlType === MidiBinding.LINEAR_CONTROL_TYPE; + + + let canRotaryScale: boolean = + controlType === MidiControlType.Dial && + midiBinding.bindingType === MidiBinding.BINDING_TYPE_CONTROL + && midiBinding.linearControlType === MidiBinding.CIRCULAR_CONTROL_TYPE; + - let canLatch: boolean = false; - let canTrigger: boolean = false; - let showLinearControlTypeSelect: boolean = false; - let isBinaryControl: boolean = false; - let showLinearRange: boolean = false; - let canRotaryScale: boolean = false; - if (this.props.midiBinding.symbol === "__bypass") { - isBinaryControl = true; - canLatch = midiBinding.bindingType !== MidiBinding.BINDING_TYPE_NONE; - } else { - let port = nullCast(uiPlugin!.getControl(this.props.midiBinding.symbol)); - isBinaryControl = (port.isAbToggle() || port.isOnOffSwitch()); - if (midiBinding.bindingType !== MidiBinding.BINDING_TYPE_NONE) { - canLatch = isBinaryControl; - canTrigger = port.trigger_property; - showLinearControlTypeSelect = !(canLatch || canTrigger); - showLinearRange = showLinearControlTypeSelect && midiBinding.linearControlType === MidiBinding.LINEAR_CONTROL_TYPE; - canRotaryScale = showLinearControlTypeSelect && midiBinding.linearControlType === MidiBinding.CIRCULAR_CONTROL_TYPE; - } - } return (
this.handleNoteChange(e, extra)} value={midiBinding.note} - + > { - this.generateMidiSelects() + this.generateMidiNoteSelects() } { - if (this.props.listen) - { + onClick={() => { + if (this.props.listen) { this.props.onListen(-2, "", false) } else { this.props.onListen(this.props.instanceId, this.props.midiBinding.symbol, false) } }} size="large"> - { this.props.listen ? ( + {this.props.listen ? ( ) : ( - + )}
@@ -223,26 +276,25 @@ const MidiBindingView = style={{ width: 80 }} onChange={(e, extra) => this.handleControlChange(e, extra)} value={midiBinding.control} - renderValue={(value)=> { return "CC-" + value}} + renderValue={(value) => { return "CC-" + value }} > { this.generateControlSelects() } { - if (this.props.listen) - { + onClick={() => { + if (this.props.listen) { this.props.onListen(-2, "", false) } else { this.props.onListen(this.props.instanceId, this.props.midiBinding.symbol, true) } }} size="large"> - { this.props.listen ? ( + {this.props.listen ? ( ) : ( - + )} @@ -250,35 +302,58 @@ const MidiBindingView = ) } { - ((canLatch) && + ((controlType === MidiControlType.Toggle && midiBinding.bindingType === MidiBinding.BINDING_TYPE_NOTE) && + ( + +
+ +
+ )) + } + { + ((controlType === MidiControlType.Toggle && midiBinding.bindingType === MidiBinding.BINDING_TYPE_CONTROL) && + ( + +
+ +
+ )) + } + { + (controlType === MidiControlType.Trigger && midiBinding.bindingType === MidiBinding.BINDING_TYPE_CONTROL) && (
- )) - } - { - (canTrigger) && - ( -
- (Trigger) -
) } { - (showLinearControlTypeSelect) && + (controlType === MidiControlType.Dial && midiBinding.bindingType === MidiBinding.BINDING_TYPE_CONTROL) && (
@@ -298,8 +373,8 @@ const MidiBindingView =
Min:  { this.handleMinChange(value); } } - /> + min={0} max={1} onChange={(value) => { this.handleMinChange(value); }} + />
) } @@ -308,7 +383,7 @@ const MidiBindingView =
Max:  { this.handleMaxChange(value); } } /> + min={0} max={1} onChange={(value) => { this.handleMaxChange(value); }} />
) } @@ -317,7 +392,7 @@ const MidiBindingView =
Scale:  { this.handleScaleChange(value); } } /> + min={-100} max={100} onChange={(value) => { this.handleScaleChange(value); }} />
) } diff --git a/react/src/PiPedalModel.tsx b/react/src/PiPedalModel.tsx index 286869b..83a0fb8 100644 --- a/react/src/PiPedalModel.tsx +++ b/react/src/PiPedalModel.tsx @@ -1488,6 +1488,10 @@ export class PiPedalModel //implements PiPedalModel } + sendPedalboardControlTrigger(instanceId: number, key: string, value: number) : void { + // no state change, no saving the value, just send it to the realtime thread/ + this._setServerControl("previewControl", instanceId, key, value); + } setPedalboardControl(instanceId: number, key: string, value: number): void { diff --git a/react/src/PluginControl.tsx b/react/src/PluginControl.tsx index abdd3b8..79a577a 100644 --- a/react/src/PluginControl.tsx +++ b/react/src/PluginControl.tsx @@ -502,15 +502,16 @@ const PluginControl = let uiControl = this.props.uiControl; if (uiControl) { - this.model.setPedalboardControl(this.props.instanceId,uiControl.symbol,1); + let value = uiControl.max_value; + if (uiControl.max_value === uiControl.default_value) + { + value = uiControl.min_value; + } + this.model.sendPedalboardControlTrigger(this.props.instanceId,uiControl.symbol,value); } } handleTriggerMouseUp() { - let uiControl = this.props.uiControl; - if (uiControl) - { - this.model.setPedalboardControl(this.props.instanceId,uiControl.symbol,0); - } + // triggers are reset on the audio thread. } previewInputValue(value: number, commitValue: boolean) { let range = this.valueToRange(value); diff --git a/src/Lv2Effect.cpp b/src/Lv2Effect.cpp index 248e985..4680216 100644 --- a/src/Lv2Effect.cpp +++ b/src/Lv2Effect.cpp @@ -305,6 +305,7 @@ void Lv2Effect::PreparePortIndices() size_t nPorts = info->ports().size(); isInputControlPort.resize(nPorts); this->defaultInputControlValues.resize(nPorts); + this->isInputTriggerControlPort.resize(nPorts); for (int i = 0; i < info->ports().size(); ++i) { @@ -343,11 +344,16 @@ void Lv2Effect::PreparePortIndices() } else if (port->is_control_port()) { - controlIndex[port->symbol()] = port->index(); + controlIndex[port->symbol()] = portIndex; if (port->is_input()) { - this->isInputControlPort[i] = true; - this->defaultInputControlValues[i] = port->default_value(); + this->isInputControlPort[portIndex] = true; + this->defaultInputControlValues[portIndex] = port->default_value(); + if (port->trigger_property()) + { + this->isInputTriggerControlPort[portIndex] = true; + } + } } } diff --git a/src/Lv2Effect.hpp b/src/Lv2Effect.hpp index fdc2419..bfb715b 100644 --- a/src/Lv2Effect.hpp +++ b/src/Lv2Effect.hpp @@ -96,6 +96,7 @@ namespace pipedal uint64_t maxInputControlPort = 0; std::vector isInputControlPort; std::vector defaultInputControlValues; + std::vector isInputTriggerControlPort;; virtual std::string GetUri() const { return info->uri(); } diff --git a/src/Lv2Pedalboard.cpp b/src/Lv2Pedalboard.cpp index 01ffb6c..2aa7806 100644 --- a/src/Lv2Pedalboard.cpp +++ b/src/Lv2Pedalboard.cpp @@ -169,6 +169,33 @@ std::vector Lv2Pedalboard::PrepareItems( { pLv2Effect->Run(frames, this->ringBufferWriter); }); + + // Reset any trigger controls to default state after processing + if (pLv2Effect->IsLv2Effect()) + { + Lv2Effect*lv2Effect = (Lv2Effect*)pLv2Effect; + auto pluginInfo = pHost->GetPluginInfo(item.uri()); + if (pluginInfo) { + for (auto control: pluginInfo->ports()) + { + if (control->trigger_property() && control->is_input() && control->is_control_port()) + { + int controlIndex = lv2Effect->GetControlIndex(control->symbol()); + if (controlIndex >= 0) + { + float defaultValue = control->default_value(); + this->processActions.push_back( + [pLv2Effect,controlIndex,defaultValue](int32_t frames) { + pLv2Effect->SetControl(controlIndex,defaultValue); + } + ); + } + + } + } + } + + } } } if (pEffect) @@ -287,13 +314,21 @@ void Lv2Pedalboard::PrepareMidiMap(const PedalboardItem &pedalboardItem) mapping.controlIndex = controlIndex; mapping.midiBinding = binding; mapping.instanceId = pedalboardItem.instanceId(); - if (pPortInfo->IsSwitch()) + + if (pPortInfo->trigger_property()) { - mapping.mappingType = binding.switchControlType() == LATCH_CONTROL_TYPE ? MappingType::Latched : MappingType::Momentary; + mapping.mappingType = MidiControlType::Trigger; + } else if (pPortInfo->IsSwitch()) + { + mapping.mappingType = MidiControlType::Toggle; } - else + else if (pPortInfo->enumeration_property()) { - mapping.mappingType = binding.linearControlType() == LATCH_CONTROL_TYPE ? MappingType::Linear : MappingType::Circular; + mapping.mappingType = MidiControlType::Select; + } + else + { + mapping.mappingType = MidiControlType::Dial; } if (binding.bindingType() == BINDING_TYPE_NOTE) { @@ -651,46 +686,108 @@ void Lv2Pedalboard::OnMidiMessage(size_t size, uint8_t *message, { switch (mapping.mappingType) { - case MappingType::Circular: - case MappingType::Linear: + case MidiControlType::Trigger: { - float thisRange = (mapping.midiBinding.maxValue() - mapping.midiBinding.minValue()) * range + mapping.midiBinding.minValue(); - float value = mapping.pPortInfo->rangeToValue(thisRange); - this->SetControlValue(mapping.effectIndex, mapping.controlIndex, value); - pfnCallback(callbackHandle, mapping.instanceId, mapping.pPortInfo->index(), value); - break; - } - case MappingType::Latched: - { - range = std::round(range); - - if (!mapping.hasLastValue) + bool triggered = false; + if (mapping.midiBinding.switchControlType() == SwitchControlTypeT::TRIGGER_ON_RISING_EDGE + || mapping.midiBinding.bindingType() == BINDING_TYPE_NOTE + ) { - mapping.lastValue = 0; - mapping.hasLastValue = true; + if (mapping.lastValue < range) + { + if (!mapping.lastValueIncreasing) { + triggered = true; + } + mapping.lastValueIncreasing = true; + mapping.lastValue = range; + } else { + mapping.lastValueIncreasing = false; + mapping.lastValue = range; + } + } else { + triggered = true; } - if (range != mapping.lastValue && range == 1) + if (triggered) { IEffect *pEffect = this->realtimeEffects[mapping.effectIndex]; + float value = mapping.pPortInfo->max_value(); + if (value == mapping.pPortInfo->default_value()) + { + value = mapping.pPortInfo->min_value(); + } + this->SetControlValue(mapping.effectIndex, mapping.controlIndex, value); + // do NOT notify anyone! + } + break; + } + case MidiControlType::Toggle: + { + bool triggered = false; + + range = std::round(range); + + if (mapping.midiBinding.switchControlType() == SwitchControlTypeT::TOGGLE_ON_RISING_EDGE) + { + if (range > mapping.lastValue) + { + if (!mapping.lastValueIncreasing) { + triggered = true; + } + mapping.lastValueIncreasing = true; + mapping.lastValue = range; + } else { + mapping.lastValueIncreasing = false; + mapping.lastValue = range; + } + if (triggered) + { + IEffect *pEffect = this->realtimeEffects[mapping.effectIndex]; + float currentValue = pEffect->GetControlValue(mapping.controlIndex); + + currentValue = currentValue == 0 ? 1 : 0; + pEffect->SetControl(mapping.controlIndex, currentValue); + pfnCallback(callbackHandle, mapping.instanceId, mapping.pPortInfo->index(), currentValue); + } + } else if (mapping.midiBinding.switchControlType() == SwitchControlTypeT::TOGGLE_ON_VALUE) + { + triggered = true; + mapping.lastValue = range; + IEffect *pEffect = this->realtimeEffects[mapping.effectIndex]; float currentValue = pEffect->GetControlValue(mapping.controlIndex); - currentValue = currentValue == 0 ? 1 : 0; + if (currentValue != range) + { + pEffect->SetControl(mapping.controlIndex, range); + pfnCallback(callbackHandle, mapping.instanceId, mapping.pPortInfo->index(), range); + } + + } else { + // any control value toggles. + triggered = true; + mapping.lastValue = range; + IEffect *pEffect = this->realtimeEffects[mapping.effectIndex]; + float currentValue = pEffect->GetControlValue(mapping.controlIndex); + currentValue = currentValue == 0 ? 1: 0; pEffect->SetControl(mapping.controlIndex, currentValue); pfnCallback(callbackHandle, mapping.instanceId, mapping.pPortInfo->index(), currentValue); } - mapping.lastValue = range; break; } - case MappingType::Momentary: + case MidiControlType::Select: + case MidiControlType::Dial: { IEffect *pEffect = this->realtimeEffects[mapping.effectIndex]; - float value = mapping.pPortInfo->rangeToValue(range); - if (pEffect->GetControlValue(mapping.controlIndex) != value) + range = mapping.midiBinding.adjustRange(range); + float currentValue = mapping.pPortInfo->rangeToValue(range); + if (pEffect->GetControlValue(mapping.controlIndex) != currentValue) { - this->SetControlValue(mapping.effectIndex, mapping.controlIndex, value); - pfnCallback(callbackHandle, mapping.instanceId, mapping.pPortInfo->index(), value); + this->SetControlValue(mapping.effectIndex, mapping.controlIndex,currentValue); + pfnCallback(callbackHandle, mapping.instanceId, mapping.pPortInfo->index(), currentValue); } break; } + case MidiControlType::None: + default: + break; } } } diff --git a/src/Lv2Pedalboard.hpp b/src/Lv2Pedalboard.hpp index ee949f8..3ddcf29 100644 --- a/src/Lv2Pedalboard.hpp +++ b/src/Lv2Pedalboard.hpp @@ -72,12 +72,13 @@ namespace pipedal RealtimeRingBufferWriter *ringBufferWriter; - enum class MappingType + enum class MidiControlType { - Linear, - Circular, - Momentary, - Latched, + None, + Select, + Dial, + Toggle, + Trigger }; class MidiMapping { @@ -89,8 +90,9 @@ namespace pipedal int controlIndex = -1; int key; // key to the note or control. internal use only. bool hasLastValue = false; - float lastValue = -1; - MappingType mappingType; + bool lastValueIncreasing = false; + float lastValue = 0; + MidiControlType mappingType; MidiBinding midiBinding; }; diff --git a/src/MidiBinding.hpp b/src/MidiBinding.hpp index 0fd0338..f66c14e 100644 --- a/src/MidiBinding.hpp +++ b/src/MidiBinding.hpp @@ -57,10 +57,20 @@ class MapFeature; const int LINEAR_CONTROL_TYPE = 0; const int CIRCULAR_CONTROL_TYPE = 1; - const int LATCH_CONTROL_TYPE = 0; - const int MOMENTARY_CONTROL_TYPE = 1; +enum class SwitchControlTypeT { + TRIGGER_ON_RISING_EDGE = 0, + TRIGGER_ON_ANY = 2, + TOGGLE_ON_RISING_EDGE = 0, + TOGGLE_ON_VALUE = 1, + TOGGLE_ON_ANY = 2, + + LATCH_CONTROL_TYPE = 0, + MOMENTARY_CONTROL_TYPE = 1, + +}; + class MidiBinding { public: @@ -74,7 +84,7 @@ private: float maxValue_ = 1; float rotaryScale_ = 1; int linearControlType_ = LINEAR_CONTROL_TYPE; - int switchControlType_ = LATCH_CONTROL_TYPE; + int switchControlType_ = (int)SwitchControlTypeT::LATCH_CONTROL_TYPE; public: static MidiBinding SystemBinding(const std::string&symbol) { @@ -105,8 +115,15 @@ public: GETTER_SETTER(maxValue); GETTER_SETTER(rotaryScale); GETTER_SETTER(linearControlType); - GETTER_SETTER(switchControlType); + SwitchControlTypeT switchControlType() const { return (SwitchControlTypeT)switchControlType_; } + void switchControlType(SwitchControlTypeT value) { switchControlType_ = (int)value; } + + + float adjustRange(float range) + { + return maxValue_*range+minValue_*(1.0f-range); + } DECLARE_JSON_MAP(MidiBinding); }; diff --git a/todo.txt b/todo.txt index 4fd65aa..999bc0f 100644 --- a/todo.txt +++ b/todo.txt @@ -1,13 +1,40 @@ +- Test midi bindings. -- Start and end connectors are missing in PedalboardView when no device selected (Do they have zero outputs?) + -- Toggle + -- Switch + -- List + -- Dial. +-- make sure trigger controls are set to default value after load/save. + +- reset trigger controls after processing. +- reset triggers before loading. +- review trigger binding behavior. +- triggers dont' change save state. +- Feature for directory locations. + + +- State of MIDI plugins: + - plugin routing is implemented. (maybe needs a not-cheating UI) + - MIDI message routing is not. + - No routing of midi messages yet. + - Midi Channel Filter dialog is implemented, but not yet hooked up. + - Need to think about Midi Channel filters for system and control bindings. A separate filter? + - Bunch of new LV2 interfaces probably needed -- most especially program selection. + - SF2 plugin loads the SF2 file on the Scheduler thread, blocking everyone else fo sevearl seconds. Completion + - becomes a SIGNIFICANT issue, since it's going to leak upward of 1G of memory! + - Severe memory trash/state trash of the SF2 plugin in LV2 restore state. (starts complaining about DBus notification failures!) + - Suspect the bin64 library which has several buffer overrun problems. + + +X Start and end connectors are missing in PedalboardView when no device selected (Do they have zero outputs?) X Review docs changes once we go live. - Localisation: support non-UTF8 code pages. - unicode commandline arguments. (probably the hotspot name) - review unicode filenames (this is probably ok) -- BUG: gcs when we have an animated output control +- BUG: gc problems when we have an animated output control on phones only. Pri Description