diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index db8fe47..a0f3d56 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -5,11 +5,12 @@ "includePath": [ "${default}", "${workspaceFolder}", - "${workspaceFolder}/src", + "${workspaceFolder}/build/src/**", + "${workspaceFolder}/src/**", + "${workspaceFolder}/**", "/usr/include/lilv-0", "/usr/include/x86_64-linux-gnu", - "/usr/lib", - "~/src/vst3sdk" + "/usr/lib" ], "compilerPath": "/usr/bin/gcc-12", "cStandard": "c17", diff --git a/react/src/FilePropertyControl.tsx b/react/src/FilePropertyControl.tsx index f050d7c..8b0ff4b 100644 --- a/react/src/FilePropertyControl.tsx +++ b/react/src/FilePropertyControl.tsx @@ -56,6 +56,9 @@ const styles = (theme: Theme) => createStyles({ backgroundColor: theme.palette.secondary.main, opacity: theme.palette.mode === 'light' ? 0.38 : 0.3 }, + controlFrame: { + display: "flex", flexDirection: "column", alignItems: "start", justifyContent: "space-between", height: 116 + }, displayValue: { position: "absolute", top: 0, @@ -63,10 +66,20 @@ const styles = (theme: Theme) => createStyles({ right: 0, bottom: 4, textAlign: "center", - background: "white", - color: "#666", + background: theme.mainBackground, + color: theme.palette.text.secondary, // zIndex: -1, + }, + titleSection: { + flex: "0 0 auto", alignSelf:"stretch", marginBottom: 8, marginLeft: 8, marginRight: 0 + }, + midSection: { + flex: "1 1 1", display: "flex",flexFlow: "column nowrap",alignContent: "center",justifyContent: "center" + }, + editSection: { + flex: "0 0 0", position: "relative", width: 60, height: 28,minHeight: 28 } + }); @@ -191,6 +204,7 @@ const FilePropertyControl = render() { //let classes = this.props.classes; let fileProperty = this.props.fileProperty; + let classes = this.props.classes; let value = "\u00A0"; if (this.state.hasValue) @@ -206,9 +220,11 @@ const FilePropertyControl = let item_width = 264; return ( -
+
{/* TITLE SECTION */} -
+
@@ -220,9 +236,9 @@ const FilePropertyControl =
{/* CONTROL SECTION */} -
+
- { this.onFileClick() }} > + { this.onFileClick() }} >
@@ -238,7 +254,7 @@ const FilePropertyControl =
{/* LABEL/EDIT SECTION*/} -
+
); diff --git a/react/src/Lv2Plugin.tsx b/react/src/Lv2Plugin.tsx index c451ba8..6192ab9 100644 --- a/react/src/Lv2Plugin.tsx +++ b/react/src/Lv2Plugin.tsx @@ -25,6 +25,16 @@ interface Deserializable { 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 { deserialize(input: any): Port { @@ -445,7 +455,30 @@ export enum ControlType { Tuner, Vu, DbVu, - OutputSelect + OutputText +} + +const textUnits : Set = new Set([ + 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 { @@ -468,6 +501,7 @@ deserialize(input: any): UiControl { 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; @@ -489,10 +523,13 @@ deserialize(input: any): UiControl { } else if (this.units === Units.db) { this.controlType = ControlType.DbVu; } else if (this.enumeration_property) { - this.controlType = ControlType.OutputSelect; + 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; @@ -566,6 +603,7 @@ deserialize(input: any): UiControl { is_program_controller: boolean = true; custom_units: string = ""; connection_optional: boolean = false; + pipedal_ledColor: string = ""; // Return the value of the closest scale_point. @@ -602,8 +640,8 @@ deserialize(input: any): UiControl { isTrigger(): boolean { return this.controlType === ControlType.Trigger; } - isOutputSelect(): boolean { - return !this.is_input && this.controlType === ControlType.OutputSelect; + isOutputText(): boolean { + return !this.is_input && this.controlType === ControlType.OutputText; } @@ -666,6 +704,29 @@ deserialize(input: any): UiControl { 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) { @@ -705,13 +766,8 @@ deserialize(input: any): UiControl { case Units.pc: text += "%"; break; - case Units.s: - text += "s"; + default: break; - // Midinote: not handled. - // semitone12TET not handled. - - } return text; diff --git a/react/src/PluginControl.tsx b/react/src/PluginControl.tsx index 7b04b23..2404b2d 100644 --- a/react/src/PluginControl.tsx +++ b/react/src/PluginControl.tsx @@ -68,6 +68,13 @@ const styles = (theme: Theme) => createStyles({ backgroundColor: theme.palette.primary.main, opacity: theme.palette.mode === 'light' ? 0.38 : 0.3 }, + controlFrame: { + display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "space-between", height: 116 + }, + + titleSection: { + flex: "0 0 auto", alignSelf:"stretch", marginBottom: 8, marginLeft: 0, marginRight: 0 + }, displayValue: { position: "absolute", top: 0, @@ -78,7 +85,14 @@ const styles = (theme: Theme) => createStyles({ background: theme.mainBackground, color: theme.palette.text.secondary, // zIndex: -1, + }, + midSection: { + flex: "1 1 1", display: "flex",flexFlow: "column nowrap",alignContent: "center",justifyContent: "center" + }, + editSection: { + flex: "0 0 0", position: "relative", width: 60, height: 28,minHeight: 28 } + }); @@ -723,9 +737,16 @@ const PluginControl = return ( -
+
{/* TITLE SECTION */} -
+
{/* CONTROL SECTION */} -
+
+ {isTrigger ? (
{/* LABEL/EDIT SECTION*/} -
+
{(!(isSelect || isOnOffSwitch || isTrigger)) && ( (isAbSwitch) ? ( diff --git a/react/src/PluginControlView.tsx b/react/src/PluginControlView.tsx index dcf77d2..ae47131 100644 --- a/react/src/PluginControlView.tsx +++ b/react/src/PluginControlView.tsx @@ -126,11 +126,12 @@ const styles = (theme: Theme) => createStyles({ normalGrid: { position: "relative", - paddingLeft: 22, + paddingLeft: 25, paddingRight: 34, flex: "1 1 auto", display: "flex", flexDirection: "row", flexWrap: "wrap", justifyContent: "flex-start", alignItems: "flex_start", + rowGap: 10 }, @@ -147,12 +148,26 @@ const styles = (theme: Theme) => createStyles({ marginBottom: 8 }, + controlPadding: { flex: "0 0 auto", marginTop: 0, - marginBottom: 0 + marginBottom: 0, + height: 116 }, + controlPair: { + display: "flex", flexFlow: "row nowrap", + flex: "0 0 auto", + height: 116 + }, + controlSpacer: { + display: "none", + minWidth: 0, + width: 0, + height: 116 + }, + portGroup: { marginLeft: 8, marginTop: 0, @@ -375,6 +390,34 @@ const PluginControlView = } + push_control(controls: ReactNode[], pluginControl: UiControl,controlValues: ControlValue[]) + { + // combine lamps with their previous control + if ((pluginControl.isLamp() || pluginControl.isDbVu()) && controls.length !== 0) + { + let newControl = this.makeStandardControl(pluginControl,controlValues); + let previousControl = controls[controls.length-1]; + let pair = ( +
+ {previousControl} + {newControl} +
+ ); + controls[controls.length-1] = pair; + // push a spacer control in order to make placing of extended controls predictable. + // (e.g.. inserting at position 4 still places the extended control after four previous controls + controls.push(( + +
+ )); + + } else { + controls.push( + this.makeStandardControl(pluginControl, controlValues) + ) + } + } + getStandardControlNodes(plugin: UiPlugin, controlValues: ControlValue[]): ControlNodes { let result: ControlNodes = []; let portGroupMap: { [id: string]: ControlGroup } = {}; @@ -394,10 +437,9 @@ const PluginControlView = while (i + 1 < plugin.controls.length && plugin.controls[i + 1].port_group === pluginControl.port_group) { ++i; pluginControl = plugin.controls[i]; + if (!pluginControl.isHidden()) { - groupControls.push( - this.makeStandardControl(pluginControl, controlValues) - ) + this.push_control(groupControls,pluginControl,controlValues); indexes.push(pluginControl.index); } } @@ -407,9 +449,7 @@ const PluginControlView = ) portGroupMap[pluginControl.port_group] = controlGroup; } else { - result.push( - this.makeStandardControl(pluginControl, controlValues) - ); + this.push_control(result,pluginControl,controlValues); } } } diff --git a/react/src/PluginOutputControl.tsx b/react/src/PluginOutputControl.tsx index 0d1e231..c5de88e 100644 --- a/react/src/PluginOutputControl.tsx +++ b/react/src/PluginOutputControl.tsx @@ -32,6 +32,36 @@ import ControlTooltip from './ControlTooltip'; +function makeLedGradient(color: string) { + if (color === "green") { + if (isDarkMode()) { + return "radial-gradient(circle at center, #0F0 0, #0F0 20%, #333 100%)"; + } else { + return "radial-gradient(circle at center, #4F4 0, #4F4 40%, #464 100%)"; + } + } + if (color === "blue") { + if (isDarkMode()) { + return "radial-gradient(circle at center, #00F 0, #00F 20%, #333 100%)"; + } else { + return "radial-gradient(circle at center, #44F 0, #44F 40%, #446 100%)"; + } + } + if (color === "yellow") { + if (isDarkMode()) { + return "radial-gradient(circle at center, #FF0 0, #FF0 20%, #333 100%)"; + } else { + return "radial-gradient(circle at center, #FF4 0, #FF4 40%, #664 100%)"; + } + } + if (isDarkMode()) { + return "radial-gradient(circle at center, #F000 0, #F00 20%, #333 100%)"; + } else { + return "radial-gradient(circle at center, #F44 0, #F44 40%, #644 100%)"; + } + +} + const styles = (theme: Theme) => createStyles({ @@ -60,7 +90,26 @@ const styles = (theme: Theme) => createStyles({ background: "white", color: "#666", // zIndex: -1, - } + }, + controlFrame: { + display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "space-between", height: 116 + }, + + titleSection: { + flex: "0 0 auto", alignSelf:"stretch", marginBottom: 8, marginLeft: 0, marginRight: 0 + }, + + midSection: { + flex: "1 1 1", display: "flex",flexFlow: "column nowrap",alignContent: "center",justifyContent: "center" + }, + + editSection: { + flex: "0 0 28", position: "relative", width: 60, height: 28, minHeight: 28 + }, + editSectionNoContent: { + flex: "0 0 28", position: "relative", width: 1, height: 28, minHeight: 28 + } + }); @@ -251,7 +300,10 @@ const PluginOutputControl = } } - isShortSelect(control: UiControl) { + isShortSelectOrText(control: UiControl) { + if (control.scale_points.length === 0) { + return true; + } for (let scale_point of control.scale_points) { if (scale_point.label.length > 12) return false; } @@ -276,9 +328,9 @@ const PluginOutputControl = let y = (control.max_value - value) * this.VU_HEIGHT / (control.max_value - control.min_value); return y; } - render() { + let classes = this.props.classes; let control: UiControl = this.props.uiControl; @@ -292,11 +344,11 @@ const PluginOutputControl = } } - let isSelect = control.isOutputSelect(); + let isText = control.isOutputText(); - let item_width: number | undefined = isSelect ? 160 : 80; - if (isSelect) { - if (this.isShortSelect(control)) { + let item_width: number | undefined = isText ? 160 : 80; + if (isText) { + if (this.isShortSelectOrText(control)) { item_width = 80; } } @@ -316,11 +368,13 @@ const PluginOutputControl = let redLevel = this.dbVuMap(0); let yellowLevel = this.dbVuMap(-10); return ( -
+
{/* TITLE SECTION */} -
+
- {control.name === "" ? "\u00A0" : control.name} @@ -328,7 +382,8 @@ const PluginOutputControl =
{/* CONTROL SECTION */} -
+
@@ -341,27 +396,33 @@ const PluginOutputControl =
+
+ +
); } else if (control.isVu()) { + // yyx: convert this to a horizontal progress bar. item_width = undefined; return ( -
+
{/* TITLE SECTION */} -
+
- {control.name === "" ? "\u00A0" : control.name}
{/* CONTROL SECTION */} -
+
@@ -371,8 +432,7 @@ const PluginOutputControl =
{/* LABEL/EDIT SECTION*, strictly a placeholder for visual alignment purposes.*/} -
-   +
@@ -380,15 +440,23 @@ const PluginOutputControl = } else if (control.isLamp()) { item_width = undefined; let attachedLamp = control.name === "" || control.name === "\u00A0"; + + let ledGradient: string; + if (this.props.uiControl.pipedal_ledColor.length === 0) { + ledGradient = (isDarkMode() ? "radial-gradient(circle at center, #F00 0, #F00 20%, #333 100%)" + : "radial-gradient(circle at center, #F44 0, #F44 40%, #644 100%)"); + } else { + ledGradient = makeLedGradient(this.props.uiControl.pipedal_ledColor); + } return ( -
+
{/* TITLE SECTION */} -
+
- {control.name === "" ? "\u00A0" : (control.name)} @@ -396,12 +464,17 @@ const PluginOutputControl =
{/* CONTROL SECTION */} -
-
+
+
@@ -409,46 +482,52 @@ const PluginOutputControl =
{/* LABEL/EDIT SECTION*, strictly a placeholder for visual alignment purposes.*/} -
-   +
); - } if (control.isOutputSelect()) { + } if (control.isOutputText()) { return ( -
+
{/* TITLE SECTION */} -
+
- {control.name} + }}> {control.name === "" ? "\u00A0": control.name}
{/* CONTROL SECTION */} -
+
{text}
{/* LABEL/EDIT SECTION*, strictly a placeholder for visual alignment purposes.*/} -
-   +
); } else { return ( -
+
{/* TITLE SECTION */} -
+
- {control.name} @@ -456,15 +535,15 @@ const PluginOutputControl =
{/* CONTROL SECTION */} -
- +
+ {text}
{/* LABEL/EDIT SECTION*, strictly a placeholder for visual alignment purposes.*/} -
-   +
); diff --git a/react/src/SplitUiControls.tsx b/react/src/SplitUiControls.tsx index e7206dc..4e4b7cd 100644 --- a/react/src/SplitUiControls.tsx +++ b/react/src/SplitUiControls.tsx @@ -38,6 +38,8 @@ export const SplitTypeControl: UiControl = new UiControl().deserialize({ not_on_gui: false, toggled_property: false, trigger_property: false, + pipedal_ledColor: "", + scale_points: ScalePoint.deserialize_array( [ { value: 0, label: "A/B" }, @@ -62,6 +64,7 @@ export const SplitAbControl: UiControl = new UiControl().deserialize({ not_on_gui: false, toggled_property: false, trigger_property: false, + pipedal_ledColor: "", scale_points: ScalePoint.deserialize_array( [ { value: 0, label: "A" }, @@ -87,6 +90,8 @@ export const SplitMixControl: UiControl = new UiControl().deserialize({ toggled_property: false, trigger_property: false, + pipedal_ledColor: "", + scale_points: [] }); @@ -105,6 +110,7 @@ export const SplitPanLeftControl: UiControl = new UiControl().deserialize({ not_on_gui: false, toggled_property: false, trigger_property: false, + pipedal_ledColor: "", scale_points: [] }); @@ -124,6 +130,7 @@ export const SplitVolLeftControl: UiControl = new UiControl().deserialize({ not_on_gui: false, toggled_property: false, trigger_property: false, + pipedal_ledColor: "", units: Units.db, scale_points: [ @@ -150,6 +157,7 @@ export const SplitPanRightControl: UiControl = new UiControl().deserialize({ not_on_gui: false, toggled_property: false, trigger_property: false, + pipedal_ledColor: "", scale_points: [] }); @@ -169,6 +177,7 @@ export const SplitVolRightControl: UiControl = new UiControl().deserialize({ not_on_gui: false, toggled_property: false, trigger_property: false, + pipedal_ledColor: "", units: Units.db, scale_points: [ diff --git a/react/src/Units.tsx b/react/src/Units.tsx index 9a1af10..2819942 100644 --- a/react/src/Units.tsx +++ b/react/src/Units.tsx @@ -1,4 +1,4 @@ -// Copyright (c) 2022 Robin Davies +// Copyright (c) 2024 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 diff --git a/src/Base64.hpp b/src/Base64.hpp index c356861..77d3f1a 100644 --- a/src/Base64.hpp +++ b/src/Base64.hpp @@ -55,13 +55,14 @@ namespace macaron 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; + size_t in_len = size; size_t out_len = 4 * ((in_len + 2) / 3); std::string ret(out_len, '\0'); - size_t i; + size_t i = 0; char *p = const_cast(ret.c_str()); - for (i = 0; i < in_len - 2; i += 3) + for (i = 0; i+2 < in_len; i += 3) { *p++ = sEncodingTable[(data[i] >> 2) & 0x3F]; *p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)]; diff --git a/src/ModFileTypes.cpp b/src/ModFileTypes.cpp index e9c6737..9274292 100644 --- a/src/ModFileTypes.cpp +++ b/src/ModFileTypes.cpp @@ -44,6 +44,10 @@ const std::vector ModFileTypes::ModDirectories = {"nammodel", "NeuralAmpModels", "Neural Amp Models", {".nam"}}, // Ratatoille, Mike's NAM. {"aidadspmodel", "shared/aidaaix", "AIDA IAX Models", {".json", ".aidaiax"}}, // Ratatoille {"mlmodel", "ToobMlModels", "ML Models", {".json"}}, // + + // pipedal-specific types. use pipedal_ui:filetypes instead (or in addition to) mod:fileTypes. + {"recording", "shared/audio/Recordings", "Recordings", {"audio/*"}}, // Recordings from recordings plugins. + // }; @@ -134,7 +138,7 @@ void ModFileTypes::CreateDefaultDirectories(const std::filesystem::path &rootDir fs::create_directories(path); } - + if (!fs::exists(rootDirectory / "shared" / "audio" / "Cab IR Files")) { fs::create_symlink( diff --git a/src/PiPedalUI.hpp b/src/PiPedalUI.hpp index b6e96a6..da403ae 100644 --- a/src/PiPedalUI.hpp +++ b/src/PiPedalUI.hpp @@ -59,6 +59,8 @@ #define PIPEDAL_UI__yBottom PIPEDAL_UI_PREFIX "yBottom" #define PIPEDAL_UI__width PIPEDAL_UI_PREFIX "width" +#define PIPEDAL_UI__ledColor PIPEDAL_UI_PREFIX "ledColor" + namespace pipedal { @@ -122,14 +124,16 @@ namespace pipedal { UiFileProperty(PluginHost*pHost, const LilvNode*node, const std::filesystem::path&resourcePath); UiFileProperty(const std::string&name, const std::string&patchProperty,const std::string &directory); - + std::vector& modDirectories() { return modDirectories_; } const std::vector& modDirectories() const { return modDirectories_; } bool useLegacyModDirectory() const { return useLegacyModDirectory_; } void useLegacyModDirectory(bool value) { useLegacyModDirectory_ = value; } const std::string &label() const { return label_; } + int32_t index() const { return index_; } + void index(int32_t value) { index_ = value; } const std::string &directory() const { return directory_; } void directory(const std::string &path) { directory_ = path; } diff --git a/src/PluginHost.cpp b/src/PluginHost.cpp index fa11927..29cc1f8 100644 --- a/src/PluginHost.cpp +++ b/src/PluginHost.cpp @@ -150,6 +150,7 @@ void PluginHost::LilvUris::Initialize(LilvWorld *pWorld) pipedalUI__mimeType = lilv_new_uri(pWorld, PIPEDAL_UI__mimeType); pipedalUI__outputPorts = lilv_new_uri(pWorld, PIPEDAL_UI__outputPorts); pipedalUI__text = lilv_new_uri(pWorld, PIPEDAL_UI__text); + pipedalUI__ledColor = lilv_new_uri(pWorld,PIPEDAL_UI__ledColor); pipedalUI__frequencyPlot = lilv_new_uri(pWorld, PIPEDAL_UI__frequencyPlot); pipedalUI__xLeft = lilv_new_uri(pWorld, PIPEDAL_UI__xLeft); @@ -194,6 +195,7 @@ void PluginHost::LilvUris::Initialize(LilvWorld *pWorld) dc__format = lilv_new_uri(pWorld, "http://purl.org/dc/terms/format"); mod__fileTypes = lilv_new_uri(pWorld, "http://moddevices.com/ns/mod#fileTypes"); + pipedalui__fileTypes =lilv_new_uri(pWorld, PIPEDAL_UI__fileTypes); } void PluginHost::LilvUris::Free() @@ -623,8 +625,11 @@ std::shared_ptr Lv2PluginInfo::FindWritablePathProperties(PluginHost // rfs:range atom:Path? if (lilv_world_ask(pWorld, propertyUri, lv2Host->lilvUris->rdfs__range, lv2Host->lilvUris->atom__Path)) { + + AutoLilvNode label = lilv_world_get(pWorld, propertyUri, lv2Host->lilvUris->rdfs__label, nullptr); std::string strLabel = label.AsString(); + if (strLabel.length() != 0) { std::filesystem::path path = this->bundle_path(); @@ -636,7 +641,20 @@ std::shared_ptr Lv2PluginInfo::FindWritablePathProperties(PluginHost std::make_shared( strLabel, propertyUri.AsUri(), lv2DirectoryName); - AutoLilvNodes mod__fileTypes = lilv_world_find_nodes(pWorld, propertyUri, lv2Host->lilvUris->mod__fileTypes, nullptr); + + AutoLilvNode indexNode = lilv_world_get(pWorld, propertyUri, lv2Host->lilvUris->lv2core__index, nullptr); + int32_t index = indexNode.AsInt(-1); + fileProperty->index(index); + + + // if there's a pipedalui_fileTypes node, use that instead. + + AutoLilvNodes mod__fileTypes = lilv_world_find_nodes(pWorld, propertyUri, lv2Host->lilvUris->pipedalui__fileTypes, nullptr); + if (!mod__fileTypes) + { + mod__fileTypes = lilv_world_find_nodes(pWorld, propertyUri, lv2Host->lilvUris->mod__fileTypes, nullptr); + } + LILV_FOREACH(nodes, i, mod__fileTypes) { // "nam,nammodel" @@ -979,6 +997,15 @@ Lv2PortInfo::Lv2PortInfo(PluginHost *host, const LilvPlugin *plugin, const LilvP 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); + AutoLilvNode port_ledColor = lilv_port_get(plugin,pPort,host->lilvUris->pipedalUI__ledColor); + if (port_ledColor) + { + auto value = lilv_node_as_string(port_ledColor); + if (value) { + this->pipedal_ledColor_ = value; + } + } + LilvScalePoints *pScalePoints = lilv_port_get_scale_points(plugin, pPort); LILV_FOREACH(scale_points, iSP, pScalePoints) { @@ -1650,6 +1677,8 @@ json_map::storage_type Lv2PortInfo::jmap{ MAP_REF(Lv2PortInfo, not_on_gui), MAP_REF(Lv2PortInfo, buffer_type), MAP_REF(Lv2PortInfo, port_group), + MAP_REF(Lv2PortInfo, pipedal_ledColor), + json_map::enum_reference("units", &Lv2PortInfo::units_, get_units_enum_converter()), MAP_REF(Lv2PortInfo, comment)}}; @@ -1715,6 +1744,7 @@ json_map::storage_type Lv2PluginUiPort::jmap{{ MAP_REF(Lv2PluginUiPort, trigger_property), MAP_REF(Lv2PluginUiPort, scale_points), MAP_REF(Lv2PluginUiPort, port_group), + MAP_REF(Lv2PluginUiPort, pipedal_ledColor), json_map::enum_reference("units", &Lv2PluginUiPort::units_, get_units_enum_converter()), MAP_REF(Lv2PluginUiPort, comment), diff --git a/src/PluginHost.hpp b/src/PluginHost.hpp index 5fa685d..c869765 100644 --- a/src/PluginHost.hpp +++ b/src/PluginHost.hpp @@ -227,6 +227,7 @@ namespace pipedal std::string comment_; PiPedalUI::ptr piPedalUI_; + std::string pipedal_ledColor_; public: bool IsSwitch() const @@ -293,6 +294,7 @@ namespace pipedal LV2_PROPERTY_GETSET(port_group); LV2_PROPERTY_GETSET(comment); LV2_PROPERTY_GETSET_SCALAR(units); + LV2_PROPERTY_GETSET(pipedal_ledColor); LV2_PROPERTY_GETSET(buffer_type); @@ -497,6 +499,7 @@ namespace pipedal 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()), + pipedal_ledColor_(pPort->pipedal_ledColor()), comment_(pPort->comment()), units_(pPort->units()), connection_optional_(pPort->connection_optional()) { @@ -540,6 +543,7 @@ namespace pipedal bool trigger_property_ = false; std::vector scale_points_; std::string port_group_; + std::string pipedal_ledColor_; Units units_ = Units::none; std::string comment_; @@ -572,6 +576,7 @@ namespace pipedal LV2_PROPERTY_GETSET_SCALAR(is_program_controller); LV2_PROPERTY_GETSET(custom_units); LV2_PROPERTY_GETSET(connection_optional); + LV2_PROPERTY_GETSET(pipedal_ledColor); public: static json_map::storage_type jmap; @@ -707,6 +712,8 @@ namespace pipedal AutoLilvNode pipedalUI__outputPorts; AutoLilvNode pipedalUI__text; + AutoLilvNode pipedalUI__ledColor; + AutoLilvNode time_Position; AutoLilvNode time_barBeat; AutoLilvNode time_beatsPerMinute; @@ -717,6 +724,7 @@ namespace pipedal AutoLilvNode ui__portNotification; AutoLilvNode ui__plugin; + AutoLilvNode ui__ledColor; AutoLilvNode ui__protocol; AutoLilvNode ui__floatProtocol; AutoLilvNode ui__peakProtocol; @@ -735,6 +743,7 @@ namespace pipedal AutoLilvNode dc__format; AutoLilvNode mod__fileTypes; + AutoLilvNode pipedalui__fileTypes; };