From 2328467ee82d09c441062bb19876b56d2beeb72c Mon Sep 17 00:00:00 2001 From: Robin Davies Date: Fri, 9 Aug 2024 09:07:22 -0400 Subject: [PATCH 1/4] Broken Split controls; enable/disable Toob ML gain control. --- react/src/ControlViewFactory.tsx | 5 +- react/src/Lv2Plugin.tsx | 2 +- react/src/ToobMLView.tsx | 99 ++++++++++++++++++-------------- 3 files changed, 61 insertions(+), 45 deletions(-) diff --git a/react/src/ControlViewFactory.tsx b/react/src/ControlViewFactory.tsx index 8c913346..ae6698a3 100644 --- a/react/src/ControlViewFactory.tsx +++ b/react/src/ControlViewFactory.tsx @@ -29,13 +29,14 @@ import IControlViewFactory from './IControlViewFactory'; import { GxTunerViewFactory } from './GxTunerView'; import ToobPowerstage2ViewFactory from './ToobPowerStage2View'; import ToobSpectrumAnalyzerViewFactory from './ToobSpectrumAnalyzerView'; -// import ToobMLViewFactory from './ToobMLView'; +import ToobMLViewFactory from './ToobMLView'; + let pluginFactories: IControlViewFactory[] = [ new GxTunerViewFactory(), new ToobPowerstage2ViewFactory(), new ToobSpectrumAnalyzerViewFactory(), - //new ToobMLViewFactory() + new ToobMLViewFactory() ]; diff --git a/react/src/Lv2Plugin.tsx b/react/src/Lv2Plugin.tsx index 978ceb1b..379f2195 100644 --- a/react/src/Lv2Plugin.tsx +++ b/react/src/Lv2Plugin.tsx @@ -506,7 +506,7 @@ export class UiControl implements Deserializable { this.controlType = ControlType.Vu; } } - else if (this.isValidEnumeration()) + if (this.isValidEnumeration()) { this.controlType = ControlType.Select; if (this.scale_points.length === 2) diff --git a/react/src/ToobMLView.tsx b/react/src/ToobMLView.tsx index f5fe4e17..dc69c766 100644 --- a/react/src/ToobMLView.tsx +++ b/react/src/ToobMLView.tsx @@ -25,12 +25,10 @@ import createStyles from '@mui/styles/createStyles'; import withStyles from '@mui/styles/withStyles'; import IControlViewFactory from './IControlViewFactory'; -import { PiPedalModelFactory, PiPedalModel,MonitorPortHandle } from "./PiPedalModel"; +import { PiPedalModelFactory, PiPedalModel, MonitorPortHandle } from "./PiPedalModel"; import { PedalboardItem } from './Pedalboard'; -import PluginControlView, { ControlGroup,ControlViewCustomization } from './PluginControlView'; -import ToobFrequencyResponseView from './ToobFrequencyResponseView'; - - +import PluginControlView, { ControlGroup, ControlViewCustomization } from './PluginControlView'; +// import ToobFrequencyResponseView from './ToobFrequencyResponseView'; const styles = (theme: Theme) => createStyles({ }); @@ -46,13 +44,12 @@ interface ToobMLState { const ToobMLView = withStyles(styles, { withTheme: true })( - class extends React.Component - implements ControlViewCustomization - { + class extends React.Component + implements ControlViewCustomization { model: PiPedalModel; gainRef: React.RefObject; - customizationId: number = 1; + customizationId: number = 1; constructor(props: ToobMLProps) { super(props); @@ -65,57 +62,75 @@ const ToobMLView = subscribedId?: number = undefined; monitorPortHandle?: MonitorPortHandle = undefined; - removeGainEnabledSubscription() - { - if (this.monitorPortHandle) - { + removeGainEnabledSubscription() { + if (this.monitorPortHandle) { this.model.unmonitorPort(this.monitorPortHandle); this.monitorPortHandle = undefined; } } - addGainEnabledSubscription(instanceId: number) - { + addGainEnabledSubscription(instanceId: number) { this.removeGainEnabledSubscription(); this.subscribedId = instanceId; - this.monitorPortHandle = this.model.monitorPort(instanceId,"gainEnable",0.1, + this.monitorPortHandle = this.model.monitorPort(instanceId, "gainEnable", 0.1, (value: number) => { - if (this.gainRef.current) - { - this.gainRef.current.style.opacity = value !== 0.0? "1.0": "0.4"; + if (this.gainRef.current) { + this.gainRef.current.style.opacity = value !== 0.0 ? "1.0" : "0.4"; + if (value !== 0.0) { + this.gainRef.current.style.pointerEvents = 'auto'; + } else { + this.gainRef.current.style.pointerEvents = 'none'; + } } - this.setState({gainEnabled: value !== 0.0 }); + this.setState({ gainEnabled: value !== 0.0 }); }); } - componentDidUpdate() - { - if (this.props.instanceId !== this.subscribedId) - { - this.removeGainEnabledSubscription(); - this.addGainEnabledSubscription(this.props.instanceId); + // componentDidUpdate() { + // if (this.props.instanceId !== this.subscribedId) { + // this.removeGainEnabledSubscription(); + // this.addGainEnabledSubscription(this.props.instanceId); - } - } - componentDidMount() - { + // } + // } + componentDidMount() { this.addGainEnabledSubscription(this.props.instanceId); + if (this.gainRef.current) { + this.gainRef.current.style.opacity = '0.4'; + this.gainRef.current.style.pointerEvents = 'none'; + } } - componentWillUnmount() - { + componentWillUnmount() { this.removeGainEnabledSubscription(); } - ModifyControls(controls: (React.ReactNode| ControlGroup)[]): (React.ReactNode| ControlGroup)[] - { - let group = controls[4] as ControlGroup; - group.controls.splice(0,0, - ( ) - ); - - let gainControl: React.ReactElement = controls[2] as React.ReactElement; - if (gainControl) + ModifyControls(controls: (React.ReactNode | ControlGroup)[]): (React.ReactNode | ControlGroup)[] { + // Find EQ group + // let group = controls.find((control) => typeof control !== 'string' && (control as ControlGroup).name === "EQ") as ControlGroup; + // if (group) { + // group.controls.splice(0,0, + // ( ) + // ); + // } + let controlIndex: number = -1; + let controlValues = this.props.item.controlValues + for (let i:number = 0; i < controlValues.length; ++i) { - controls[2] = (
{ gainControl}
); + let ctl: any = controls[i]; + let key: any = ctl?.props?.uiControl?.symbol??""; + + if (key === "gain") + { + controlIndex = i; + break; + } + } + + if (controlIndex !== -1 ) { + + let gainControl: React.ReactElement = controls[controlIndex] as React.ReactElement; + controls[controlIndex] = (
{gainControl}
); } return controls; } From 31f1b9a84aaeea6c4033b8fcd791011ebea5e4a7 Mon Sep 17 00:00:00 2001 From: Robin Davies Date: Sat, 10 Aug 2024 10:24:43 -0400 Subject: [PATCH 2/4] Midi Bindings for split controls. --- CMakeLists.txt | 4 +- README.md | 4 +- docs/download.md | 6 +- react/src/Lv2Plugin.tsx | 146 ++++++++++++++++++++++++++++++- react/src/MidiBindingView.tsx | 11 ++- react/src/MidiBindingsDialog.tsx | 94 +++++++++++++++----- react/src/PiPedalModel.tsx | 1 + src/Lv2Pedalboard.cpp | 29 ++---- src/PiPedalModel.cpp | 14 ++- src/PluginHost.cpp | 4 + src/PluginHost.hpp | 2 + src/SplitEffect.cpp | 6 +- src/SplitEffect.hpp | 2 +- 13 files changed, 256 insertions(+), 67 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 851ef76f..fca0ed89 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,10 @@ cmake_minimum_required(VERSION 3.16.0) project(pipedal - VERSION 1.2.33 + VERSION 1.2.34 DESCRIPTION "PiPedal Guitar Effect Pedal For Raspberry Pi" HOMEPAGE_URL "https://rerdavies.github.io/pipedal" ) -set (DISPLAY_VERSION "v1.2.31beta1") +set (DISPLAY_VERSION "v1.2.34beta2") set (CMAKE_INSTALL_PREFIX "/usr/") diff --git a/README.md b/README.md index 7c9f6568..9a563854 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@
-Download: v1.2.33 Beta1 +Download: v1.2.34 Beta1 Website: [https://rerdavies.github.io/pipedal](https://rerdavies.github.io/pipedal). -> NEW version 1.2.33 beta1 release, providing support for Raspberry Pi OS Bookworm. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details. +> NEW version 1.2.34 beta2 release, providing support for Raspberry Pi OS Bookworm. See the [release notes](https://rerdavies.github.io/pipedal/ReleaseNotes) for details. Use your Raspberry Pi as a guitar effects pedal. Configure and control PiPedal with your phone or tablet. PiPedal running on a Raspberry Pi 4 provides stable super-low-latency audio via external USB audio devices, or internal Raspberry Pi audio hats. diff --git a/docs/download.md b/docs/download.md index 167f4b7c..453b855e 100644 --- a/docs/download.md +++ b/docs/download.md @@ -4,17 +4,17 @@ Download the most recent Debian (.deb) package for your platform: -- Raspberry Pi OS Bookworm (64-bit) v1.2.33 Beta1 +- Raspberry Pi OS Bookworm (64-bit) v1.2.34 Beta2 - Ubuntu 21.04 or Raspberry Pi OS Buster (64-bit) v1.1.31 -v1.2.33 Beta1 is does not currently support Ubuntu 21.04, or older versions of Raspberry Pi OS. +v1.2.34 Beta1 is does not currently support Ubuntu 21.04, or older versions of Raspberry Pi OS. Install the package by running ``` sudo apt update cd ~/Downloads - sudo apt-get install ./pipedal_1.2.33_arm64.deb + sudo apt-get install ./pipedal_1.2.34_arm64.deb ``` Follow the instructions in [_Configuring PiPedal After Installation_](https://rerdavies.github.io/pipedal/Configuring.html) to complete the installation. diff --git a/react/src/Lv2Plugin.tsx b/react/src/Lv2Plugin.tsx index 379f2195..4ec695f6 100644 --- a/react/src/Lv2Plugin.tsx +++ b/react/src/Lv2Plugin.tsx @@ -520,7 +520,10 @@ export class UiControl implements Deserializable { } } return this; - + } + applyProperties(properties: Partial): UiControl + { + return {...this,...properties}; } private hasScalePoint(value: number): boolean { for (let scale_point of this.scale_points) @@ -726,7 +729,7 @@ export class UiControl implements Deserializable { text += "s"; break; // Midinote: not handled. - // semitone12TET not handled. + // semitone12TET not handled. @@ -759,6 +762,7 @@ export class UiControl implements Deserializable { } + export class UiPlugin implements Deserializable { deserialize(input: any): UiPlugin { @@ -802,6 +806,11 @@ export class UiPlugin implements Deserializable { } 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) { @@ -860,3 +869,136 @@ export class UiPlugin implements Deserializable { 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, + + } + ); +} + + diff --git a/react/src/MidiBindingView.tsx b/react/src/MidiBindingView.tsx index b9fc6e73..4df072fb 100644 --- a/react/src/MidiBindingView.tsx +++ b/react/src/MidiBindingView.tsx @@ -32,6 +32,7 @@ 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'; @@ -45,6 +46,7 @@ interface MidiBindingViewProps extends WithStyles { instanceId: number; listen: boolean; midiBinding: MidiBinding; + uiPlugin: UiPlugin; onChange: (instanceId: number, newBinding: MidiBinding) => void; onListen: (instanceId: number, key: string, listenForControl: boolean) => void; } @@ -139,8 +141,7 @@ const MidiBindingView = render() { let classes = this.props.classes; let midiBinding = this.props.midiBinding; - let pedalboardItem = this.model.pedalboard.get().getItem(this.props.instanceId); - let uiPlugin = this.model.getUiPlugin(pedalboardItem.uri); + let uiPlugin = this.props.uiPlugin; if (!uiPlugin) { return (
); } @@ -258,7 +259,11 @@ const MidiBindingView = value={midiBinding.switchControlType} > Toggle - Momentary + { + (midiBinding.bindingType === MidiBinding.BINDING_TYPE_NOTE) ? + "Note on/off" + : "Control value" + }
)) diff --git a/react/src/MidiBindingsDialog.tsx b/react/src/MidiBindingsDialog.tsx index 5b9efb8b..bd4c6f14 100644 --- a/react/src/MidiBindingsDialog.tsx +++ b/react/src/MidiBindingsDialog.tsx @@ -33,6 +33,7 @@ import IconButton from '@mui/material/IconButton'; import MidiBinding from './MidiBinding'; import MidiBindingView from './MidiBindingView'; import Snackbar from '@mui/material/Snackbar'; +import { UiPlugin,makeSplitUiPlugin } from './Lv2Plugin'; const styles = (theme: Theme) => createStyles({ dialogAppBar: { @@ -62,6 +63,10 @@ const styles = (theme: Theme) => createStyles({ }, }); +function not_null(value: T | null) { + if (!value) throw Error("Unexpected null value"); + return value; +} export interface MidiBindingDialogProps extends WithStyles { open: boolean, @@ -189,7 +194,43 @@ export const MidiBindingDialog = let item = v.value; - let plugin = this.model.getUiPlugin(item.uri); + let isSplit = item.uri === "uri://two-play/pipedal/pedalboard#Split"; + let plugin : UiPlugin | null = this.model.getUiPlugin(item.uri); + if (plugin === null && isSplit) + { + plugin = makeSplitUiPlugin(); + let splitType = item.getControlValue("splitType"); + not_null(plugin.getControl("splitType")).not_on_gui = true; + switch (splitType) + { + case 0: // A/B + not_null(plugin.getControl("select")).not_on_gui = false; + not_null(plugin.getControl("mix")).not_on_gui = true; + not_null(plugin.getControl("volL")).not_on_gui = true; + not_null(plugin.getControl("panL")).not_on_gui = true; + not_null(plugin.getControl("volR")).not_on_gui = true; + not_null(plugin.getControl("panR")).not_on_gui = true; + break; + case 1: //mixer + not_null(plugin.getControl("select")).not_on_gui = true; + not_null(plugin.getControl("mix")).not_on_gui = false; + not_null(plugin.getControl("volL")).not_on_gui = true; + not_null(plugin.getControl("panL")).not_on_gui = true; + not_null(plugin.getControl("volR")).not_on_gui = true; + not_null(plugin.getControl("panR")).not_on_gui = true; + break; + case 2: // L/R + not_null(plugin.getControl("select")).not_on_gui = true; + not_null(plugin.getControl("mix")).not_on_gui = true; + not_null(plugin.getControl("volL")).not_on_gui = false; + not_null(plugin.getControl("panL")).not_on_gui = false; + not_null(plugin.getControl("volR")).not_on_gui = false; + not_null(plugin.getControl("panR")).not_on_gui = false; + break; + } + + // xxx + } if (plugin) { result.push( @@ -200,29 +241,33 @@ export const MidiBindingDialog = ); - result.push( - - - - Bypass - - - - { - if (instanceId === -2) - { - this.cancelListenForControl(); - } else { - this.handleListenForControl(instanceId, symbol, listenForControl); - } - }} - listen={item.instanceId === this.state.listenInstanceId && this.state.listenSymbol === "__bypass"} - onChange={(instanceId: number, newItem: MidiBinding) => this.handleItemChanged(instanceId, newItem)} - /> - - - ); + if (!isSplit) + { + result.push( + + + + Bypass + + + + { + if (instanceId === -2) + { + this.cancelListenForControl(); + } else { + this.handleListenForControl(instanceId, symbol, listenForControl); + } + }} + listen={item.instanceId === this.state.listenInstanceId && this.state.listenSymbol === "__bypass"} + onChange={(instanceId: number, newItem: MidiBinding) => this.handleItemChanged(instanceId, newItem)} + /> + + + ); + } for (let i = 0; i < plugin.controls.length; ++i) { let control = plugin.controls[i]; @@ -244,6 +289,7 @@ export const MidiBindingDialog = { diff --git a/react/src/PiPedalModel.tsx b/react/src/PiPedalModel.tsx index fe277ddd..3b98ccfb 100644 --- a/react/src/PiPedalModel.tsx +++ b/react/src/PiPedalModel.tsx @@ -423,6 +423,7 @@ export class PiPedalModel //implements PiPedalModel //if (retry !== 0) { if (this.restartExpected) { this.setState(State.ApplyingChanges); + this.restartExpected = false; } else { this.setState(State.Reconnecting); } diff --git a/src/Lv2Pedalboard.cpp b/src/Lv2Pedalboard.cpp index 038b1d4e..129e9b4b 100644 --- a/src/Lv2Pedalboard.cpp +++ b/src/Lv2Pedalboard.cpp @@ -261,15 +261,15 @@ void Lv2Pedalboard::PrepareMidiMap(const PedalboardItem &pedalboardItem) { if (pedalboardItem.midiBindings().size() != 0) { - auto pluginInfo = pHost->GetPluginInfo(pedalboardItem.uri()); - const Lv2PluginInfo *pPluginInfo; - if (pluginInfo == nullptr && pedalboardItem.uri() == SPLIT_PEDALBOARD_ITEM_URI) + Lv2PluginInfo::ptr pluginInfo; + if (pedalboardItem.uri() == SPLIT_PEDALBOARD_ITEM_URI) { - pPluginInfo = GetSplitterPluginInfo(); + pluginInfo = GetSplitterPluginInfo(); } else { - pPluginInfo = pluginInfo.get(); + pluginInfo = pHost->GetPluginInfo(pedalboardItem.uri()); + } int effectIndex = this->GetIndexOfInstanceId(pedalboardItem.instanceId()); @@ -351,21 +351,10 @@ void Lv2Pedalboard::PrepareMidiMap(const Pedalboard &pedalboard) auto &item = pedalboard.items()[i]; PrepareMidiMap(item); - auto pluginInfo = pHost->GetPluginInfo(item.uri()); - - if (pluginInfo) - { - for (size_t bindingIndex = 0; bindingIndex < item.midiBindings().size(); ++bindingIndex) - { - auto &binding = item.midiBindings()[i]; - { - } - } - } - std::sort(this->midiMappings.begin(), this->midiMappings.end(), - [](const MidiMapping &left, const MidiMapping &right) - { return left.key < right.key; }); } + std::sort(this->midiMappings.begin(), this->midiMappings.end(), + [](const MidiMapping &left, const MidiMapping &right) + { return left.key < right.key; }); } void Lv2Pedalboard::Activate() { @@ -606,7 +595,7 @@ void Lv2Pedalboard::OnMidiMessage(size_t size, uint8_t *message, if (size < 3) return; index = message[1]; - value = 127; + value = message[2] == 0? 0: 127; // zero velocity = note off. } else if (cmd == 0xB0) // midi control. { diff --git a/src/PiPedalModel.cpp b/src/PiPedalModel.cpp index cf209d55..a001da1b 100644 --- a/src/PiPedalModel.cpp +++ b/src/PiPedalModel.cpp @@ -1266,21 +1266,20 @@ void PiPedalModel::OnNotifyMidiValueChanged(int64_t instanceId, int portIndex, f PedalboardItem *item = this->pedalboard.GetItem(instanceId); if (item) { - const Lv2PluginInfo *pPluginInfo; + Lv2PluginInfo::ptr pPluginInfo; if (item->uri() == SPLIT_PEDALBOARD_ITEM_URI) { pPluginInfo = GetSplitterPluginInfo(); } else { - auto pluginInfo = lv2Host.GetPluginInfo(item->uri()); - pPluginInfo = pluginInfo.get(); + pPluginInfo = lv2Host.GetPluginInfo(item->uri()); } if (pPluginInfo) { if (portIndex == -1) { - // bypass! + // bypass! yyy this->value!!! this->pedalboard.SetItemEnabled(instanceId, value != 0); // take a snapshot incase a client unsusbscribes in the notification handler (in which case the mutex won't protect us) IPiPedalModelSubscriber **t = new IPiPedalModelSubscriber *[this->subscribers.size()]; @@ -1675,16 +1674,15 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet void PiPedalModel::UpdateDefaults(PedalboardItem *pedalboardItem) { - std::shared_ptr t = lv2Host.GetPluginInfo(pedalboardItem->uri()); - const Lv2PluginInfo *pPlugin = t.get(); - if (pPlugin == nullptr) + std::shared_ptr pPlugin = lv2Host.GetPluginInfo(pedalboardItem->uri()); + if (!pPlugin) { if (pedalboardItem->uri() == SPLIT_PEDALBOARD_ITEM_URI) { pPlugin = GetSplitterPluginInfo(); } } - if (pPlugin != nullptr) + if (pPlugin) { for (size_t i = 0; i < pPlugin->ports().size(); ++i) { diff --git a/src/PluginHost.cpp b/src/PluginHost.cpp index ea74f978..ecdc638e 100644 --- a/src/PluginHost.cpp +++ b/src/PluginHost.cpp @@ -1353,6 +1353,10 @@ Lv2PortGroup::Lv2PortGroup(PluginHost *lv2Host, const std::string &groupUri) name_ = nodeAsString(nameNode); } +bool Lv2PluginInfo::isSplit() const +{ + return uri_ == SPLIT_PEDALBOARD_ITEM_URI; +} std::shared_ptr PluginHost::GetHostWorkerThread() { return pHostWorkerThread; diff --git a/src/PluginHost.hpp b/src/PluginHost.hpp index 9423ff20..29b52e3e 100644 --- a/src/PluginHost.hpp +++ b/src/PluginHost.hpp @@ -337,8 +337,10 @@ namespace pipedal friend class PluginHost; public: + using ptr = std::shared_ptr; Lv2PluginInfo(PluginHost *lv2Host, LilvWorld *pWorld, const LilvPlugin *); Lv2PluginInfo() {} + bool isSplit() const ; private: std::shared_ptr FindWritablePathProperties(PluginHost *lv2Host, const LilvPlugin *pPlugin); diff --git a/src/SplitEffect.cpp b/src/SplitEffect.cpp index 13ba8860..54acba24 100644 --- a/src/SplitEffect.cpp +++ b/src/SplitEffect.cpp @@ -33,6 +33,8 @@ static std::shared_ptr MakeBypassPortInfo() bypassPortInfo->is_input(true); bypassPortInfo->is_control_port(true); bypassPortInfo->index(-1); + + bypassPortInfo->min_value(0); bypassPortInfo->max_value(1); bypassPortInfo->default_value(1); @@ -159,9 +161,9 @@ static Lv2PluginInfo makeSplitterPluginInfo() return result; } -Lv2PluginInfo g_splitterPluginInfo = makeSplitterPluginInfo(); +Lv2PluginInfo::ptr g_splitterPluginInfo = std::make_shared(makeSplitterPluginInfo()); -const Lv2PluginInfo *pipedal::GetSplitterPluginInfo() { return &g_splitterPluginInfo; } +Lv2PluginInfo::ptr pipedal::GetSplitterPluginInfo() { return g_splitterPluginInfo; } int SplitEffect::GetControlIndex(const std::string &symbol) const { diff --git a/src/SplitEffect.hpp b/src/SplitEffect.hpp index 22384cbc..51142139 100644 --- a/src/SplitEffect.hpp +++ b/src/SplitEffect.hpp @@ -36,7 +36,7 @@ namespace pipedal class Lv2PluginInfo; class Lv2PortInfo; - const Lv2PluginInfo *GetSplitterPluginInfo(); + std::shared_ptr GetSplitterPluginInfo(); const Lv2PortInfo *GetBypassPortInfo(); From 8c7eaa52f819a52479f975fbaf58a085b0d30a6c Mon Sep 17 00:00:00 2001 From: Robin Davies Date: Sat, 10 Aug 2024 11:45:54 -0400 Subject: [PATCH 3/4] Rendering of stereo connectors in dark mode. --- react/src/PedalboardView.tsx | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/react/src/PedalboardView.tsx b/react/src/PedalboardView.tsx index c5014bc0..33971b43 100644 --- a/react/src/PedalboardView.tsx +++ b/react/src/PedalboardView.tsx @@ -367,12 +367,14 @@ const PedalboardView = frameRef: React.RefObject; scrollRef: React.RefObject; - + private bgColor: string; constructor(props: PedalboardProps) { super(props); this.model = PiPedalModelFactory.getInstance(); + this.bgColor = props.theme.palette.background.default + if (!props.selectedId) props.selectedId = -1; this.state = { pedalboard: this.model.pedalboard.get(), @@ -692,6 +694,7 @@ const PedalboardView = let y_ = item.bounds.y + CELL_HEIGHT / 2; let numberOfOutputs = item.numberOfOutputs; let color = enabled ? ENABLED_CONNECTOR_COLOR : DISABLED_CONNECTOR_COLOR; + let stereoCenterColor = this.bgColor; let svgPath = new SvgPathBuilder().moveTo(x_, y_).lineTo(x_ + CELL_WIDTH, y_).toString(); if (numberOfOutputs === 2) { @@ -699,7 +702,7 @@ const PedalboardView = )); output.push(( - + )); } else if (numberOfOutputs === 1) { output.push(( @@ -727,14 +730,14 @@ const PedalboardView = if (item.numberOfInputs === 2 && item.topChildren[0].numberOfInputs === 2) { output.push(()); - output.push(()); + output.push(()); } else if (item.numberOfInputs !== 0 && item.topChildren[0].numberOfInputs !== 0) { output.push(()); } if (item.numberOfInputs === 2 && item.bottomChildren[0].numberOfInputs === 2) { output.push(()); - output.push(()); + output.push(()); } else if (item.numberOfInputs !== 0 && item.bottomChildren[0].numberOfInputs !== 0) { output.push(()); @@ -824,7 +827,7 @@ const PedalboardView = )); output.push(( - + )); } else if (!firstPathAbsent) { output.push(( @@ -836,7 +839,7 @@ const PedalboardView = )); output.push(( - + )); } else if (!secondPathAbsent) { output.push(( @@ -868,12 +871,12 @@ const PedalboardView = // draw stereo inner lines. if (firstPathStereo) { output.push(( - + )); } if (secondPathStereo) { output.push(( - + )); } @@ -885,7 +888,7 @@ const PedalboardView = )); output.push(( - + )); @@ -958,7 +961,7 @@ const PedalboardView = let outputs: ReactNode[] = []; this.renderConnectors(outputs, layoutChain, true, false); return ( -
+
From a77b75bfcb55dc8951370e1fb31a94f1e8ae54e4 Mon Sep 17 00:00:00 2001 From: Robin Davies Date: Sat, 10 Aug 2024 13:41:50 -0400 Subject: [PATCH 4/4] Force permissions on PiPedal data directories --- src/ConfigMain.cpp | 107 ++++++++++++++++++++++++++++++++++++++++++++- src/Storage.cpp | 15 ++++--- 2 files changed, 115 insertions(+), 7 deletions(-) diff --git a/src/ConfigMain.cpp b/src/ConfigMain.cpp index 4961e2f2..cf4fd778 100644 --- a/src/ConfigMain.cpp +++ b/src/ConfigMain.cpp @@ -37,6 +37,7 @@ #include #include "AudioConfig.hpp" #include "WifiChannelSelectors.hpp" +#include #if JACK_HOST #define INSTALL_JACK_SERVICE 1 @@ -592,6 +593,100 @@ static std::string RandomChars(int nchars) return s.str(); } +void SetVarPermissions( + const std::filesystem::path &path, + std::filesystem::perms directoryPermissions, + std::filesystem::perms filePermissions, + uid_t uid, gid_t gid) +{ + namespace fs = std::filesystem; + using namespace std::filesystem; + try { + if (fs::exists(path)) { + + if (fs::is_directory(path)) { + fs::permissions(path, directoryPermissions, fs::perm_options::replace); + for (const auto& entry : fs::recursive_directory_iterator(path)) { + try { + if (fs::is_directory(entry)) + { + fs::permissions(entry.path(), directoryPermissions, fs::perm_options::replace); + } else { + fs::permissions(entry.path(),filePermissions, fs::perm_options::replace); + } + } catch (const std::exception &e) + { + std::cout << "Error: failed to set permissions on file " << entry.path() << std::endl; + } + if (chown(path.c_str(),uid,gid) != 0) + { + std::cout << "Error: failed to set ownership of file " << entry.path() << std::endl; + } + + } + } else { + fs::permissions(path,filePermissions, fs::perm_options::replace); + chown(path.c_str(),uid,gid); + } + } else { + std::cout << "Error: Path does not exist: " << path << std::endl; + } + } catch (const std::filesystem::filesystem_error& e) { + std::cerr << "Error: " << e.what() << std::endl; + } +} + + +static void FixPermissions() +{ + namespace fs = std::filesystem; + using namespace std::filesystem; + + fs::create_directories("/var/pipedal"); + + fs::perms directoryPermissions = + fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec | + fs::perms::group_read | fs::perms::group_write | fs::perms::group_exec | + fs::perms::others_read | fs::perms::others_exec | + fs::perms::set_gid; + + fs::perms filePermissions = + fs::perms::owner_read | fs::perms::owner_write | + fs::perms::group_read | fs::perms::group_write | + fs::perms::others_read ; + + uid_t uid; + struct passwd *passwd; + if ((passwd = getpwnam("pipedal_d")) == nullptr) + { + cout << "Error: " << "User 'pipedal_d' does not exist." << endl; + return; + } + uid = passwd->pw_uid; + gid_t gid = passwd->pw_gid; + SetVarPermissions("/var/pipedal",directoryPermissions,filePermissions,uid,gid); + + fs::perms wpa_supplicant_perms = + fs::perms::owner_read | fs::perms::owner_write | + fs::perms::group_read | fs::perms::group_write; + + std::filesystem::path wpa_config_path{ "/etc/pipedal/config/wpa_supplicant/wpa_supplicant-pipedal.conf"}; + try { + fs::permissions(wpa_config_path,wpa_supplicant_perms, fs::perm_options::replace); + + struct group*grp = getgrnam("netdev"); + if (grp != nullptr) + { + if (chown(wpa_config_path.c_str(),0,grp->gr_gid) != 0) + { + cout << "Error: Failed to change ownership of " << wpa_config_path << endl; + } + } + } catch (const std::exception&e) + { + cout << "Error: Failed to set permissions on " << wpa_config_path << ". " << e.what() << endl; + } +} static void PrepareServiceConfigurationFile(uint16_t portNumber) { ServiceConfiguration serviceConfiguration; @@ -827,6 +922,7 @@ void Install(const std::filesystem::path &programPrefix, const std::string endpo sysExec(SYSTEMCTL_BIN " daemon-reload"); + FixPermissions(); RestartService(false); EnableService(); } @@ -1070,6 +1166,7 @@ int main(int argc, char **argv) bool enable_p2p = false, disable_p2p = false; bool list_p2p_channels = false; bool get_current_port = false; + bool fix_permissions = false; bool nosudo = false; bool excludeShutdownService = false; std::string prefixOption; @@ -1092,7 +1189,8 @@ int main(int argc, char **argv) parser.AddOption("--enable-p2p", &enable_p2p); parser.AddOption("--disable-p2p", &disable_p2p); parser.AddOption("--list-p2p-channels", &list_p2p_channels); - + parser.AddOption("--fix-permissions", &fix_permissions); + parser.AddOption("--get-current-port", &get_current_port); // private. For debug use only. @@ -1104,7 +1202,7 @@ int main(int argc, char **argv) int actionCount = help + get_current_port + install + uninstall + stop + start + enable + disable + enable_ap + disable_ap + restart + enable_p2p + disable_p2p - + list_p2p_channels + + list_p2p_channels + fix_permissions ; if (actionCount > 1) { @@ -1169,6 +1267,11 @@ int main(int argc, char **argv) try { + if (fix_permissions) + { + FixPermissions(); + return EXIT_SUCCESS; + } if (install) { std::filesystem::path prefix; diff --git a/src/Storage.cpp b/src/Storage.cpp index a6b4c436..87cd4356 100644 --- a/src/Storage.cpp +++ b/src/Storage.cpp @@ -1624,14 +1624,19 @@ std::string Storage::UploadUserFile(const std::string &directory, const std::str throw std::logic_error("patchProperty directory not implemented."); } { - std::filesystem::create_directories(path.parent_path()); + try { + std::filesystem::create_directories(path.parent_path()); - std::ofstream f(path, std::ios_base::trunc | std::ios_base::binary); - if (!f.is_open()) + std::ofstream f(path, std::ios_base::trunc | std::ios_base::binary); + if (!f.is_open()) + { + throw std::logic_error(SS("Can't create file " << path << ".")); + } + f.write(fileBody.c_str(), fileBody.length()); + } catch (const std::exception &e) { - throw std::logic_error(SS("Can't create file " << path << ".")); + Lv2Log::error(SS("Upload failed. " << e.what())); } - f.write(fileBody.c_str(), fileBody.length()); } return path.string(); }