diff --git a/vite/src/pipedal/Lv2Plugin.tsx b/vite/src/pipedal/Lv2Plugin.tsx index d45e4b4..e7297b6 100644 --- a/vite/src/pipedal/Lv2Plugin.tsx +++ b/vite/src/pipedal/Lv2Plugin.tsx @@ -1,4 +1,5 @@ -// Copyright (c) Robin E.R. Davies +// Copyright (c) 2022 Robin Davies +// Copyright (c) Fulgencio Ruiz Rubio. // // 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 @@ -835,15 +836,44 @@ export class UiControl implements Deserializable { valueToRange(value: number): number { if (this.toggled_property) return value === 0 ? 0 : 1; - if (this.integer_property || this.enumeration_property) { value = Math.round(value); } - let range = (value - this.min_value) / (this.max_value - this.min_value); + let range: number; + if (this.is_logarithmic) { + let minValue = this.min_value; + if (minValue === 0) // LSP plugins do this. + { + minValue = 0.0001; + } + range = Math.log(value / minValue) / Math.log(this.max_value / minValue); + if (!isFinite(range)) { + if (range < 0) { + range = 0; + } else { + range = 1.0; + } + } else if (isNaN(range)) { + range = 0; + } + if (this.range_steps > 1) { + range = Math.round(range * (this.range_steps - 1)) / (this.range_steps - 1); + } + } else { + range = (value - this.min_value) / (this.max_value - this.min_value); + if (!isFinite(range)) { + if (range < 0) { + range = 0; + } else { + range = 1.0; + } + } else if (isNaN(range)) { + range = 0; + } + } + if (range > 1) range = 1; if (range < 0) range = 0; - - return range; } @@ -852,17 +882,67 @@ export class UiControl implements Deserializable { if (range > 1) range = 1; if (this.toggled_property) return range === 0 ? 0 : 1; + if (this.range_steps > 1) { + range = Math.round(range * (this.range_steps - 1)) / (this.range_steps - 1); + } + + let value: number; - let value = range * (this.max_value - this.min_value) + this.min_value; - if (this.integer_property || this.enumeration_property) { - value = Math.round(value); + if (this.min_value === this.max_value) { + value = this.min_value; + } else { + if (this.is_logarithmic) { + let minValue = this.min_value; + if (minValue === 0) // LSP controls. + { + minValue = 0.0001; + } + value = minValue * Math.pow(this.max_value / minValue, range); + if (!isFinite(value)) { + value = this.max_value; + } else if (isNaN(value)) { + value = this.min_value; + } + } else { + value = range * (this.max_value - this.min_value) + this.min_value; + } + if (this.integer_property) { + value = Math.round(value); + } } return value; } + clampValue(value: number): number { return this.rangeToValue(this.valueToRange(value)); } + getDisplayUnits(): string { + if (this.custom_units !== "") { + return this.custom_units.replace("%f", ""); + } + switch (this.units) { + case Units.bar: return "bar"; + case Units.beat: return "beats"; + case Units.bpm: return "bpm"; + case Units.cent: return "cents"; + case Units.cm: return "cm"; + case Units.db: return "dB"; + case Units.hz: return "Hz"; + case Units.khz: return "kHz"; + case Units.km: return "km"; + case Units.m: return "m"; + case Units.mhz: return "MHz"; + case Units.midiNote: return "MIDI note"; + case Units.min: return "min"; + case Units.ms: return "ms"; + case Units.pc: return "%"; + case Units.s: return "s"; + case Units.semitone12TET: return "st"; + default: return ""; + } + } + formatDisplayValue(value: number): string { if (this.integer_property) { value = Math.round(value); @@ -893,56 +973,9 @@ export class UiControl implements Deserializable { return semitone12TETValue(value); } - let text = this.formatShortValue(value); - - switch (this.units) { - case Units.bpm: - text += "bpm"; - break; - case Units.cent: - text += "cents"; - break; - case Units.cm: - text += "cm"; - break; - case Units.db: - text += "dB"; - break; - case Units.hz: - text += "Hz"; - break; - case Units.khz: - text += "kHz"; - break; - case Units.km: - text += "km"; - break; - case Units.m: - text += "m"; - break; - case Units.mhz: - text += "MHz"; - break; - case Units.min: - text += "min"; - break; - case Units.ms: - text += "ms"; - break; - case Units.pc: - text += "%"; - break; - case Units.unknown: - if (this.custom_units !== "") { - text = this.custom_units.replace("%f", text); - } - break; - default: - break; - - } - return text; + return this.formatShortValue(value) + this.getDisplayUnits(); } + formatShortValue(value: number): string { if (this.enumeration_property) { for (let i = 0; i < this.scale_points.length; ++i) { diff --git a/vite/src/pipedal/MidiBindingView.tsx b/vite/src/pipedal/MidiBindingView.tsx index f7b72a2..e8372f2 100644 --- a/vite/src/pipedal/MidiBindingView.tsx +++ b/vite/src/pipedal/MidiBindingView.tsx @@ -1,4 +1,5 @@ -// Copyright (c) Robin E.R. Davies +// Copyright (c) 2022 Robin Davies +// Copyright (c) Fulgencio Ruiz Rubio. // // 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 @@ -25,7 +26,7 @@ import { withStyles } from "tss-react/mui"; import { createStyles } from './WithStyles'; import Snackbar from '@mui/material/Snackbar'; -import { UiPlugin } from './Lv2Plugin'; +import { UiPlugin, UiControl } from './Lv2Plugin'; import MenuItem from '@mui/material/MenuItem'; import Select from '@mui/material/Select'; @@ -96,6 +97,7 @@ interface MidiBindingViewProps extends WithStyles { midiBinding: MidiBinding; midiControlType: MidiControlType; canDoTapTempo: boolean; + uiControl?: UiControl; onChange: (instanceId: number, newBinding: MidiBinding) => void; } @@ -188,15 +190,28 @@ const MidiBindingView = this.props.onChange(this.props.instanceId, newBinding); } handleMinChange(value: number): void { + if (!this.props.uiControl) return; let newBinding = this.props.midiBinding.clone(); - newBinding.minValue = value; + newBinding.minValue = this.props.uiControl.valueToRange(value) ; this.props.onChange(this.props.instanceId, newBinding); } handleMaxChange(value: number): void { + if (!this.props.uiControl) return; let newBinding = this.props.midiBinding.clone(); - newBinding.maxValue = value; + newBinding.maxValue = this.props.uiControl.valueToRange(value); + console.log(value, newBinding.maxValue) this.props.onChange(this.props.instanceId, newBinding); } + + private getDisplayUnit(): string { + let control = this.props.uiControl; + if (!control) return ""; + if (control.custom_units !== "") { + return control.custom_units.replace("%f", ""); + } + + return ` ${control.getDisplayUnits()}` + } handleCtlMinChange(value: number): void { let newBinding = this.props.midiBinding.clone(); newBinding.minControlValue = Math.round(value); @@ -638,21 +653,36 @@ const MidiBindingView = ) } { - showLinearRange && ( + showLinearRange && (() => { + let uiControl = this.props.uiControl; + let displayMin = uiControl ? uiControl.min_value : 0; + let displayMax = uiControl ? uiControl.max_value : 1; + let unit = this.getDisplayUnit(); + let isInteger = uiControl?.integer_property ?? false; + return (
Min Val:  - { this.handleMinChange(value); }} + { this.handleMinChange(value); }} /> + {unit !== "" && ( +  {unit.trim()} + )}
Max Val:  - { this.handleMaxChange(value); }} /> + { this.handleMaxChange(value); }} /> + {unit !== "" && ( +  {unit.trim()} + )}
- ) + ); + })() } { canRotaryScale && ( diff --git a/vite/src/pipedal/MidiBindingsDialog.tsx b/vite/src/pipedal/MidiBindingsDialog.tsx index e746d55..750343e 100644 --- a/vite/src/pipedal/MidiBindingsDialog.tsx +++ b/vite/src/pipedal/MidiBindingsDialog.tsx @@ -214,6 +214,7 @@ export const MidiBindingDialog = midiControlType={ getMidiControlType(plugin, "__bypass") } canDoTapTempo={false } + uiControl={plugin.getControl("__bypass")} onChange={(instanceId: number, newItem: MidiBinding) => this.handleItemChanged(instanceId, newItem)} /> @@ -258,6 +259,7 @@ export const MidiBindingDialog = midiControlType={getMidiControlType(plugin, symbol)} midiBinding={item.getMidiBinding(symbol)} canDoTapTempo={canDoTapTempo(plugin, symbol)} + uiControl={control} onChange={(instanceId: number, newItem: MidiBinding) => this.handleItemChanged(instanceId, newItem)} /> diff --git a/vite/src/pipedal/ModGuiHost.tsx b/vite/src/pipedal/ModGuiHost.tsx index d123916..92d89cb 100644 --- a/vite/src/pipedal/ModGuiHost.tsx +++ b/vite/src/pipedal/ModGuiHost.tsx @@ -862,7 +862,7 @@ class FilmstripControl implements ModGuiControl { console.error("pipedal: Invalid mod-widget-rotation value: " + rotationAttr); return; } - let range = this.valueToRange(this.value); + let range = this.props.pluginControl.valueToRange(this.value); this.frameElement.style.transform = `rotate(${rotation * range - rotation / 2}deg)`; return; @@ -926,13 +926,13 @@ class FilmstripControl implements ModGuiControl { let pluginControl = this.props.pluginControl; let value = this.isPointerDown ? this.pointerDownValue : this.value; - value = this.clampValue(value); + value = this.props.pluginControl.clampValue(value); let isHorizontalStrip = this.filmstripInfo.frameWidth / this.filmstripInfo.frameHeight < this.filmstripInfo.width / this.filmstripInfo.height; if (isHorizontalStrip) { - let range = this.valueToRange(value); + let range = this.props.pluginControl.valueToRange(value); if (range < 0) { range = 0; @@ -1045,101 +1045,6 @@ class FilmstripControl implements ModGuiControl { this.pointerDownValue = this.value; } - - valueToRange(value: number): number { - let uiControl = this.props.pluginControl; - if (uiControl) { - let range: number; - if (uiControl.is_logarithmic) { - let minValue = uiControl.min_value; - if (minValue === 0) // LSP plugins do this. - { - minValue = 0.0001; - } - range = Math.log(value / minValue) / Math.log(uiControl.max_value / minValue); - if (!isFinite(range)) { - if (range < 0) { - range = 0; - } else { - range = 1.0; - } - } else if (isNaN(range)) { - range = 0; - } - } else { - range = (value - uiControl.min_value) / (uiControl.max_value - uiControl.min_value); - if (!isFinite(range)) { - if (range < 0) { - range = 0; - } else { - range = 1.0; - } - } else if (isNaN(range)) { - range = 0; - } - } - - if (range > 1) range = 1; - if (range < 0) range = 0; - return range; - } - return 0; - } - rangeToValue(range: number): number { - if (range < 0) range = 0; - if (range > 1) range = 1; - let uiControl = this.props.pluginControl; - if (uiControl) { - let value: number; - if (uiControl.min_value === uiControl.max_value) { - value = uiControl.min_value; - } else { - if (uiControl.is_logarithmic) { - let minValue = uiControl.min_value; - if (minValue === 0) // LSP controls. - { - minValue = 0.0001; - } - value = minValue * Math.pow(uiControl.max_value / minValue, range); - if (!isFinite(value)) { - value = uiControl.max_value; - } else if (isNaN(value)) { - value = uiControl.min_value; - } - } else { - value = range * (uiControl.max_value - uiControl.min_value) + uiControl.min_value; - } - } - return value; - } - return 0; - } - - clampValue(value: number): number { - let control = this.props.pluginControl; - if (control.enumeration_property) { - value = control.clampSelectValue(value); - } else if (control.toggled_property) { - if (value < (control.min_value + control.max_value) / 2) { - value = control.min_value; - } else { - value = control.max_value; - } - } else if (control.integer_property) { - value = Math.round(value); - } else if (control.range_steps != 0) { - let step = (control.max_value - control.min_value) / control.range_steps; - value = Math.round((value - control.min_value) / step) * step + control.min_value; - } - if (value < control.min_value) { - value = control.min_value; - } - if (value > control.max_value) { - value = control.max_value; - } - return value; - } - handlePointerMove(event: PointerEvent) { if (!this.frameElement) { return; // Not mounted yet @@ -1167,14 +1072,14 @@ class FilmstripControl implements ModGuiControl { let dRange = -dy / rate; - let range = this.valueToRange(this.pointerDownValue); + let range = this.props.pluginControl.valueToRange(this.pointerDownValue); range += dRange; if (range < 0) range = 0; if (range > 1) range = 1; - let newValue = this.rangeToValue(range); + let newValue = this.props.pluginControl.rangeToValue(range); this.pointerDownValue = newValue; this.setControlValue(newValue); - this.props.onValueChanged(this.props.instanceId, this.props.pluginControl.symbol, this.clampValue(newValue)); + this.props.onValueChanged(this.props.instanceId, this.props.pluginControl.symbol, this.props.pluginControl.clampValue(newValue)); } handlePointerUp(event: PointerEvent) { let index = this.pointerIds.indexOf(event.pointerId); @@ -1229,11 +1134,11 @@ class FilmstripControl implements ModGuiControl { } let dRange = event.deltaY / rate; - let range = this.valueToRange(this.value); + let range = this.props.pluginControl.valueToRange(this.value); range += dRange; if (range < 0) range = 0; if (range > 1) range = 1; - let newValue = this.rangeToValue(range); + let newValue = this.props.pluginControl.rangeToValue(range); this.setControlValue(newValue); this.props.onValueChanged(this.props.instanceId, this.props.pluginControl.symbol, newValue); } diff --git a/vite/src/pipedal/PluginControl.tsx b/vite/src/pipedal/PluginControl.tsx index 826010a..f7de1ec 100644 --- a/vite/src/pipedal/PluginControl.tsx +++ b/vite/src/pipedal/PluginControl.tsx @@ -1,4 +1,5 @@ -// Copyright (c) Robin E.R. Davies +// Copyright (c) 2022 Robin Davies +// Copyright (c) Fulgencio Ruiz Rubio. // // 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 @@ -203,7 +204,6 @@ const CustomPluginControl = ); - return (
); } }, @@ -342,8 +342,9 @@ const PluginControl = result = this.currentValue; // reset the value! } // clamp and quantize. - let range = this.valueToRange(result); - result = this.rangeToValue(range); + let range = this.props.uiControl?.valueToRange(result) ?? 0; + result = this.props.uiControl?.rangeToValue(range) ?? 0; + let displayVal = this.props.uiControl?.formatShortValue(result) ?? ""; if (event.currentTarget) { event.currentTarget.value = displayVal; @@ -682,8 +683,8 @@ const PluginControl = this.setState({ previewValue: undefined }); } previewInputValue(value: number, commitValue: boolean) { - let range = this.valueToRange(value); - value = this.rangeToValue(range); + let range = this.props.uiControl?.valueToRange(value) ?? 0; + value = this.props.uiControl?.rangeToValue(range) ?? 0; let imgElement = this.imgRef.current if (imgElement) { @@ -712,13 +713,13 @@ const PluginControl = } previewRange(dRange: number, commitValue: boolean): void { - let range = this.valueToRange(this.currentValue) + dRange; + let range = (this.props.uiControl?.valueToRange(this.currentValue) ?? 0) + dRange; if (range > 1) range = 1; if (range < 0) range = 0; - let value = this.rangeToValue(range); + let value = this.props.uiControl?.rangeToValue(range) ?? 0; // apply value quantization and clipping. - range = this.valueToRange(value); + range = this.props.uiControl?.valueToRange(value) ?? 0; if (this.props.uiControl?.isGraphicEq()) { let imgElement = this.imgRef.current @@ -835,89 +836,6 @@ const PluginControl = if (!uiControl) return ""; return uiControl.formatDisplayValue(value); - - - } - - valueToRange(value: number): number { - let uiControl = this.props.uiControl; - if (uiControl) { - if (uiControl.integer_property) { - value = Math.round(value); - } - let range: number; - if (uiControl.is_logarithmic) { - let minValue = uiControl.min_value; - if (minValue === 0) // LSP plugins do this. - { - minValue = 0.0001; - } - range = Math.log(value / minValue) / Math.log(uiControl.max_value / minValue); - if (!isFinite(range)) { - if (range < 0) { - range = 0; - } else { - range = 1.0; - } - } else if (isNaN(range)) { - range = 0; - } - if (uiControl.range_steps > 1) { - range = Math.round(range * (uiControl.range_steps - 1)) / (uiControl.range_steps - 1); - } - } else { - range = (value - uiControl.min_value) / (uiControl.max_value - uiControl.min_value); - if (!isFinite(range)) { - if (range < 0) { - range = 0; - } else { - range = 1.0; - } - } else if (isNaN(range)) { - range = 0; - } - } - - if (range > 1) range = 1; - if (range < 0) range = 0; - return range; - } - return 0; - } - rangeToValue(range: number): number { - if (range < 0) range = 0; - if (range > 1) range = 1; - let uiControl = this.props.uiControl; - if (uiControl) { - if (uiControl.range_steps > 1) { - range = Math.round(range * (uiControl.range_steps - 1)) / (uiControl.range_steps - 1); - } - let value: number; - if (uiControl.min_value === uiControl.max_value) { - value = uiControl.min_value; - } else { - if (uiControl.is_logarithmic) { - let minValue = uiControl.min_value; - if (minValue === 0) // LSP controls. - { - minValue = 0.0001; - } - value = minValue * Math.pow(uiControl.max_value / minValue, range); - if (!isFinite(value)) { - value = uiControl.max_value; - } else if (isNaN(value)) { - value = uiControl.min_value; - } - } else { - value = range * (uiControl.max_value - uiControl.min_value) + uiControl.min_value; - } - if (uiControl.integer_property) { - value = Math.round(value); - } - } - return value; - } - return 0; } rangeToRotationTransform(range: number): string { @@ -926,22 +844,10 @@ const PluginControl = } getEqPosition(): number { - let range = 0; - let uiControl = this.props.uiControl; - if (uiControl) { - let value = this.props.value; - range = this.valueToRange(value); - } - return range; - + return this.props.uiControl?.valueToRange(this.props.value) ?? 0; } getRotationTransform(): string { - let range = 0; - let uiControl = this.props.uiControl; - if (uiControl) { - let value = this.props.value; - range = this.valueToRange(value); - } + let range = this.props.uiControl?.valueToRange(this.props.value) ?? 0; return this.rangeToRotationTransform(range); }