Plugin Presets.

This commit is contained in:
Robin Davies
2022-03-02 13:26:59 -05:00
parent f677b8b608
commit 2869ec0e00
32 changed files with 2401 additions and 914 deletions
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<path d="M19.495,8.991l-0.003-3.993c0-1.1-0.891-2-1.99-2h-4h-3h-4c-1.1,0-1.99,0.9-1.99,2L4.509,8.991L4.502,8.998v10
c0,1.1,0.891,2,1.99,2h0.01h11h0.01c1.1,0,1.99-0.9,1.99-2v-10L19.495,8.991z M17.502,9.998v9h-11v-9v-5h5h1h5V9.998z"/>
<line fill="none" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-miterlimit="10" x1="6.002" y1="11.998" x2="12.002" y2="11.998"/>
<line fill="none" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-miterlimit="10" x1="15.261" y1="11.998" x2="18.066" y2="11.998"/>
<path d="M12.002,7.998c-1.474,0-2.75,0.81-3.443,2h3.443c1.103,0,2,0.896,2,2s-0.897,2-2,2H8.558c0.694,1.189,1.97,2,3.444,2
c2.206,0,4-1.794,4-4S14.208,7.998,12.002,7.998z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

+2 -2
View File
@@ -1,5 +1,5 @@
{
"socket_server_port": 80,
"socket_server_port": 8080,
"socket_server_address": "*",
"max_upload_size": 1048576,
@@ -8,4 +8,4 @@
]
}
}
+1 -1
View File
@@ -801,7 +801,7 @@ const AppThemed = withStyles(appStyles)(class extends ResizeResponsiveComponent<
<div className={classes.errorContentMask} />
<div style={{ flex: "2 2 3px", height: 20 }} >&nbsp;</div>
<div className={classes.errorMessageBox} style={{ position: "relative" }} >
<div style={{ fontSize: "30px", position: "absolute", left: 0, top: 0, color: "#A00" }}>
<div style={{ fontSize: "30px", position: "absolute", left: 0, top: 3, color: "#A00" }}>
<ErrorOutlineIcon color="inherit" fontSize="inherit" style={{ float: "left", marginRight: "12px" }} />
</div>
<div style={{ marginLeft: 40, marginTop: 3 }}>
+1 -1
View File
@@ -32,7 +32,7 @@ import ToobCabSimViewFactory from './ToobCabSimView';
import ToobPowerStage2Factory from './ToobPowerStage2View';
import ToobSpectrumAnalyzerViewFactory from './ToobSpectrumAnalyzerView';
import ToobMLViewFactory from './ToobMLView';
import {ToobTunerViewFactory,GxTunerViewFactory> from './GxTunerView';
import {ToobTunerViewFactory,GxTunerViewFactory} from './GxTunerView';
let pluginFactories: IControlViewFactory[] = [
+14 -6
View File
@@ -41,6 +41,7 @@ const styles = (theme: Theme) => createStyles({
interface GxTunerProps extends WithStyles<typeof styles> {
instanceId: number;
item: PedalBoardItem;
isToobTuner: boolean;
}
interface GxTunerState {
@@ -79,6 +80,11 @@ const GxTunerView =
{
let refFreqIndex = this.getControlIndex("REFFREQ");
let thresholdIndex = this.getControlIndex("THRESHOLD");
let muteIndex = -1;
if (this.props.isToobTuner)
{
muteIndex = this.getControlIndex("MUTE")
}
let result: (React.ReactNode | ControlGroup)[] = [];
@@ -87,6 +93,10 @@ const GxTunerView =
result.push(controls[refFreqIndex]);
result.push(controls[thresholdIndex]);
if (muteIndex !== -1)
{
result.push(controls[muteIndex]);
}
return result;
}
render() {
@@ -102,26 +112,24 @@ const GxTunerView =
class GxTunerViewFactory implements IControlViewFactory {
export class GxTunerViewFactory implements IControlViewFactory {
uri: string = GXTUNER_URI;
Create(model: PiPedalModel, pedalBoardItem: PedalBoardItem): React.ReactNode {
return (<GxTunerView instanceId={pedalBoardItem.instanceId} item={pedalBoardItem} />);
return (<GxTunerView isToobTuner={false} instanceId={pedalBoardItem.instanceId} item={pedalBoardItem} />);
}
}
// ToobTuner uses the same controls and control semantics.
class ToobTunerViewFactory implements IControlViewFactory {
export class ToobTunerViewFactory implements IControlViewFactory {
uri: string = TOOBTUNER_URI;
Create(model: PiPedalModel, pedalBoardItem: PedalBoardItem): React.ReactNode {
return (<GxTunerView instanceId={pedalBoardItem.instanceId} item={pedalBoardItem} />);
return (<GxTunerView isToobTuner={true} instanceId={pedalBoardItem.instanceId} item={pedalBoardItem} />);
}
}
export ToobTunerViewFactory;
export GxTunerViewFactory;
+6 -10
View File
@@ -55,8 +55,6 @@ const NARROW_DISPLAY_THRESHOLD = 600;
const FILTER_STORAGE_KEY = "com.twoplay.pipedal.load_dlg.filter";
let lastClickTime: number = 0;
const pluginGridStyles = (theme: Theme) => createStyles({
frame: {
@@ -327,7 +325,8 @@ export const LoadPluginDialog =
}
onDoubleClick(e: SyntheticEvent, uri: string): void {
this.props.onOk(uri);
// handled with onClick e.detail which provides better behaviour on Chrome.
// this.props.onOk(uri);
}
@@ -342,18 +341,15 @@ export const LoadPluginDialog =
}
onClick(e: SyntheticEvent, uri: string): void {
onClick(e: React.MouseEvent<HTMLDivElement>, uri: string): void {
e.preventDefault();
let currentTime = Date.now();
let dt = currentTime-lastClickTime;
let DOUBLE_CLICK_TIME = 500;
let isDoubleClick = dt < DOUBLE_CLICK_TIME;
lastClickTime = currentTime;
this.setState({ selected_uri: uri });
// a better doubleclick: tracks how many recent clicks in the same area.
// regardless of whether we re-rendered in the interrim.
let isDoubleClick = e.detail === 2;
// we have to synthesize double clicks because
// DOM rewrites interfere with natural double click.
if (isDoubleClick)
+4 -5
View File
@@ -184,8 +184,8 @@ export const MainPage =
}
}
handleSelectPluginPreset(instanceId: number, presetName: string) {
this.model.loadPluginPreset(instanceId, presetName);
handleSelectPluginPreset(instanceId: number, presetInstanceId: number) {
this.model.loadPluginPreset(instanceId, presetInstanceId);
}
onPedalBoardChanged(value: PedalBoard) {
this.setState({ pedalBoard: value });
@@ -325,12 +325,11 @@ export const MainPage =
<span className={classes.author}>{author}</span>
</span>
</div>
<div style={{ flex: "0 0 auto" }}>
<div style={{ flex: "0 0 auto", verticalAlign: "center" }}>
<PluginInfoDialog plugin_uri={pluginUri} />
</div>
<div style={{ flex: "0 0 auto" }}>
<PluginPresetSelector pluginUri={presetsUri}
onSelectPreset={(presetName) => this.handleSelectPluginPreset(pedalBoardItem!.instanceId, presetName)}
<PluginPresetSelector pluginUri={presetsUri} instanceId={pedalBoardItem?.instanceId ?? 0}
/>
</div>
</div>
+1 -1
View File
@@ -133,7 +133,7 @@ export class PedalBoardItem implements Deserializable<PedalBoardItem> {
return true;
}
}
throw new PiPedalArgumentError("Control not found.");
return false;
}
setMidiBinding(midiBinding: MidiBinding): boolean {
if (this.midiBindings)
+106 -16
View File
@@ -31,7 +31,7 @@ import { BankIndex } from './Banks';
import JackHostStatus from './JackHostStatus';
import JackServerSettings from './JackServerSettings';
import MidiBinding from './MidiBinding';
import PluginPreset from './PluginPreset';
import {PluginUiPresets} from './PluginPreset';
import WifiConfigSettings from './WifiConfigSettings';
import WifiChannel from './WifiChannel';
import AlsaDeviceInfo from './AlsaDeviceInfo';
@@ -47,6 +47,16 @@ export enum State {
export type ControlValueChangedHandler = (key: string, value: number) => void;
export type PluginPresetsChangedHandler = (pluginUri: string) => void;
export interface PluginPresetsChangedHandle {
_id: number;
_handler: PluginPresetsChangedHandler;
};
export interface ZoomedControlInfo {
source: HTMLElement;
instanceId: number;
@@ -72,7 +82,6 @@ export interface ControlValueChangedHandle {
_ControlValueChangedHandle: number;
};
interface ControlValueChangeItem {
handle: number;
instanceId: number;
@@ -341,6 +350,8 @@ export interface PiPedalModel {
saveCurrentPreset(): void;
saveCurrentPresetAs(newName: string): void;
saveCurrentPluginPresetAs(pluginInstanceId: number,newName: string): void;
loadPreset(instanceId: number): void;
updatePresets(presets: PresetIndex): Promise<void>;
deletePresetItem(instanceId: number): Promise<number>;
@@ -372,8 +383,10 @@ export interface PiPedalModel {
getJackStatus(): Promise<JackHostStatus>;
getPluginPresets(uri: string): Promise<PluginPreset[]>;
loadPluginPreset(instanceId: number, presetName: string): void;
getPluginPresets(uri: string): Promise<PluginUiPresets>;
loadPluginPreset(pluginInstanceId: number, presetInstanceId: number): void;
updatePluginPresets(uri: string, presets: PluginUiPresets): Promise<void>;
duplicatePluginPreset(uri: string, instanceId: number): Promise<number>;
shutdown(): Promise<void>;
restart(): Promise<void>;
@@ -384,14 +397,17 @@ export interface PiPedalModel {
addControlValueChangeListener(instanceId: number, onValueChanged: ControlValueChangedHandler): ControlValueChangedHandle
removeControlValueChangeListener(handle: ControlValueChangedHandle): void;
addPluginPresetsChangedListener(onPluginPresetsChanged: PluginPresetsChangedHandler) : PluginPresetsChangedHandle;
removePluginPresetsChangedListener(handle: PluginPresetsChangedHandle): void;
listenForAtomOutput(instanceId: number, onComplete: (instanceId: number, atomOutput: any) => void): ListenHandle;
cancelListenForAtomOutput(listenHandle: ListenHandle): void;
download(targetType: string, isntanceId: number): void;
download(targetType: string, isntanceId: number|string): void;
uploadPreset(file: File, uploadAfter: number): Promise<number>;
uploadPreset(uploadPage: string,file: File, uploadAfter: number): Promise<number>;
uploadBank(file: File, uploadAfter: number): Promise<number>;
setWifiConfigSettings(wifiConfigSettings: WifiConfigSettings): Promise<void>;
@@ -500,6 +516,9 @@ class PiPedalModelImpl implements PiPedalModel {
let presetsChangedBody = body as PresetsChangedBody;
let presets = new PresetIndex().deserialize(presetsChangedBody.presets);
this.presets.set(presets);
} else if (message === "onPluginPresetsChanged") {
let pluginUri = body as string;
this.handlePluginPresetsChanged(pluginUri);
} else if (message === "onJackConfigurationChanged") {
this.jackConfiguration.set(new JackConfiguration().deserialize(body));
} else if (message === "onLoadPluginPreset") {
@@ -973,6 +992,41 @@ class PiPedalModelImpl implements PiPedalModel {
}
}
_pluginPresetsChangedHandles: PluginPresetsChangedHandle[] = [];
addPluginPresetsChangedListener(onPluginPresetsChanged: PluginPresetsChangedHandler) : PluginPresetsChangedHandle {
let handle = ++this.nextListenHandle;
let t: PluginPresetsChangedHandle = {
_id: handle,
_handler: onPluginPresetsChanged
};
this._pluginPresetsChangedHandles.push(t);
return t;
}
removePluginPresetsChangedListener(handle: PluginPresetsChangedHandle): void
{
let pos = -1;
for (let i = 0; i < this._pluginPresetsChangedHandles.length; ++i)
{
if (this._pluginPresetsChangedHandles[i]._id === handle._id)
{
pos = i;
break;
}
}
if (pos !== -1)
{
this._pluginPresetsChangedHandles.splice(pos,1);
}
}
firePluginPresetsChanged(pluginUri: string): void {
for (let i = 0; i < this._pluginPresetsChangedHandles.length; ++i)
{
this._pluginPresetsChangedHandles[i]._handler(pluginUri);
}
}
_setPedalBoardControlValue(instanceId: number, key: string, value: number, notifyServer: boolean): void {
let pedalBoard = this.pedalBoard.get();
@@ -1244,6 +1298,18 @@ class PiPedalModelImpl implements PiPedalModel {
return data;
});
}
saveCurrentPluginPresetAs(pluginInstanceId: number,newName: string): Promise<number> {
// default behaviour is to save after the currently selected preset.
let request: any = {
instanceId: pluginInstanceId,
name: newName,
};
return nullCast(this.webSocket)
.request<number>("savePluginPresetAs", request)
.then((data) => {
return data;
});
}
loadPreset(instanceId: number): void {
@@ -1473,18 +1539,22 @@ class PiPedalModelImpl implements PiPedalModel {
return result;
}
presetCache: { [uri: string]: PluginPreset[]; } = {};
presetCache: { [uri: string]: PluginUiPresets } = {};
getPluginPresets(uri: string): Promise<PluginPreset[]> {
return new Promise<PluginPreset[]>((resolve, reject) => {
uncachePluginPreset(pluginUri: string): void {
delete this.presetCache[pluginUri];
}
getPluginPresets(uri: string): Promise<PluginUiPresets> {
return new Promise<PluginUiPresets>((resolve, reject) => {
if (this.presetCache[uri] !== undefined) {
resolve(this.presetCache[uri]);
} else {
this.webSocket!.request<any>("getPluginPresets", uri)
.then((result) => {
this.presetCache[uri] = PluginPreset.deserialize_array(result);
resolve(result);
let presets = new PluginUiPresets().deserialize(result);
this.presetCache[uri] = presets;
resolve(presets);
})
.catch((error) => {
reject(error);
@@ -1493,10 +1563,30 @@ class PiPedalModelImpl implements PiPedalModel {
});
}
loadPluginPreset(instanceId: number, presetName: string): void {
this.webSocket?.send("loadPluginPreset", { instanceId: instanceId, presetName: presetName });
loadPluginPreset(pluginInstanceId: number, presetInstanceId: number): void {
this.webSocket?.send("loadPluginPreset", { pluginInstanceId: pluginInstanceId, presetInstanceId: presetInstanceId });
}
handlePluginPresetsChanged(pluginUri: string):void {
this.uncachePluginPreset(pluginUri);
this.firePluginPresetsChanged(pluginUri);
}
updatePluginPresets(pluginUri: string, presets: PluginUiPresets): Promise<void>
{
this.presetCache[pluginUri] = presets;
this.firePluginPresetsChanged(pluginUri);
return nullCast(this.webSocket).request<void>("updatePluginPresets", presets);
}
duplicatePluginPreset(pluginUri: string, instanceId: number): Promise<number>
{
return nullCast(this.webSocket).request<number>("copyPluginPreset", {
pluginUri: pluginUri, instanceId: instanceId });
}
shutdown(): Promise<void> {
return this.webSocket!.request<void>("shutdown");
}
@@ -1596,19 +1686,19 @@ class PiPedalModelImpl implements PiPedalModel {
this.webSocket?.send("cancelListenForAtomOutput", listenHandle._handle);
}
download(targetType: string, instanceId: number): void {
download(targetType: string, instanceId: number | string): void {
if (instanceId === -1) return;
let url = this.varServerUrl + targetType + "?id=" + instanceId;
window.open(url, "_blank");
}
uploadPreset(file: File, uploadAfter: number): Promise<number> {
uploadPreset(uploadPage: string,file: File, uploadAfter: number): Promise<number> {
let result = new Promise<number>((resolve, reject) => {
try {
if (file.size > this.maxUploadSize) {
reject("File is too large.");
}
let url = this.varServerUrl + "uploadPreset";
let url = this.varServerUrl + uploadPage;
if (uploadAfter && uploadAfter !== -1) {
url += "?uploadAfter=" + uploadAfter;
}
+1 -1
View File
@@ -179,7 +179,7 @@ const PluginInfoDialog = withStyles(styles)((props: PluginInfoProps) => {
return (
<div>
<IconButton
style={{ display: (props.plugin_uri !== "") ? "block" : "none" }}
style={{ display: (props.plugin_uri !== "") ? "inline-flex" : "none" }}
onClick={handleClickOpen}
size="large">
<InfoOutlinedIcon />
+57 -9
View File
@@ -19,21 +19,69 @@
export default class PluginPreset {
deserialize(input: any): PluginPreset {
this.presetUri = input.presetUri;
this.name = input.name;
export class PluginUiPreset {
deserialize(input: any): PluginUiPreset {
this.instanceId = input.instanceId;
this.label = input.label;
return this;
}
static deserialize_array(input: any) : PluginPreset[] {
let result: PluginPreset[] = [];
static deserialize_array(input: any) : PluginUiPreset[] {
let result: PluginUiPreset[] = [];
for (let i = 0; i < input.length; ++i)
{
result.push(new PluginPreset().deserialize(input[i]));
result.push(new PluginUiPreset().deserialize(input[i]));
}
return result;
}
presetUri: string = "";
name: string = "";
instanceId: number = -1;
label: string = "";
static equals(left: PluginUiPreset, right: PluginUiPreset): boolean
{
return left.instanceId === right.instanceId && left.label === right.label;
}
}
export class PluginUiPresets {
deserialize(input: any): PluginUiPresets {
this.pluginUri = input.pluginUri;
this.presets = PluginUiPreset.deserialize_array(input.presets);
return this;
}
clone(): PluginUiPresets {
return new PluginUiPresets().deserialize(this);
}
movePreset(from: number, to: number): void
{
let t = this.presets[from];
this.presets.splice(from,1);
this.presets.splice(to,0,t);
}
getItem(instanceId: number): PluginUiPreset | undefined {
for (let i = 0; i < this.presets.length; ++i)
{
if (this.presets[i].instanceId === instanceId)
{
return this.presets[i];
}
}
return undefined;
}
static equals(left: PluginUiPresets,right: PluginUiPresets): boolean
{
if (left.pluginUri !== right.pluginUri) return false;
if (left.presets.length !== right.presets.length) return false;
for (let i = 0; i < left.presets.length; ++i)
{
if (!PluginUiPreset.equals(left.presets[i],right.presets[i]))
{
return false;
}
}
return true;
}
pluginUri: string = "";
presets: PluginUiPreset[] = [];
}
+91 -76
View File
@@ -19,30 +19,31 @@
import { SyntheticEvent, Component } from 'react';
import IconButton from '@mui/material/IconButton';
import Typography from '@mui/material/Typography';
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
import { PiPedalModel, PiPedalModelFactory, PluginPresetsChangedHandle } from './PiPedalModel';
import { Theme } from '@mui/material/styles';
import { WithStyles } from '@mui/styles';
import withStyles from '@mui/styles/withStyles';
import createStyles from '@mui/styles/createStyles';
import PresetDialog from './PresetDialog';
import PluginPresetsDialog from './PluginPresetsDialog';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import Fade from '@mui/material/Fade';
import RenameDialog from './RenameDialog'
import PluginPreset from './PluginPreset';
import {PluginUiPresets} from './PluginPreset';
import Divider from "@mui/material/Divider";
interface PluginPresetSelectorProps extends WithStyles<typeof styles> {
pluginUri?: string;
onSelectPreset: (presetName: string) => void;
instanceId: number;
}
interface PluginPresetSelectorState {
presets: PluginPreset[] | null;
presets : PluginUiPresets;
enabled: boolean;
presetChanged: boolean;
showPresetsDialog: boolean;
showEditPresetsDialog: boolean;
presetsMenuAnchorRef: HTMLElement | null;
@@ -52,10 +53,15 @@ interface PluginPresetSelectorState {
renameDialogActionName: string;
renameDialogOnOk?: (name: string) => void;
saveAsName: string;
}
const styles = (theme: Theme) => createStyles({
itemIcon: {
width: 24, height: 24, marginRight: "4px", opacity: 0.6
}
});
@@ -69,25 +75,27 @@ const PluginPresetSelector =
super(props);
this.model = PiPedalModelFactory.getInstance();
this.state = {
presets: null,
presets: new PluginUiPresets(),
enabled: false,
presetChanged: false,
showPresetsDialog: false,
showEditPresetsDialog: false,
presetsMenuAnchorRef: null,
renameDialogOpen: false,
renameDialogDefaultName: "",
renameDialogActionName: "",
renameDialogOnOk: undefined
renameDialogOnOk: undefined,
saveAsName: ""
};
this.handleDialogClose = this.handleDialogClose.bind(this);
this.handlePresetsMenuClose = this.handlePresetsMenuClose.bind(this);
}
handleLoadPluginPreset(presetName: string)
handleLoadPluginPreset(instanceId: number)
{
this.handlePresetsMenuClose();
this.props.onSelectPreset(presetName);
this.model.loadPluginPreset(this.props.instanceId,instanceId);
}
handlePresetMenuClick(e: SyntheticEvent): void {
@@ -97,24 +105,16 @@ const PluginPresetSelector =
this.setState({ presetsMenuAnchorRef: null });
}
handlePresetsMenuSave(e: SyntheticEvent): void {
handlePluginPresetsMenuSaveAs(e: SyntheticEvent): void {
this.handlePresetsMenuClose();
e.stopPropagation();
this.model.saveCurrentPreset();
}
handlePresetsMenuSaveAs(e: SyntheticEvent): void {
this.handlePresetsMenuClose();
e.stopPropagation();
let currentPresets = this.model.presets.get();
let item = currentPresets.getItem(currentPresets.selectedInstanceId);
if (item == null) return;
let name = item.name;
let name = this.state.saveAsName;
this.renameDialogOpen(name, "Save As")
.then((newName) => {
return this.model.saveCurrentPresetAs(newName);
this.setState({saveAsName: newName});
return this.model.saveCurrentPluginPresetAs(this.props.instanceId,newName);
})
.then((newInstanceId) => {
// s'fine. dealt with by updates, but we do need error handling.
@@ -125,26 +125,6 @@ const PluginPresetSelector =
;
}
handlePresetsMenuRename(e: SyntheticEvent): void {
this.handlePresetsMenuClose();
e.stopPropagation();
let currentPresets = this.model.presets.get();
let item = currentPresets.getItem(currentPresets.selectedInstanceId);
if (item == null) return;
let name = item.name;
this.renameDialogOpen(name, "Rename")
.then((newName) => {
if (newName === name) return;
return this.model.renamePresetItem(this.model.presets.get().selectedInstanceId, newName);
})
.catch((error) => {
this.showError(error);
})
;
}
showError(error: string) {
this.model.showAlert(error);
}
@@ -168,6 +148,49 @@ const PluginPresetSelector =
return result;
}
loadPresets():void {
if (!this.currentUri) return;
let captureUri: string = this.currentUri;
this.model.getPluginPresets(captureUri)
.then((presets: PluginUiPresets) => {
if (captureUri === this.currentUri)
{
this.setState({presets: presets});
}
})
.catch(error => {
if (captureUri === this.currentUri)
{
this.setState({presets: new PluginUiPresets()});
}
});
}
onPluginPresetsChanged(pluginUri: string): void {
if (pluginUri === this.props.pluginUri)
{
this.loadPresets();
}
}
_pluginPresetsChangedHandle?: PluginPresetsChangedHandle;
componentDidMount()
{
this._pluginPresetsChangedHandle = this.model.addPluginPresetsChangedListener(
(pluginUri: string) => {
this.onPluginPresetsChanged(pluginUri);
}
);
}
componentWillUnmount()
{
if (this._pluginPresetsChangedHandle)
{
this.model.removePluginPresetsChangedListener(this._pluginPresetsChangedHandle);
this._pluginPresetsChangedHandle = undefined;
}
}
currentUri?: string = "";
componentDidUpdate()
{
@@ -176,23 +199,9 @@ const PluginPresetSelector =
this.currentUri = this.props.pluginUri;
if (!this.props.pluginUri)
{
this.setState({presets: null});
this.setState({presets: new PluginUiPresets()});
} else {
this.setState({presets: null});
let captureUri = this.currentUri;
this.model.getPluginPresets(this.props.pluginUri)
.then((presets: PluginPreset[]) => {
if (captureUri === this.currentUri)
{
this.setState({presets: presets});
}
})
.catch(error => {
if (captureUri === this.currentUri)
{
this.setState({presets: null});
}
});
this.loadPresets();
}
}
}
@@ -214,9 +223,9 @@ const PluginPresetSelector =
}
handleMenuEditPresets(): void {
handleMenuEditPluginPresets(): void {
this.handlePresetsMenuClose();
this.showEditPresetsDialog(true);
this.showEditPluginPresetsDialog(true);
}
handleSave() {
@@ -229,7 +238,7 @@ const PluginPresetSelector =
showEditPresetsDialog: false
});
}
showEditPresetsDialog(show: boolean) {
showEditPluginPresetsDialog(show: boolean) {
this.setState({
showPresetsDialog: show,
showEditPresetsDialog: true
@@ -252,9 +261,11 @@ const PluginPresetSelector =
}
render() {
let classes = this.props.classes;
//let classes = this.props.classes;
//let classes = this.props.classes;
if ((!this.props.pluginUri) || (!this.state.presets) ||this.state.presets.length === 0)
if ((!this.props.pluginUri))
{
return (<div/>);
}
@@ -276,23 +287,27 @@ const PluginPresetSelector =
}
>
<Typography variant="caption" color="textSecondary" style={{marginLeft: 16,marginTop:4,marginBottom: 4}}>Plugin presets</Typography>
<MenuItem onClick={(e) => this.handlePluginPresetsMenuSaveAs(e)}>Save plugin preset...</MenuItem>
<MenuItem onClick={(e) => this.handleMenuEditPluginPresets()}>Manage plugin presets...</MenuItem>
{ this.state.presets.presets.length !== 0 &&
(<Divider/>)
}
{
this.state.presets.map((preset) => {
return (<MenuItem key={preset.presetUri}
onClick={(e) => this.handleLoadPluginPreset(preset.presetUri)}>{preset.name}</MenuItem>);
this.state.presets.presets.map((preset) => {
return (<MenuItem key={preset.instanceId}
onClick={(e) => this.handleLoadPluginPreset(preset.instanceId)}
><img src="img/ic_pluginpreset2.svg" className={classes.itemIcon} alt="" />
{preset.label}</MenuItem>);
})
}
{/*
<Divider />
<MenuItem onClick={(e) => this.handlePresetsMenuSave(e)}>Save plugin preset</MenuItem>
<MenuItem onClick={(e) => this.handlePresetsMenuSaveAs(e)}>Save plugin preset as...</MenuItem>
<MenuItem onClick={(e) => this.handlePresetsMenuRename(e)}>Rename...</MenuItem>
<MenuItem onClick={(e) => this.handleMenuEditPresets()}>Manage presets...</MenuItem>
*/}
</Menu>
<PresetDialog show={this.state.showPresetsDialog} isEditDialog={this.state.showEditPresetsDialog} onDialogClose={() => this.handleDialogClose()} />
<PluginPresetsDialog
instanceId={this.props.instanceId}
presets={this.state.presets}
show={this.state.showPresetsDialog}
isEditDialog={this.state.showEditPresetsDialog}
onDialogClose={() => this.handleDialogClose()} />
<RenameDialog open={this.state.renameDialogOpen}
defaultName={this.state.renameDialogDefaultName}
acceptActionName={this.state.renameDialogActionName}
+488
View File
@@ -0,0 +1,488 @@
// Copyright (c) 2021 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 React, { SyntheticEvent,Component } from 'react';
import IconButton from '@mui/material/IconButton';
import Typography from '@mui/material/Typography';
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
import Button from "@mui/material/Button";
import ButtonBase from "@mui/material/ButtonBase";
import DialogEx from './DialogEx';
import AppBar from '@mui/material/AppBar';
import Toolbar from '@mui/material/Toolbar';
import DraggableGrid, { ScrollDirection } from './DraggableGrid';
import MoreVertIcon from '@mui/icons-material//MoreVert';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import Fade from '@mui/material/Fade';
import UploadDialog from './UploadDialog';
import {PluginUiPresets,PluginUiPreset} from './PluginPreset';
import SelectHoverBackground from './SelectHoverBackground';
import CloseIcon from '@mui/icons-material//Close';
import ArrowBackIcon from '@mui/icons-material//ArrowBack';
import EditIcon from '@mui/icons-material//Edit';
import RenameDialog from './RenameDialog';
import Slide, {SlideProps} from '@mui/material/Slide';
import { createStyles, Theme } from '@mui/material/styles';
import { WithStyles, withStyles} from '@mui/styles';
interface PluginPresetsDialogProps extends WithStyles<typeof styles> {
show: boolean;
isEditDialog: boolean;
instanceId: number;
presets :PluginUiPresets;
onDialogClose: () => void;
};
interface PluginPresetsDialogState {
showActionBar: boolean;
selectedItem: number;
renameOpen: boolean;
moreMenuAnchorEl: HTMLElement | null;
openUploadDialog: boolean;
};
const styles = (theme: Theme) => createStyles({
dialogAppBar: {
position: 'relative',
top: 0, left: 0
},
dialogActionBar: {
position: 'relative',
top: 0, left: 0,
background: "black"
},
dialogTitle: {
marginLeft: theme.spacing(2),
flex: 1,
},
itemFrame: {
display: "flex",
flexDirection: "row",
flexWrap: "nowrap",
width: "100%",
height: "56px",
alignItems: "center",
textAlign: "left",
justifyContent: "center",
paddingLeft: 8
},
iconFrame: {
flex: "0 0 auto",
},
itemIcon: {
width: 24, height: 24, margin: 12, opacity: 0.6
},
itemLabel: {
flex: "1 1 1px",
marginLeft: 8
}
});
const Transition = React.forwardRef(function Transition(
props: SlideProps, ref: React.Ref<unknown>
) {
return (<Slide direction="up" ref={ref} {...props} />);
});
const PluginPresetsDialog = withStyles(styles, { withTheme: true })(
class extends Component<PluginPresetsDialogProps, PluginPresetsDialogState> {
model: PiPedalModel;
constructor(props: PluginPresetsDialogProps) {
super(props);
this.model = PiPedalModelFactory.getInstance();
this.handleDialogClose = this.handleDialogClose.bind(this);
this.state = {
showActionBar: false,
selectedItem: -1,
renameOpen: false,
moreMenuAnchorEl: null,
openUploadDialog: false
};
}
selectItemAtIndex(index: number) {
let instanceId = this.props.presets.presets[index].instanceId;
this.setState({ selectedItem: instanceId });
}
isEditMode() {
return this.state.showActionBar || this.props.isEditDialog;
}
onMoreClick(e: SyntheticEvent): void {
this.setState({ moreMenuAnchorEl: e.currentTarget as HTMLElement })
}
handleDownloadPresets() {
this.handleMoreClose();
if (this.props.presets.pluginUri !== "")
{
this.model.download("downloadPluginPresets", this.props.presets.pluginUri);
}
}
handleUploadPresets() {
this.handleMoreClose();
this.setState({ openUploadDialog: true });
}
handleMoreClose(): void {
this.setState({ moreMenuAnchorEl: null });
}
componentDidUpdate()
{
if (!this.props.presets.getItem(this.state.selectedItem))
{
if (this.props.presets.presets.length !== 0)
{
this.setState({selectedItem: this.props.presets.presets[0].instanceId});
}
}
}
componentDidMount() {
// scroll selected item into view.
}
componentWillUnmount() {
}
getSelectedIndex() {
let instanceId = this.state.selectedItem;
let presets = this.props.presets;
for (let i = 0; i < presets.presets.length; ++i) {
if (presets.presets[i].instanceId === instanceId) return i;
}
return -1;
}
handleDeleteClick() {
let selectedItem = this.state.selectedItem;
if (selectedItem !== -1) {
let newPresets = this.props.presets.clone();
for (let i = 0; i < newPresets.presets.length; ++i)
{
if (newPresets.presets[i].instanceId === selectedItem)
{
newPresets.presets.splice(i,1);
this.model.updatePluginPresets(newPresets.pluginUri,newPresets)
.catch((error) => {
this.model.showAlert(error);
});
let newPos = i;
if (newPos >= newPresets.presets.length) {
--i;
}
let newSelection = i < 0? -1: newPresets.presets[i].instanceId;
this.setState({selectedItem: newSelection});
}
}
}
}
handleDialogClose() {
this.props.onDialogClose();
}
handleItemClick(instanceId: number): void {
if (this.isEditMode()) {
this.setState({ selectedItem: instanceId });
} else {
this.model.loadPreset(instanceId);
this.props.onDialogClose();
}
}
showActionBar(show: boolean): void {
this.setState({ showActionBar: show });
}
mapElement(el: any): React.ReactNode {
let presetEntry = el as PluginUiPreset;
let classes = this.props.classes;
let selectedItem = this.state.selectedItem;
return (
<div key={presetEntry.instanceId} style={{ background: "white" }} >
<ButtonBase style={{ width: "100%", height: 48 }}
onClick={() => this.handleItemClick(presetEntry.instanceId)}
>
<SelectHoverBackground selected={presetEntry.instanceId === selectedItem} showHover={true} />
<div className={classes.itemFrame}>
<div className={classes.iconFrame}>
<img src="img/ic_pluginpreset2.svg" className={classes.itemIcon} alt="" />
</div>
<div className={classes.itemLabel}>
<Typography>
{presetEntry.label}
</Typography>
</div>
</div>
</ButtonBase>
</div>
);
}
updateServerPluginPresets(newPresets: PluginUiPresets) {
newPresets = newPresets.clone();
this.model.updatePluginPresets(this.getPluginUri(),newPresets)
.catch((error) => {
this.model.showAlert(error);
});
}
moveElement(from: number, to: number): void {
let newPresets: PluginUiPresets = this.props.presets.clone();
newPresets.movePreset(from, to);
this.setState({
selectedItem: newPresets.presets[to].instanceId
});
this.updateServerPluginPresets(newPresets);
}
getSelectedName(): string {
let item = this.props.presets.getItem(this.state.selectedItem);
if (item) return item.label;
return "";
}
handleRenameClick() {
let item = this.props.presets.getItem(this.state.selectedItem);
if (item) {
this.setState({ renameOpen: true });
}
}
handleRenameOk(text: string) {
let item = this.props.presets.getItem(this.state.selectedItem);
if (!item) return;
if (item.label !== text) {
let newPresets = this.props.presets.clone();
let newItem = newPresets.getItem(this.state.selectedItem);
if (!newItem) return;
newItem.label = text;
this.updateServerPluginPresets(newPresets);
}
this.setState({ renameOpen: false });
}
getPluginUri() : string {
let pedalBoardItem = this.model.pedalBoard.get().getItem(this.props.instanceId);
return pedalBoardItem.uri;
}
handleCopy() {
let item = this.props.presets.getItem(this.state.selectedItem);
if (!item) return;
this.model.duplicatePluginPreset(this.getPluginUri(),this.state.selectedItem)
.then((newId) => {
this.setState({ selectedItem: newId });
}).catch((error) => {
this.onError(error);
});
}
onError(error: string): void {
this.model?.showAlert(error);
}
render() {
let classes = this.props.classes;
let actionBarClass = this.props.isEditDialog ? classes.dialogAppBar : classes.dialogActionBar;
let defaultSelectedIndex = this.getSelectedIndex();
let title: string = "";
let pluginUri = this.getPluginUri();
let plugin = this.model.getUiPlugin(pluginUri);
if (plugin) {
title = "Presets - " + plugin.name;
}
return (
<DialogEx tag="PluginPresetsDialog" fullScreen open={this.props.show}
onClose={() => { this.handleDialogClose() }} TransitionComponent={Transition}
style={{userSelect: "none"}}>
<div style={{ display: "flex", flexDirection: "column", flexWrap: "nowrap", width: "100%", height: "100%", overflow: "hidden" }}>
<div style={{ flex: "0 0 auto" }}>
<AppBar className={classes.dialogAppBar} style={{ display: this.isEditMode() ? "none" : "block" }} >
<Toolbar>
<IconButton edge="start" color="inherit" onClick={this.handleDialogClose} aria-label="back"
disabled={this.isEditMode()}
>
<ArrowBackIcon />
</IconButton>
<Typography variant="h6" className={classes.dialogTitle}>
{title}
</Typography>
<IconButton color="inherit" onClick={(e) => this.showActionBar(true)} >
<EditIcon />
</IconButton>
</Toolbar>
</AppBar>
<AppBar className={actionBarClass} style={{ display: this.isEditMode() ? "block" : "none" }}
onClick={(e) => { e.stopPropagation(); e.preventDefault(); }}
>
<Toolbar>
{(!this.props.isEditDialog) ? (
<IconButton edge="start" color="inherit" onClick={(e) => this.showActionBar(false)} aria-label="close">
<CloseIcon />
</IconButton>
) : (
<IconButton edge="start" color="inherit" onClick={this.handleDialogClose} aria-label="back"
>
<ArrowBackIcon />
</IconButton>
)}
<Typography variant="h6" className={classes.dialogTitle}>
{ title }
</Typography>
{(this.props.presets.getItem(this.state.selectedItem) != null)
&& (
<div style={{ flex: "0 0 auto" }}>
<Button color="inherit" onClick={(e) => this.handleCopy()}>
Copy
</Button>
<Button color="inherit" onClick={() => this.handleRenameClick()}>
Rename
</Button>
<RenameDialog
open={this.state.renameOpen}
defaultName={this.getSelectedName()}
acceptActionName={"Rename"}
onClose={() => { this.setState({ renameOpen: false }) }}
onOk={(text: string) => {
this.handleRenameOk(text);
}
}
/>
<IconButton color="inherit" onClick={(e) => this.handleDeleteClick()} >
<img src="/img/old_delete_outline_white_24dp.svg" alt="Delete" style={{ width: 24, height: 24, opacity: 0.6 }} />
</IconButton>
<IconButton color="inherit" onClick={(e) => { this.onMoreClick(e) }} >
<MoreVertIcon />
</IconButton>
<Menu
id="more-menu"
anchorEl={this.state.moreMenuAnchorEl}
keepMounted
open={Boolean(this.state.moreMenuAnchorEl)}
onClose={() => this.handleMoreClose()}
TransitionComponent={Fade}
>
<MenuItem onClick={() => { this.handleDownloadPresets(); }}
disabled={this.props.presets.presets.length === 0}
>
<ListItemIcon>
<img src="img/file_download_black_24dp.svg" style={{ width: 24, height: 24, opacity: 0.6 }} alt="" />
</ListItemIcon>
<ListItemText>
Download presets
</ListItemText>
</MenuItem>
<MenuItem onClick={() => { this.handleUploadPresets() }}>
<ListItemIcon>
<img src="img/file_upload_black_24dp.svg" style={{ width: 24, height: 24, opacity: 0.6 }} alt="" />
</ListItemIcon>
<ListItemText>
Upload presets
</ListItemText>
</MenuItem>
</Menu>
</div>
)
}
</Toolbar>
</AppBar>
</div>
<div style={{ flex: "1 1 auto", position: "relative", overflow: "hidden" }} >
<DraggableGrid
onLongPress={(item) => this.showActionBar(true)}
canDrag={this.isEditMode()}
onDragStart={(index, x, y) => { this.selectItemAtIndex(index) }}
moveElement={(from, to) => { this.moveElement(from, to); }}
scroll={ScrollDirection.Y}
defaultSelectedIndex={defaultSelectedIndex}
>
{
this.props.presets.presets.map((element) => {
return this.mapElement(element);
})
}
</DraggableGrid>
</div>
</div>
<UploadDialog
title="Upload Plugin Presets"
extension='.piPluginPresets'
uploadPage='uploadPluginPresets'
onUploaded={(instanceId) => {} }
uploadAfter={this.state.selectedItem}
open={this.state.openUploadDialog}
onClose={() => { this.setState({ openUploadDialog: false }) }} />
</DialogEx>
);
}
}
);
export default PluginPresetsDialog;
+3
View File
@@ -446,6 +446,9 @@ const PresetDialog = withStyles(styles, { withTheme: true })(
</div>
</div>
<UploadDialog
title='Upload preset'
extension='.piPreset'
uploadPage='uploadPreset'
onUploaded={(instanceId) => this.setState({selectedItem: instanceId}) }
uploadAfter={this.state.selectedItem}
open={this.state.openUploadDialog}
+5 -1
View File
@@ -369,7 +369,11 @@ const PresetSelector =
acceptActionName={this.state.renameDialogActionName}
onClose={() => this.handleRenameDialogClose()}
onOk={(name: string) => this.handleRenameDialogOk(name)} />
<UploadDialog onUploaded={(instanceId) => { this.model.loadPreset(instanceId); }}
<UploadDialog
title='Upload preset'
extension='.piPreset'
uploadPage='uploadPreset'
onUploaded={(instanceId) => { this.model.loadPreset(instanceId); }}
open={this.state.openUploadDialog}
uploadAfter={-1}
onClose={() => { this.setState({ openUploadDialog: false }) }} />
+2
View File
@@ -85,6 +85,8 @@ export default class RenameDialog extends ResizeResponsiveComponent<RenameDialog
const handleOk = () => {
let text = nullCast(this.refText.current).value;
text = text.trim();
if (text.length === 0) return;
onOk(text);
}
const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>): void => {
+12 -8
View File
@@ -29,14 +29,18 @@ import ResizeResponsiveComponent from './ResizeResponsiveComponent';
import Typography from '@mui/material/Typography';
const PRESET_EXTENSION = ".piPreset";
const BANK_EXTENSION = ".piBank";
// const PRESET_EXTENSION = ".piPreset";
// const BANK_EXTENSION = ".piBank";
// const PLUGIN_PRESETS_EXTENSION = ".piPluginPresets";
export interface UploadDialogProps {
open: boolean,
onClose: () => void,
uploadAfter: number,
onUploaded: (instanceId: number) => void
onUploaded: (instanceId: number) => void,
title: string,
extension: string,
uploadPage: string
};
@@ -94,11 +98,11 @@ export default class UploadDialog extends ResizeResponsiveComponent<UploadDialog
let uploadAfter = this.props.uploadAfter;
for (let i = 0; i < fileList.length; ++i)
{
uploadAfter = await this.model.uploadPreset(fileList[i],uploadAfter);
uploadAfter = await this.model.uploadPreset(this.props.uploadPage,fileList[i],uploadAfter);
}
this.props.onUploaded(uploadAfter);
} catch(error) {
this.model.showAlert(error);
this.model.showAlert(error +"");
};
this.props.onClose();
}
@@ -132,7 +136,7 @@ export default class UploadDialog extends ResizeResponsiveComponent<UploadDialog
for (let i = 0; i < fileList.files.length; ++i) {
let file = fileList.files[i];
let extension = this.getFileExtension(file.name);
if (extension !== PRESET_EXTENSION && extension !== BANK_EXTENSION) return false;
if (extension !== this.props.extension) return false;
}
return true;
}
@@ -157,7 +161,7 @@ export default class UploadDialog extends ResizeResponsiveComponent<UploadDialog
fullScreen={this.state.fullScreen}
style={{userSelect: "none"}}
>
<DialogTitle>Upload preset</DialogTitle>
<DialogTitle>{this.props.title}</DialogTitle>
<DialogContent>
<div style={{
width: "100%", height: 100, marginBottom: 0,
@@ -188,7 +192,7 @@ export default class UploadDialog extends ResizeResponsiveComponent<UploadDialog
<input
type="file"
hidden
accept=".piPreset"
accept={this.props.extension}
multiple
onChange={(e)=> this.handleButtonSelect(e)}
/>
+529 -467
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -58,7 +58,8 @@ public:
constexpr static const char* content_disposition = "Content-Disposition";
constexpr static const char* access_control_allow_origin = "Access-Control-Allow-Origin";
constexpr static const char* access_control_allow_methods= "Access-Control-Allow-Methods";
constexpr static const char* access_control_allow_headers = "Acess-Control-Allow-Headers";
constexpr static const char* access_control_allow_headers = "Access-Control-Allow-Headers";
constexpr static const char* access_control_request_headers = "Access-Control-Request-Headers";
constexpr static const char* origin = "Origin";
constexpr static const char* date = "Date";
};
+1
View File
@@ -101,6 +101,7 @@ add_custom_command(
set (PIPEDAL_SOURCES
PluginPreset.cpp PluginPreset.hpp
SysExec.cpp SysExec.hpp
BeastServer.cpp BeastServer.hpp HtmlHelper.cpp HtmlHelper.hpp pch.h Uri.cpp Uri.hpp
WifiConfigSettings.hpp WifiConfigSettings.cpp
+62 -37
View File
@@ -123,6 +123,9 @@ void Lv2Host::LilvUris::Initialize(LilvWorld*pWorld)
time_beatsPerMinute = lilv_new_uri(pWorld, LV2_TIME__beatsPerMinute);
time_speed = lilv_new_uri(pWorld, LV2_TIME__speed);
appliesTo = lilv_new_uri(pWorld,LV2_CORE__appliesTo);
}
@@ -151,6 +154,8 @@ void Lv2Host::LilvUris::Free()
time_beatsPerMinute.Free();
time_speed.Free();
appliesTo.Free();
isA.Free();
}
static std::string nodeAsString(const LilvNode *node)
@@ -535,8 +540,24 @@ static bool ports_sort_compare(std::shared_ptr<Lv2PortInfo> &p1, const std::shar
return p1->index() < p2->index();
}
bool Lv2PluginInfo::HasFactoryPresets(Lv2Host*lv2Host, const LilvPlugin*plugin)
{
NodesAutoFree nodes = lilv_plugin_get_related(plugin,lv2Host->lilvUris.pset_Preset);
bool result = false;
LILV_FOREACH(nodes, iNode, nodes)
{
result = true;
break;
}
return result;
}
Lv2PluginInfo::Lv2PluginInfo(Lv2Host *lv2Host, const LilvPlugin *pPlugin)
{
const LilvNode* pluginUri = lilv_plugin_get_uri(pPlugin);
this->has_factory_presets_ = HasFactoryPresets(lv2Host,pPlugin);
this->uri_ = nodeAsString(lilv_plugin_get_uri(pPlugin));
NodeAutoFree name = (lilv_plugin_get_name(pPlugin));
@@ -606,7 +627,10 @@ Lv2PluginInfo::Lv2PluginInfo(Lv2Host *lv2Host, const LilvPlugin *pPlugin)
}
std::sort(ports_.begin(), ports_.end(), ports_sort_compare);
this->is_valid_ = isValid;
}
std::vector<std::string> supportedFeatures = {
@@ -947,7 +971,8 @@ void Lv2Host::fn_LilvSetPortValueFunc(const char* port_symbol,
std::vector<ControlValue> Lv2Host::LoadPluginPreset(PedalBoardItem*pedalBoardItem, const std::string&presetUri)
std::vector<ControlValue> Lv2Host::LoadFactoryPluginPreset(
PedalBoardItem*pedalBoardItem, const std::string&presetUri)
{
std::vector<ControlValue> result;
@@ -1005,10 +1030,26 @@ std::vector<ControlValue> Lv2Host::LoadPluginPreset(PedalBoardItem*pedalBoardIte
return result;
}
std::vector<Lv2PluginPreset> Lv2Host::GetPluginPresets(const std::string &pluginUri)
{
std::vector<Lv2PluginPreset> result;
struct PresetCallbackState {
Lv2Host *pHost;
std::map<std::string,float> *values;
bool failed = false;
};
void Lv2Host::PortValueCallback(const char*symbol,void*user_data,const void* value,uint32_t size, uint32_t type)
{
PresetCallbackState *pState = static_cast<PresetCallbackState*>(user_data);
Lv2Host*pHost = pState->pHost;
if (type == pHost->urids->atom_Float)
{
(*pState->values)[symbol] = *static_cast<const float*>(value);
} else {
pState->failed = true;
}
}
PluginPresets Lv2Host::GetFactoryPluginPresets(const std::string &pluginUri)
{
const LilvPlugins*plugins = lilv_world_get_all_plugins(this->pWorld);
NodeAutoFree uriNode = lilv_new_uri(pWorld, pluginUri.c_str());
@@ -1021,42 +1062,31 @@ std::vector<Lv2PluginPreset> Lv2Host::GetPluginPresets(const std::string &plugin
throw PiPedalStateException("No such plugin.");
}
PluginPresets result;
result.pluginUri_ = pluginUri;
LilvNodes* presets = lilv_plugin_get_related(plugin, lilvUris.pset_Preset);
LILV_FOREACH(nodes, i, presets) {
const LilvNode* preset = lilv_nodes_get(presets, i);
lilv_world_load_resource(pWorld, preset);
LilvNodes* labels = lilv_world_find_nodes(
pWorld, preset, lilvUris.rdfs_label, NULL);
if (labels) {
const LilvNode* label = lilv_nodes_get_first(labels);
std::string presetName = nodeAsString(label);
result.push_back(Lv2PluginPreset(nodeAsString(preset),presetName));
lilv_nodes_free(labels);
} else {
std::stringstream s;
s << "Preset <" << nodeAsString(lilv_nodes_get(presets,i)) << "> has no rdfs:label.";
LilvState *state = lilv_state_new_from_world(pWorld,this->mapFeature.GetMap(),preset);
if (state != nullptr)
{
std::string label = lilv_state_get_label(state);
std::map<std::string,float> controlValues;
PresetCallbackState cbData { this, &controlValues,false};
lilv_state_emit_port_values(state,PortValueCallback,(void*)&cbData);
lilv_state_free(state);
Lv2Log::warning(s.str());
}
if (!cbData.failed)
{
result.presets_.push_back(PluginPreset(result.nextInstanceId_++,std::move(label),std::move(controlValues)));
}
}
}
lilv_nodes_free(presets);
auto& collation = std::use_facet<std::collate<char> >(std::locale());
auto compare = [&collation] (const Lv2PluginPreset&left, const Lv2PluginPreset&right)
{
return collation.compare(
left.name_.c_str(),
left.name_.c_str()+left.name_.size(),
right.name_.c_str(),
right.name_.c_str()+right.name_.size()) < 0;
};
std::sort(result.begin(),result.end(),compare);
return result;
return result;
}
@@ -1162,7 +1192,7 @@ json_map::storage_type<Lv2PluginInfo> Lv2PluginInfo::jmap{{
json_map::reference("ports", &Lv2PluginInfo::ports_),
json_map::reference("port_groups", &Lv2PluginInfo::port_groups_),
json_map::reference("is_valid", &Lv2PluginInfo::is_valid_),
json_map::reference("has_factory_presets", &Lv2PluginInfo::has_factory_presets_),
}};
json_map::storage_type<Lv2PluginUiInfo> Lv2PluginUiInfo::jmap{{
@@ -1217,8 +1247,3 @@ json_map::storage_type<Lv2PluginUiPortGroup> Lv2PluginUiPortGroup::jmap{{
MAP_REF(Lv2PluginUiPortGroup, name),
}};
JSON_MAP_BEGIN(Lv2PluginPreset)
JSON_MAP_REFERENCE(Lv2PluginPreset,presetUri)
JSON_MAP_REFERENCE(Lv2PluginPreset,name)
JSON_MAP_END()
+11 -21
View File
@@ -33,7 +33,7 @@
#include "lv2.h"
#include "Units.hpp"
#include "PluginPreset.hpp"
namespace pipedal {
@@ -60,23 +60,6 @@ class JackChannelSelection;
class Lv2PluginPreset {
public:
Lv2PluginPreset(const std::string &presetUri, const std::string&name)
: presetUri_(presetUri),
name_(name)
{
}
Lv2PluginPreset() { }
std::string presetUri_;
std::string name_;
DECLARE_JSON_MAP(Lv2PluginPreset);
};
class Lv2PluginClass {
public:
friend class Lv2Host;
@@ -321,6 +304,7 @@ public:
Lv2PluginInfo(Lv2Host*lv2Host,const LilvPlugin*);
Lv2PluginInfo() { }
private:
bool HasFactoryPresets(Lv2Host*lv2Host, const LilvPlugin*plugin);
std::string uri_;
std::string name_;
std::string plugin_class_;
@@ -328,6 +312,7 @@ private:
std::vector<std::string> required_features_;
std::vector<std::string> optional_features_;
std::vector<std::string> extensions_;
bool has_factory_presets_ = false;
std::string author_name_;
std::string author_homepage_;
@@ -353,6 +338,7 @@ public:
LV2_PROPERTY_GETSET(ports)
LV2_PROPERTY_GETSET(is_valid)
LV2_PROPERTY_GETSET(port_groups)
LV2_PROPERTY_GETSET(has_factory_presets)
const Lv2PortInfo& getPort(const std::string&symbol)
{
@@ -623,6 +609,9 @@ private:
LilvNodePtr time_beatsPerMinute;
LilvNodePtr time_speed;
LilvNodePtr appliesTo;
LilvNodePtr isA;
};
LilvUris lilvUris;
@@ -682,6 +671,7 @@ private:
virtual LV2_URID_Map* GetLv2UridMap() {
return this->mapFeature.GetMap();
}
static void PortValueCallback(const char*symbol,void*user_data,const void* value,uint32_t size, uint32_t type);
virtual Lv2Effect*CreateEffect(const PedalBoardItem &pedalBoardItem);
void LoadPluginClassesFromLilv();
@@ -733,9 +723,9 @@ public:
return this->mapFeature.UridToString(urid);
}
std::vector<Lv2PluginPreset> GetPluginPresets(const std::string &pluginUri);
std::vector<ControlValue> LoadPluginPreset(PedalBoardItem*pedalBoardItem, const std::string&presetUri);
PluginPresets GetFactoryPluginPresets(const std::string &pluginUri);
std::vector<ControlValue> LoadFactoryPluginPreset(PedalBoardItem*pedalBoardItem,
const std::string&presetUri);
};
+212 -124
View File
@@ -18,7 +18,7 @@
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "pch.h"
#include <sched.h>
#include "PiPedalModel.hpp"
#include "JackHost.hpp"
#include "Lv2Log.hpp"
@@ -92,6 +92,10 @@ PiPedalModel::~PiPedalModel()
void PiPedalModel::LoadLv2PluginInfo(const PiPedalConfiguration&configuration)
{
storage.SetDataRoot(configuration.GetLocalStoragePath().c_str());
storage.Initialize();
// Not all Lv2 directories have the lv2 base declarations. Load a full set of plugin classes generated on a default /usr/local/lib/lv2 directory.
std::filesystem::path pluginClassesPath = configuration.GetDocRoot() / "plugin_classes.json";
try
@@ -109,6 +113,20 @@ void PiPedalModel::LoadLv2PluginInfo(const PiPedalConfiguration&configuration)
lv2Host.Load(configuration.GetLv2Path().c_str());
// Copy all presets out of Lilv data to json files
// so that we can close lilv while we're actually
// running.
for (const auto&plugin: lv2Host.GetPlugins())
{
if (plugin->has_factory_presets())
{
if (!storage.HasPluginPresets(plugin->uri()))
{
PluginPresets pluginPresets = lv2Host.GetFactoryPluginPresets(plugin->uri());
storage.SavePluginPresets(plugin->uri(),pluginPresets);
}
}
}
}
void PiPedalModel::Load(const PiPedalConfiguration &configuration)
@@ -117,8 +135,6 @@ void PiPedalModel::Load(const PiPedalConfiguration &configuration)
this->jackServerSettings.ReadJackConfiguration();
storage.SetDataRoot(configuration.GetLocalStoragePath().c_str());
storage.Initialize();
// lv2Host.Load(configuration.GetLv2Path().c_str());
@@ -131,7 +147,7 @@ void PiPedalModel::Load(const PiPedalConfiguration &configuration)
this->pedalBoard = currentPreset.preset_;
this->hasPresetChanged = currentPreset.modified_;
}
updateDefaults(&this->pedalBoard);
UpdateDefaults(&this->pedalBoard);
std::unique_ptr<JackHost> p{JackHost::CreateInstance(lv2Host.asIHost())};
this->jackHost = std::move(p);
@@ -150,6 +166,11 @@ void PiPedalModel::Load(const PiPedalConfiguration &configuration)
#endif
}
struct sched_param scheduler_params;
scheduler_params.sched_priority = 10;
memset(&scheduler_params,0,sizeof(sched_param));
sched_setscheduler(0,SCHED_RR,&scheduler_params);
this->jackConfiguration = jackHost->GetServerConfiguration();
if (this->jackConfiguration.isValid())
{
@@ -204,8 +225,8 @@ void PiPedalModel::RemoveNotificationSubsription(IPiPedalModelSubscriber *pSubsc
}
int64_t clientId = pSubscriber->GetClientId();
this->deleteMidiListeners(clientId);
this->deleteAtomOutputListeners(clientId);
this->DeleteMidiListeners(clientId);
this->DeleteAtomOutputListeners(clientId);
for (int i = 0; i < this->outstandingParameterRequests.size(); ++i)
{
@@ -218,17 +239,17 @@ void PiPedalModel::RemoveNotificationSubsription(IPiPedalModelSubscriber *pSubsc
}
}
void PiPedalModel::previewControl(int64_t clientId, int64_t pedalItemId, const std::string &symbol, float value)
void PiPedalModel::PreviewControl(int64_t clientId, int64_t pedalItemId, const std::string &symbol, float value)
{
jackHost->SetControlValue(pedalItemId, symbol, value);
}
void PiPedalModel::setControl(int64_t clientId, int64_t pedalItemId, const std::string &symbol, float value)
void PiPedalModel::SetControl(int64_t clientId, int64_t pedalItemId, const std::string &symbol, float value)
{
{
std::lock_guard<std::recursive_mutex> guard(mutex);
previewControl(clientId, pedalItemId, symbol, value);
PreviewControl(clientId, pedalItemId, symbol, value);
this->pedalBoard.SetControlValue(pedalItemId, symbol, value);
{
@@ -245,12 +266,12 @@ void PiPedalModel::setControl(int64_t clientId, int64_t pedalItemId, const std::
}
delete[] t;
this->setPresetChanged(clientId, true);
this->SetPresetChanged(clientId, true);
}
}
}
void PiPedalModel::fireJackConfigurationChanged(const JackConfiguration &jackConfiguration)
void PiPedalModel::FireJackConfigurationChanged(const JackConfiguration &jackConfiguration)
{
// noify subscribers.
IPiPedalModelSubscriber **t = new IPiPedalModelSubscriber *[this->subscribers.size()];
@@ -266,7 +287,7 @@ void PiPedalModel::fireJackConfigurationChanged(const JackConfiguration &jackCon
delete[] t;
}
void PiPedalModel::fireBanksChanged(int64_t clientId)
void PiPedalModel::FireBanksChanged(int64_t clientId)
{
// noify subscribers.
IPiPedalModelSubscriber **t = new IPiPedalModelSubscriber *[this->subscribers.size()];
@@ -282,7 +303,7 @@ void PiPedalModel::fireBanksChanged(int64_t clientId)
delete[] t;
}
void PiPedalModel::firePedalBoardChanged(int64_t clientId)
void PiPedalModel::FirePedalBoardChanged(int64_t clientId)
{
// noify subscribers.
IPiPedalModelSubscriber **t = new IPiPedalModelSubscriber *[this->subscribers.size()];
@@ -303,25 +324,25 @@ void PiPedalModel::firePedalBoardChanged(int64_t clientId)
std::shared_ptr<Lv2PedalBoard> lv2PedalBoard{this->lv2Host.CreateLv2PedalBoard(this->pedalBoard)};
this->lv2PedalBoard = lv2PedalBoard;
jackHost->SetPedalBoard(lv2PedalBoard);
updateRealtimeVuSubscriptions();
updateRealtimeMonitorPortSubscriptions();
UpdateRealtimeVuSubscriptions();
UpdateRealtimeMonitorPortSubscriptions();
}
}
void PiPedalModel::setPedalBoard(int64_t clientId, PedalBoard &pedalBoard)
void PiPedalModel::SetPedalBoard(int64_t clientId, PedalBoard &pedalBoard)
{
{
std::lock_guard<std::recursive_mutex> guard(mutex);
this->pedalBoard = pedalBoard;
updateDefaults(&this->pedalBoard);
UpdateDefaults(&this->pedalBoard);
this->firePedalBoardChanged(clientId);
this->setPresetChanged(clientId, true);
this->FirePedalBoardChanged(clientId);
this->SetPresetChanged(clientId, true);
}
}
void PiPedalModel::setPedalBoardItemEnable(int64_t clientId, int64_t pedalItemId, bool enabled)
void PiPedalModel::SetPedalBoardItemEnable(int64_t clientId, int64_t pedalItemId, bool enabled)
{
std::lock_guard(this->mutex);
std::lock_guard<std::recursive_mutex> guard{mutex};
{
this->pedalBoard.SetItemEnabled(pedalItemId, enabled);
@@ -337,14 +358,14 @@ void PiPedalModel::setPedalBoardItemEnable(int64_t clientId, int64_t pedalItemId
t[i]->OnItemEnabledChanged(clientId, pedalItemId, enabled);
}
delete[] t;
this->setPresetChanged(clientId, true);
this->SetPresetChanged(clientId, true);
// Notify audo thread.
this->jackHost->SetBypass(pedalItemId, enabled);
}
}
void PiPedalModel::getPresets(PresetIndex *pResult)
void PiPedalModel::GetPresets(PresetIndex *pResult)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
@@ -352,29 +373,29 @@ void PiPedalModel::getPresets(PresetIndex *pResult)
pResult->presetChanged(this->hasPresetChanged);
}
PedalBoard PiPedalModel::getPreset(int64_t instanceId)
PedalBoard PiPedalModel::GetPreset(int64_t instanceId)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
return this->storage.GetPreset(instanceId);
}
void PiPedalModel::getBank(int64_t instanceId, BankFile *pResult)
void PiPedalModel::GetBank(int64_t instanceId, BankFile *pResult)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
this->storage.GetBankFile(instanceId, pResult);
}
void PiPedalModel::setPresetChanged(int64_t clientId, bool value)
void PiPedalModel::SetPresetChanged(int64_t clientId, bool value)
{
if (value != this->hasPresetChanged)
{
hasPresetChanged = value;
firePresetsChanged(clientId);
FirePresetsChanged(clientId);
}
}
void PiPedalModel::firePresetsChanged(int64_t clientId)
void PiPedalModel::FirePresetsChanged(int64_t clientId)
{
std::lock_guard(this->mutex);
std::lock_guard<std::recursive_mutex> guard{mutex};
{
// 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()];
@@ -385,7 +406,7 @@ void PiPedalModel::firePresetsChanged(int64_t clientId)
size_t n = this->subscribers.size();
PresetIndex presets;
getPresets(&presets);
GetPresets(&presets);
for (size_t i = 0; i < n; ++i)
{
@@ -394,66 +415,125 @@ void PiPedalModel::firePresetsChanged(int64_t clientId)
delete[] t;
}
}
void PiPedalModel::saveCurrentPreset(int64_t clientId)
void PiPedalModel::FirePluginPresetsChanged(const std::string & pluginUri)
{
std::lock_guard(this->mutex);
std::lock_guard<std::recursive_mutex> guard{mutex};
{
// 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()];
for (size_t i = 0; i < subscribers.size(); ++i)
{
t[i] = this->subscribers[i];
}
size_t n = this->subscribers.size();
storage.saveCurrentPreset(this->pedalBoard);
this->setPresetChanged(clientId, false);
for (size_t i = 0; i < n; ++i)
{
t[i]->OnPluginPresetsChanged(pluginUri);
}
delete[] t;
}
}
int64_t PiPedalModel::saveCurrentPresetAs(int64_t clientId, const std::string &name, int64_t saveAfterInstanceId)
void PiPedalModel::SaveCurrentPreset(int64_t clientId)
{
std::lock_guard(this->mutex);
std::lock_guard<std::recursive_mutex> guard{mutex};
storage.SaveCurrentPreset(this->pedalBoard);
this->SetPresetChanged(clientId, false);
}
uint64_t PiPedalModel::CopyPluginPreset(const std::string&pluginUri,uint64_t presetId)
{
uint64_t result = storage.CopyPluginPreset(pluginUri,presetId);
FirePluginPresetsChanged(pluginUri);
return result;
}
void PiPedalModel::UpdatePluginPresets(const PluginUiPresets&pluginPresets)
{
storage.UpdatePluginPresets(pluginPresets);
FirePluginPresetsChanged(pluginPresets.pluginUri_);
}
int64_t PiPedalModel::SavePluginPresetAs(int64_t instanceId, const std::string&name)
{
auto *item = this->pedalBoard.GetItem(instanceId);
if (!item)
{
throw PiPedalException("Plugin not found.");
}
std::map<std::string,float> values;
for (const auto & controlValue: item->controlValues())
{
values[controlValue.key()] = controlValue.value();
}
uint64_t presetId = storage.SavePluginPreset(item->uri(),name,values);
FirePluginPresetsChanged(item->uri());
return presetId;
}
int64_t PiPedalModel::SaveCurrentPresetAs(int64_t clientId, const std::string &name, int64_t saveAfterInstanceId)
{
std::lock_guard<std::recursive_mutex> guard{mutex};
int64_t result = storage.saveCurrentPresetAs(this->pedalBoard, name, saveAfterInstanceId);
this->hasPresetChanged = false;
firePresetsChanged(clientId);
FirePresetsChanged(clientId);
return result;
}
int64_t PiPedalModel::uploadPreset(const BankFile &bankFile, int64_t uploadAfter)
void PiPedalModel::UploadPluginPresets(const PluginPresets&pluginPresets)
{
std::lock_guard(this->mutex);
if (pluginPresets.pluginUri_.length() == 0)
{
throw PiPedalException("Invalid plugin presets.");
}
std::lock_guard<std::recursive_mutex> guard{mutex};
storage.SavePluginPresets(pluginPresets.pluginUri_,pluginPresets);
FirePluginPresetsChanged(pluginPresets.pluginUri_);
}
int64_t PiPedalModel::UploadPreset(const BankFile &bankFile, int64_t uploadAfter)
{
std::lock_guard<std::recursive_mutex> guard{mutex};
int64_t newPreset = this->storage.UploadPreset(bankFile, uploadAfter);
firePresetsChanged(-1);
FirePresetsChanged(-1);
return newPreset;
}
int64_t PiPedalModel::uploadBank(BankFile &bankFile, int64_t uploadAfter)
int64_t PiPedalModel::UploadBank(BankFile &bankFile, int64_t uploadAfter)
{
std::lock_guard(this->mutex);
std::lock_guard<std::recursive_mutex> guard{mutex};
int64_t newPreset = this->storage.UploadBank(bankFile, uploadAfter);
fireBanksChanged(-1);
FireBanksChanged(-1);
return newPreset;
}
void PiPedalModel::loadPreset(int64_t clientId, int64_t instanceId)
void PiPedalModel::LoadPreset(int64_t clientId, int64_t instanceId)
{
std::lock_guard(this->mutex);
std::lock_guard<std::recursive_mutex> guard{mutex};
if (storage.LoadPreset(instanceId))
{
this->pedalBoard = storage.GetCurrentPreset();
updateDefaults(&this->pedalBoard);
UpdateDefaults(&this->pedalBoard);
this->hasPresetChanged = false; // no fire.
this->firePedalBoardChanged(clientId);
this->firePresetsChanged(clientId); // fire now.
this->FirePedalBoardChanged(clientId);
this->FirePresetsChanged(clientId); // fire now.
}
}
int64_t PiPedalModel::copyPreset(int64_t clientId, int64_t from, int64_t to)
int64_t PiPedalModel::CopyPreset(int64_t clientId, int64_t from, int64_t to)
{
std::lock_guard(this->mutex);
std::lock_guard<std::recursive_mutex> guard{mutex};
int64_t result = storage.CopyPreset(from, to);
if (result != -1)
{
this->firePresetsChanged(clientId); // fire now.
this->FirePresetsChanged(clientId); // fire now.
}
else
{
@@ -461,51 +541,51 @@ int64_t PiPedalModel::copyPreset(int64_t clientId, int64_t from, int64_t to)
}
return result;
}
bool PiPedalModel::updatePresets(int64_t clientId, const PresetIndex &presets)
bool PiPedalModel::UpdatePresets(int64_t clientId, const PresetIndex &presets)
{
std::lock_guard(this->mutex);
std::lock_guard<std::recursive_mutex> guard{mutex};
storage.SetPresetIndex(presets);
firePresetsChanged(clientId);
FirePresetsChanged(clientId);
return true;
}
void PiPedalModel::moveBank(int64_t clientId, int from, int to)
void PiPedalModel::MoveBank(int64_t clientId, int from, int to)
{
std::lock_guard(this->mutex);
std::lock_guard<std::recursive_mutex> guard{mutex};
storage.MoveBank(from, to);
fireBanksChanged(clientId);
FireBanksChanged(clientId);
}
int64_t PiPedalModel::deleteBank(int64_t clientId, int64_t instanceId)
int64_t PiPedalModel::DeleteBank(int64_t clientId, int64_t instanceId)
{
std::lock_guard(this->mutex);
std::lock_guard<std::recursive_mutex> guard{mutex};
int64_t selectedBank = this->storage.GetBanks().selectedBank();
int64_t newSelection = storage.DeleteBank(instanceId);
int64_t newSelectedBank = this->storage.GetBanks().selectedBank();
this->fireBanksChanged(clientId); // fire now.
this->FireBanksChanged(clientId); // fire now.
if (newSelectedBank != selectedBank)
{
this->openBank(clientId, newSelectedBank);
this->OpenBank(clientId, newSelectedBank);
}
return newSelection;
}
int64_t PiPedalModel::deletePreset(int64_t clientId, int64_t instanceId)
int64_t PiPedalModel::DeletePreset(int64_t clientId, int64_t instanceId)
{
std::lock_guard(this->mutex);
std::lock_guard<std::recursive_mutex> guard{mutex};
int64_t newSelection = storage.DeletePreset(instanceId);
this->firePresetsChanged(clientId); // fire now.
this->FirePresetsChanged(clientId); // fire now.
return newSelection;
}
bool PiPedalModel::renamePreset(int64_t clientId, int64_t instanceId, const std::string &name)
bool PiPedalModel::RenamePreset(int64_t clientId, int64_t instanceId, const std::string &name)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
if (storage.RenamePreset(instanceId, name))
{
this->firePresetsChanged(clientId);
this->FirePresetsChanged(clientId);
return true;
}
else
@@ -514,7 +594,7 @@ bool PiPedalModel::renamePreset(int64_t clientId, int64_t instanceId, const std:
}
}
void PiPedalModel::setWifiConfigSettings(const WifiConfigSettings &wifiConfigSettings)
void PiPedalModel::SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSettings)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
@@ -539,23 +619,23 @@ void PiPedalModel::setWifiConfigSettings(const WifiConfigSettings &wifiConfigSet
delete[] t;
}
}
WifiConfigSettings PiPedalModel::getWifiConfigSettings()
WifiConfigSettings PiPedalModel::GetWifiConfigSettings()
{
std::lock_guard<std::recursive_mutex> guard(mutex);
return this->storage.GetWifiConfigSettings();
}
JackConfiguration PiPedalModel::getJackConfiguration()
JackConfiguration PiPedalModel::GetJackConfiguration()
{
std::lock_guard<std::recursive_mutex> guard(mutex); // copy atomically.
return this->jackConfiguration;
}
void PiPedalModel::setJackChannelSelection(int64_t clientId, const JackChannelSelection &channelSelection)
void PiPedalModel::SetJackChannelSelection(int64_t clientId, const JackChannelSelection &channelSelection)
{
std::lock_guard<std::recursive_mutex> guard(mutex); // copy atomically.
this->storage.SetJackChannelSelection(channelSelection);
this->fireChannelSelectionChanged(clientId);
this->FireChannelSelectionChanged(clientId);
this->lv2Host.OnConfigurationChanged(jackConfiguration, channelSelection);
if (this->jackHost->IsOpen())
@@ -568,13 +648,13 @@ void PiPedalModel::setJackChannelSelection(int64_t clientId, const JackChannelSe
std::shared_ptr<Lv2PedalBoard> lv2PedalBoard{this->lv2Host.CreateLv2PedalBoard(this->pedalBoard)};
this->lv2PedalBoard = lv2PedalBoard;
jackHost->SetPedalBoard(lv2PedalBoard);
this->updateRealtimeVuSubscriptions();
this->UpdateRealtimeVuSubscriptions();
}
}
void PiPedalModel::fireChannelSelectionChanged(int64_t clientId)
void PiPedalModel::FireChannelSelectionChanged(int64_t clientId)
{
std::lock_guard(this->mutex);
std::lock_guard<std::recursive_mutex> guard{mutex};
{
// 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()];
@@ -594,7 +674,7 @@ void PiPedalModel::fireChannelSelectionChanged(int64_t clientId)
}
}
JackChannelSelection PiPedalModel::getJackChannelSelection()
JackChannelSelection PiPedalModel::GetJackChannelSelection()
{
std::lock_guard<std::recursive_mutex> guard(mutex);
JackChannelSelection t = this->storage.GetJackChannelSelection(this->jackConfiguration);
@@ -605,17 +685,17 @@ JackChannelSelection PiPedalModel::getJackChannelSelection()
return t;
}
int64_t PiPedalModel::addVuSubscription(int64_t instanceId)
int64_t PiPedalModel::AddVuSubscription(int64_t instanceId)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
int64_t subscriptionId = ++nextSubscriptionId;
activeVuSubscriptions.push_back(VuSubscription{subscriptionId, instanceId});
updateRealtimeVuSubscriptions();
UpdateRealtimeVuSubscriptions();
return subscriptionId;
}
void PiPedalModel::removeVuSubscription(int64_t subscriptionHandle)
void PiPedalModel::RemoveVuSubscription(int64_t subscriptionHandle)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
for (auto i = activeVuSubscriptions.begin(); i != activeVuSubscriptions.end(); ++i)
@@ -626,7 +706,7 @@ void PiPedalModel::removeVuSubscription(int64_t subscriptionHandle)
break;
}
}
updateRealtimeVuSubscriptions();
UpdateRealtimeVuSubscriptions();
}
void PiPedalModel::OnNotifyMidiValueChanged(int64_t instanceId, int portIndex, float value)
@@ -664,7 +744,7 @@ void PiPedalModel::OnNotifyMidiValueChanged(int64_t instanceId, int portIndex, f
}
delete[] t;
this->setPresetChanged(-1, true);
this->SetPresetChanged(-1, true);
return;
}
else
@@ -692,7 +772,7 @@ void PiPedalModel::OnNotifyMidiValueChanged(int64_t instanceId, int portIndex, f
}
delete[] t;
this->setPresetChanged(-1, true);
this->SetPresetChanged(-1, true);
return;
}
}
@@ -721,7 +801,7 @@ void PiPedalModel::OnNotifyVusSubscription(const std::vector<VuUpdate> &updates)
}
}
void PiPedalModel::updateRealtimeVuSubscriptions()
void PiPedalModel::UpdateRealtimeVuSubscriptions()
{
std::set<int64_t> addedInstances;
@@ -737,23 +817,23 @@ void PiPedalModel::updateRealtimeVuSubscriptions()
jackHost->SetVuSubscriptions(instanceids);
}
void PiPedalModel::updateRealtimeMonitorPortSubscriptions()
void PiPedalModel::UpdateRealtimeMonitorPortSubscriptions()
{
jackHost->SetMonitorPortSubscriptions(this->activeMonitorPortSubscriptions);
}
int64_t PiPedalModel::monitorPort(int64_t instanceId, const std::string &key, float updateInterval, PortMonitorCallback onUpdate)
int64_t PiPedalModel::MonitorPort(int64_t instanceId, const std::string &key, float updateInterval, PortMonitorCallback onUpdate)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
int64_t subscriptionId = ++nextSubscriptionId;
activeMonitorPortSubscriptions.push_back(
MonitorPortSubscription{subscriptionId, instanceId, key, updateInterval, onUpdate});
updateRealtimeMonitorPortSubscriptions();
UpdateRealtimeMonitorPortSubscriptions();
return subscriptionId;
}
void PiPedalModel::unmonitorPort(int64_t subscriptionHandle)
void PiPedalModel::UnmonitorPort(int64_t subscriptionHandle)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
@@ -762,7 +842,7 @@ void PiPedalModel::unmonitorPort(int64_t subscriptionHandle)
if ((*i).subscriptionHandle == subscriptionHandle)
{
activeMonitorPortSubscriptions.erase(i);
updateRealtimeMonitorPortSubscriptions();
UpdateRealtimeMonitorPortSubscriptions();
break;
}
}
@@ -785,7 +865,7 @@ void PiPedalModel::OnNotifyMonitorPort(const MonitorPortUpdate &update)
}
}
void PiPedalModel::getLv2Parameter(
void PiPedalModel::GetLv2Parameter(
int64_t clientId,
int64_t instanceId,
const std::string uri,
@@ -837,40 +917,40 @@ void PiPedalModel::getLv2Parameter(
this->jackHost->getRealtimeParameter(request);
}
BankIndex PiPedalModel::getBankIndex() const
BankIndex PiPedalModel::GetBankIndex() const
{
std::lock_guard<std::recursive_mutex> guard(const_cast<std::recursive_mutex&>(mutex));
return storage.GetBanks();
}
void PiPedalModel::renameBank(int64_t clientId, int64_t bankId, const std::string &newName)
void PiPedalModel::RenameBank(int64_t clientId, int64_t bankId, const std::string &newName)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
storage.RenameBank(bankId, newName);
fireBanksChanged(clientId);
FireBanksChanged(clientId);
}
int64_t PiPedalModel::saveBankAs(int64_t clientId, int64_t bankId, const std::string &newName)
int64_t PiPedalModel::SaveBankAs(int64_t clientId, int64_t bankId, const std::string &newName)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
int64_t newId = storage.SaveBankAs(bankId, newName);
fireBanksChanged(clientId);
FireBanksChanged(clientId);
return newId;
}
void PiPedalModel::openBank(int64_t clientId, int64_t bankId)
void PiPedalModel::OpenBank(int64_t clientId, int64_t bankId)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
storage.LoadBank(bankId);
fireBanksChanged(clientId);
FireBanksChanged(clientId);
firePresetsChanged(clientId);
FirePresetsChanged(clientId);
this->pedalBoard = storage.GetCurrentPreset();
updateDefaults(&this->pedalBoard);
UpdateDefaults(&this->pedalBoard);
this->hasPresetChanged = false;
this->firePedalBoardChanged(clientId);
this->FirePedalBoardChanged(clientId);
}
JackServerSettings PiPedalModel::GetJackServerSettings()
@@ -914,7 +994,7 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet
storage.SaveCurrentPreset(currentPreset);
this->jackConfiguration.SetIsRestarting(true);
fireJackConfigurationChanged(this->jackConfiguration);
FireJackConfigurationChanged(this->jackConfiguration);
this->jackHost->UpdateServerConfiguration(
jackServerSettings,
[this](bool success, const std::string &errorMessage)
@@ -931,7 +1011,7 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet
{
this->jackConfiguration.SetIsRestarting(false);
this->jackConfiguration.SetErrorStatus(errorMessage);
fireJackConfigurationChanged(this->jackConfiguration);
FireJackConfigurationChanged(this->jackConfiguration);
}
else
{
@@ -939,22 +1019,22 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet
// so just sit tight and wait for the restart.
#ifdef JUNK
this->jackConfiguration.SetErrorStatus("");
fireJackConfigurationChanged(this->jackConfiguration);
FireJackConfigurationChanged(this->jackConfiguration);
// restart the pedalboard on a new instance.
std::shared_ptr<Lv2PedalBoard> lv2PedalBoard{this->lv2Host.CreateLv2PedalBoard(this->pedalBoard)};
this->lv2PedalBoard = lv2PedalBoard;
jackHost->SetPedalBoard(lv2PedalBoard);
updateRealtimeVuSubscriptions();
updateRealtimeMonitorPortSubscriptions();
UpdateRealtimeVuSubscriptions();
UpdateRealtimeMonitorPortSubscriptions();
#endif
}
});
}
}
void PiPedalModel::updateDefaults(PedalBoardItem *pedalBoardItem)
void PiPedalModel::UpdateDefaults(PedalBoardItem *pedalBoardItem)
{
std::shared_ptr<Lv2PluginInfo> t = lv2Host.GetPluginInfo(pedalBoardItem->uri());
const Lv2PluginInfo *pPlugin = t.get();
@@ -984,42 +1064,50 @@ void PiPedalModel::updateDefaults(PedalBoardItem *pedalBoardItem)
}
for (size_t i = 0; i < pedalBoardItem->topChain().size(); ++i)
{
updateDefaults(&(pedalBoardItem->topChain()[i]));
UpdateDefaults(&(pedalBoardItem->topChain()[i]));
}
for (size_t i = 0; i < pedalBoardItem->bottomChain().size(); ++i)
{
updateDefaults(&(pedalBoardItem->bottomChain()[i]));
UpdateDefaults(&(pedalBoardItem->bottomChain()[i]));
}
}
void PiPedalModel::updateDefaults(PedalBoard *pedalBoard)
void PiPedalModel::UpdateDefaults(PedalBoard *pedalBoard)
{
for (size_t i = 0; i < pedalBoard->items().size(); ++i)
{
updateDefaults(&(pedalBoard->items()[i]));
UpdateDefaults(&(pedalBoard->items()[i]));
}
}
std::vector<Lv2PluginPreset> PiPedalModel::GetPluginPresets(std::string pluginUri)
PluginPresets PiPedalModel::GetPluginPresets(const std::string& pluginUri)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
return lv2Host.GetPluginPresets(pluginUri);
return storage.GetPluginPresets(pluginUri);
}
void PiPedalModel::LoadPluginPreset(int64_t instanceId, const std::string &presetUri)
PluginUiPresets PiPedalModel::GetPluginUiPresets(const std::string& pluginUri)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
PedalBoardItem *pedalBoardItem = this->pedalBoard.GetItem(instanceId);
return storage.GetPluginUiPresets(pluginUri);
}
void PiPedalModel::LoadPluginPreset(int64_t pluginInstanceId, uint64_t presetInstanceId)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
PedalBoardItem *pedalBoardItem = this->pedalBoard.GetItem(pluginInstanceId);
if (pedalBoardItem != nullptr)
{
std::vector<ControlValue> controlValues = lv2Host.LoadPluginPreset(pedalBoardItem, presetUri);
jackHost->SetPluginPreset(instanceId, controlValues);
std::vector<ControlValue> controlValues = storage.GetPluginPresetValues(pedalBoardItem->uri(), presetInstanceId);
jackHost->SetPluginPreset(pluginInstanceId, controlValues);
for (size_t i = 0; i < controlValues.size(); ++i)
{
const ControlValue &value = controlValues[i];
this->pedalBoard.SetControlValue(instanceId, value.key(), value.value());
this->pedalBoard.SetControlValue(pluginInstanceId, value.key(), value.value());
}
IPiPedalModelSubscriber **t = new IPiPedalModelSubscriber *[this->subscribers.size()];
@@ -1030,13 +1118,13 @@ void PiPedalModel::LoadPluginPreset(int64_t instanceId, const std::string &prese
size_t n = this->subscribers.size();
for (size_t i = 0; i < n; ++i)
{
t[i]->OnLoadPluginPreset(instanceId, controlValues);
t[i]->OnLoadPluginPreset(pluginInstanceId, controlValues);
}
delete[] t;
}
}
void PiPedalModel::deleteAtomOutputListeners(int64_t clientId)
void PiPedalModel::DeleteAtomOutputListeners(int64_t clientId)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
for (size_t i = 0; i < atomOutputListeners.size(); ++i)
@@ -1050,7 +1138,7 @@ void PiPedalModel::deleteAtomOutputListeners(int64_t clientId)
jackHost->SetListenForAtomOutput(atomOutputListeners.size() != 0);
}
void PiPedalModel::deleteMidiListeners(int64_t clientId)
void PiPedalModel::DeleteMidiListeners(int64_t clientId)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
for (size_t i = 0; i < midiEventListeners.size(); ++i)
@@ -1111,7 +1199,7 @@ void PiPedalModel::OnNotifyMidiListen(bool isNote, uint8_t noteOrControl)
jackHost->SetListenForMidiEvent(midiEventListeners.size() != 0);
}
void PiPedalModel::listenForMidiEvent(int64_t clientId, int64_t clientHandle, bool listenForControlsOnly)
void PiPedalModel::ListenForMidiEvent(int64_t clientId, int64_t clientHandle, bool listenForControlsOnly)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
MidiListener listener{clientId, clientHandle, listenForControlsOnly};
@@ -1119,7 +1207,7 @@ void PiPedalModel::listenForMidiEvent(int64_t clientId, int64_t clientHandle, bo
jackHost->SetListenForMidiEvent(true);
}
void PiPedalModel::listenForAtomOutputs(int64_t clientId, int64_t clientHandle, uint64_t instanceId)
void PiPedalModel::ListenForAtomOutputs(int64_t clientId, int64_t clientHandle, uint64_t instanceId)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
AtomOutputListener listener{clientId, clientHandle, instanceId};
@@ -1127,7 +1215,7 @@ void PiPedalModel::listenForAtomOutputs(int64_t clientId, int64_t clientHandle,
jackHost->SetListenForAtomOutput(true);
}
void PiPedalModel::cancelListenForMidiEvent(int64_t clientId, int64_t clientHandle)
void PiPedalModel::CancelListenForMidiEvent(int64_t clientId, int64_t clientHandle)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
for (size_t i = 0; i < midiEventListeners.size(); ++i)
@@ -1144,7 +1232,7 @@ void PiPedalModel::cancelListenForMidiEvent(int64_t clientId, int64_t clientHand
jackHost->SetListenForMidiEvent(false);
}
}
void PiPedalModel::cancelListenForAtomOutputs(int64_t clientId, int64_t clientHandle)
void PiPedalModel::CancelListenForAtomOutputs(int64_t clientId, int64_t clientHandle)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
for (size_t i = 0; i < atomOutputListeners.size(); ++i)
+64 -55
View File
@@ -46,6 +46,7 @@ public:
virtual void OnControlChanged(int64_t clientId, int64_t pedalItemId, const std::string &symbol, float value) = 0;
virtual void OnPedalBoardChanged(int64_t clientId, const PedalBoard &pedalBoard) = 0;
virtual void OnPresetsChanged(int64_t clientId, const PresetIndex&presets) = 0;
virtual void OnPluginPresetsChanged(const std::string&pluginUri) = 0;
virtual void OnChannelSelectionChanged(int64_t clientId, const JackChannelSelection&channelSelection) = 0;
virtual void OnVuMeterUpdate(const std::vector<VuUpdate>& updates) = 0;
virtual void OnBankIndexChanged(const BankIndex& bankIndex) = 0;
@@ -77,8 +78,8 @@ private:
int64_t clientHandle;
uint64_t instanceId;
};
void deleteMidiListeners(int64_t clientId);
void deleteAtomOutputListeners(int64_t clientId);
void DeleteMidiListeners(int64_t clientId);
void DeleteAtomOutputListeners(int64_t clientId);
std::vector<MidiListener> midiEventListeners;
std::vector<AtomOutputListener> atomOutputListeners;
@@ -96,15 +97,16 @@ private:
std::vector<IPiPedalModelSubscriber*> subscribers;
void setPresetChanged(int64_t clientId,bool value);
void firePresetsChanged(int64_t clientId);
void firePedalBoardChanged(int64_t clientId);
void fireChannelSelectionChanged(int64_t clientId);
void fireBanksChanged(int64_t clientId);
void fireJackConfigurationChanged(const JackConfiguration&jackConfiguration);
void SetPresetChanged(int64_t clientId,bool value);
void FirePresetsChanged(int64_t clientId);
void FirePluginPresetsChanged(const std::string &pluginUri);
void FirePedalBoardChanged(int64_t clientId);
void FireChannelSelectionChanged(int64_t clientId);
void FireBanksChanged(int64_t clientId);
void FireJackConfigurationChanged(const JackConfiguration&jackConfiguration);
void updateDefaults(PedalBoardItem*pedalBoardItem);
void updateDefaults(PedalBoard*pedalBoard);
void UpdateDefaults(PedalBoardItem*pedalBoardItem);
void UpdateDefaults(PedalBoard*pedalBoard);
class VuSubscription {
@@ -117,8 +119,8 @@ private:
std::vector<MonitorPortSubscription> activeMonitorPortSubscriptions;
void updateRealtimeVuSubscriptions();
void updateRealtimeMonitorPortSubscriptions();
void UpdateRealtimeVuSubscriptions();
void UpdateRealtimeMonitorPortSubscriptions();
void OnVuUpdate(const std::vector<VuUpdate>& updates);
std::vector<RealtimeParameterRequest*> outstandingParameterRequests;
@@ -140,61 +142,68 @@ public:
void LoadLv2PluginInfo(const PiPedalConfiguration&configuration);
void Load(const PiPedalConfiguration&configuration);
const Lv2Host& getPlugins() const { return lv2Host; }
PedalBoard getCurrentPedalBoardCopy() {
const Lv2Host& GetLv2Host() const { return lv2Host; }
PedalBoard GetCurrentPedalBoardCopy()
{
std::lock_guard<std::recursive_mutex> guard(mutex);
return pedalBoard;
return pedalBoard; // can return a referece because we'd lose mutex protection
}
std::vector<Lv2PluginPreset> GetPluginPresets(std::string pluginUri);
void LoadPluginPreset(int64_t instanceId,const std::string &presetUri);
PluginUiPresets GetPluginUiPresets(const std::string& pluginUri);
PluginPresets GetPluginPresets(const std::string&pluginUri);
void LoadPluginPreset(int64_t pluginInstanceId,uint64_t presetInstanceId);
void AddNotificationSubscription(IPiPedalModelSubscriber* pSubscriber);
void RemoveNotificationSubsription(IPiPedalModelSubscriber* pSubscriber);
void setPedalBoardItemEnable(int64_t clientId, int64_t instanceId, bool enabled);
void setControl(int64_t clientId,int64_t pedalItemId, const std::string&symbol,float value);
void previewControl(int64_t clientId,int64_t pedalItemId, const std::string&symbol,float value);
void setPedalBoard(int64_t clientId,PedalBoard &pedalBoard);
void SetPedalBoardItemEnable(int64_t clientId, int64_t instanceId, bool enabled);
void SetControl(int64_t clientId,int64_t pedalItemId, const std::string&symbol,float value);
void PreviewControl(int64_t clientId,int64_t pedalItemId, const std::string&symbol,float value);
void SetPedalBoard(int64_t clientId,PedalBoard &pedalBoard);
void getPresets(PresetIndex*pResult);
PedalBoard getPreset(int64_t instanceId);
void getBank(int64_t instanceId, BankFile*pBank);
void GetPresets(PresetIndex*pResult);
PedalBoard GetPreset(int64_t instanceId);
void GetBank(int64_t instanceId, BankFile*pBank);
int64_t uploadBank(BankFile&bankFile, int64_t uploadAfter = -1);
int64_t uploadPreset(const BankFile&bankFile, int64_t uploadAfter = -1);
void saveCurrentPreset(int64_t clientId);
int64_t saveCurrentPresetAs(int64_t clientId, const std::string& name,int64_t saveAfterInstanceId = -1);
int64_t UploadBank(BankFile&bankFile, int64_t uploadAfter = -1);
int64_t UploadPreset(const BankFile&bankFile, int64_t uploadAfter = -1);
void UploadPluginPresets(const PluginPresets&pluginPresets);
void SaveCurrentPreset(int64_t clientId);
int64_t SaveCurrentPresetAs(int64_t clientId, const std::string& name,int64_t saveAfterInstanceId = -1);
int64_t SavePluginPresetAs(int64_t instanceId, const std::string&name);
void loadPreset(int64_t clientId, int64_t instanceId);
bool updatePresets(int64_t clientId,const PresetIndex&presets);
int64_t deletePreset(int64_t clientId,int64_t instanceId);
bool renamePreset(int64_t clientId,int64_t instanceId,const std::string&name);
int64_t copyPreset(int64_t clientId, int64_t fromIndex, int64_t toIndex);
void LoadPreset(int64_t clientId, int64_t instanceId);
bool UpdatePresets(int64_t clientId,const PresetIndex&presets);
void UpdatePluginPresets(const PluginUiPresets&pluginPresets);
int64_t DeletePreset(int64_t clientId,int64_t instanceId);
bool RenamePreset(int64_t clientId,int64_t instanceId,const std::string&name);
int64_t CopyPreset(int64_t clientId, int64_t fromIndex, int64_t toIndex);
uint64_t CopyPluginPreset(const std::string&pluginUri,uint64_t presetId);
void moveBank(int64_t clientId,int from, int to);
int64_t deleteBank(int64_t clientId, int64_t instanceId);
void MoveBank(int64_t clientId,int from, int to);
int64_t DeleteBank(int64_t clientId, int64_t instanceId);
int64_t duplicatePreset(int64_t clientId, int64_t instanceId) { return copyPreset(clientId,instanceId,-1); }
int64_t DuplicatePreset(int64_t clientId, int64_t instanceId) { return CopyPreset(clientId,instanceId,-1); }
JackConfiguration getJackConfiguration();
JackConfiguration GetJackConfiguration();
void setJackChannelSelection(int64_t clientId,const JackChannelSelection &channelSelection);
JackChannelSelection getJackChannelSelection();
void SetJackChannelSelection(int64_t clientId,const JackChannelSelection &channelSelection);
JackChannelSelection GetJackChannelSelection();
void setWifiConfigSettings(const WifiConfigSettings&wifiConfigSettings);
WifiConfigSettings getWifiConfigSettings();
void SetWifiConfigSettings(const WifiConfigSettings&wifiConfigSettings);
WifiConfigSettings GetWifiConfigSettings();
int64_t addVuSubscription(int64_t instanceId);
void removeVuSubscription(int64_t subscriptionHandle);
int64_t AddVuSubscription(int64_t instanceId);
void RemoveVuSubscription(int64_t subscriptionHandle);
int64_t monitorPort(int64_t instanceId,const std::string &key,float updateInterval, PortMonitorCallback onUpdate);
void unmonitorPort(int64_t subscriptionHandle);
int64_t MonitorPort(int64_t instanceId,const std::string &key,float updateInterval, PortMonitorCallback onUpdate);
void UnmonitorPort(int64_t subscriptionHandle);
void getLv2Parameter(
void GetLv2Parameter(
int64_t clientId,
int64_t instanceId,
const std::string uri,
@@ -202,22 +211,22 @@ public:
std::function<void (const std::string& error)> onError
);
BankIndex getBankIndex() const;
void renameBank(int64_t clientId,int64_t bankId, const std::string& newName);
int64_t saveBankAs(int64_t clientId,int64_t bankId, const std::string& newName);
void openBank(int64_t clientId,int64_t bankId);
BankIndex GetBankIndex() const;
void RenameBank(int64_t clientId,int64_t bankId, const std::string& newName);
int64_t SaveBankAs(int64_t clientId,int64_t bankId, const std::string& newName);
void OpenBank(int64_t clientId,int64_t bankId);
JackHostStatus getJackStatus() {
JackHostStatus GetJackStatus() {
return this->jackHost->getJackStatus();
}
JackServerSettings GetJackServerSettings();
void SetJackServerSettings(const JackServerSettings& jackServerSettings);
void listenForMidiEvent(int64_t clientId, int64_t clientHandle, bool listenForControlsOnly);
void cancelListenForMidiEvent(int64_t clientId, int64_t clientHandle);
void ListenForMidiEvent(int64_t clientId, int64_t clientHandle, bool listenForControlsOnly);
void CancelListenForMidiEvent(int64_t clientId, int64_t clientHandle);
void listenForAtomOutputs(int64_t clientId, int64_t clientHandle, uint64_t instanceId);
void cancelListenForAtomOutputs(int64_t clientId, int64_t clientHandle);
void ListenForAtomOutputs(int64_t clientId, int64_t clientHandle, uint64_t instanceId);
void CancelListenForAtomOutputs(int64_t clientId, int64_t clientHandle);
std::vector<AlsaDeviceInfo> GetAlsaDevices();
const std::filesystem::path& GetWebRoot() const;
+95 -44
View File
@@ -108,14 +108,14 @@ JSON_MAP_END()
class LoadPluginPresetBody {
public:
int64_t instanceId_;
std::string presetName_;
uint64_t pluginInstanceId_;
uint64_t presetInstanceId_;
DECLARE_JSON_MAP(LoadPluginPresetBody);
};
JSON_MAP_BEGIN(LoadPluginPresetBody)
JSON_MAP_REFERENCE(LoadPluginPresetBody, instanceId)
JSON_MAP_REFERENCE(LoadPluginPresetBody, presetName)
JSON_MAP_REFERENCE(LoadPluginPresetBody, pluginInstanceId)
JSON_MAP_REFERENCE(LoadPluginPresetBody, presetInstanceId)
JSON_MAP_END()
class FromToBody
@@ -189,6 +189,19 @@ JSON_MAP_REFERENCE(SaveCurrentPresetAsBody, name)
JSON_MAP_REFERENCE(SaveCurrentPresetAsBody, saveAfterInstanceId)
JSON_MAP_END()
class SavePluginPresetAsBody
{
public:
int64_t instanceId_ = -1;
std::string name_;
DECLARE_JSON_MAP(SavePluginPresetAsBody);
};
JSON_MAP_BEGIN(SavePluginPresetAsBody)
JSON_MAP_REFERENCE(SavePluginPresetAsBody, instanceId)
JSON_MAP_REFERENCE(SavePluginPresetAsBody, name)
JSON_MAP_END()
class RenameBankBody {
public:
int64_t bankId_;
@@ -231,6 +244,19 @@ JSON_MAP_REFERENCE(CopyPresetBody, fromId)
JSON_MAP_REFERENCE(CopyPresetBody, toId)
JSON_MAP_END()
class CopyPluginPresetBody
{
public:
std::string pluginUri_;
uint64_t instanceId_;
DECLARE_JSON_MAP(CopyPluginPresetBody);
};
JSON_MAP_BEGIN(CopyPluginPresetBody)
JSON_MAP_REFERENCE(CopyPluginPresetBody, pluginUri)
JSON_MAP_REFERENCE(CopyPluginPresetBody, instanceId)
JSON_MAP_END()
class PedalBoardItemEnabledBody
{
public:
@@ -374,11 +400,11 @@ public:
{
for (int i = 0; i < this->activePortMonitors.size(); ++i)
{
model.unmonitorPort(activePortMonitors[i].subscriptionHandle);
model.UnmonitorPort(activePortMonitors[i].subscriptionHandle);
}
for (int i = 0; i < this->activeVuSubscriptions.size(); ++i)
{
model.removeVuSubscription(activeVuSubscriptions[i].subscriptionHandle);
model.RemoveVuSubscription(activeVuSubscriptions[i].subscriptionHandle);
}
model.RemoveNotificationSubsription(this);
@@ -708,42 +734,42 @@ public:
{
ControlChangedBody message;
pReader->read(&message);
this->model.previewControl(message.clientId_, message.instanceId_, message.symbol_, message.value_);
this->model.PreviewControl(message.clientId_, message.instanceId_, message.symbol_, message.value_);
}
else if (message == "setControl")
{
ControlChangedBody message;
pReader->read(&message);
this->model.setControl(message.clientId_, message.instanceId_, message.symbol_, message.value_);
this->model.SetControl(message.clientId_, message.instanceId_, message.symbol_, message.value_);
}
else if (message == "listenForMidiEvent")
{
ListenForMidiEventBody body;
pReader->read(&body);
this->model.listenForMidiEvent(this->clientId,body.handle_, body.listenForControlsOnly_);
this->model.ListenForMidiEvent(this->clientId,body.handle_, body.listenForControlsOnly_);
}
else if (message == "cancelListenForMidiEvent")
{
uint64_t handle;
pReader->read(&handle);
this->model.cancelListenForMidiEvent(this->clientId,handle);
this->model.CancelListenForMidiEvent(this->clientId,handle);
}
else if (message == "listenForAtomOutput")
{
ListenForAtomOutputBody body;
pReader->read(&body);
this->model.listenForAtomOutputs(this->clientId,body.handle_, body.instanceId_);
this->model.ListenForAtomOutputs(this->clientId,body.handle_, body.instanceId_);
}
else if (message == "cancelListenForAtomOutput")
{
int64_t handle;
pReader->read(&handle);
this->model.cancelListenForMidiEvent(this->clientId,handle);
this->model.CancelListenForMidiEvent(this->clientId,handle);
}
else if (message == "getJackStatus")
{
JackHostStatus status = model.getJackStatus();
JackHostStatus status = model.GetJackStatus();
this->Reply(replyTo,"getJackStatus",status);
} else if (message == "getAlsaDevices")
{
@@ -762,13 +788,13 @@ public:
{
std::string uri;
pReader->read(&uri);
this->Reply(replyTo,"getPluginPresets",this->model.GetPluginPresets(uri));
this->Reply(replyTo,"getPluginPresets",this->model.GetPluginUiPresets(uri));
}
else if (message == "loadPluginPreset")
{
LoadPluginPresetBody body;
pReader->read(&body);
this->model.LoadPluginPreset(body.instanceId_,body.presetName_);
this->model.LoadPluginPreset(body.pluginInstanceId_,body.presetInstanceId_);
}
else if (message == "setJackServerSettings") {
@@ -792,11 +818,11 @@ public:
}
this->model.setWifiConfigSettings(wifiConfigSettings);
this->model.SetWifiConfigSettings(wifiConfigSettings);
this->Reply(replyTo,"setWifiConfigSettings");
}
else if (message == "getWifiConfigSettings") {
this->Reply(replyTo, "getWifiConfigSettings", model.getWifiConfigSettings());
this->Reply(replyTo, "getWifiConfigSettings", model.GetWifiConfigSettings());
}
@@ -805,42 +831,49 @@ public:
}
else if (message == "getBankIndex") {
BankIndex bankIndex = model.getBankIndex();
BankIndex bankIndex = model.GetBankIndex();
this->Reply(replyTo, "getBankIndex", bankIndex);
}
else if (message == "getJackConfiguration")
{
JackConfiguration configuration = this->model.getJackConfiguration();
JackConfiguration configuration = this->model.GetJackConfiguration();
this->Reply(replyTo, "getJackConfiguration", configuration);
}
else if (message == "getJackSettings")
{
JackChannelSelection selection = this->model.getJackChannelSelection();
JackChannelSelection selection = this->model.GetJackChannelSelection();
this->Reply(replyTo, "getJackSettings", selection);
}
else if (message == "saveCurrentPreset")
{
this->model.saveCurrentPreset(this->clientId);
this->model.SaveCurrentPreset(this->clientId);
}
else if (message == "saveCurrentPresetAs")
{
SaveCurrentPresetAsBody body;
pReader->read(&body);
int64_t result = this->model.saveCurrentPresetAs(this->clientId, body.name_, body.saveAfterInstanceId_);
int64_t result = this->model.SaveCurrentPresetAs(this->clientId, body.name_, body.saveAfterInstanceId_);
Reply(replyTo, "saveCurrentPresetsAs", result);
}
else if (message == "savePluginPresetAs")
{
SavePluginPresetAsBody body;
pReader->read(&body);
int64_t result = this->model.SavePluginPresetAs(body.instanceId_, body.name_);
Reply(replyTo, "saveCurrentPresetsAs", result);
}
else if (message == "getPresets")
{
PresetIndex presets;
this->model.getPresets(&presets);
this->model.GetPresets(&presets);
Reply(replyTo, "getPresets", presets);
}
else if (message == "setPedalBoardItemEnable")
{
PedalBoardItemEnabledBody body;
pReader->read(&body);
model.setPedalBoardItemEnable(body.clientId_, body.instanceId_, body.enabled_);
model.SetPedalBoardItemEnable(body.clientId_, body.instanceId_, body.enabled_);
}
else if (message == "setCurrentPedalBoard")
{
@@ -848,22 +881,22 @@ public:
SetCurrentPedalBoardBody body;
pReader->read(&body);
this->model.setPedalBoard(body.clientId_, body.pedalBoard_);
this->model.SetPedalBoard(body.clientId_, body.pedalBoard_);
}
}
else if (message == "currentPedalBoard")
{
auto pedalBoard = model.getCurrentPedalBoardCopy();
auto pedalBoard = model.GetCurrentPedalBoardCopy();
Reply(replyTo, "currentPedalBoard", pedalBoard);
}
else if (message == "plugins")
{
auto ui_plugins = model.getPlugins().GetUiPlugins();
auto ui_plugins = model.GetLv2Host().GetUiPlugins();
Reply(replyTo, "plugins", ui_plugins);
}
else if (message == "pluginClasses")
{
auto classes = model.getPlugins().GetLv2PluginClass();
auto classes = model.GetLv2Host().GetLv2PluginClass();
Reply(replyTo, "pluginClasses", classes);
}
else if (message == "hello")
@@ -875,7 +908,7 @@ public:
{
JackChannelSelection jackSettings;
pReader->read(&jackSettings);
this->model.setJackChannelSelection(this->clientId, jackSettings);
this->model.SetJackChannelSelection(this->clientId, jackSettings);
}
else if (message == "version")
{
@@ -887,20 +920,27 @@ public:
{
int64_t instanceId = 0;
pReader->read(&instanceId);
model.loadPreset(this->clientId, instanceId);
model.LoadPreset(this->clientId, instanceId);
}
else if (message == "updatePresets")
{
PresetIndex newIndex;
pReader->read(&newIndex);
bool result = model.updatePresets(this->clientId, newIndex);
bool result = model.UpdatePresets(this->clientId, newIndex);
this->Reply(replyTo, "updatePresets", result);
}
else if (message == "updatePluginPresets")
{
PluginUiPresets pluginPresets;
pReader->read(&pluginPresets);
model.UpdatePluginPresets(pluginPresets);
this->Reply(replyTo, "updatePluginPresets", true);
}
else if (message == "moveBank")
{
FromToBody body;
pReader->read(&body);
model.moveBank(this->clientId, body.from_, body.to_);
model.MoveBank(this->clientId, body.from_, body.to_);
this->Reply(replyTo, "moveBank");
}
else if (message == "shutdown")
@@ -920,14 +960,14 @@ public:
{
int64_t instanceId = 0;
pReader->read(&instanceId);
int64_t result = model.deletePreset(this->clientId, instanceId);
int64_t result = model.DeletePreset(this->clientId, instanceId);
this->Reply(replyTo, "deletePresetItem", result);
}
else if (message == "deleteBankItem")
{
int64_t instanceId = 0;
pReader->read(&instanceId);
uint64_t result = model.deleteBank(this->clientId, instanceId);
uint64_t result = model.DeleteBank(this->clientId, instanceId);
this->Reply(replyTo, "deleteBankItem", result);
}
else if (message == "renameBank")
@@ -948,7 +988,7 @@ public:
try {
model.renameBank(this->clientId,body.bankId_, body.newName_);
model.RenameBank(this->clientId,body.bankId_, body.newName_);
this->Reply(replyTo, "renameBank");
} catch (const std::exception &e)
{
@@ -960,7 +1000,7 @@ public:
int64_t bankId = -1;
pReader->read(&bankId);
try {
model.openBank(this->clientId,bankId);;
model.OpenBank(this->clientId,bankId);;
this->Reply(replyTo, "openBank");
} catch (const std::exception &e)
{
@@ -972,7 +1012,7 @@ public:
RenameBankBody body;
pReader->read(&body);
try {
int64_t newId = model.saveBankAs(this->clientId,body.bankId_, body.newName_);
int64_t newId = model.SaveBankAs(this->clientId,body.bankId_, body.newName_);
this->Reply(replyTo, "saveBankAs",newId);
} catch (const std::exception &e)
{
@@ -984,22 +1024,29 @@ public:
RenamePresetBody body;
pReader->read(&body);
bool result = model.renamePreset(body.clientId_, body.instanceId_, body.name_);
bool result = model.RenamePreset(body.clientId_, body.instanceId_, body.name_);
this->Reply(replyTo, "renamePresetItem", result);
}
else if (message == "copyPreset")
{
CopyPresetBody body;
pReader->read(&body);
int64_t result = model.copyPreset(body.clientId_, body.fromId_, body.toId_);
int64_t result = model.CopyPreset(body.clientId_, body.fromId_, body.toId_);
this->Reply(replyTo, "copyPreset", result);
}
else if (message == "copyPluginPreset")
{
CopyPluginPresetBody body;
pReader->read(&body);
uint64_t result = model.CopyPluginPreset(body.pluginUri_,body.instanceId_);
this->Reply(replyTo, "copyPluginPreset", result);
}
else if (message =="getLv2Parameter")
{
GetLv2ParameterBody body;
pReader->read(&body);
model.getLv2Parameter(
model.GetLv2Parameter(
this->clientId,
body.instanceId_,
body.uri_,
@@ -1018,7 +1065,7 @@ public:
MonitorPortBody body;
pReader->read(&body);
int64_t subscriptionHandle = model.monitorPort(
int64_t subscriptionHandle = model.MonitorPort(
body.instanceId_,
body.key_,
body.updateRate_,
@@ -1067,7 +1114,7 @@ public:
}
}
}
model.unmonitorPort(subscriptionHandle);
model.UnmonitorPort(subscriptionHandle);
}
}
else if (message == "addVuSubscription")
@@ -1076,7 +1123,7 @@ public:
pReader->read(&instanceId);
int64_t subscriptionHandle = model.addVuSubscription(instanceId);
int64_t subscriptionHandle = model.AddVuSubscription(instanceId);
{
std::lock_guard<std::recursive_mutex> guard(subscriptionMutex);
@@ -1100,7 +1147,7 @@ public:
}
}
}
model.removeVuSubscription(subscriptionHandle);
model.RemoveVuSubscription(subscriptionHandle);
}
else if (message == "imageList")
{
@@ -1194,6 +1241,10 @@ public:
body.presets_ = const_cast<PresetIndex *>(&presets);
Send("onPresetsChanged", body);
}
virtual void OnPluginPresetsChanged(const std::string&pluginUri)
{
Send("onPluginPresetsChanged", pluginUri);
}
virtual void OnJackConfigurationChanged(const JackConfiguration &jackConfiguration)
{
Send("onJackConfigurationChanged", jackConfiguration);
+47
View File
@@ -0,0 +1,47 @@
#include "PluginPreset.hpp"
using namespace pipedal;
JSON_MAP_BEGIN(PluginUiPreset)
JSON_MAP_REFERENCE(PluginUiPreset,label)
JSON_MAP_REFERENCE(PluginUiPreset,instanceId)
JSON_MAP_END()
JSON_MAP_BEGIN(PluginUiPresets)
JSON_MAP_REFERENCE(PluginUiPresets,pluginUri)
JSON_MAP_REFERENCE(PluginUiPresets,presets)
JSON_MAP_END()
JSON_MAP_BEGIN(PluginPresetIndexEntry)
JSON_MAP_REFERENCE(PluginPresetIndexEntry,pluginUri)
JSON_MAP_REFERENCE(PluginPresetIndexEntry,fileName)
JSON_MAP_END()
JSON_MAP_BEGIN(PluginPresetIndex)
JSON_MAP_REFERENCE(PluginPresetIndex,entries)
JSON_MAP_REFERENCE(PluginPresetIndex,nextInstanceId)
JSON_MAP_END()
JSON_MAP_BEGIN(PluginPreset)
JSON_MAP_REFERENCE(PluginPreset,instanceId)
JSON_MAP_REFERENCE(PluginPreset,label)
JSON_MAP_REFERENCE(PluginPreset,controlValues)
JSON_MAP_END()
JSON_MAP_BEGIN(PluginPresets)
JSON_MAP_REFERENCE(PluginPresets,pluginUri)
JSON_MAP_REFERENCE(PluginPresets,presets)
JSON_MAP_REFERENCE(PluginPresets,nextInstanceId)
JSON_MAP_END()
// JSON_MAP_BEGIN(PluginPreset)
// JSON_MAP_REFERENCE(PluginPreset,presetUri)
// JSON_MAP_REFERENCE(PluginPreset,name)
// JSON_MAP_REFERENCE(PluginPreset,factoryPreset)
// JSON_MAP_REFERENCE(PluginPreset,modified)
// JSON_MAP_REFERENCE(PluginPreset,selected)
// JSON_MAP_END()
+136
View File
@@ -0,0 +1,136 @@
// Copyright (c) 2021 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.
#pragma once
#include "json.hpp"
#include "PiPedalException.hpp"
#include <utility>
namespace pipedal {
class PluginPresetIndexEntry {
public:
PluginPresetIndexEntry() { }
PluginPresetIndexEntry(const std::string &pluginUri, const std::string&fileName)
: pluginUri_(pluginUri),
fileName_(fileName)
{
}
std::string pluginUri_;
std::string fileName_;
DECLARE_JSON_MAP(PluginPresetIndexEntry);
};
class PluginUiPreset {
public:
std::string label_;
uint64_t instanceId_;
DECLARE_JSON_MAP(PluginUiPreset);
};
class PluginUiPresets {
public:
std::string pluginUri_;
std::vector<PluginUiPreset> presets_;
DECLARE_JSON_MAP(PluginUiPresets);
};
class PluginPresetIndex {
public:
std::vector<PluginPresetIndexEntry> entries_;
uint64_t nextInstanceId_ = 1;
DECLARE_JSON_MAP(PluginPresetIndex);
};
class PluginPreset {
public:
PluginPreset() { }
PluginPreset(uint64_t instanceId, std::string&&label, std::map<std::string,float> && controlValues)
: instanceId_(instanceId),
label_(std::forward<std::string>(label)),
controlValues_(std::forward<std::map<std::string,float>>(controlValues))
{
}
PluginPreset(uint64_t instanceId, const std::string&label, const std::map<std::string,float> & controlValues)
: instanceId_(instanceId),
label_(label),
controlValues_(controlValues)
{
}
uint64_t instanceId_;
std::string label_;
std::map<std::string,float> controlValues_;
DECLARE_JSON_MAP(PluginPreset);
};
class PluginPresets {
public:
std::string pluginUri_;
std::vector<PluginPreset> presets_;
uint64_t nextInstanceId_ = 1;
const PluginPreset&GetPreset(uint64_t presetId) {
for (const auto&preset: presets_)
{
if (preset.instanceId_ == presetId)
{
return preset;
}
}
throw PiPedalException("Preset id not found.");
}
int Find(const std::string &label)
{
for (size_t i = 0; i < presets_.size(); ++i)
{
if (presets_[i].label_ == label)
{
return i;
}
}
return -1;
}
int Find(uint64_t instanceId)
{
for (size_t i = 0; i < presets_.size(); ++i)
{
if (presets_[i].instanceId_ == instanceId)
{
return i;
}
}
return -1;
}
DECLARE_JSON_MAP(PluginPresets);
};
} // namespace
+276 -1
View File
@@ -165,11 +165,13 @@ void Storage::Initialize()
try
{
std::filesystem::create_directories(this->GetPresetsDirectory());
std::filesystem::create_directories(this->GetPluginPresetsDirectory());
}
catch (const std::exception &e)
{
throw PiPedalStateException("Can't create presets directory. (" + (std::string)this->GetPresetsDirectory() + ") (" + e.what() + ")");
}
LoadPluginPresetIndex();
LoadBankIndex();
LoadCurrentBank();
try
@@ -205,6 +207,10 @@ std::filesystem::path Storage::GetPresetsDirectory() const
{
return this->dataRoot / "presets";
}
std::filesystem::path Storage::GetPluginPresetsDirectory() const
{
return this->dataRoot / "plugin_presets";
}
std::filesystem::path Storage::GetCurrentPresetPath() const
{
return this->dataRoot / "currentPreset.json";
@@ -255,6 +261,43 @@ void Storage::LoadBankIndex()
SaveBankIndex();
}
}
void Storage::LoadPluginPresetIndex()
{
try
{
auto path = GetPluginPresetsDirectory() / "index.json";
std::ifstream s;
if (std::filesystem::exists(path))
{
s.open(path);
json_reader reader(s);
reader.read(&pluginPresetIndex);
}
}
catch (const std::exception &)
{
}
}
void Storage::SavePluginPresetIndex()
{
if (pluginPresetIndexChanged)
{
pluginPresetIndexChanged = false;
std::ofstream os;
auto path = GetPluginPresetsDirectory() / "index.json";
os.open(path, std::ios_base::trunc);
if (os.fail())
{
throw PiPedalException(SS("Can't write to " << path));
}
json_writer writer(os);
writer.write(this->pluginPresetIndex);
}
}
void Storage::SaveBankIndex()
{
std::ofstream os;
@@ -374,7 +417,7 @@ bool Storage::LoadPreset(int64_t instanceId)
}
return true;
}
void Storage::saveCurrentPreset(const PedalBoard &pedalBoard)
void Storage::SaveCurrentPreset(const PedalBoard &pedalBoard)
{
auto &item = currentBank.getItem(currentBank.selectedPreset());
item.preset(pedalBoard);
@@ -860,6 +903,238 @@ bool Storage::RestoreCurrentPreset(CurrentPreset*pResult)
return false;
}
}
bool Storage::HasPluginPresets(const std::string &pluginUri) const
{
for (const auto &entry: this->pluginPresetIndex.entries_)
{
if (entry.pluginUri_ == pluginUri)
{
return true;
}
}
return false;
}
std::filesystem::path Storage::GetPluginPresetPath(const std::string &pluginUri) const
{
for (const auto &entry: this->pluginPresetIndex.entries_)
{
if (entry.pluginUri_ == pluginUri)
{
return this->GetPluginPresetsDirectory() / entry.fileName_;
}
}
throw PiPedalArgumentException("Plugin preset file not found.");
}
void Storage::SavePluginPresets(const std::string&pluginUri, const PluginPresets&presets)
{
std::string name;
std::filesystem::path path;
bool presetAdded = false;
if (!HasPluginPresets(pluginUri))
{
name = SS(pluginPresetIndex.nextInstanceId_ << ".json");
path = GetPluginPresetsDirectory() / name;
presetAdded = true;
} else {
path = GetPluginPresetPath(pluginUri);
}
auto tempPath = path.string()+".$$$";
{
std::ofstream os;
os.open(tempPath, std::ios_base::trunc);
if (os.fail())
{
throw PiPedalException(SS("Can't write to " << path));
}
json_writer writer(os);
writer.write(presets);
}
if (std::filesystem::exists(path))
{
std::filesystem::remove(path);
}
std::filesystem::rename(tempPath,path);
if (presetAdded)
{
pluginPresetIndex.entries_.push_back(
PluginPresetIndexEntry(
pluginUri,name
)
);
pluginPresetIndex.nextInstanceId_++;
this->pluginPresetIndexChanged = true;
SavePluginPresetIndex();
}
}
PluginPresets Storage::GetPluginPresets(const std::string&pluginUri) const
{
PluginPresets result;
if (!HasPluginPresets(pluginUri))
{
result.pluginUri_ = pluginUri;
return result;
}
std::filesystem::path path = GetPluginPresetPath(pluginUri);
std::ifstream s;
s.open(path);
if (s.fail())
{
return result;
}
json_reader reader(s);
reader.read(&result);
return result;
}
PluginUiPresets Storage::GetPluginUiPresets(const std::string&pluginUri) const
{
PluginPresets presets = GetPluginPresets(pluginUri);
PluginUiPresets result;
result.pluginUri_ = presets.pluginUri_;
for (size_t i = 0; i < presets.presets_.size(); ++i)
{
const auto& preset = presets.presets_[i];
result.presets_.push_back(
PluginUiPreset
{
preset.label_,
preset.instanceId_
}
);
}
return result;
}
std::vector<ControlValue> Storage::GetPluginPresetValues(const std::string&pluginUri, uint64_t instanceId)
{
auto presets = GetPluginPresets(pluginUri);
for (const auto & preset: presets.presets_)
{
if (preset.instanceId_ == instanceId)
{
std::vector<ControlValue> result;
for (const auto &valuePair: preset.controlValues_)
{
result.push_back(ControlValue(valuePair.first.c_str(),valuePair.second));
}
return result;
}
}
throw PiPedalException("Plugin preset not found.");
}
uint64_t Storage::SavePluginPreset(
const std::string&pluginUri,
const std::string&name,
const std::map<std::string,float> & values)
{
auto presets = GetPluginPresets(pluginUri);
uint64_t result = -1;
bool existing = false;
for (size_t i = 0; i < presets.presets_.size(); ++i)
{
auto & preset = presets.presets_[i];
if (preset.label_ == name)
{
preset.controlValues_ = values;
existing = true;
result = preset.instanceId_;
break;
}
}
if (!existing)
{
result = presets.nextInstanceId_++;
presets.presets_.push_back(
PluginPreset(
result,
name,
values
));
}
this->SavePluginPresets(pluginUri,presets);
return result;
}
void Storage::UpdatePluginPresets(const PluginUiPresets &pluginPresets)
{
// handles deletions, renaming, and reordering only.
// If you need to add a preset, you neet to call SavePluginPreset or DulicatePluginPreset instead.
PluginPresets presets = this->GetPluginPresets(pluginPresets.pluginUri_);
PluginPresets newPresets;
newPresets.pluginUri_ = pluginPresets.pluginUri_;
newPresets.nextInstanceId_ = presets.nextInstanceId_;
for (const auto&preset: pluginPresets.presets_)
{
PluginPreset newPreset = presets.GetPreset(preset.instanceId_);
newPreset.label_ = preset.label_;
newPresets.presets_.push_back(std::move(newPreset));
}
SavePluginPresets(newPresets.pluginUri_,newPresets);
}
static std::string stripCopySuffix(const std::string &s)
{
int pos = s.length()-1;
if (pos >= 0 && s[pos] == ')')
{
--pos;
while (pos >= 0 && s[pos] >= '0' && s[pos] <= '9')
{
--pos;
}
if (pos >= 0 && s[pos] == '(')
{
--pos;
}
while (pos >= 0 && s[pos] == ' ')
{
--pos;
}
return s.substr(0,pos+1);
}
return s;
}
uint64_t Storage::CopyPluginPreset(const std::string&pluginUri,uint64_t presetId)
{
PluginPresets presets = this->GetPluginPresets(pluginUri);
size_t pos = presets.Find(presetId);
if (pos == -1)
{
throw PiPedalException("Perset not found.");
}
PluginPreset t = presets.presets_[pos];
t.instanceId_ = presets.nextInstanceId_++;
std::string baseName = stripCopySuffix(t.label_);
std::string name = baseName;
if (presets.Find(name) != -1)
{
int nCopy = 1;
while (true)
{
name = SS(baseName << " (" << nCopy << ")");
if (presets.Find(name) == -1)
{
break;
}
++nCopy;
}
}
t.label_ = name;
presets.presets_.insert(presets.presets_.begin()+pos+1,t);
SavePluginPresets(presets.pluginUri_,presets);
return t.instanceId_;
}
JSON_MAP_BEGIN(CurrentPreset)
+18 -3
View File
@@ -22,6 +22,7 @@
#include <iostream>
#include "PedalBoard.hpp"
#include "Presets.hpp"
#include "PluginPreset.hpp"
#include "Banks.hpp"
#include "JackConfiguration.hpp"
#include "WifiConfigSettings.hpp"
@@ -45,17 +46,18 @@ private:
std::filesystem::path dataRoot;
BankIndex bankIndex;
BankFile currentBank;
PluginPresetIndex pluginPresetIndex;
private:
static std::string SafeEncodeName(const std::string& name);
static std::string SafeDecodeName(const std::string& name);
std::filesystem::path GetPresetsDirectory() const;
std::filesystem::path GetPluginPresetsDirectory() const;
std::filesystem::path GetIndexFileName() const;
std::filesystem::path GetBankFileName(const std::string & name) const;
std::filesystem::path GetChannelSelectionFileName();
std::filesystem::path GetCurrentPresetPath() const;
void LoadBankIndex();
void SaveBankIndex();
void ReIndex();
@@ -85,7 +87,7 @@ public:
void LoadWifiConfigSettings();
void LoadBank(int64_t instanceId);
const PedalBoard& GetCurrentPreset();
void saveCurrentPreset(const PedalBoard&pedalBoard);
void SaveCurrentPreset(const PedalBoard&pedalBoard);
int64_t saveCurrentPresetAs(const PedalBoard&pedalBoard, const std::string&namne,int64_t saveAfterInstanceId = -1);
void GetPresetIndex(PresetIndex*pResult);
@@ -117,7 +119,20 @@ public:
void SaveCurrentPreset(const CurrentPreset &currentPreset);
bool RestoreCurrentPreset(CurrentPreset*pResult);
private:
bool pluginPresetIndexChanged = false;
void LoadPluginPresetIndex();
void SavePluginPresetIndex();
std::filesystem::path GetPluginPresetPath(const std::string &pluginUri) const;
public:
bool HasPluginPresets(const std::string&pluginUri) const;
void SavePluginPresets(const std::string&pluginUri, const PluginPresets&presets);
PluginPresets GetPluginPresets(const std::string&pluginUri) const;
PluginUiPresets GetPluginUiPresets(const std::string&pluginUri) const;
std::vector<ControlValue> GetPluginPresetValues(const std::string&pluginUri, uint64_t instanceId);
uint64_t SavePluginPreset(const std::string&pluginUri, const std::string&name, const std::map<std::string,float> & values);
void UpdatePluginPresets(const PluginUiPresets &pluginPresets);
uint64_t CopyPluginPreset(const std::string&pluginUri,uint64_t presetId);
};
+3 -3
View File
@@ -614,7 +614,6 @@ void json_reader::throw_format_error(const char*error)
} else {
for (int i = 0; i < 40; ++i)
{
if (is_.peek()) break;
int c = get();
if (c == -1) break;
if (c == '\r') {
@@ -623,11 +622,12 @@ void json_reader::throw_format_error(const char*error)
{
s << "\\n";
} else {
s << c;
s << (char)c;
}
}
}
s << "'.";
throw PiPedalException(s.str());
std::string message = s.str();
throw PiPedalException(message);
}
+53 -1
View File
@@ -278,7 +278,7 @@ public:
os << text;
}
using string_view = boost::string_view;
json_writer(std::ostream &os, bool compressed = false, bool allowNaN = true)
json_writer(std::ostream &os, bool compressed = true, bool allowNaN = true)
: os(os)
, compressed(compressed)
, allowNaN_(allowNaN)
@@ -534,6 +534,26 @@ public:
end_object();
}
template <typename U>
void write(const std::map<std::string,U> &map)
{
start_object();
bool firstTime = true;
for (const auto&v : map)
{
if (!firstTime)
{
write_raw(",");
write_raw(CRLF);
}
indent();
firstTime = false;
write_member(v.first.c_str(),v.second);
}
end_object();
}
@@ -664,6 +684,38 @@ public:
read(*pObject);
}
template <typename U>
void read(std::map<std::string,U> *pMap)
{
char c;
consume('{');
while (true)
{
c = peek();
if (c == '}')
{
c = get();
break;
}
std::string key = read_string();
consume(':');
skip_whitespace();
U u;
this->read(&u);
(*pMap)[key] = u;
skip_whitespace();
if (peek() == ',')
{
c = get();
}
}
}
template<typename T>
void read(std::unique_ptr<T> *pUniquePtr)
{
+86 -20
View File
@@ -43,6 +43,7 @@ using namespace pipedal;
#define PRESET_EXTENSION ".piPreset"
#define BANK_EXTENSION ".piBank"
#define PLUGIN_PRESETS_EXTENSION ".piPluginPresets"
sem_t signalSemaphore;
@@ -91,23 +92,32 @@ public:
{
return false;
}
if (request_uri.segment(1) == "downloadPreset")
{
std::string strInstanceId = request_uri.query("id");
if (strInstanceId != "")
return true;
}
else if (request_uri.segment(1) == "uploadPreset")
std::string segment = request_uri.segment(1);
if (segment == "uploadPluginPresets")
{
return true;
}
if (request_uri.segment(1) == "downloadBank")
if (segment == "downloadPluginPresets")
{
return true;
}
if (segment == "downloadPreset")
{
std::string strInstanceId = request_uri.query("id");
if (strInstanceId != "")
return true;
}
else if (request_uri.segment(1) == "uploadBank")
else if (segment == "uploadPreset")
{
return true;
}
if (segment == "downloadBank")
{
std::string strInstanceId = request_uri.query("id");
if (strInstanceId != "")
return true;
}
else if (segment == "uploadBank")
{
return true;
}
@@ -124,11 +134,24 @@ public:
return result;
}
void GetPluginPresets(const uri &request_uri, std::string*pName,std::string *pContent)
{
std::string pluginUri = request_uri.query("id");
auto plugin = model->GetLv2Host().GetPluginInfo(pluginUri);
*pName = plugin->name();
PluginPresets pluginPresets = model->GetPluginPresets(pluginUri);
std::stringstream s;
json_writer writer(s);
writer.write(pluginPresets);
*pContent = s.str();
}
void GetPreset(const uri &request_uri, std::string *pName, std::string *pContent)
{
std::string strInstanceId = request_uri.query("id");
int64_t instanceId = std::stol(strInstanceId);
auto pedalBoard = model->getPreset(instanceId);
auto pedalBoard = model->GetPreset(instanceId);
// a certain elegance to using same file format for banks and presets.
BankFile file;
@@ -147,7 +170,7 @@ public:
std::string strInstanceId = request_uri.query("id");
int64_t instanceId = std::stol(strInstanceId);
BankFile bank;
model->getBank(instanceId, &bank);
model->GetBank(instanceId, &bank);
std::stringstream s;
json_writer writer(s, true); // do what we can to reduce the file size.
@@ -164,7 +187,19 @@ public:
{
try
{
if (request_uri.segment(1) == "downloadPreset")
std::string segment = request_uri.segment(1);
if (segment == "downloadPluginPresets")
{
std::string name;
std::string content;
GetPluginPresets(request_uri,&name,&content);
res.set(HttpField::content_type, "application/octet-stream");
res.set(HttpField::cache_control, "no-cache");
res.setContentLength(content.length());
res.set(HttpField::content_disposition, GetContentDispositionHeader(name, PLUGIN_PRESETS_EXTENSION));
return;
}
if (segment == "downloadPreset")
{
std::string name;
std::string content;
@@ -176,7 +211,7 @@ public:
res.set(HttpField::content_disposition, GetContentDispositionHeader(name, PRESET_EXTENSION));
return;
}
if (request_uri.segment(1) == "downloadBank")
if (segment == "downloadBank")
{
std::string name;
std::string content;
@@ -211,7 +246,19 @@ public:
{
try
{
if (request_uri.segment(1) == "downloadPreset")
std::string segment = request_uri.segment(1);
if (segment == "downloadPluginPresets")
{
std::string name;
std::string content;
GetPluginPresets(request_uri,&name,&content);
res.set(HttpField::content_type, "application/octet-stream");
res.set(HttpField::cache_control, "no-cache");
res.setContentLength(content.length());
res.set(HttpField::content_disposition, GetContentDispositionHeader(name, PLUGIN_PRESETS_EXTENSION));
res.setBody(content);
} else if (segment == "downloadPreset")
{
std::string name;
std::string content;
@@ -223,7 +270,7 @@ public:
res.set(HttpField::content_disposition, GetContentDispositionHeader(name, PRESET_EXTENSION));
res.setBody(content);
}
else if (request_uri.segment(1) == "downloadBank")
else if (segment == "downloadBank")
{
std::string name;
std::string content;
@@ -260,7 +307,26 @@ public:
{
try
{
if (request_uri.segment(1) == "uploadPreset")
std::string segment = request_uri.segment(1);
if (segment == "uploadPluginPresets")
{
const std::string& presetBody = req.body();
std::stringstream s(presetBody);
json_reader reader(s);
PluginPresets presets;
reader.read(&presets);
model->UploadPluginPresets(presets);
res.set(HttpField::content_type, "application/json");
res.set(HttpField::cache_control, "no-cache");
std::stringstream sResult;
sResult << -1;
std::string result = sResult.str();
res.setContentLength(result.length());
res.setBody(result);
} else if (segment == "uploadPreset")
{
const std::string& presetBody = req.body();
std::stringstream s(presetBody);
@@ -276,7 +342,7 @@ public:
BankFile bankFile;
reader.read(&bankFile);
uint64_t instanceId = model->uploadPreset(bankFile, uploadAfter);
uint64_t instanceId = model->UploadPreset(bankFile, uploadAfter);
res.set(HttpField::content_type, "application/json");
res.set(HttpField::cache_control, "no-cache");
@@ -287,7 +353,7 @@ public:
res.setBody(result);
}
else if (request_uri.segment(1) == "uploadBank")
else if (segment == "uploadBank")
{
const std::string& presetBody = req.body();
std::istringstream s(presetBody);
@@ -303,7 +369,7 @@ public:
BankFile bankFile;
reader.read(&bankFile);
uint64_t instanceId = model->uploadBank(bankFile, uploadAfter);
uint64_t instanceId = model->UploadBank(bankFile, uploadAfter);
res.set(HttpField::content_type, "application/json");
res.set(HttpField::cache_control, "no-cache");
@@ -430,7 +496,7 @@ int main(int argc, char *argv[])
if (help || parser.Arguments().size() == 0)
{
std::cout << "pipedald - Pipedal web socket server.\n"
"Copyright (c) 2021 Robin Davies.\n"
"Copyright (c) 2022 Robin Davies.\n"
"\n";
}
}