From ae7e6f5abeea0dce32169dd14099cd187c366713 Mon Sep 17 00:00:00 2001 From: Robin Davies Date: Thu, 24 Oct 2024 04:18:37 -0400 Subject: [PATCH] Trigger properties. --- react/src/Lv2Plugin.tsx | 14 +- react/src/MidiBindingView.tsx | 2 +- react/src/PluginControl.tsx | 244 ++++++++++++++++++---------------- react/src/SplitUiControls.tsx | 12 ++ src/PluginHost.cpp | 5 +- src/PluginHost.hpp | 8 +- 6 files changed, 161 insertions(+), 124 deletions(-) diff --git a/react/src/Lv2Plugin.tsx b/react/src/Lv2Plugin.tsx index 29b54c3..dbf9538 100644 --- a/react/src/Lv2Plugin.tsx +++ b/react/src/Lv2Plugin.tsx @@ -436,6 +436,7 @@ export enum ControlType { OnOffSwitch, ABSwitch, Select, + Trigger, Tuner, Vu, @@ -444,7 +445,7 @@ export enum ControlType { } export class UiControl implements Deserializable { - deserialize(input: any): UiControl { +deserialize(input: any): UiControl { this.symbol = input.symbol; this.name = input.name; this.index = input.index; @@ -458,7 +459,7 @@ export class UiControl implements Deserializable { this.integer_property = input.integer_property; this.enumeration_property = input.enumeration_property; this.toggled_property = input.toggled_property; - this.trigger = input.trigger; + this.trigger_property = input.trigger_property; this.not_on_gui = input.not_on_gui; this.scale_points = ScalePoint.deserialize_array(input.scale_points); this.port_group = input.port_group; @@ -499,6 +500,10 @@ export class UiControl implements Deserializable { this.controlType = ControlType.OnOffSwitch; } } + if (this.is_input && this.trigger_property) + { + this.controlType = ControlType.Trigger; + } return this; } applyProperties(properties: Partial): UiControl { @@ -546,7 +551,7 @@ export class UiControl implements Deserializable { range_steps: number = 0; integer_property: boolean = false; enumeration_property: boolean = false; - trigger: boolean = false; + trigger_property: boolean = false; not_on_gui: boolean = false; toggled_property: boolean = false; scale_points: ScalePoint[] = []; @@ -590,6 +595,9 @@ export class UiControl implements Deserializable { isSelect(): boolean { return this.controlType === ControlType.Select; } + isTrigger(): boolean { + return this.controlType === ControlType.Trigger; + } isOutputSelect(): boolean { return !this.is_input && this.controlType === ControlType.OutputSelect; } diff --git a/react/src/MidiBindingView.tsx b/react/src/MidiBindingView.tsx index 4df072f..2a9c8fc 100644 --- a/react/src/MidiBindingView.tsx +++ b/react/src/MidiBindingView.tsx @@ -160,7 +160,7 @@ const MidiBindingView = isBinaryControl = (port.isAbToggle() || port.isOnOffSwitch()); if (midiBinding.bindingType !== MidiBinding.BINDING_TYPE_NONE) { canLatch = isBinaryControl; - canTrigger = port.trigger; + canTrigger = port.trigger_property; showLinearControlTypeSelect = !(canLatch || canTrigger); showLinearRange = showLinearControlTypeSelect && midiBinding.linearControlType === MidiBinding.LINEAR_CONTROL_TYPE; canRotaryScale = showLinearControlTypeSelect && midiBinding.linearControlType === MidiBinding.CIRCULAR_CONTROL_TYPE; diff --git a/react/src/PluginControl.tsx b/react/src/PluginControl.tsx index 64a6dd1..dcfdb87 100644 --- a/react/src/PluginControl.tsx +++ b/react/src/PluginControl.tsx @@ -18,6 +18,7 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import React, { TouchEvent, PointerEvent, ReactNode, Component, SyntheticEvent } from 'react'; +import Button from '@mui/material/Button'; import { Theme } from '@mui/material/styles'; import { WithStyles } from '@mui/styles'; import createStyles from '@mui/styles/createStyles'; @@ -27,10 +28,11 @@ import Typography from '@mui/material/Typography'; import Input from '@mui/material/Input'; import Select from '@mui/material/Select'; import Switch from '@mui/material/Switch'; -import Utility, {nullCast} from './Utility'; +import Utility, { nullCast } from './Utility'; import MenuItem from '@mui/material/MenuItem'; -import {PiPedalModel,PiPedalModelFactory} from './PiPedalModel'; -import {ReactComponent as DialIcon} from './svg/fx_dial.svg'; +import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel'; +import { ReactComponent as DialIcon } from './svg/fx_dial.svg'; +import { isDarkMode } from './DarkMode'; const MIN_ANGLE = -135; const MAX_ANGLE = 135; @@ -94,8 +96,7 @@ type PluginControlState = { const PluginControl = withStyles(styles, { withTheme: true })( - class extends Component - { + class extends Component { frameRef: React.RefObject; imgRef: React.RefObject; @@ -140,23 +141,18 @@ const PluginControl = return Utility.needsZoomedControls(); } - showZoomedControl() - { - if (this.props.uiControl && this.frameRef.current) - { - this.model.zoomUiControl(this.frameRef.current,this.props.instanceId,this.props.uiControl); + showZoomedControl() { + if (this.props.uiControl && this.frameRef.current) { + this.model.zoomUiControl(this.frameRef.current, this.props.instanceId, this.props.uiControl); } } - hideZoomedControl() - { - if (this.frameRef.current && this.model.zoomedUiControl.get()?.source === this.frameRef.current) - { + hideZoomedControl() { + if (this.frameRef.current && this.model.zoomedUiControl.get()?.source === this.frameRef.current) { this.model.clearZoomedControl(); } } - componentWillUnmount() - { + componentWillUnmount() { this.hideZoomedControl(); } inputChanged: boolean = false; @@ -172,12 +168,11 @@ const PluginControl = } onInputFocus(event: SyntheticEvent): void { this.displayValueRef.current!.style.display = "none"; - if (Utility.hasIMEKeyboard()) - { + if (Utility.hasIMEKeyboard()) { event.preventDefault(); event.stopPropagation(); this.inputRef.current?.blur(); - this.props.requestIMEEdit(nullCast(this.props.uiControl),this.props.value) + this.props.requestIMEEdit(nullCast(this.props.uiControl), this.props.value) } } onInputKeyPress(e: any): void { @@ -221,7 +216,7 @@ const PluginControl = // clamp and quantize. let range = this.valueToRange(result); result = this.rangeToValue(range); - let displayVal = this.props.uiControl?.formatShortValue(result)??""; + let displayVal = this.props.uiControl?.formatShortValue(result) ?? ""; if (event.currentTarget) { event.currentTarget.value = displayVal; } @@ -270,11 +265,9 @@ const PluginControl = capturedPointers: number[] = []; - onBodyPointerDownCapture(e_: any): any - { + onBodyPointerDownCapture(e_: any): any { let e = e_ as PointerEvent; - if (this.isExtraTouch(e)) - { + if (this.isExtraTouch(e)) { this.isTap = false; this.captureElement!.setPointerCapture(e.pointerId); this.capturedPointers.push(e.pointerId); @@ -305,10 +298,8 @@ const PluginControl = e.preventDefault(); e.stopPropagation(); - if (this.isTouchDevice()) - { - if (this.props.uiControl?.isDial()??false) - { + if (this.isTouchDevice()) { + if (this.props.uiControl?.isDial() ?? false) { this.isTap = false; this.showZoomedControl(); return; @@ -318,8 +309,7 @@ const PluginControl = ++this.pointersDown; this.mouseDown = true; - if (this.pointersDown === 1) - { + if (this.pointersDown === 1) { this.isTap = true; this.tapStartMs = Date.now(); } else { @@ -340,8 +330,8 @@ const PluginControl = this.captureElement = img; document.body.addEventListener( "pointerdown", - this.onBodyPointerDownCapture,true - ); + this.onBodyPointerDownCapture, true + ); img.setPointerCapture(e.pointerId); if (img.style) { @@ -350,11 +340,10 @@ const PluginControl = } } else { - if (this.isExtraTouch(e)) - { + if (this.isExtraTouch(e)) { ++this.pointersDown; this.isTap = false; - + } } } @@ -367,7 +356,7 @@ const PluginControl = if (this.isCapturedPointer(e)) { --this.pointersDown; this.isTap = false; - + this.releaseCapture(e); } @@ -378,21 +367,16 @@ const PluginControl = let ultraHigh = false; let high = false; - if (e.ctrlKey) - { + if (e.ctrlKey) { ultraHigh = true; } - if (e.shiftKey) - { + if (e.shiftKey) { high = true; } - if (e.pointerType === "touch") - { - if (this.pointersDown >= 3) - { + if (e.pointerType === "touch") { + if (this.pointersDown >= 3) { ultraHigh = true; - } else if (this.pointersDown === 2) - { + } else if (this.pointersDown === 2) { high = true; } } @@ -411,28 +395,24 @@ const PluginControl = } private lastTapMs = 0; - resetToDefaultValue(uiControl: UiControl): void - { + resetToDefaultValue(uiControl: UiControl): void { let value = uiControl.default_value; - this.model.setPedalboardControl(this.props.instanceId,uiControl.symbol,value); + this.model.setPedalboardControl(this.props.instanceId, uiControl.symbol, value); } onPointerDoubleTap() { let uiControl = this.props.uiControl; - if (uiControl) - { - if (uiControl.isDial()) - { + if (uiControl) { + if (uiControl.isDial()) { this.resetToDefaultValue(uiControl); } } } onPointerTap() { let tapTime = Date.now(); - let dT = tapTime-this.lastTapMs; + let dT = tapTime - this.lastTapMs; this.lastTapMs = tapTime; - if (dT < 500) - { + if (dT < 500) { this.onPointerDoubleTap(); } } @@ -442,32 +422,29 @@ const PluginControl = if (this.isCapturedPointer(e)) { --this.pointersDown; - + e.preventDefault(); let dRange = this.updateRange(e) this.previewRange(dRange, true); this.releaseCapture(e); - if (this.isTap) - { - let ms = Date.now()-this.tapStartMs; - if (ms < 200) - { + if (this.isTap) { + let ms = Date.now() - this.tapStartMs; + if (ms < 200) { this.onPointerTap(); } } } else { --this.pointersDown; - + } } - releaseCapture(e: PointerEvent) - { + releaseCapture(e: PointerEvent) { let img = this.imgRef.current; - + if (img && img.style) { img.releasePointerCapture(e.pointerId); img.style.opacity = "" + DEFAULT_OPACITY; @@ -480,14 +457,14 @@ const PluginControl = } document.body.removeEventListener( "pointerdown", - this.onBodyPointerDownCapture,true - ); + this.onBodyPointerDownCapture, true + ); this.mouseDown = false; } - clickSlop() { + clickSlop() { return 3.5; // maybe larger on touch devices. } onPointerMove(e: PointerEvent): void { @@ -499,15 +476,27 @@ const PluginControl = let x = e.clientX; let y = e.clientY; let dx = x - this.startX; - let dy = y-this.startY; - let distance = Math.sqrt(dx*dx+dy*dy); - if (distance >= this.clickSlop()) - { + let dy = y - this.startY; + let distance = Math.sqrt(dx * dx + dy * dy); + if (distance >= this.clickSlop()) { this.isTap = false; } } } - + handleTriggerMouseDown() { + let uiControl = this.props.uiControl; + if (uiControl) + { + this.model.setPedalboardControl(this.props.instanceId,uiControl.symbol,1); + } + } + handleTriggerMouseUp() { + let uiControl = this.props.uiControl; + if (uiControl) + { + this.model.setPedalboardControl(this.props.instanceId,uiControl.symbol,0); + } + } previewInputValue(value: number, commitValue: boolean) { let range = this.valueToRange(value); value = this.rangeToValue(range); @@ -547,13 +536,12 @@ const PluginControl = } let inputElement = this.inputRef.current; if (inputElement) { - let v = this.props.uiControl?.formatShortValue(value)??""; + let v = this.props.uiControl?.formatShortValue(value) ?? ""; inputElement.value = v; } let displayValue = this.displayValueRef.current; - if (displayValue) - { - let v = this.formatDisplayValue(this.props.uiControl,value); + if (displayValue) { + let v = this.formatDisplayValue(this.props.uiControl, value); displayValue.childNodes[0].textContent = v; } let selectElement = this.selectRef.current; @@ -577,9 +565,9 @@ const PluginControl = } onSelectChanged(e: any, value: any) { let target = e.target; - setTimeout(()=> { + setTimeout(() => { this.props.onChange(target.value); - },0); + }, 0); } @@ -610,7 +598,7 @@ const PluginControl = ); } else { return ( -
{ this.inputRef.current!.focus(); }} > - - {this.formatDisplayValue(control, value)} + + {this.formatDisplayValue(control, value)}
) diff --git a/react/src/SplitUiControls.tsx b/react/src/SplitUiControls.tsx index 0dc28e0..e7206dc 100644 --- a/react/src/SplitUiControls.tsx +++ b/react/src/SplitUiControls.tsx @@ -37,6 +37,7 @@ export const SplitTypeControl: UiControl = new UiControl().deserialize({ enumeration_property: true, not_on_gui: false, toggled_property: false, + trigger_property: false, scale_points: ScalePoint.deserialize_array( [ { value: 0, label: "A/B" }, @@ -60,6 +61,7 @@ export const SplitAbControl: UiControl = new UiControl().deserialize({ enumeration_property: true, not_on_gui: false, toggled_property: false, + trigger_property: false, scale_points: ScalePoint.deserialize_array( [ { value: 0, label: "A" }, @@ -83,6 +85,8 @@ export const SplitMixControl: UiControl = new UiControl().deserialize({ enumeration_property: false, not_on_gui: false, toggled_property: false, + trigger_property: false, + scale_points: [] }); @@ -100,6 +104,8 @@ export const SplitPanLeftControl: UiControl = new UiControl().deserialize({ enumeration_property: false, not_on_gui: false, toggled_property: false, + trigger_property: false, + scale_points: [] }); @@ -117,6 +123,8 @@ export const SplitVolLeftControl: UiControl = new UiControl().deserialize({ enumeration_property: false, not_on_gui: false, toggled_property: false, + trigger_property: false, + units: Units.db, scale_points: [ new ScalePoint().deserialize({ @@ -141,6 +149,8 @@ export const SplitPanRightControl: UiControl = new UiControl().deserialize({ enumeration_property: false, not_on_gui: false, toggled_property: false, + trigger_property: false, + scale_points: [] }); @@ -158,6 +168,8 @@ export const SplitVolRightControl: UiControl = new UiControl().deserialize({ enumeration_property: false, not_on_gui: false, toggled_property: false, + trigger_property: false, + units: Units.db, scale_points: [ new ScalePoint().deserialize({ diff --git a/src/PluginHost.cpp b/src/PluginHost.cpp index 8239316..9030417 100644 --- a/src/PluginHost.cpp +++ b/src/PluginHost.cpp @@ -118,6 +118,7 @@ void PluginHost::LilvUris::Initialize(LilvWorld *pWorld) core__toggled = lilv_new_uri(pWorld, LV2_CORE__toggled); core__connectionOptional = lilv_new_uri(pWorld, LV2_CORE__connectionOptional); portprops__not_on_gui_property_uri = lilv_new_uri(pWorld, LV2_PORT_PROPS__notOnGUI); + portprops__trigger = lilv_new_uri(pWorld,LV2_PORT_PROPS__trigger); midi__event = lilv_new_uri(pWorld, LV2_MIDI__MidiEvent); core__designation = lilv_new_uri(pWorld, LV2_CORE__designation); portgroups__group = lilv_new_uri(pWorld, LV2_PORT_GROUPS__group); @@ -932,6 +933,7 @@ Lv2PortInfo::Lv2PortInfo(PluginHost *host, const LilvPlugin *plugin, const LilvP this->toggled_property_ = lilv_port_has_property(plugin, pPort, host->lilvUris->core__toggled); this->not_on_gui_ = lilv_port_has_property(plugin, pPort, host->lilvUris->portprops__not_on_gui_property_uri); this->connection_optional_ = lilv_port_has_property(plugin, pPort, host->lilvUris->core__connectionOptional); + this->trigger_property_ = lilv_port_has_property(plugin,pPort,host->lilvUris->portprops__trigger); LilvScalePoints *pScalePoints = lilv_port_get_scale_points(plugin, pPort); LILV_FOREACH(scale_points, iSP, pScalePoints) @@ -1593,7 +1595,7 @@ json_map::storage_type Lv2PortInfo::jmap{ MAP_REF(Lv2PortInfo, is_logarithmic), MAP_REF(Lv2PortInfo, display_priority), MAP_REF(Lv2PortInfo, range_steps), - MAP_REF(Lv2PortInfo, trigger), + MAP_REF(Lv2PortInfo, trigger_property), MAP_REF(Lv2PortInfo, integer_property), MAP_REF(Lv2PortInfo, enumeration_property), MAP_REF(Lv2PortInfo, toggled_property), @@ -1662,6 +1664,7 @@ json_map::storage_type Lv2PluginUiPort::jmap{{ MAP_REF(Lv2PluginUiPort, enumeration_property), MAP_REF(Lv2PluginUiPort, not_on_gui), MAP_REF(Lv2PluginUiPort, toggled_property), + MAP_REF(Lv2PluginUiPort, trigger_property), MAP_REF(Lv2PluginUiPort, scale_points), MAP_REF(Lv2PluginUiPort, port_group), diff --git a/src/PluginHost.hpp b/src/PluginHost.hpp index 88339d3..2638207 100644 --- a/src/PluginHost.hpp +++ b/src/PluginHost.hpp @@ -214,7 +214,7 @@ namespace pipedal bool is_logarithmic_ = false; int display_priority_ = -1; int range_steps_ = 0; - bool trigger_ = false; + bool trigger_property_ = false; bool integer_property_ = false; bool enumeration_property_ = false; bool toggled_property_ = false; @@ -285,7 +285,7 @@ namespace pipedal LV2_PROPERTY_GETSET_SCALAR(is_logarithmic); LV2_PROPERTY_GETSET_SCALAR(display_priority); LV2_PROPERTY_GETSET_SCALAR(range_steps); - LV2_PROPERTY_GETSET_SCALAR(trigger); + LV2_PROPERTY_GETSET_SCALAR(trigger_property); LV2_PROPERTY_GETSET_SCALAR(integer_property); LV2_PROPERTY_GETSET_SCALAR(enumeration_property); LV2_PROPERTY_GETSET_SCALAR(toggled_property); @@ -496,6 +496,7 @@ namespace pipedal default_value_(pPort->default_value()), range_steps_(pPort->range_steps()), display_priority_(pPort->display_priority()), is_logarithmic_(pPort->is_logarithmic()), integer_property_(pPort->integer_property()), enumeration_property_(pPort->enumeration_property()), toggled_property_(pPort->toggled_property()), not_on_gui_(pPort->not_on_gui()), scale_points_(pPort->scale_points()), + trigger_property_(pPort->trigger_property()), comment_(pPort->comment()), units_(pPort->units()), connection_optional_(pPort->connection_optional()) { @@ -536,6 +537,7 @@ namespace pipedal bool enumeration_property_ = false; bool not_on_gui_ = false; bool toggled_property_ = false; + bool trigger_property_ = false; std::vector scale_points_; std::string port_group_; @@ -561,6 +563,7 @@ namespace pipedal LV2_PROPERTY_GETSET_SCALAR(integer_property); LV2_PROPERTY_GETSET_SCALAR(enumeration_property); LV2_PROPERTY_GETSET_SCALAR(toggled_property); + LV2_PROPERTY_GETSET_SCALAR(trigger_property); LV2_PROPERTY_GETSET_SCALAR(not_on_gui); LV2_PROPERTY_GETSET(scale_points); LV2_PROPERTY_GETSET(units); @@ -664,6 +667,7 @@ namespace pipedal AutoLilvNode core__toggled; AutoLilvNode core__connectionOptional; AutoLilvNode portprops__not_on_gui_property_uri; + AutoLilvNode portprops__trigger; AutoLilvNode midi__event; AutoLilvNode core__designation; AutoLilvNode portgroups__group;