Files
op-pedal/react/src/Lv2Plugin.tsx
T
2024-12-03 14:06:57 -05:00

1027 lines
32 KiB
TypeScript

// Copyright (c) 2022 Robin 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.
import Units from './Units';
interface Deserializable<T> {
deserialize(input: any): T;
}
const noteNames: string[] = [
"C","C#","D","Eb","E","F","F#","G","Ab","A","Bb","B","C"
];
function semitone12TETValue(value: number) : string {
let iValue = Math.round(value) % 12;
return noteNames[iValue];
}
export class Port implements Deserializable<Port> {
deserialize(input: any): Port {
this.port_index = input.port_index;
this.symbol = input.symbol;
this.name = input.name;
this.min_value = input.min_value;
this.max_value = input.max_value;
this.default_value = input.default_value;
this.scale_points = ScalePoint.deserialize_array(input.scale_points);
this.is_input = input.is_input;
this.is_output = input.is_output;
this.is_control_port = input.is_control_port;
this.is_audio_port = input.is_audio_port;
this.is_atom_port = input.is_atom_port;
this.is_valid = input.is_valid;
this.supports_midi = input.supports_midi;
this.supports_time_position = input.supports_time_position;
this.port_group = input.port_group;
this.comment = input.comment;
this.is_bypass = input.is_bypass;
this.is_program_controller = input.is_program_controller;
return this;
}
static EmptyPorts: Port[] = [];
static deserialize_array(input: any): Port[] {
let result: Port[] = [];
for (let i = 0; i < input.length; ++i) {
result[i] = new Port().deserialize(input[i]);
}
return result;
}
port_index: number = -1;
symbol: string = "";
name: string = "";
min_value: number = 0;
max_value: number = 1;
default_value: number = 0.5;
scale_points: ScalePoint[] = [];
is_input: boolean = false;
is_output: boolean = false
is_control_port: boolean = false;
is_audio_port: boolean = false;
is_atom_port: boolean = false;
is_valid: boolean = false;
supports_midi: boolean = false;
supports_time_position: boolean = false;
port_group: string = "";
comment: string = "";
is_bypass: boolean = false;
is_program_controller: boolean = false;
}
export class PortGroup {
deserialize(input: any): PortGroup {
this.uri = input.uri;
this.symbol = input.symbol;
this.name = input.name;
this.parent_group = input.parent_group;
this.program_list_id = input.program_list_id ?? -1;
return this;
}
static deserialize_array(input: any): PortGroup[] {
let result: PortGroup[] = [];
for (let i = 0; i < input.length; ++i) {
result.push(new PortGroup().deserialize(input[i]));
}
return result;
}
uri: string = "";
symbol: string = "";
name: string = "";
parent_group: string = "";
program_list_id: number = -1;
};
export class UiFileType {
deserialize(input: any): UiFileType {
this.name = input.name;
this.fileExtension = input.fileExtension;
this.mimeType = input.mimeType;
return this;
}
static deserialize_array(input: any): UiFileType[] {
let result: UiFileType[] = [];
for (let i = 0; i < input.length; ++i) {
result[i] = new UiFileType().deserialize(input[i]);
}
return result;
}
private static IsAndroid(): boolean {
return /Android/i.test(navigator.userAgent);
}
static MergeMimeTypes(fileTypes: UiFileType[]): string {
if (fileTypes.length === 0) {
return "";
}
if (this.IsAndroid()) {
return ""; // allow selection of .zip files, plus whatever. We'll catch it later.
} else {
// chrome desktop thinks "application/octet-stream" is .exe, .com, or .bat.
// Feed it file extensions isntead.
let result = "";
for (let i = 0; i < fileTypes.length; ++i) {
let fileType = fileTypes[i];
if (fileType.fileExtension !== "") {
if (result.length !== 0) // prefer file extensions.
{
result += ",";
result += fileType.fileExtension;
}
} else {
if (fileType.mimeType !== result) {
let t = "";
if (result.startsWith("audio/") && fileType.mimeType.startsWith("audio/")) {
t = "audio/*";
} else if (result.startsWith("video/") && fileType.mimeType.startsWith("video/")) {
t = "video/*";
} else if (result.startsWith("text/") && fileType.mimeType.startsWith("text/")) {
t = "text/*";
}
if (t.length !== 0) {
if (result.length !== 0) {
result += ",";
}
result += t;
}
}
}
}
let hasZip = false;
let types = result.split(",");
for (let thisType of types) {
if (thisType === ".zip") {
hasZip = true;
break;
}
}
// add .zip files
if (!hasZip) {
if (result.length !== 0) {
result += ",.zip";
}
}
return result;
}
}
name: string = "";
fileExtension: string = "";
mimeType: string = "";
}
export class UiPropertyNotification {
deserialize(input: any): UiPropertyNotification {
this.portIndex = input.portIndex;
this.symbol = input.symbol;
this.plugin = input.plugin;
this.protocol = input.protocol;
return this;
}
static deserialize_array(input: any): UiPropertyNotification[] {
let result: UiPropertyNotification[] = [];
for (let i = 0; i < input.length; ++i) {
result[i] = new UiPropertyNotification().deserialize(input[i]);
}
return result;
}
portIndex: number = -1;
symbol: string = "";
plugin: string = "";
protocol: string = "";
};
export class UiFrequencyPlot {
deserialize(input: any): UiFrequencyPlot {
this.patchProperty = input.patchProperty;
this.index = input.index;
this.portGroup = input.portGroup;
this.xLeft = input.xLeft;
this.xRight = input.xRight;
this.xLog = input.xLog;
this.yTop = input.yTop;
this.yBottom = input.yBottom;
this.width = input.width;
return this;
}
static deserialize_array(input: any): UiFrequencyPlot[] {
let result: UiFrequencyPlot[] = [];
for (let i = 0; i < input.length; ++i) {
result[i] = new UiFrequencyPlot().deserialize(input[i]);
}
return result;
}
patchProperty: string = "";
index: number = -1;
portGroup: string = "";
xLeft: number = -1;
xRight: number = -1;
xLog: boolean = true;
yTop: number = -1;
yBottom: number = -1;
width: number = -1;
};
export class UiFileProperty {
deserialize(input: any): UiFileProperty {
this.label = input.label;
this.fileTypes = UiFileType.deserialize_array(input.fileTypes);
this.patchProperty = input.patchProperty;
this.directory = input.directory;
this.index = input.index;
this.portGroup = input.portGroup;
this.resourceDirectory = input.resourceDirectory ?? "";
this.modDirectories = input.modDirectories;
this.useLegacyModDirectory = input.useLegacyModDirectory;
return this;
}
static deserialize_array(input: any): UiFileProperty[] {
let result: UiFileProperty[] = [];
for (let i = 0; i < input.length; ++i) {
result[i] = new UiFileProperty().deserialize(input[i]);
}
return result;
}
private static getFileExtension(name: string): string {
let pos = name.lastIndexOf('.');
let filenamePos = name.lastIndexOf('/') + 1;
filenamePos = Math.max(name.lastIndexOf('\\') + 1);
filenamePos = Math.max(name.lastIndexOf(':') + 1);
if (pos < filenamePos) {
return "";
}
if (pos !== -1) {
return name.substring(pos);
}
return "";
}
wantsFile(filename: string): boolean {
if (this.fileTypes.length === 0) {
return true;
}
let extension = UiFileProperty.getFileExtension(filename);
if (extension === ".zip") {
return true;
}
for (let fileType of this.fileTypes) {
if (fileType.fileExtension === extension) {
return true;
}
if (fileType.fileExtension === "*" || fileType.fileExtension === "") {
return true;
}
}
return false;
}
label: string = "";
fileTypes: UiFileType[] = [];
patchProperty: string = "";
directory: string = "";
index: number = -1;
portGroup: string = "";
resourceDirectory: string = "";
modDirectories: string[] = [];
useLegacyModDirectory: boolean = false;
};
export class Lv2Plugin implements Deserializable<Lv2Plugin> {
deserialize(input: any): Lv2Plugin {
this.uri = input.uri;
this.name = input.name;
this.brand = input.name ? input.name : "";
this.label = input.label ? input.label : this.name;
this.plugin_class = input.plugin_class;
this.supported_features = input.supported_features;
this.required_features = input.required_features;
this.optional_features = input.optional_features;
this.author_name = input.author_name;
this.author_homepage = input.author_homepage;
this.comment = input.comment;
this.ports = Port.deserialize_array(input.ports);
this.port_groups = PortGroup.deserialize_array(input.port_groups);
if (input.fileProperties) {
this.fileProperties = UiFileProperty.deserialize_array(input.fileProperties)
} else {
this.fileProperties = [];
}
if (input.frequencyPlots) {
this.frequencyPlots = UiFrequencyPlot.deserialize_array(input.frequencyPlots)
} else {
this.frequencyPlots = [];
}
if (input.uiPortNotifications) {
this.uiPortNotifications = UiPropertyNotification.deserialize_array(input.uiPortNotifications);
} else {
this.uiPortNotifications = [];
}
return this;
}
static EmptyFeatures: string[] = [];
uri: string = "";
name: string = "";
brand: string = "";
label: string = "";
plugin_class: string = "";
supported_features: string[] = Lv2Plugin.EmptyFeatures;
required_features: string[] = Lv2Plugin.EmptyFeatures;
optional_features: string[] = Lv2Plugin.EmptyFeatures;
author_name: string = "";
author_homepage: string = "";
comment: string = "";
ports: Port[] = Port.EmptyPorts;
port_groups: PortGroup[] = [];
fileProperties: UiFileProperty[] = [];
frequencyPlots: UiFrequencyPlot[] = [];
uiPortNotifications: UiPropertyNotification[] = [];
}
export class ScalePoint implements Deserializable<ScalePoint> {
deserialize(input: any): ScalePoint {
this.value = input.value;
this.label = input.label;
return this;
}
static deserialize_array(input: any): ScalePoint[] {
let result: ScalePoint[] = [];
for (let i = 0; i < input.length; ++i) {
result[i] = new ScalePoint().deserialize(input[i]);
}
return result;
}
value: number = 0;
label: string = "";
}
export enum PluginType {
// Reserved types used in pedalboards.
None = "",
InvalidPlugin = "InvalidPlugin",
Plugin = "Plugin",
AllpassPlugin = "AllpassPlugin",
AmplifierPlugin = "AmplifierPlugin",
AnalyserPlugin = "AnalyserPlugin",
BandpassPlugin = "BandpassPlugin",
ChorusPlugin = "ChorusPlugin",
CombPlugin = "CombPlugin",
CompressorPlugin = "CompressorPlugin",
ConstantPlugin = "ConstantPlugin",
ConverterPlugin = "ConverterPlugin",
DelayPlugin = "DelayPlugin",
DistortionPlugin = "DistortionPlugin",
DynamicsPlugin = "DynamicsPlugin",
EQPlugin = "EQPlugin",
EnvelopePlugin = "EnvelopePlugin",
ExpanderPlugin = "ExpanderPlugin",
FilterPlugin = "FilterPlugin",
FlangerPlugin = "FlangerPlugin",
FunctionPlugin = "FunctionPlugin",
GatePlugin = "GatePlugin",
GeneratorPlugin = "GeneratorPlugin",
HighpassPlugin = "HighpassPlugin",
InstrumentPlugin = "InstrumentPlugin",
LimiterPlugin = "LimiterPlugin",
LowpassPlugin = "LowpassPlugin",
MixerPlugin = "MixerPlugin",
ModulatorPlugin = "ModulatorPlugin",
MultiEQPlugin = "MultiEQPlugin",
OscillatorPlugin = "OscillatorPlugin",
ParaEQPlugin = "ParaEQPlugin",
PhaserPlugin = "PhaserPlugin",
PitchPlugin = "PitchPlugin",
ReverbPlugin = "ReverbPlugin",
SimulatorPlugin = "SimulatorPlugin",
SpatialPlugin = "SpatialPlugin",
SpectralPlugin = "SpectralPlugin",
UtilityPlugin = "UtilityPlugin",
WaveshaperPlugin = "WaveshaperPlugin",
// psuedo plugin type for the Amps node of the filter dialog.
PiPedalAmpsNode = "PiPedalAmpsNode",
// pseudo plugin types for splitter.
SplitA = "SplitA",
SplitB = "SplitB", //"img/fx_split_b.svg";
SplitMix = "SplitMix", //SplitMix; //"img/fx_dial.svg";
SplitLR = "SplitLR", //"img/fx_dial.svg";
// psuedo plugin type for misc icons.
ErrorPlugin = "ErrorPlugin",
Terminal = "Terminal", //"img/fx_terminal.svg";
}
export enum ControlType {
Dial,
OnOffSwitch,
ABSwitch,
Select,
Trigger,
Tuner,
Vu,
DbVu,
OutputText
}
const textUnits : Set<Units> = new Set<Units>([
Units.none,
Units.unknown,
Units.bar,
Units.beat,
Units.bpm,
Units.cent,
Units.cm,
Units.hz,
Units.khz,
Units.km,
Units.m,
Units.min,
Units.ms,
Units.s,
Units.semitone12TET
]);
function displayUnitAsText(unit: Units): boolean
{
return textUnits.has(unit);
}
export class UiControl implements Deserializable<UiControl> {
deserialize(input: any): UiControl {
this.symbol = input.symbol;
this.name = input.name;
this.index = input.index;
this.is_input = input.is_input;
this.min_value = input.min_value;
this.max_value = input.max_value;
this.default_value = input.default_value;
this.is_logarithmic = input.is_logarithmic;
this.display_priority = input.display_priority;
this.range_steps = input.range_steps;
this.integer_property = input.integer_property;
this.enumeration_property = input.enumeration_property;
this.toggled_property = input.toggled_property;
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;
this.units = input.units as Units;
this.pipedal_ledColor = input.pipedal_ledColor;
this.comment = input.comment ?? "";
this.is_bypass = input.is_bypass ? true : false;
this.is_program_controller = input.is_program_controller ? true : false;
this.custom_units = input.custom_units ?? "";
this.connection_optional = input.connection_optional ? true : false;
if (this.is_bypass) {
this.not_on_gui = true;
}
this.controlType = ControlType.Dial;
if (!this.is_input) {
if (this.units === Units.midiNote || this.units === Units.hz) {
this.controlType = ControlType.Tuner;
} else if (this.units === Units.db) {
this.controlType = ControlType.DbVu;
} else if (this.enumeration_property) {
this.controlType = ControlType.OutputText;
} else if (displayUnitAsText(this.units))
{
this.controlType = ControlType.OutputText
} else {
this.controlType = ControlType.Vu;
}
}
if (this.isValidEnumeration()) {
this.controlType = ControlType.Select;
if (this.scale_points.length === 2) {
this.controlType = ControlType.ABSwitch;
}
} else {
if (this.toggled_property || (this.integer_property && this.min_value === 0 && this.max_value === 1)) {
this.controlType = ControlType.OnOffSwitch;
}
}
if (this.is_input && this.trigger_property)
{
this.controlType = ControlType.Trigger;
}
return this;
}
applyProperties(properties: Partial<UiControl>): UiControl {
return { ...this, ...properties };
}
private hasScalePoint(value: number): boolean {
for (let scale_point of this.scale_points) {
if (scale_point.value === value) return true;
}
return false;
}
private isValidEnumeration(): boolean {
if (this.enumeration_property) return true;
if (this.toggled_property && this.min_value === 0 && this.max_value === 1) {
if (this.hasScalePoint(this.min_value) && this.hasScalePoint(this.max_value)) {
return true;
}
}
if (this.integer_property && this.min_value === 0 && this.max_value === 1) {
if (this.hasScalePoint(this.min_value) && this.hasScalePoint(this.max_value)) {
return true;
}
}
return false;
}
controlType: ControlType = ControlType.Dial; // non-serializable.
static deserialize_array(input: any): UiControl[] {
let result: UiControl[] = [];
for (let i = 0; i < input.length; ++i) {
result[i] = new UiControl().deserialize(input[i]);
}
return result;
}
symbol: string = "";
name: string = "";
index: number = -1;
is_input: boolean = true;
min_value: number = 0;
max_value: number = 1;
default_value: number = 0.5;
is_logarithmic: boolean = false;
display_priority: number = -1;
range_steps: number = 0;
integer_property: boolean = false;
enumeration_property: boolean = false;
trigger_property: boolean = false;
not_on_gui: boolean = false;
toggled_property: boolean = false;
scale_points: ScalePoint[] = [];
port_group: string = "";
units: Units = Units.none;
comment: string = "";
is_bypass: boolean = true;
is_program_controller: boolean = true;
custom_units: string = "";
connection_optional: boolean = false;
pipedal_ledColor: string = "";
// Return the value of the closest scale_point.
clampSelectValue(value: number): number {
if (this.scale_points.length !== 0) {
let minError = 1.0E100;
let bestValue = value;
for (let i = 0; i < this.scale_points.length; ++i) {
let error = Math.abs(this.scale_points[i].value - value);
if (error < minError) {
minError = error;
bestValue = this.scale_points[i].value;
}
}
return bestValue;
} else {
return value;
}
}
isHidden(): boolean {
return this.not_on_gui || (this.connection_optional && !this.is_input);
}
isOnOffSwitch(): boolean {
return this.controlType === ControlType.OnOffSwitch;
}
isAbToggle(): boolean {
return this.controlType === ControlType.ABSwitch;
}
isSelect(): boolean {
return this.controlType === ControlType.Select;
}
isTrigger(): boolean {
return this.controlType === ControlType.Trigger;
}
isOutputText(): boolean {
return !this.is_input && this.controlType === ControlType.OutputText;
}
isLamp(): boolean {
return this.toggled_property && this.scale_points.length === 0 && !this.is_input;
}
isDial(): boolean {
return this.controlType === ControlType.Dial;
}
isTuner(): boolean {
return this.controlType === ControlType.Tuner;
}
isVu(): boolean {
return this.controlType === ControlType.Vu;
}
isDbVu(): boolean {
return this.controlType === ControlType.DbVu;
}
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);
if (range > 1) range = 1;
if (range < 0) range = 0;
return range;
}
rangeToValue(range: number): number {
if (range < 0) range = 0;
if (range > 1) range = 1;
if (this.toggled_property) return range === 0 ? 0 : 1;
let value = range * (this.max_value - this.min_value) + this.min_value;
if (this.integer_property || this.enumeration_property) {
value = Math.round(value);
}
return value;
}
clampValue(value: number): number {
return this.rangeToValue(this.valueToRange(value));
}
formatDisplayValue(value: number): string {
if (this.integer_property) {
value = Math.round(value);
}
for (let i = 0; i < this.scale_points.length; ++i) {
let scalePoint = this.scale_points[i];
if (scalePoint.value === value) {
return scalePoint.label;
}
}
if (this.units === Units.s)
{
let iValue = Math.round(value);
if (iValue >= 60)
{
let minutes = Math.floor(iValue/60);
let seconds = iValue % 60;
if (seconds < 10)
{
return minutes + ":0" + seconds;
}
return minutes + ":" + seconds;
} else {
return this.formatShortValue(value);
}
}
if (this.units === Units.semitone12TET)
{
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;
default:
break;
}
return text;
}
formatShortValue(value: number): string {
if (this.enumeration_property) {
for (let i = 0; i < this.scale_points.length; ++i) {
let scale_point = this.scale_points[i];
if (scale_point.value === value) {
return scale_point.label;
}
}
return "#invalid";
} else if (this.integer_property) {
return value.toFixed(0);
} else {
if (value >= 100 || value <= -100) {
return value.toFixed(0);
}
if (value >= 10 || value <= -10) {
return value.toFixed(1);
}
return value.toFixed(2);
}
}
}
export class UiPlugin implements Deserializable<UiPlugin> {
deserialize(input: any): UiPlugin {
this.uri = input.uri;
this.name = input.name;
this.brand = input.brand ? input.brand : "";
this.label = input.label ? input.label : this.name;
this.plugin_type = input.plugin_type as PluginType;
this.plugin_display_type = input.plugin_display_type;
this.author_name = input.author_name;
this.author_homepage = input.author_homepage;
this.audio_inputs = input.audio_inputs;
this.audio_outputs = input.audio_outputs;
this.has_midi_input = input.has_midi_input;
this.has_midi_output = input.has_midi_output;
this.description = input.description;
this.controls = UiControl.deserialize_array(input.controls);
this.port_groups = PortGroup.deserialize_array(input.port_groups);
if (input.fileProperties) {
this.fileProperties = UiFileProperty.deserialize_array(input.fileProperties)
} else {
this.fileProperties = [];
}
if (input.frequencyPlots) {
this.frequencyPlots = UiFrequencyPlot.deserialize_array(input.frequencyPlots)
} else {
this.frequencyPlots = [];
}
this.is_vst3 = input.is_vst3;
return this;
}
static deserialize_array(input: any): UiPlugin[] {
let result: UiPlugin[] = [];
for (let i = 0; i < input.length; ++i) {
result[i] = new UiPlugin().deserialize(input[i]);
}
return result;
}
isSplit(): boolean {
return this.uri === "uri://two-play/pipedal/pedalboard#Split";
}
getControl(key: string): UiControl | undefined {
for (let i = 0; i < this.controls.length; ++i) {
let control = this.controls[i];
if (control.symbol === key) {
return control;
}
}
return undefined;
}
getPortGroupByUri(uri: string): PortGroup | null {
for (let i = 0; i < this.port_groups.length; ++i) {
let port_group = this.port_groups[i];
if (port_group.uri === uri) {
return port_group;
}
}
return null;
}
getPortGroupBySymbol(symbol: string): PortGroup | null {
for (let i = 0; i < this.port_groups.length; ++i) {
let port_group = this.port_groups[i];
if (port_group.symbol === symbol) {
return port_group;
}
}
return null;
}
uri: string = "";
name: string = "";
brand: string = "";
label: string = "";
plugin_type: PluginType = PluginType.InvalidPlugin;
plugin_display_type: string = "";
author_name: string = "";
author_homepage: string = "";
audio_inputs: number = 0;
audio_outputs: number = 0;
has_midi_input: number = 0;
has_midi_output: number = 0;
description: string = "";
controls: UiControl[] = [];
port_groups: PortGroup[] = [];
fileProperties: UiFileProperty[] = [];
frequencyPlots: UiFrequencyPlot[] = [];
is_vst3: boolean = false;
}
export function makeSplitUiPlugin(): UiPlugin {
return new UiPlugin().deserialize({
uri: "uri://two-play/pipedal/pedalboard#Split",
name: "Split",
brand: "",
label: "",
plugin_type: PluginType.SplitA,
plugin_display_type: "Split",
author_name: "",
author_homepage: "",
audio_inputs: 1,
audio_outputs: 1,
has_midi_input: 0,
has_midi_output: 0,
description: "",
controls: [
new UiControl().applyProperties({
symbol: "splitType",
name: "Type",
index: 0,
is_input: true,
min_value: 0.0,
max_value: 2.0,
enumeration_property: true,
scale_points: [
new ScalePoint().deserialize({ value: 0, label: "A/B" }),
new ScalePoint().deserialize({ value: 1, label: "mix" }),
new ScalePoint().deserialize({ value: 1, label: "L/R" }),
],
is_bypass: false,
is_program_controller: false,
custom_units: "",
connection_optional: false,
}),
new UiControl().applyProperties({
symbol: "select",
name: "Select",
index: 1,
is_input: true,
min_value: 0.0,
max_value: 1.0,
enumeration_property: true,
scale_points: [
new ScalePoint().deserialize({ value: 0, label: "A" }),
new ScalePoint().deserialize({ value: 1, label: "B" }),
],
is_bypass: false,
is_program_controller: false,
custom_units: "",
connection_optional: false,
}),
new UiControl().applyProperties({
symbol: "mix",
name: "Mix",
index: 2,
is_input: true,
min_value: -1.0,
max_value: 1.0,
is_bypass: false,
is_program_controller: false,
custom_units: "",
connection_optional: false,
}),
new UiControl().applyProperties({
symbol: "panL",
name: "Pan Top",
index: 3,
is_input: true,
min_value: -1.0,
max_value: 1.0,
is_bypass: false,
is_program_controller: false,
custom_units: "",
connection_optional: false,
}),
new UiControl().applyProperties({
symbol: "volL",
name: "Vol Top",
index: 4,
is_input: true,
min_value: -60.0,
max_value: 12.0,
is_bypass: false,
is_program_controller: false,
custom_units: "",
connection_optional: false,
}),
new UiControl().applyProperties({
symbol: "panR",
name: "Pan Bottom",
index: 5,
is_input: true,
min_value: -1.0,
max_value: 1.0,
is_bypass: false,
is_program_controller: false,
custom_units: "",
connection_optional: false,
}),
new UiControl().applyProperties({
symbol: "volR",
name: "Vol Bottom",
index: 6,
is_input: true,
min_value: -60.0,
max_value: 12.0,
is_bypass: false,
is_program_controller: false,
custom_units: "",
connection_optional: false,
})
],
port_groups: [],
fileProperties: [],
frequencyPlots: [],
is_vst3: false,
}
);
}