Merge pull request #156 from rerdavies/1.2.34

1.2.34 Merge.
This commit is contained in:
Robin Davies
2024-08-10 14:09:38 -04:00
committed by GitHub
18 changed files with 445 additions and 129 deletions
+2 -2
View File
@@ -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/")
+2 -2
View File
@@ -1,12 +1,12 @@
<img src='docs/GithubBanner.png' width="100%" /><br/>
Download: <a href='https://rerdavies.github.io/pipedal/download.html'>v1.2.33 Beta1</a>
Download: <a href='https://rerdavies.github.io/pipedal/download.html'>v1.2.34 Beta1</a>
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.
+3 -3
View File
@@ -4,17 +4,17 @@
Download the most recent Debian (.deb) package for your platform:
- <a href="https://github.com/rerdavies/pipedal/releases/download/v1.2.33/pipedal_1.2.33_arm64.deb">Raspberry Pi OS Bookworm (64-bit) v1.2.33 Beta1</a>
- <a href="https://github.com/rerdavies/pipedal/releases/download/v1.2.34/pipedal_1.2.34_arm64.deb">Raspberry Pi OS Bookworm (64-bit) v1.2.34 Beta2</a>
- <a href="https://github.com/rerdavies/pipedal/releases/download/v1.1.31/pipedal_1.1.31_arm64.deb">Ubuntu 21.04 or Raspberry Pi OS Buster (64-bit) v1.1.31</a>
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.
+3 -2
View File
@@ -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()
];
+145 -3
View File
@@ -506,7 +506,7 @@ export class UiControl implements Deserializable<UiControl> {
this.controlType = ControlType.Vu;
}
}
else if (this.isValidEnumeration())
if (this.isValidEnumeration())
{
this.controlType = ControlType.Select;
if (this.scale_points.length === 2)
@@ -520,7 +520,10 @@ export class UiControl implements Deserializable<UiControl> {
}
}
return this;
}
applyProperties(properties: Partial<UiControl>): 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<UiControl> {
text += "s";
break;
// Midinote: not handled.
// semitone12TET not handled.
// semitone12TET not handled.
@@ -759,6 +762,7 @@ export class UiControl implements Deserializable<UiControl> {
}
export class UiPlugin implements Deserializable<UiPlugin> {
deserialize(input: any): UiPlugin
{
@@ -802,6 +806,11 @@ export class UiPlugin implements Deserializable<UiPlugin> {
}
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<UiPlugin> {
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,
}
);
}
+8 -3
View File
@@ -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<typeof styles> {
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 (<div />);
}
@@ -258,7 +259,11 @@ const MidiBindingView =
value={midiBinding.switchControlType}
>
<MenuItem value={MidiBinding.LATCH_CONTROL_TYPE}>Toggle</MenuItem>
<MenuItem value={MidiBinding.MOMENTARY_CONTROL_TYPE}>Momentary</MenuItem>
<MenuItem value={MidiBinding.MOMENTARY_CONTROL_TYPE}>{
(midiBinding.bindingType === MidiBinding.BINDING_TYPE_NOTE) ?
"Note on/off"
: "Control value"
}</MenuItem>
</Select>
</div>
))
+70 -24
View File
@@ -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<T>(value: T | null) {
if (!value) throw Error("Unexpected null value");
return value;
}
export interface MidiBindingDialogProps extends WithStyles<typeof styles> {
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(
<tr>
@@ -200,29 +241,33 @@ export const MidiBindingDialog =
</td>
</tr>
);
result.push(
<tr>
<td className={classes.nameTd}>
<Typography noWrap style={{ verticalAlign: "center", height: 48 }}>
Bypass
</Typography>
</td>
<td className={classes.bindingTd}>
<MidiBindingView instanceId={item.instanceId} midiBinding={item.getMidiBinding("__bypass")}
onListen={(instanceId: number, symbol: string, listenForControl: boolean) => {
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)}
/>
</td>
</tr>
);
if (!isSplit)
{
result.push(
<tr>
<td className={classes.nameTd}>
<Typography noWrap style={{ verticalAlign: "center", height: 48 }}>
Bypass
</Typography>
</td>
<td className={classes.bindingTd}>
<MidiBindingView instanceId={item.instanceId} midiBinding={item.getMidiBinding("__bypass")}
uiPlugin={plugin}
onListen={(instanceId: number, symbol: string, listenForControl: boolean) => {
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)}
/>
</td>
</tr>
);
}
for (let i = 0; i < plugin.controls.length; ++i) {
let control = plugin.controls[i];
@@ -244,6 +289,7 @@ export const MidiBindingDialog =
</td>
<td className={classes.bindingTd}>
<MidiBindingView instanceId={item.instanceId}
uiPlugin={plugin}
midiBinding={item.getMidiBinding(symbol)}
listen={item.instanceId === this.state.listenInstanceId && this.state.listenSymbol === symbol}
onListen={(instanceId: number, symbol: string, listenForControl: boolean) => {
+13 -10
View File
@@ -367,12 +367,14 @@ const PedalboardView =
frameRef: React.RefObject<HTMLDivElement>;
scrollRef: React.RefObject<HTMLDivElement>;
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 =
<path key={this.renderKey++} d={svgPath} stroke={color} strokeWidth={SVG_STEREO_STROKE_WIDTH} />
));
output.push((
<path key={this.renderKey++} d={svgPath} stroke="white" strokeWidth={SVG_STROKE_WIDTH} />
<path key={this.renderKey++} d={svgPath} stroke={stereoCenterColor} strokeWidth={SVG_STROKE_WIDTH} />
));
} else if (numberOfOutputs === 1) {
output.push((
@@ -727,14 +730,14 @@ const PedalboardView =
if (item.numberOfInputs === 2 && item.topChildren[0].numberOfInputs === 2) {
output.push((<path key={this.renderKey++} d={topStartPath} stroke={topColor} strokeWidth={SVG_STEREO_STROKE_WIDTH} />));
output.push((<path key={this.renderKey++} d={topStartPath} stroke="white" strokeWidth={SVG_STROKE_WIDTH} />));
output.push((<path key={this.renderKey++} d={topStartPath} stroke={this.bgColor} strokeWidth={SVG_STROKE_WIDTH} />));
} else if (item.numberOfInputs !== 0 && item.topChildren[0].numberOfInputs !== 0) {
output.push((<path key={this.renderKey++} d={topStartPath} stroke={topColor} strokeWidth={SVG_STROKE_WIDTH} />));
}
if (item.numberOfInputs === 2 && item.bottomChildren[0].numberOfInputs === 2) {
output.push((<path key={this.renderKey++} d={bottomStartPath} stroke={bottomColor} strokeWidth={SVG_STEREO_STROKE_WIDTH} />));
output.push((<path key={this.renderKey++} d={bottomStartPath} stroke="white" strokeWidth={SVG_STROKE_WIDTH} />));
output.push((<path key={this.renderKey++} d={bottomStartPath} stroke={this.bgColor} strokeWidth={SVG_STROKE_WIDTH} />));
} else if (item.numberOfInputs !== 0 && item.bottomChildren[0].numberOfInputs !== 0) {
output.push((<path key={this.renderKey++} d={bottomStartPath} stroke={bottomColor} strokeWidth={SVG_STROKE_WIDTH} />));
@@ -824,7 +827,7 @@ const PedalboardView =
<path key={this.renderKey++} d={firstPath} stroke={firstPathColor} strokeWidth={SVG_STEREO_STROKE_WIDTH} />
));
output.push((
<path key={this.renderKey++} d={firstPath} stroke="white" strokeWidth={SVG_STROKE_WIDTH} />
<path key={this.renderKey++} d={firstPath} stroke={this.bgColor} strokeWidth={SVG_STROKE_WIDTH} />
));
} else if (!firstPathAbsent) {
output.push((
@@ -836,7 +839,7 @@ const PedalboardView =
<path key={this.renderKey++} d={secondPath} stroke={secondPathColor} strokeWidth={SVG_STEREO_STROKE_WIDTH} />
));
output.push((
<path key={this.renderKey++} d={secondPath} stroke="white" strokeWidth={SVG_STROKE_WIDTH} />
<path key={this.renderKey++} d={secondPath} stroke={this.bgColor} strokeWidth={SVG_STROKE_WIDTH} />
));
} else if (!secondPathAbsent) {
output.push((
@@ -868,12 +871,12 @@ const PedalboardView =
// draw stereo inner lines.
if (firstPathStereo) {
output.push((
<path key={this.renderKey++} d={firstPath} stroke="white" strokeWidth={SVG_STROKE_WIDTH} />
<path key={this.renderKey++} d={firstPath} stroke={this.bgColor} strokeWidth={SVG_STROKE_WIDTH} />
));
}
if (secondPathStereo) {
output.push((
<path key={this.renderKey++} d={secondPath} stroke="white" strokeWidth={SVG_STROKE_WIDTH} />
<path key={this.renderKey++} d={secondPath} stroke={this.bgColor} strokeWidth={SVG_STROKE_WIDTH} />
));
}
@@ -885,7 +888,7 @@ const PedalboardView =
<path key={this.renderKey++} d={thirdPath} stroke={secondPathColor} strokeWidth={SVG_STEREO_STROKE_WIDTH} />
));
output.push((
<path key={this.renderKey++} d={thirdPath} stroke="white" strokeWidth={SVG_STROKE_WIDTH} />
<path key={this.renderKey++} d={thirdPath} stroke={this.bgColor} strokeWidth={SVG_STROKE_WIDTH} />
));
@@ -958,7 +961,7 @@ const PedalboardView =
let outputs: ReactNode[] = [];
this.renderConnectors(outputs, layoutChain, true, false);
return (
<div style={{width: layoutSize.width, height: layoutSize.height, overflow: "hidden"}}>
<div key="connectors" style={{width: layoutSize.width, height: layoutSize.height, overflow: "hidden"}}>
<svg width={layoutSize.width} height={layoutSize.height}
xmlns="http://www.w3.org/2000/svg" viewBox={"0 0 " + layoutSize.width + " " + layoutSize.height}>
<g fill="none">
+1
View File
@@ -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);
}
+57 -42
View File
@@ -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<ToobMLProps, ToobMLState>
implements ControlViewCustomization
{
class extends React.Component<ToobMLProps, ToobMLState>
implements ControlViewCustomization {
model: PiPedalModel;
gainRef: React.RefObject<HTMLDivElement>;
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() {
this.addGainEnabledSubscription(this.props.instanceId);
if (this.gainRef.current) {
this.gainRef.current.style.opacity = '0.4';
this.gainRef.current.style.pointerEvents = 'none';
}
}
componentDidMount()
{
this.addGainEnabledSubscription(this.props.instanceId);
}
componentWillUnmount()
{
componentWillUnmount() {
this.removeGainEnabledSubscription();
}
ModifyControls(controls: (React.ReactNode| ControlGroup)[]): (React.ReactNode| ControlGroup)[]
{
let group = controls[4] as ControlGroup;
group.controls.splice(0,0,
( <ToobFrequencyResponseView instanceId={this.props.instanceId} />)
);
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,
// ( <ToobFrequencyResponseView
// instanceId={this.props.instanceId}
// />)
// );
// }
let controlIndex: number = -1;
let controlValues = this.props.item.controlValues
for (let i:number = 0; i < controlValues.length; ++i)
{
controls[2] = (<div ref={this.gainRef}> { gainControl} </div>);
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] = (<div ref={this.gainRef}> {gainControl} </div>);
}
return controls;
}
+105 -2
View File
@@ -37,6 +37,7 @@
#include <random>
#include "AudioConfig.hpp"
#include "WifiChannelSelectors.hpp"
#include <grp.h>
#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;
+9 -20
View File
@@ -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.
{
+6 -8
View File
@@ -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<Lv2PluginInfo> t = lv2Host.GetPluginInfo(pedalboardItem->uri());
const Lv2PluginInfo *pPlugin = t.get();
if (pPlugin == nullptr)
std::shared_ptr<Lv2PluginInfo> 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)
{
+4
View File
@@ -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<HostWorkerThread> PluginHost::GetHostWorkerThread()
{
return pHostWorkerThread;
+2
View File
@@ -337,8 +337,10 @@ namespace pipedal
friend class PluginHost;
public:
using ptr = std::shared_ptr<Lv2PluginInfo>;
Lv2PluginInfo(PluginHost *lv2Host, LilvWorld *pWorld, const LilvPlugin *);
Lv2PluginInfo() {}
bool isSplit() const ;
private:
std::shared_ptr<PiPedalUI> FindWritablePathProperties(PluginHost *lv2Host, const LilvPlugin *pPlugin);
+4 -2
View File
@@ -33,6 +33,8 @@ static std::shared_ptr<Lv2PortInfo> 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<Lv2PluginInfo>(makeSplitterPluginInfo());
const Lv2PluginInfo *pipedal::GetSplitterPluginInfo() { return &g_splitterPluginInfo; }
Lv2PluginInfo::ptr pipedal::GetSplitterPluginInfo() { return g_splitterPluginInfo; }
int SplitEffect::GetControlIndex(const std::string &symbol) const
{
+1 -1
View File
@@ -36,7 +36,7 @@ namespace pipedal
class Lv2PluginInfo;
class Lv2PortInfo;
const Lv2PluginInfo *GetSplitterPluginInfo();
std::shared_ptr<Lv2PluginInfo> GetSplitterPluginInfo();
const Lv2PortInfo *GetBypassPortInfo();
+10 -5
View File
@@ -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();
}