Interim commit for COnvolutionReverb
This commit is contained in:
Vendored
+4
-1
@@ -79,7 +79,10 @@
|
|||||||
// Resolved by CMake Tools:
|
// Resolved by CMake Tools:
|
||||||
"program": "${command:cmake.launchTargetPath}",
|
"program": "${command:cmake.launchTargetPath}",
|
||||||
|
|
||||||
"args": [ "[alsa_test]" ],
|
"args": [
|
||||||
|
//"[Dev]"
|
||||||
|
"[json_variants]"
|
||||||
|
],
|
||||||
|
|
||||||
"stopAtEntry": false,
|
"stopAtEntry": false,
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
|
|||||||
@@ -0,0 +1,194 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 Robin E. R. Davies
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* 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, { TouchEvent, PointerEvent, ReactNode, Component, SyntheticEvent } from 'react';
|
||||||
|
import { Theme } from '@mui/material/styles';
|
||||||
|
import { WithStyles } from '@mui/styles';
|
||||||
|
import createStyles from '@mui/styles/createStyles';
|
||||||
|
import withStyles from '@mui/styles/withStyles';
|
||||||
|
import { PiPedalFileProperty, PiPedalFileType } from './Lv2Plugin';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import Input from '@mui/material/Input';
|
||||||
|
import Select from '@mui/material/Select';
|
||||||
|
import Switch from '@mui/material/Switch';
|
||||||
|
import Utility, { nullCast } from './Utility';
|
||||||
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
|
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
||||||
|
import ButtonBase from '@mui/material/ButtonBase'
|
||||||
|
import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
|
||||||
|
|
||||||
|
const MIN_ANGLE = -135;
|
||||||
|
const MAX_ANGLE = 135;
|
||||||
|
const FONT_SIZE = "0.8em";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const SELECTED_OPACITY = 0.8;
|
||||||
|
const DEFAULT_OPACITY = 0.6;
|
||||||
|
const RANGE_SCALE = 120; // 120 pixels to move from 0 to 1.
|
||||||
|
const FINE_RANGE_SCALE = RANGE_SCALE * 10; // 1200 pixels to move from 0 to 1.
|
||||||
|
const ULTRA_FINE_RANGE_SCALE = RANGE_SCALE * 50; // 12000 pixels to move from 0 to 1.
|
||||||
|
|
||||||
|
|
||||||
|
export const StandardItemSize = { width: 80, height: 140 }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const styles = (theme: Theme) => createStyles({
|
||||||
|
frame: {
|
||||||
|
position: "relative",
|
||||||
|
margin: "12px"
|
||||||
|
},
|
||||||
|
switchTrack: {
|
||||||
|
height: '100%',
|
||||||
|
width: '100%',
|
||||||
|
borderRadius: 14 / 2,
|
||||||
|
zIndex: -1,
|
||||||
|
transition: theme.transitions.create(['opacity', 'background-color'], {
|
||||||
|
duration: theme.transitions.duration.shortest
|
||||||
|
}),
|
||||||
|
backgroundColor: theme.palette.secondary.main,
|
||||||
|
opacity: theme.palette.mode === 'light' ? 0.38 : 0.3
|
||||||
|
},
|
||||||
|
displayValue: {
|
||||||
|
position: "absolute",
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 4,
|
||||||
|
textAlign: "center",
|
||||||
|
background: "white",
|
||||||
|
color: "#666",
|
||||||
|
// zIndex: -1,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export interface FilePropertyControlProps extends WithStyles<typeof styles> {
|
||||||
|
instanceId: number;
|
||||||
|
fileProperty: PiPedalFileProperty;
|
||||||
|
value: string;
|
||||||
|
onFileClick: (fileProperty: PiPedalFileProperty,value: string) => void;
|
||||||
|
theme: Theme;
|
||||||
|
}
|
||||||
|
type FilePropertyControlState = {
|
||||||
|
error: boolean;
|
||||||
|
showDialog: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const FilePropertyControl =
|
||||||
|
withStyles(styles, { withTheme: true })(
|
||||||
|
class extends Component<FilePropertyControlProps, FilePropertyControlState>
|
||||||
|
{
|
||||||
|
|
||||||
|
model: PiPedalModel;
|
||||||
|
|
||||||
|
|
||||||
|
constructor(props: FilePropertyControlProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
error: false,
|
||||||
|
showDialog: false
|
||||||
|
};
|
||||||
|
this.model = PiPedalModelFactory.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
}
|
||||||
|
inputChanged: boolean = false;
|
||||||
|
|
||||||
|
onDrag(e: SyntheticEvent) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
onFileClick() {
|
||||||
|
this.props.onFileClick(this.props.fileProperty,this.props.value);
|
||||||
|
}
|
||||||
|
private fileNameOnly(path: string): string {
|
||||||
|
let slashPos = path.lastIndexOf('/');
|
||||||
|
if (slashPos < 0)
|
||||||
|
{
|
||||||
|
slashPos = 0;
|
||||||
|
} else {
|
||||||
|
++slashPos;
|
||||||
|
}
|
||||||
|
let extPos = path.lastIndexOf('.');
|
||||||
|
if (extPos < 0 || extPos < slashPos)
|
||||||
|
{
|
||||||
|
extPos = path.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return path.substring(slashPos,extPos);
|
||||||
|
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
let classes = this.props.classes;
|
||||||
|
let fileProperty = this.props.fileProperty;
|
||||||
|
|
||||||
|
let value = this.props.value;
|
||||||
|
if (!value || value.length === 0)
|
||||||
|
{
|
||||||
|
value = "\u00A0";
|
||||||
|
}
|
||||||
|
value = this.fileNameOnly(value);
|
||||||
|
|
||||||
|
let item_width = 264;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "flex-start", width: item_width, margin: 8, paddingLeft: 8}}>
|
||||||
|
{/* TITLE SECTION */}
|
||||||
|
<div style={{ flex: "0 0 auto", width: "100%", marginBottom: 8, marginLeft: 0, marginRight: 0 }}>
|
||||||
|
<Typography variant="caption" display="block" noWrap style={{
|
||||||
|
width: "100%",
|
||||||
|
textAlign: "start"
|
||||||
|
}}> {fileProperty.name}</Typography>
|
||||||
|
</div>
|
||||||
|
{/* CONTROL SECTION */}
|
||||||
|
|
||||||
|
<div style={{ flex: "0 0 auto", width: "100%" }}>
|
||||||
|
|
||||||
|
<ButtonBase style={{ width: "100%", borderRadius: "4px 4px 0px 0px", overflow: "hidden",marginTop: 8}} onClick={()=> {this.onFileClick()}} >
|
||||||
|
<div style={{width: "100%", background: "rgba(0,0,0,0.07)", borderRadius: "4px 4px 0px 0px"}}>
|
||||||
|
<div style={{ display: "flex", alignItems: "center", flexFlow: "row nowrap" }}>
|
||||||
|
<Typography noWrap={true} style={{ flex: "1 1 100%", textAlign: "start", verticalAlign: "center", paddingTop: 4,paddingBottom: 4,paddingLeft: 4}}
|
||||||
|
variant='caption'
|
||||||
|
>{value}</Typography>
|
||||||
|
<MoreHorizIcon style={{ flex: "0 0 auto", width: "16px", height: "16px", verticalAlign: "center",opacity: 0.5, marginRight: 4 }} />
|
||||||
|
</div>
|
||||||
|
<div style={{ height: "1px", width: "100%", background: "black" }}> </div>
|
||||||
|
</div>
|
||||||
|
</ButtonBase>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* LABEL/EDIT SECTION*/}
|
||||||
|
<div style={{ flex: "0 0 auto", position: "relative", width: 60 }}>
|
||||||
|
</div>
|
||||||
|
</div >
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export default FilePropertyControl;
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
// Copyright (c) 2023 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 from 'react';
|
||||||
|
|
||||||
|
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
||||||
|
|
||||||
|
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
import Radio from '@mui/material/Radio';
|
||||||
|
import List from '@mui/material/List';
|
||||||
|
import ListItem from '@mui/material/ListItem';
|
||||||
|
import ListItemButton from '@mui/material/ListItemButton';
|
||||||
|
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||||
|
import ListItemText from '@mui/material/ListItemText';
|
||||||
|
import FileUploadIcon from '@mui/icons-material/FileUpload';
|
||||||
|
import AudioFileIcon from '@mui/icons-material/AudioFile';
|
||||||
|
|
||||||
|
import Dialog from '@mui/material/Dialog';
|
||||||
|
import DialogActions from '@mui/material/DialogActions';
|
||||||
|
import DialogTitle from '@mui/material/DialogTitle';
|
||||||
|
import DialogContent from '@mui/material/DialogContent';
|
||||||
|
import DeleteIcon from '@mui/icons-material/Delete';
|
||||||
|
import IconButton from '@mui/material/IconButton';
|
||||||
|
|
||||||
|
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
||||||
|
import { PiPedalFileProperty, PiPedalFileType } from './Lv2Plugin';
|
||||||
|
import ButtonBase from '@mui/material/ButtonBase';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import DialogEx from './DialogEx';
|
||||||
|
import { ModelTraining } from '@mui/icons-material';
|
||||||
|
|
||||||
|
export interface FilePropertyDialogProps {
|
||||||
|
open: boolean,
|
||||||
|
fileProperty: PiPedalFileProperty,
|
||||||
|
selectedFile: string,
|
||||||
|
onOk: (fileProperty: PiPedalFileProperty, selectedItem: string) => void,
|
||||||
|
onClose: () => void
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface FilePropertyDialogState {
|
||||||
|
fullScreen: boolean;
|
||||||
|
selectedFile: string;
|
||||||
|
hasSelection: boolean;
|
||||||
|
files: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class FilePropertyDialog extends ResizeResponsiveComponent<FilePropertyDialogProps, FilePropertyDialogState> {
|
||||||
|
|
||||||
|
|
||||||
|
constructor(props: FilePropertyDialogProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
|
||||||
|
this.model = PiPedalModelFactory.getInstance();
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
fullScreen: false,
|
||||||
|
selectedFile: props.selectedFile,
|
||||||
|
hasSelection: false,
|
||||||
|
files: ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]
|
||||||
|
};
|
||||||
|
|
||||||
|
this.requestFiles();
|
||||||
|
}
|
||||||
|
private mounted: boolean = false;
|
||||||
|
private model: PiPedalModel;
|
||||||
|
|
||||||
|
private requestFiles() {
|
||||||
|
this.model.requestFileList(this.props.fileProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
onWindowSizeChanged(width: number, height: number): void {
|
||||||
|
this.setState({ fullScreen: height < 200 })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
super.componentDidMount();
|
||||||
|
this.mounted = true;
|
||||||
|
}
|
||||||
|
componentWillUnmount() {
|
||||||
|
super.componentWillUnmount();
|
||||||
|
this.mounted = false;
|
||||||
|
}
|
||||||
|
componentDidUpdate(prevProps: Readonly<FilePropertyDialogProps>, prevState: Readonly<FilePropertyDialogState>, snapshot?: any): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
private fileNameOnly(path: string): string {
|
||||||
|
let slashPos = path.lastIndexOf('/');
|
||||||
|
if (slashPos < 0) {
|
||||||
|
slashPos = 0;
|
||||||
|
} else {
|
||||||
|
++slashPos;
|
||||||
|
}
|
||||||
|
let extPos = path.lastIndexOf('.');
|
||||||
|
if (extPos < 0 || extPos < slashPos) {
|
||||||
|
extPos = path.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return path.substring(slashPos, extPos);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private isFileInList(selectedFile: string) {
|
||||||
|
|
||||||
|
let hasSelection = false;
|
||||||
|
for (var file of this.state.files) {
|
||||||
|
if (file === selectedFile) {
|
||||||
|
hasSelection = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hasSelection;
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelect(selectedFile: string) {
|
||||||
|
this.setState({ selectedFile: selectedFile, hasSelection: this.isFileInList(selectedFile) })
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DialogEx onClose={() => this.props.onClose()} open={this.props.open} tag="FilePropertyDialog" fullWidth maxWidth="xl" style={{ height: "90%" }}
|
||||||
|
PaperProps={{ style: { minHeight: "90%", maxHeight: "90%", overflowY: "visible" } }}
|
||||||
|
>
|
||||||
|
<DialogTitle >{this.props.fileProperty.name}</DialogTitle>
|
||||||
|
<div style={{ flex: "0 0 auto", height: "1px", background: "rgba(0,0,0,0.2" }}> </div>
|
||||||
|
<div style={{ flex: "1 1 auto", height: "300", display: "flex", flexFlow: "row nowrap", overflowX: "auto", overflowY: "visible" }}>
|
||||||
|
<div style={{ flex: "1 1 100%", display: "flex", flexFlow: "column wrap", justifyContent: "start", alignItems: "flex-start" }}>
|
||||||
|
{
|
||||||
|
this.state.files.map(
|
||||||
|
(value: string, index: number) => {
|
||||||
|
let selected = value === this.state.selectedFile;
|
||||||
|
let selectBg = selected ? "rgba(0,0,0,0.15)" : "rgba(0,0,0,0.0)";
|
||||||
|
return (
|
||||||
|
<ButtonBase
|
||||||
|
style={{ width: "320px", flex: "0 0 48px", position: "relative" }}
|
||||||
|
onClick={() => this.onSelect(value)}
|
||||||
|
>
|
||||||
|
<div style={{ position: "absolute", background: selectBg, width: "100%", height: "100%" }} />
|
||||||
|
<div style={{ display: "flex", flexFlow: "row nowrap", justifyContent: "start", alignItems: "center", width: "100%", height: "100%" }}>
|
||||||
|
<AudioFileIcon style={{ flex: "0 0 auto", opacity: 0.7, marginRight: 8, marginLeft: 4 }} />
|
||||||
|
<Typography noWrap style={{ flex: "1 1 auto", textAlign: "left" }}>{this.fileNameOnly(value)}</Typography>
|
||||||
|
</div>
|
||||||
|
</ButtonBase>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style={{ flex: "0 0 auto", height: "1px", background: "rgba(0,0,0,0.2" }}> </div>
|
||||||
|
<DialogActions style={{ justifyContent: "stretch" }}>
|
||||||
|
<div style={{ display: "flex", width: "100%", flexFlow: "row nowrap" }}>
|
||||||
|
<Button style={{ flex: "0 0 auto" }} startIcon={<FileUploadIcon />}>
|
||||||
|
<input hidden accept="audio/x-wav" name="Upload Impuse File" type="file" multiple />
|
||||||
|
Upload
|
||||||
|
</Button>
|
||||||
|
<IconButton style={{visibility: (this.state.hasSelection? "visible": "hidden")}} aria-label="delete selected file" component="label">
|
||||||
|
<DeleteIcon />
|
||||||
|
</IconButton>
|
||||||
|
<div style={{ flex: "1 1 auto" }}> </div>
|
||||||
|
<Button onClick={() => this.props.onClose()} aria-label="cancel">
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button style={{ flex: "0 0 auto" }} onClick={() => this.props.onClose()} color="secondary" disabled={!this.state.hasSelection} aria-label="select">
|
||||||
|
Select
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</DialogActions>
|
||||||
|
</DialogEx>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -105,6 +105,49 @@ export class PortGroup {
|
|||||||
program_list_id: number = -1;
|
program_list_id: number = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export class PiPedalFileType {
|
||||||
|
deserialize(input: any): PiPedalFileType {
|
||||||
|
this.name = input.name;
|
||||||
|
this.fileExtension = input.fileExtension;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
static deserialize_array(input: any): PiPedalFileType[]
|
||||||
|
{
|
||||||
|
let result: PiPedalFileType[] = [];
|
||||||
|
for (let i = 0; i < input.length; ++i)
|
||||||
|
{
|
||||||
|
result[i] = new PiPedalFileType().deserialize(input[i]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
name: string = "";
|
||||||
|
fileExtension: string = "";
|
||||||
|
}
|
||||||
|
export class PiPedalFileProperty {
|
||||||
|
deserialize(input: any): PiPedalFileProperty
|
||||||
|
{
|
||||||
|
this.name = input.name;
|
||||||
|
this.fileTypes = PiPedalFileType.deserialize_array(input.fileTypes);
|
||||||
|
this.patchProperty = input.patchProperty;
|
||||||
|
this.defaultFile = input.defaultFile;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
static deserialize_array(input: any): PiPedalFileProperty[]
|
||||||
|
{
|
||||||
|
let result: PiPedalFileProperty[] = [];
|
||||||
|
for (let i = 0; i < input.length; ++i)
|
||||||
|
{
|
||||||
|
result[i] = new PiPedalFileProperty().deserialize(input[i]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
name: string = "";
|
||||||
|
fileTypes: PiPedalFileType[] = [];
|
||||||
|
patchProperty: string = "";
|
||||||
|
defaultFile: string = "";
|
||||||
|
|
||||||
|
};
|
||||||
export class Lv2Plugin implements Deserializable<Lv2Plugin> {
|
export class Lv2Plugin implements Deserializable<Lv2Plugin> {
|
||||||
deserialize(input: any): Lv2Plugin
|
deserialize(input: any): Lv2Plugin
|
||||||
{
|
{
|
||||||
@@ -119,6 +162,12 @@ export class Lv2Plugin implements Deserializable<Lv2Plugin> {
|
|||||||
this.comment = input.comment;
|
this.comment = input.comment;
|
||||||
this.ports= Port.deserialize_array(input.ports);
|
this.ports= Port.deserialize_array(input.ports);
|
||||||
this.port_groups = PortGroup.deserialize_array(input.port_groups);
|
this.port_groups = PortGroup.deserialize_array(input.port_groups);
|
||||||
|
if (input.fileProperties)
|
||||||
|
{
|
||||||
|
this.fileProperties = PiPedalFileProperty.deserialize_array(input.fileProperties)
|
||||||
|
} else {
|
||||||
|
this.fileProperties = [];
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
static EmptyFeatures: string[] = [];
|
static EmptyFeatures: string[] = [];
|
||||||
@@ -134,6 +183,7 @@ export class Lv2Plugin implements Deserializable<Lv2Plugin> {
|
|||||||
comment: string = "";
|
comment: string = "";
|
||||||
ports: Port[] = Port.EmptyPorts;
|
ports: Port[] = Port.EmptyPorts;
|
||||||
port_groups: PortGroup[] = [];
|
port_groups: PortGroup[] = [];
|
||||||
|
fileProperties: PiPedalFileProperty[] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -474,6 +524,13 @@ export class UiPlugin implements Deserializable<UiPlugin> {
|
|||||||
this.description = input.description;
|
this.description = input.description;
|
||||||
this.controls = UiControl.deserialize_array(input.controls);
|
this.controls = UiControl.deserialize_array(input.controls);
|
||||||
this.port_groups = PortGroup.deserialize_array(input.port_groups);
|
this.port_groups = PortGroup.deserialize_array(input.port_groups);
|
||||||
|
if (input.fileProperties)
|
||||||
|
{
|
||||||
|
this.fileProperties = PiPedalFileProperty.deserialize_array(input.fileProperties)
|
||||||
|
} else {
|
||||||
|
this.fileProperties = [];
|
||||||
|
}
|
||||||
|
|
||||||
this.is_vst3 = input.is_vst3;
|
this.is_vst3 = input.is_vst3;
|
||||||
return this;
|
return this;
|
||||||
|
|
||||||
@@ -524,6 +581,7 @@ export class UiPlugin implements Deserializable<UiPlugin> {
|
|||||||
description: string = "";
|
description: string = "";
|
||||||
controls: UiControl[] = [];
|
controls: UiControl[] = [];
|
||||||
port_groups: PortGroup[] = [];
|
port_groups: PortGroup[] = [];
|
||||||
|
fileProperties: PiPedalFileProperty[] = [];
|
||||||
is_vst3 : boolean = false;
|
is_vst3 : boolean = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,27 @@ export class ControlValue implements Deserializable<ControlValue> {
|
|||||||
key: string;
|
key: string;
|
||||||
value: number;
|
value: number;
|
||||||
|
|
||||||
|
}
|
||||||
|
export class PropertyValue implements Deserializable<PropertyValue> {
|
||||||
|
deserialize(input: any): PropertyValue {
|
||||||
|
this.propertyUri = input.propertyUri;
|
||||||
|
this.value = input.value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
static deserializeArray(input: any[]): PropertyValue[] {
|
||||||
|
let result: PropertyValue[] = [];
|
||||||
|
for (let i = 0; i < input.length; ++i) {
|
||||||
|
result[i] = new PropertyValue().deserialize(input[i]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
setValue(value: number) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
propertyUri: string = "";
|
||||||
|
value: any = null;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PedalBoardItem implements Deserializable<PedalBoardItem> {
|
export class PedalBoardItem implements Deserializable<PedalBoardItem> {
|
||||||
@@ -67,6 +88,7 @@ export class PedalBoardItem implements Deserializable<PedalBoardItem> {
|
|||||||
this.midiBindings = MidiBinding.deserialize_array(input.midiBindings);
|
this.midiBindings = MidiBinding.deserialize_array(input.midiBindings);
|
||||||
|
|
||||||
this.controlValues = ControlValue.deserializeArray(input.controlValues);
|
this.controlValues = ControlValue.deserializeArray(input.controlValues);
|
||||||
|
this.propertyValues = PropertyValue.deserializeArray(input.propertyValues);
|
||||||
this.vstState = input.vstState ?? "";
|
this.vstState = input.vstState ?? "";
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -136,6 +158,17 @@ export class PedalBoardItem implements Deserializable<PedalBoardItem> {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
setPropertyValue(propertyUri: string, value: any): boolean {
|
||||||
|
for (let i = 0; i < this.propertyValues.length; ++i) {
|
||||||
|
let v = this.propertyValues[i];
|
||||||
|
if (v.propertyUri === propertyUri) {
|
||||||
|
if (v.value === value) return false;
|
||||||
|
v.value = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
setMidiBinding(midiBinding: MidiBinding): boolean {
|
setMidiBinding(midiBinding: MidiBinding): boolean {
|
||||||
if (this.midiBindings)
|
if (this.midiBindings)
|
||||||
{
|
{
|
||||||
@@ -183,6 +216,7 @@ export class PedalBoardItem implements Deserializable<PedalBoardItem> {
|
|||||||
uri: string = "";
|
uri: string = "";
|
||||||
pluginName?: string;
|
pluginName?: string;
|
||||||
controlValues: ControlValue[] = ControlValue.EmptyArray;
|
controlValues: ControlValue[] = ControlValue.EmptyArray;
|
||||||
|
propertyValues: PropertyValue[] = [];
|
||||||
midiBindings: MidiBinding[] = [];
|
midiBindings: MidiBinding[] = [];
|
||||||
vstState: string = "";
|
vstState: string = "";
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
// 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.
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
import { UiPlugin, UiControl, PluginType } from './Lv2Plugin';
|
import { UiPlugin, UiControl, PluginType, PiPedalFileProperty } from './Lv2Plugin';
|
||||||
|
|
||||||
import { PiPedalArgumentError, PiPedalStateError } from './PiPedalError';
|
import { PiPedalArgumentError, PiPedalStateError } from './PiPedalError';
|
||||||
|
|
||||||
@@ -50,6 +50,7 @@ export enum State {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type ControlValueChangedHandler = (key: string, value: number) => void;
|
export type ControlValueChangedHandler = (key: string, value: number) => void;
|
||||||
|
export type PropertyValueChangedHandler = (properyUri: string, value: number) => void;
|
||||||
|
|
||||||
|
|
||||||
export type PluginPresetsChangedHandler = (pluginUri: string) => void;
|
export type PluginPresetsChangedHandler = (pluginUri: string) => void;
|
||||||
@@ -94,6 +95,18 @@ interface ControlValueChangeItem {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface PropertyValueChangedHandle {
|
||||||
|
_PropertyValueChangedHandle: number;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
interface PropertyValueChangeItem {
|
||||||
|
handle: number;
|
||||||
|
instanceId: number;
|
||||||
|
onValueChanged: PropertyValueChangedHandler;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
class MidiEventListener {
|
class MidiEventListener {
|
||||||
constructor(handle: number, callback: (isNote: boolean, noteOrControl: number) => void) {
|
constructor(handle: number, callback: (isNote: boolean, noteOrControl: number) => void) {
|
||||||
this.handle = handle;
|
this.handle = handle;
|
||||||
@@ -354,9 +367,9 @@ export interface PiPedalModel {
|
|||||||
loadPedalBoardPlugin(itemId: number, selectedUri: string): number;
|
loadPedalBoardPlugin(itemId: number, selectedUri: string): number;
|
||||||
|
|
||||||
setPedalBoardControlValue(instanceId: number, key: string, value: number): void;
|
setPedalBoardControlValue(instanceId: number, key: string, value: number): void;
|
||||||
|
setPedalBoardPropertyValue(instanceId: number, propertyUri: string, value: any): void;
|
||||||
setPedalBoardItemEnabled(instanceId: number, value: boolean): void;
|
setPedalBoardItemEnabled(instanceId: number, value: boolean): void;
|
||||||
previewPedalBoardValue(instanceId: number, key: string, value: number): void;
|
previewPedalBoardValue(instanceId: number, key: string, value: number): void;
|
||||||
setPedalBoardControlValue(instanceId: number, key: string, value: number): void;
|
|
||||||
deletePedalBoardPedal(instanceId: number): number | null;
|
deletePedalBoardPedal(instanceId: number): number | null;
|
||||||
|
|
||||||
movePedalBoardItem(fromInstanceId: number, toInstanceId: number): void;
|
movePedalBoardItem(fromInstanceId: number, toInstanceId: number): void;
|
||||||
@@ -456,6 +469,8 @@ export interface PiPedalModel {
|
|||||||
chooseNewDevice(): void;
|
chooseNewDevice(): void;
|
||||||
|
|
||||||
hasConfiguration(): boolean;
|
hasConfiguration(): boolean;
|
||||||
|
|
||||||
|
requestFileList(piPedalFileProperty: PiPedalFileProperty): Promise<string[]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PiPedalModelImpl implements PiPedalModel {
|
class PiPedalModelImpl implements PiPedalModel {
|
||||||
@@ -722,6 +737,7 @@ class PiPedalModelImpl implements PiPedalModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
requestPluginClasses(): Promise<boolean> {
|
requestPluginClasses(): Promise<boolean> {
|
||||||
const myRequest = new Request(this.varRequest('plugin_classes.json'));
|
const myRequest = new Request(this.varRequest('plugin_classes.json'));
|
||||||
return fetch(myRequest)
|
return fetch(myRequest)
|
||||||
@@ -892,6 +908,10 @@ class PiPedalModelImpl implements PiPedalModel {
|
|||||||
);
|
);
|
||||||
return this.webSocket.connect();
|
return this.webSocket.connect();
|
||||||
})
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.setError("Failed to connect to server.");
|
||||||
|
return false;
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const isoRequest = new Request('iso_codes.json');
|
const isoRequest = new Request('iso_codes.json');
|
||||||
return fetch(isoRequest);
|
return fetch(isoRequest);
|
||||||
@@ -1171,7 +1191,7 @@ class PiPedalModelImpl implements PiPedalModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_controlValueChangeItems: ControlValueChangeItem[] = [];
|
private _controlValueChangeItems: ControlValueChangeItem[] = [];
|
||||||
|
|
||||||
addControlValueChangeListener(instanceId: number, onValueChanged: ControlValueChangedHandler): ControlValueChangedHandle {
|
addControlValueChangeListener(instanceId: number, onValueChanged: ControlValueChangedHandler): ControlValueChangedHandle {
|
||||||
let handle = ++this.nextListenHandle;
|
let handle = ++this.nextListenHandle;
|
||||||
@@ -1186,6 +1206,21 @@ class PiPedalModelImpl implements PiPedalModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private _propertyValueChangeItems: PropertyValueChangeItem[] = [];
|
||||||
|
|
||||||
|
addPropertyValueChangeListener(instanceId: number, onValueChanged: PropertyValueChangedHandler): PropertyValueChangedHandle {
|
||||||
|
let handle = ++this.nextListenHandle;
|
||||||
|
this._propertyValueChangeItems.push({ handle: handle, instanceId: instanceId, onValueChanged: onValueChanged });
|
||||||
|
return { _PropertyValueChangedHandle: handle };
|
||||||
|
}
|
||||||
|
removePropertyValueChangeListener(handle: PropertyValueChangedHandle) {
|
||||||
|
for (let i = 0; i < this._propertyValueChangeItems.length; ++i) {
|
||||||
|
if (this._propertyValueChangeItems[i].handle === handle._PropertyValueChangedHandle) {
|
||||||
|
this._propertyValueChangeItems.splice(i, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_pluginPresetsChangedHandles: PluginPresetsChangedHandle[] = [];
|
_pluginPresetsChangedHandles: PluginPresetsChangedHandle[] = [];
|
||||||
|
|
||||||
@@ -1217,7 +1252,27 @@ class PiPedalModelImpl implements PiPedalModel {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_setPedalBoardPropertyValue(instanceId: number, propertyUri: string, value: any, notifyServer: boolean): void {
|
||||||
|
let pedalBoard = this.pedalBoard.get();
|
||||||
|
if (pedalBoard === undefined) throw new PiPedalStateError("Pedalboard not ready.");
|
||||||
|
let newPedalBoard = pedalBoard.clone();
|
||||||
|
|
||||||
|
let item = newPedalBoard.getItem(instanceId);
|
||||||
|
let changed = item.setPropertyValue(propertyUri, value);
|
||||||
|
if (changed) {
|
||||||
|
this.pedalBoard.set(newPedalBoard);
|
||||||
|
if (notifyServer) {
|
||||||
|
// FIX ME!: this._setServerProperty("setProperty", instanceId, propertyUri, value);
|
||||||
|
}
|
||||||
|
for (let i = 0; i < this._propertyValueChangeItems.length; ++i) {
|
||||||
|
let item = this._propertyValueChangeItems[i];
|
||||||
|
if (instanceId === item.instanceId) {
|
||||||
|
item.onValueChanged(propertyUri, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
_setPedalBoardControlValue(instanceId: number, key: string, value: number, notifyServer: boolean): void {
|
_setPedalBoardControlValue(instanceId: number, key: string, value: number, notifyServer: boolean): void {
|
||||||
let pedalBoard = this.pedalBoard.get();
|
let pedalBoard = this.pedalBoard.get();
|
||||||
if (pedalBoard === undefined) throw new PiPedalStateError("Pedalboard not ready.");
|
if (pedalBoard === undefined) throw new PiPedalStateError("Pedalboard not ready.");
|
||||||
@@ -1262,14 +1317,17 @@ class PiPedalModelImpl implements PiPedalModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
setPedalBoardPropertyValue(instanceId: number, propertyUri: string, value: any): void
|
||||||
|
{
|
||||||
|
this._setPedalBoardPropertyValue(instanceId, propertyUri, value, true);
|
||||||
|
}
|
||||||
setPedalBoardControlValue(instanceId: number, key: string, value: number): void {
|
setPedalBoardControlValue(instanceId: number, key: string, value: number): void {
|
||||||
this._setPedalBoardControlValue(instanceId, key, value, true);
|
this._setPedalBoardControlValue(instanceId, key, value, true);
|
||||||
}
|
}
|
||||||
setPedalBoardItemEnabled(instanceId: number, value: boolean): void {
|
setPedalBoardItemEnabled(instanceId: number, value: boolean): void {
|
||||||
this._setPedalBoardItemEnabled(instanceId, value, true);
|
this._setPedalBoardItemEnabled(instanceId, value, true);
|
||||||
}
|
}
|
||||||
_setPedalBoardItemEnabled(instanceId: number, value: boolean, notifyServer: boolean): void {
|
private _setPedalBoardItemEnabled(instanceId: number, value: boolean, notifyServer: boolean): void {
|
||||||
let pedalBoard = this.pedalBoard.get();
|
let pedalBoard = this.pedalBoard.get();
|
||||||
if (pedalBoard === undefined) throw new PiPedalStateError("Pedalboard not ready.");
|
if (pedalBoard === undefined) throw new PiPedalStateError("Pedalboard not ready.");
|
||||||
let newPedalBoard = pedalBoard.clone();
|
let newPedalBoard = pedalBoard.clone();
|
||||||
@@ -1544,6 +1602,13 @@ class PiPedalModelImpl implements PiPedalModel {
|
|||||||
return newPresetId;
|
return newPresetId;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestFileList(piPedalFileProperty: PiPedalFileProperty): Promise<string[]>
|
||||||
|
{
|
||||||
|
return nullCast(this.webSocket)
|
||||||
|
.request<string[]>('requestFileList',piPedalFileProperty);
|
||||||
|
}
|
||||||
|
|
||||||
saveCurrentPluginPresetAs(pluginInstanceId: number, newName: string): Promise<number> {
|
saveCurrentPluginPresetAs(pluginInstanceId: number, newName: string): Promise<number> {
|
||||||
// default behaviour is to save after the currently selected preset.
|
// default behaviour is to save after the currently selected preset.
|
||||||
let request: any = {
|
let request: any = {
|
||||||
@@ -2354,6 +2419,8 @@ class PiPedalModelImpl implements PiPedalModel {
|
|||||||
return jackConfig.isValid;
|
return jackConfig.isValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let instance: PiPedalModel | undefined = undefined;
|
let instance: PiPedalModel | undefined = undefined;
|
||||||
|
|||||||
@@ -272,18 +272,19 @@ class PiPedalSocket {
|
|||||||
connectInternal_(): Promise<WebSocket> {
|
connectInternal_(): Promise<WebSocket> {
|
||||||
return new Promise<WebSocket>((resolve, reject) => {
|
return new Promise<WebSocket>((resolve, reject) => {
|
||||||
let ws = new WebSocket(this.url);
|
let ws = new WebSocket(this.url);
|
||||||
|
|
||||||
let self = this;
|
let self = this;
|
||||||
|
|
||||||
ws.onmessage = this.handleMessage.bind(this);
|
ws.onmessage = this.handleMessage.bind(this);
|
||||||
ws.onclose = (event: Event) => {
|
ws.onclose = (event: Event) => {
|
||||||
ws.onclose = null;
|
ws.onclose = null;
|
||||||
ws.onerror = null;
|
ws.onerror = null;
|
||||||
reject("Can't connect to server.");
|
reject("Connection closed unexpectedly.");
|
||||||
};
|
};
|
||||||
ws.onerror = (event: Event) => {
|
ws.onerror = (event: Event) => {
|
||||||
ws.onclose = null;
|
ws.onclose = null;
|
||||||
ws.onerror = null;
|
ws.onerror = null;
|
||||||
reject("Can't connect to server.");
|
reject("Failed to connect.");
|
||||||
};
|
};
|
||||||
ws.onopen = (event: Event) => {
|
ws.onopen = (event: Event) => {
|
||||||
ws.onerror = self.handleError.bind(self);
|
ws.onerror = self.handleError.bind(self);
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ import { WithStyles } from '@mui/styles';
|
|||||||
import createStyles from '@mui/styles/createStyles';
|
import createStyles from '@mui/styles/createStyles';
|
||||||
import withStyles from '@mui/styles/withStyles';
|
import withStyles from '@mui/styles/withStyles';
|
||||||
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
||||||
import { UiPlugin, UiControl } from './Lv2Plugin';
|
import { UiPlugin, UiControl, PiPedalFileProperty,PiPedalFileType } from './Lv2Plugin';
|
||||||
import {
|
import {
|
||||||
PedalBoard, PedalBoardItem, ControlValue,
|
PedalBoard, PedalBoardItem, ControlValue,PropertyValue
|
||||||
} from './PedalBoard';
|
} from './PedalBoard';
|
||||||
import PluginControl from './PluginControl';
|
import PluginControl from './PluginControl';
|
||||||
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
||||||
@@ -34,6 +34,8 @@ import { nullCast } from './Utility'
|
|||||||
import { PiPedalStateError } from './PiPedalError';
|
import { PiPedalStateError } from './PiPedalError';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import FullScreenIME from './FullScreenIME';
|
import FullScreenIME from './FullScreenIME';
|
||||||
|
import FilePropertyControl from './FilePropertyControl';
|
||||||
|
import FilePropertyDialog from './FilePropertyDialog';
|
||||||
|
|
||||||
|
|
||||||
export const StandardItemSize = { width: 80, height: 110 };
|
export const StandardItemSize = { width: 80, height: 110 };
|
||||||
@@ -167,8 +169,7 @@ const styles = (theme: Theme) => createStyles({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export class ControlGroup {
|
export class ControlGroup {
|
||||||
constructor(name: string, controls: ReactNode[])
|
constructor(name: string, controls: ReactNode[]) {
|
||||||
{
|
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.controls = controls;
|
this.controls = controls;
|
||||||
}
|
}
|
||||||
@@ -197,6 +198,9 @@ type PluginControlViewState = {
|
|||||||
imeValue: number;
|
imeValue: number;
|
||||||
imeCaption: string;
|
imeCaption: string;
|
||||||
imeInitialHeight: number;
|
imeInitialHeight: number;
|
||||||
|
showFileDialog: boolean,
|
||||||
|
dialogFileProperty: PiPedalFileProperty,
|
||||||
|
dialogFileValue: string
|
||||||
};
|
};
|
||||||
|
|
||||||
const PluginControlView =
|
const PluginControlView =
|
||||||
@@ -214,11 +218,14 @@ const PluginControlView =
|
|||||||
imeUiControl: undefined,
|
imeUiControl: undefined,
|
||||||
imeValue: 0,
|
imeValue: 0,
|
||||||
imeCaption: "",
|
imeCaption: "",
|
||||||
imeInitialHeight: 0
|
imeInitialHeight: 0,
|
||||||
|
showFileDialog: false,
|
||||||
|
dialogFileProperty: new PiPedalFileProperty(),
|
||||||
|
dialogFileValue: ""
|
||||||
|
|
||||||
}
|
}
|
||||||
this.onPedalBoardChanged = this.onPedalBoardChanged.bind(this);
|
this.onPedalBoardChanged = this.onPedalBoardChanged.bind(this);
|
||||||
this.onValueChanged = this.onValueChanged.bind(this);
|
this.onControlValueChanged = this.onControlValueChanged.bind(this);
|
||||||
this.onPreviewChange = this.onPreviewChange.bind(this);
|
this.onPreviewChange = this.onPreviewChange.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,9 +234,12 @@ const PluginControlView =
|
|||||||
this.model.previewPedalBoardValue(this.props.instanceId, key, value);
|
this.model.previewPedalBoardValue(this.props.instanceId, key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
onValueChanged(key: string, value: number): void {
|
onControlValueChanged(key: string, value: number): void {
|
||||||
this.model.setPedalBoardControlValue(this.props.instanceId, key, value);
|
this.model.setPedalBoardControlValue(this.props.instanceId, key, value);
|
||||||
}
|
}
|
||||||
|
onPropertyValueChanged(propertyUri: string, value: any): void {
|
||||||
|
this.model.setPedalBoardPropertyValue(this.props.instanceId, propertyUri, value);
|
||||||
|
}
|
||||||
|
|
||||||
onPedalBoardChanged(value?: PedalBoard) {
|
onPedalBoardChanged(value?: PedalBoard) {
|
||||||
//let item = this.model.pedalBoard.get().maybeGetItem(this.props.instanceId);
|
//let item = this.model.pedalBoard.get().maybeGetItem(this.props.instanceId);
|
||||||
@@ -274,6 +284,30 @@ const PluginControlView =
|
|||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
makeFilePropertyUI(fileProperty: PiPedalFileProperty, propertyValues: PropertyValue[]): ReactNode {
|
||||||
|
let propertyValue: PropertyValue | undefined = undefined;
|
||||||
|
for (let i = 0; i < propertyValues.length; ++i) {
|
||||||
|
if (propertyValues[i].propertyUri === fileProperty.patchProperty) {
|
||||||
|
propertyValue = propertyValues[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!propertyValue) {
|
||||||
|
propertyValue = new PropertyValue();
|
||||||
|
propertyValue.value = fileProperty.defaultFile;
|
||||||
|
propertyValue.propertyUri = fileProperty.patchProperty;
|
||||||
|
}
|
||||||
|
return ((
|
||||||
|
|
||||||
|
<FilePropertyControl instanceId={this.props.instanceId} value={propertyValue.value}
|
||||||
|
fileProperty={fileProperty}
|
||||||
|
onFileClick={(fileProperty,selectedFile) => {
|
||||||
|
this.setState({showFileDialog: true,dialogFileProperty: fileProperty,dialogFileValue: selectedFile});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
}
|
||||||
makeStandardControl(uiControl: UiControl, controlValues: ControlValue[]): ReactNode {
|
makeStandardControl(uiControl: UiControl, controlValues: ControlValue[]): ReactNode {
|
||||||
let symbol = uiControl.symbol;
|
let symbol = uiControl.symbol;
|
||||||
|
|
||||||
@@ -290,7 +324,7 @@ const PluginControlView =
|
|||||||
return ((
|
return ((
|
||||||
|
|
||||||
<PluginControl instanceId={this.props.instanceId} uiControl={uiControl} value={controlValue.value}
|
<PluginControl instanceId={this.props.instanceId} uiControl={uiControl} value={controlValue.value}
|
||||||
onChange={(value: number) => { this.onValueChanged(controlValue!.key, value) }}
|
onChange={(value: number) => { this.onControlValueChanged(controlValue!.key, value) }}
|
||||||
onPreviewChange={(value: number) => { this.onPreviewChange(controlValue!.key, value) }}
|
onPreviewChange={(value: number) => { this.onPreviewChange(controlValue!.key, value) }}
|
||||||
requestIMEEdit={(uiControl, value) => this.requestImeEdit(uiControl, value)}
|
requestIMEEdit={(uiControl, value) => this.requestImeEdit(uiControl, value)}
|
||||||
|
|
||||||
@@ -299,7 +333,7 @@ const PluginControlView =
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getStandardControlNodes(plugin: UiPlugin, controlValues: ControlValue[]): ControlNodes {
|
getStandardControlNodes(plugin: UiPlugin, controlValues: ControlValue[],propertyValues: PropertyValue[]): ControlNodes {
|
||||||
let result: ControlNodes = [];
|
let result: ControlNodes = [];
|
||||||
|
|
||||||
for (let i = 0; i < plugin.controls.length; ++i) {
|
for (let i = 0; i < plugin.controls.length; ++i) {
|
||||||
@@ -330,6 +364,12 @@ const PluginControlView =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (let i = 0; i < plugin.fileProperties.length; ++i) {
|
||||||
|
let fileProperty = plugin.fileProperties[i];
|
||||||
|
result.push(
|
||||||
|
this.makeFilePropertyUI(fileProperty, propertyValues)
|
||||||
|
);
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,10 +392,8 @@ const PluginControlView =
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
hasGroups(nodes: (ReactNode | ControlGroup)[]): boolean
|
hasGroups(nodes: (ReactNode | ControlGroup)[]): boolean {
|
||||||
{
|
for (let i = 0; i < nodes.length; ++i) {
|
||||||
for (let i = 0; i < nodes.length; ++i)
|
|
||||||
{
|
|
||||||
let node = nodes[i];
|
let node = nodes[i];
|
||||||
if (node instanceof ControlGroup) return true;
|
if (node instanceof ControlGroup) return true;
|
||||||
}
|
}
|
||||||
@@ -374,8 +412,7 @@ const PluginControlView =
|
|||||||
if (node instanceof ControlGroup) {
|
if (node instanceof ControlGroup) {
|
||||||
let controlGroup = node as ControlGroup;
|
let controlGroup = node as ControlGroup;
|
||||||
let controls: ReactNode[] = [];
|
let controls: ReactNode[] = [];
|
||||||
for (let j = 0; j < controlGroup.controls.length; ++j)
|
for (let j = 0; j < controlGroup.controls.length; ++j) {
|
||||||
{
|
|
||||||
let item = controlGroup.controls[j];
|
let item = controlGroup.controls[j];
|
||||||
controls.push(
|
controls.push(
|
||||||
(
|
(
|
||||||
@@ -421,6 +458,7 @@ const PluginControlView =
|
|||||||
return (<div className={classes.frame} ></div>);
|
return (<div className={classes.frame} ></div>);
|
||||||
|
|
||||||
let controlValues = pedalBoardItem.controlValues;
|
let controlValues = pedalBoardItem.controlValues;
|
||||||
|
let propertyValues = pedalBoardItem.propertyValues;
|
||||||
|
|
||||||
|
|
||||||
let plugin: UiPlugin = nullCast(this.model.getUiPlugin(pedalBoardItem.uri));
|
let plugin: UiPlugin = nullCast(this.model.getUiPlugin(pedalBoardItem.uri));
|
||||||
@@ -429,14 +467,14 @@ const PluginControlView =
|
|||||||
controlValues = this.filterNotOnGui(controlValues, plugin);
|
controlValues = this.filterNotOnGui(controlValues, plugin);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let gridClass = this.state.landscapeGrid ? classes.landscapeGrid : classes.normalGrid;
|
let gridClass = this.state.landscapeGrid ? classes.landscapeGrid : classes.normalGrid;
|
||||||
let vuMeterRClass = this.state.landscapeGrid ? classes.vuMeterRLandscape : classes.vuMeterR;
|
let vuMeterRClass = this.state.landscapeGrid ? classes.vuMeterRLandscape : classes.vuMeterR;
|
||||||
let controlNodes: ControlNodes;
|
let controlNodes: ControlNodes;
|
||||||
|
|
||||||
controlNodes = this.getStandardControlNodes(plugin, controlValues);
|
controlNodes = this.getStandardControlNodes(plugin, controlValues,propertyValues);
|
||||||
|
|
||||||
if (this.props.customization)
|
if (this.props.customization) {
|
||||||
{
|
|
||||||
// allow wrapper class to insert/remove/rebuild controls.
|
// allow wrapper class to insert/remove/rebuild controls.
|
||||||
controlNodes = this.props.customization.ModifyControls(controlNodes);
|
controlNodes = this.props.customization.ModifyControls(controlNodes);
|
||||||
}
|
}
|
||||||
@@ -462,6 +500,15 @@ const PluginControlView =
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
<FilePropertyDialog open={this.state.showFileDialog}
|
||||||
|
fileProperty={this.state.dialogFileProperty}
|
||||||
|
selectedFile={this.state.dialogFileValue}
|
||||||
|
onClose={()=> { this.setState({ showFileDialog: false});}}
|
||||||
|
onOk={(fileProperty,selectedFile)=> {
|
||||||
|
this.model.setPedalBoardPropertyValue(this.props.instanceId,fileProperty.patchProperty,selectedFile)
|
||||||
|
this.setState({ showFileDialog: false});}
|
||||||
|
}
|
||||||
|
/>
|
||||||
<FullScreenIME uiControl={this.state.imeUiControl} value={this.state.imeValue}
|
<FullScreenIME uiControl={this.state.imeUiControl} value={this.state.imeValue}
|
||||||
|
|
||||||
onChange={(key, value) => this.onImeValueChange(key, value)}
|
onChange={(key, value) => this.onImeValueChange(key, value)}
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 Robin E. R. Davies
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "AutoLilvNode.hpp"
|
||||||
|
#include "PiPedalHost.hpp"
|
||||||
|
|
||||||
|
using namespace pipedal;
|
||||||
|
|
||||||
|
float AutoLilvNode::AsFloat(float defaultValue)
|
||||||
|
{
|
||||||
|
if (node == nullptr)
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
if (lilv_node_is_float(node))
|
||||||
|
{
|
||||||
|
return lilv_node_as_float(node);
|
||||||
|
}
|
||||||
|
if (lilv_node_is_int(node))
|
||||||
|
{
|
||||||
|
return lilv_node_as_int(node);
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
int AutoLilvNode::AsInt(int defaultValue)
|
||||||
|
{
|
||||||
|
if (node == nullptr)
|
||||||
|
return defaultValue;
|
||||||
|
if (lilv_node_is_int(node))
|
||||||
|
return lilv_node_as_int(node);
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
bool AutoLilvNode::AsBool(bool defaultValue)
|
||||||
|
{
|
||||||
|
if (node == nullptr)
|
||||||
|
return defaultValue;
|
||||||
|
if (lilv_node_is_int(node))
|
||||||
|
return lilv_node_as_bool(node);
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
std::string AutoLilvNode::AsUri()
|
||||||
|
{
|
||||||
|
if (node == nullptr)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (lilv_node_is_uri(node))
|
||||||
|
{
|
||||||
|
return lilv_node_as_uri(node);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
std::string AutoLilvNode::AsString()
|
||||||
|
{
|
||||||
|
if (node == nullptr)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (lilv_node_is_string(node))
|
||||||
|
{
|
||||||
|
return lilv_node_as_string(node);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 Robin E. R. Davies
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* 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 <lilv/lilv.h>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace pipedal
|
||||||
|
{
|
||||||
|
class AutoLilvNode
|
||||||
|
{
|
||||||
|
|
||||||
|
// const LilvNode* returns must not be freed, by convention.
|
||||||
|
private:
|
||||||
|
LilvNode *node = nullptr;
|
||||||
|
AutoLilvNode(const LilvNode *node) = delete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AutoLilvNode()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
AutoLilvNode(LilvNode *node)
|
||||||
|
: node(node)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
~AutoLilvNode() { Free(); }
|
||||||
|
|
||||||
|
|
||||||
|
operator const LilvNode *()
|
||||||
|
{
|
||||||
|
return this->node;
|
||||||
|
}
|
||||||
|
operator bool()
|
||||||
|
{
|
||||||
|
return this->node != nullptr;
|
||||||
|
}
|
||||||
|
LilvNode*&Get() { return node; }
|
||||||
|
|
||||||
|
AutoLilvNode&operator=(LilvNode*node) {
|
||||||
|
Free();
|
||||||
|
this->node = node;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
AutoLilvNode&operator=(AutoLilvNode&&other) {
|
||||||
|
std::swap(this->node,other.node);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float AsFloat(float defaultValue = 0);
|
||||||
|
int AsInt(int defaultValue = 0);
|
||||||
|
bool AsBool(bool defaultValue = false);
|
||||||
|
std::string AsUri();
|
||||||
|
std::string AsString();
|
||||||
|
|
||||||
|
void Free()
|
||||||
|
{
|
||||||
|
if (node != nullptr)
|
||||||
|
lilv_node_free(node);
|
||||||
|
node = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -153,7 +153,7 @@ void AvahiService::create_group(AvahiClient *c)
|
|||||||
* because it was reset previously, add our entries. */
|
* because it was reset previously, add our entries. */
|
||||||
if (avahi_entry_group_is_empty(group))
|
if (avahi_entry_group_is_empty(group))
|
||||||
{
|
{
|
||||||
Lv2Log::debug(SS("Adding service '" << name));
|
Lv2Log::debug(SS("Adding service '" << name << "'"));
|
||||||
|
|
||||||
std::string instanceTxtRecord = SS("id=" << this->instanceId);
|
std::string instanceTxtRecord = SS("id=" << this->instanceId);
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,6 @@ TEST_CASE("Avahi Service Test", "[avahi_service][dev]")
|
|||||||
|
|
||||||
service.Unannounce();
|
service.Unannounce();
|
||||||
service.Announce(81, "Test Announcement 2", "0a6045b0-1753-4104-b3e4-b9713b9cc358","pipedal");
|
service.Announce(81, "Test Announcement 2", "0a6045b0-1753-4104-b3e4-b9713b9cc358","pipedal");
|
||||||
sleep(10000);
|
sleep(10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+9
-1
@@ -5,6 +5,8 @@ set (CMAKE_INSTALL_PREFIX "/usr/")
|
|||||||
|
|
||||||
set (USE_PCH 1)
|
set (USE_PCH 1)
|
||||||
|
|
||||||
|
set(CXX_STANDARD 20)
|
||||||
|
|
||||||
include(FindPkgConfig)
|
include(FindPkgConfig)
|
||||||
|
|
||||||
#################################################################
|
#################################################################
|
||||||
@@ -135,6 +137,10 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
set (PIPEDAL_SOURCES
|
set (PIPEDAL_SOURCES
|
||||||
|
AutoLilvNode.hpp
|
||||||
|
AutoLilvNode.cpp
|
||||||
|
PiPedalUI.hpp
|
||||||
|
PiPedalUI.cpp
|
||||||
RtInversionGuard.hpp
|
RtInversionGuard.hpp
|
||||||
CpuUse.hpp CpuUse.cpp
|
CpuUse.hpp CpuUse.cpp
|
||||||
P2pConfigFiles.hpp
|
P2pConfigFiles.hpp
|
||||||
@@ -154,7 +160,9 @@ set (PIPEDAL_SOURCES
|
|||||||
WifiDirectConfigSettings.hpp WifiDirectConfigSettings.cpp
|
WifiDirectConfigSettings.hpp WifiDirectConfigSettings.cpp
|
||||||
ConfigUtil.hpp ConfigUtil.cpp
|
ConfigUtil.hpp ConfigUtil.cpp
|
||||||
|
|
||||||
RequestHandler.hpp json.cpp json.hpp Scratch.cpp PiPedalHost.hpp PiPedalHost.cpp
|
RequestHandler.hpp json.cpp json.hpp
|
||||||
|
json_variant.hpp json_variant.cpp
|
||||||
|
Scratch.cpp PiPedalHost.hpp PiPedalHost.cpp
|
||||||
PluginType.hpp PluginType.cpp
|
PluginType.hpp PluginType.cpp
|
||||||
Lv2Log.hpp Lv2Log.cpp
|
Lv2Log.hpp Lv2Log.cpp
|
||||||
PiPedalSocket.hpp PiPedalSocket.cpp
|
PiPedalSocket.hpp PiPedalSocket.cpp
|
||||||
|
|||||||
@@ -91,3 +91,39 @@ void LogFeature::Prepare(MapFeature*map)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LogFeature::LogError(const char*fmt,...)
|
||||||
|
{
|
||||||
|
va_list va;
|
||||||
|
va_start(va, fmt);
|
||||||
|
|
||||||
|
vprintf(uris.ridError,fmt,va);
|
||||||
|
|
||||||
|
}
|
||||||
|
void LogFeature::LogWarning(const char*fmt,...)
|
||||||
|
{
|
||||||
|
va_list va;
|
||||||
|
va_start(va, fmt);
|
||||||
|
|
||||||
|
vprintf(uris.ridWarning,fmt,va);
|
||||||
|
|
||||||
|
}
|
||||||
|
void LogFeature::LogNote(const char*fmt,...)
|
||||||
|
{
|
||||||
|
va_list va;
|
||||||
|
va_start(va, fmt);
|
||||||
|
|
||||||
|
vprintf(uris.ridNote,fmt,va);
|
||||||
|
|
||||||
|
}
|
||||||
|
void LogFeature::LogTrace(const char*fmt,...)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
va_list va;
|
||||||
|
va_start(va, fmt);
|
||||||
|
|
||||||
|
vprintf(uris.ridNote,fmt,va);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -61,6 +61,12 @@ namespace pipedal {
|
|||||||
LogFeature();
|
LogFeature();
|
||||||
void Prepare(MapFeature* map);
|
void Prepare(MapFeature* map);
|
||||||
|
|
||||||
|
void LogError(const char*fmt,...);
|
||||||
|
void LogWarning(const char*fmt,...);
|
||||||
|
void LogNote(const char*fmt,...);
|
||||||
|
void LogTrace(const char*fmt,...);
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const LV2_Feature* GetFeature()
|
const LV2_Feature* GetFeature()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -40,9 +40,9 @@ TEST_CASE( "PiPedalHost memory leak", "[lv2host_leak][Build][Dev]" ) {
|
|||||||
}
|
}
|
||||||
MemStats finalMemory = GetMemStats();
|
MemStats finalMemory = GetMemStats();
|
||||||
|
|
||||||
// Something lilv leaks a Dublin Core url.
|
// Something in lilv leaks a Dublin Core url.
|
||||||
// Acceptable.
|
// Acceptable.
|
||||||
const int ACCEPTABLE_ALLOCATION_LEAKS = 4;
|
const int ACCEPTABLE_ALLOCATION_LEAKS = 6;
|
||||||
const int ACCEPTABLE_MEMORY_LEAK = 400;
|
const int ACCEPTABLE_MEMORY_LEAK = 400;
|
||||||
|
|
||||||
if (finalMemory.allocations > initialMemory.allocations + ACCEPTABLE_ALLOCATION_LEAKS
|
if (finalMemory.allocations > initialMemory.allocations + ACCEPTABLE_ALLOCATION_LEAKS
|
||||||
|
|||||||
+22
-4
@@ -53,6 +53,18 @@ PedalBoardItem*PedalBoard::GetItem(long pedalItemId)
|
|||||||
return const_cast<PedalBoardItem*>(GetItem_(this->items(),pedalItemId));
|
return const_cast<PedalBoardItem*>(GetItem_(this->items(),pedalItemId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PropertyValue*PedalBoardItem::GetPropertyValue(const std::string&propertyUri)
|
||||||
|
{
|
||||||
|
for (auto&propertyValue: this->propertyValues_)
|
||||||
|
{
|
||||||
|
if (propertyValue.propertyUri() == propertyUri)
|
||||||
|
{
|
||||||
|
return &propertyValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
ControlValue* PedalBoardItem::GetControlValue(const std::string&symbol)
|
ControlValue* PedalBoardItem::GetControlValue(const std::string&symbol)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < this->controlValues().size(); ++i)
|
for (size_t i = 0; i < this->controlValues().size(); ++i)
|
||||||
@@ -164,6 +176,10 @@ PedalBoard PedalBoard::MakeDefault()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool IsPedalBoardSplitItem(const PedalBoardItem*self, const std::vector<PedalBoardItem>&value)
|
||||||
|
{
|
||||||
|
return self->uri() == SPLIT_PEDALBOARD_ITEM_URI;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -176,17 +192,19 @@ JSON_MAP_BEGIN(ControlValue)
|
|||||||
JSON_MAP_REFERENCE(ControlValue,value)
|
JSON_MAP_REFERENCE(ControlValue,value)
|
||||||
JSON_MAP_END()
|
JSON_MAP_END()
|
||||||
|
|
||||||
|
JSON_MAP_BEGIN(PropertyValue)
|
||||||
|
JSON_MAP_REFERENCE(PropertyValue,propertyUri)
|
||||||
|
JSON_MAP_REFERENCE(PropertyValue,value)
|
||||||
|
JSON_MAP_END()
|
||||||
|
|
||||||
|
|
||||||
bool IsPedalBoardSplitItem(const PedalBoardItem*self, const std::vector<PedalBoardItem>&value)
|
|
||||||
{
|
|
||||||
return self->uri() == SPLIT_PEDALBOARD_ITEM_URI;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON_MAP_BEGIN(PedalBoardItem)
|
JSON_MAP_BEGIN(PedalBoardItem)
|
||||||
JSON_MAP_REFERENCE(PedalBoardItem,instanceId)
|
JSON_MAP_REFERENCE(PedalBoardItem,instanceId)
|
||||||
JSON_MAP_REFERENCE(PedalBoardItem,uri)
|
JSON_MAP_REFERENCE(PedalBoardItem,uri)
|
||||||
JSON_MAP_REFERENCE(PedalBoardItem,isEnabled)
|
JSON_MAP_REFERENCE(PedalBoardItem,isEnabled)
|
||||||
JSON_MAP_REFERENCE(PedalBoardItem,controlValues)
|
JSON_MAP_REFERENCE(PedalBoardItem,controlValues)
|
||||||
|
JSON_MAP_REFERENCE(PedalBoardItem,propertyValues)
|
||||||
JSON_MAP_REFERENCE(PedalBoardItem,pluginName)
|
JSON_MAP_REFERENCE(PedalBoardItem,pluginName)
|
||||||
JSON_MAP_REFERENCE_CONDITIONAL(PedalBoardItem,topChain,IsPedalBoardSplitItem)
|
JSON_MAP_REFERENCE_CONDITIONAL(PedalBoardItem,topChain,IsPedalBoardSplitItem)
|
||||||
JSON_MAP_REFERENCE_CONDITIONAL(PedalBoardItem,bottomChain,&IsPedalBoardSplitItem)
|
JSON_MAP_REFERENCE_CONDITIONAL(PedalBoardItem,bottomChain,&IsPedalBoardSplitItem)
|
||||||
|
|||||||
+29
-1
@@ -20,6 +20,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
|
#include "json_variant.hpp"
|
||||||
#include "MidiBinding.hpp"
|
#include "MidiBinding.hpp"
|
||||||
|
|
||||||
namespace pipedal {
|
namespace pipedal {
|
||||||
@@ -72,25 +73,52 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
class PropertyValue {
|
||||||
|
private:
|
||||||
|
std::string propertyUri_;
|
||||||
|
json_variant value_;
|
||||||
|
public:
|
||||||
|
PropertyValue()
|
||||||
|
{
|
||||||
|
|
||||||
class PedalBoardItem: public JsonWritable {
|
}
|
||||||
|
template <typename T>
|
||||||
|
PropertyValue(const std::string&propertyUri, T value)
|
||||||
|
:propertyUri_(propertyUri)
|
||||||
|
, value_(value)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
GETTER_SETTER_REF(propertyUri)
|
||||||
|
GETTER_SETTER_REF(value)
|
||||||
|
|
||||||
|
DECLARE_JSON_MAP(PropertyValue);
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class PedalBoardItem: public JsonMemberWritable {
|
||||||
int64_t instanceId_ = 0;
|
int64_t instanceId_ = 0;
|
||||||
std::string uri_;
|
std::string uri_;
|
||||||
std::string pluginName_;
|
std::string pluginName_;
|
||||||
bool isEnabled_ = true;
|
bool isEnabled_ = true;
|
||||||
std::vector<ControlValue> controlValues_;
|
std::vector<ControlValue> controlValues_;
|
||||||
|
std::vector<PropertyValue> propertyValues_;
|
||||||
std::vector<PedalBoardItem> topChain_;
|
std::vector<PedalBoardItem> topChain_;
|
||||||
std::vector<PedalBoardItem> bottomChain_;
|
std::vector<PedalBoardItem> bottomChain_;
|
||||||
std::vector<MidiBinding> midiBindings_;
|
std::vector<MidiBinding> midiBindings_;
|
||||||
std::string vstState_;
|
std::string vstState_;
|
||||||
public:
|
public:
|
||||||
ControlValue*GetControlValue(const std::string&symbol);
|
ControlValue*GetControlValue(const std::string&symbol);
|
||||||
|
PropertyValue*GetPropertyValue(const std::string&propertyUri);
|
||||||
|
|
||||||
GETTER_SETTER(instanceId)
|
GETTER_SETTER(instanceId)
|
||||||
GETTER_SETTER_REF(uri)
|
GETTER_SETTER_REF(uri)
|
||||||
GETTER_SETTER_REF(vstState);
|
GETTER_SETTER_REF(vstState);
|
||||||
GETTER_SETTER_REF(pluginName)
|
GETTER_SETTER_REF(pluginName)
|
||||||
GETTER_SETTER(isEnabled)
|
GETTER_SETTER(isEnabled)
|
||||||
GETTER_SETTER_VEC(controlValues)
|
GETTER_SETTER_VEC(controlValues)
|
||||||
|
GETTER_SETTER_VEC(propertyValues)
|
||||||
GETTER_SETTER_VEC(topChain)
|
GETTER_SETTER_VEC(topChain)
|
||||||
GETTER_SETTER_VEC(bottomChain)
|
GETTER_SETTER_VEC(bottomChain)
|
||||||
GETTER_SETTER_VEC(midiBindings)
|
GETTER_SETTER_VEC(midiBindings)
|
||||||
|
|||||||
+69
-72
@@ -34,6 +34,7 @@
|
|||||||
#include "lv2.h"
|
#include "lv2.h"
|
||||||
#include "lv2/atom.lv2/atom.h"
|
#include "lv2/atom.lv2/atom.h"
|
||||||
#include "lv2/time/time.h"
|
#include "lv2/time/time.h"
|
||||||
|
#include "lv2/state/state.h"
|
||||||
#include "lv2/lv2plug.in/ns/ext/buf-size/buf-size.h"
|
#include "lv2/lv2plug.in/ns/ext/buf-size/buf-size.h"
|
||||||
#include "lv2/lv2plug.in/ns/ext/presets/presets.h"
|
#include "lv2/lv2plug.in/ns/ext/presets/presets.h"
|
||||||
#include "lv2/lv2plug.in/ns/ext/port-props/port-props.h"
|
#include "lv2/lv2plug.in/ns/ext/port-props/port-props.h"
|
||||||
@@ -103,7 +104,6 @@ void PiPedalHost::SetConfiguration(const PiPedalConfiguration &configuration)
|
|||||||
this->vst3Enabled = configuration.IsVst3Enabled();
|
this->vst3Enabled = configuration.IsVst3Enabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PiPedalHost::LilvUris::Initialize(LilvWorld *pWorld)
|
void PiPedalHost::LilvUris::Initialize(LilvWorld *pWorld)
|
||||||
{
|
{
|
||||||
rdfsComment = lilv_new_uri(pWorld, PiPedalHost::RDFS_COMMENT_URI);
|
rdfsComment = lilv_new_uri(pWorld, PiPedalHost::RDFS_COMMENT_URI);
|
||||||
@@ -124,6 +124,18 @@ void PiPedalHost::LilvUris::Initialize(LilvWorld *pWorld)
|
|||||||
symbolUri = lilv_new_uri(pWorld, LV2_CORE__symbol);
|
symbolUri = lilv_new_uri(pWorld, LV2_CORE__symbol);
|
||||||
nameUri = lilv_new_uri(pWorld, LV2_CORE__name);
|
nameUri = lilv_new_uri(pWorld, LV2_CORE__name);
|
||||||
|
|
||||||
|
lv2Core__name = lilv_new_uri(pWorld, LV2_CORE__name);
|
||||||
|
pipedalUI__ui = lilv_new_uri(pWorld, PIPEDAL_UI__ui);
|
||||||
|
pipedalUI__fileProperties = lilv_new_uri(pWorld,PIPEDAL_UI__fileProperties);
|
||||||
|
pipedalUI__patchProperty = lilv_new_uri(pWorld, PIPEDAL_UI__patchProperty);
|
||||||
|
pipedalUI__directory = lilv_new_uri(pWorld,PIPEDAL_UI__directory);
|
||||||
|
pipedalUI__fileTypes = lilv_new_uri(pWorld,PIPEDAL_UI__fileTypes);
|
||||||
|
pipedalUI__fileProperty = lilv_new_uri(pWorld,PIPEDAL_UI__fileProperty);
|
||||||
|
pipedalUI__fileExtension = lilv_new_uri(pWorld, PIPEDAL_UI__fileExtension);
|
||||||
|
pipedalUI__outputPorts = lilv_new_uri(pWorld, PIPEDAL_UI__outputPorts);
|
||||||
|
pipedalUI__text = lilv_new_uri(pWorld, PIPEDAL_UI__text);
|
||||||
|
pipedalUI__defaultFile = lilv_new_uri(pWorld, PIPEDAL_UI__defaultFile);
|
||||||
|
|
||||||
time_Position = lilv_new_uri(pWorld, LV2_TIME__Position);
|
time_Position = lilv_new_uri(pWorld, LV2_TIME__Position);
|
||||||
time_barBeat = lilv_new_uri(pWorld, LV2_TIME__barBeat);
|
time_barBeat = lilv_new_uri(pWorld, LV2_TIME__barBeat);
|
||||||
time_beatsPerMinute = lilv_new_uri(pWorld, LV2_TIME__beatsPerMinute);
|
time_beatsPerMinute = lilv_new_uri(pWorld, LV2_TIME__beatsPerMinute);
|
||||||
@@ -202,54 +214,6 @@ static float nodeAsFloat(const LilvNode *node, float default_ = 0)
|
|||||||
return default_;
|
return default_;
|
||||||
}
|
}
|
||||||
|
|
||||||
class NodeAutoFree : public LilvNodePtr
|
|
||||||
{
|
|
||||||
|
|
||||||
// const LilvNode* returns must not be freed, by convention.
|
|
||||||
private:
|
|
||||||
NodeAutoFree(const LilvNode *node)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
NodeAutoFree()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
NodeAutoFree(LilvNode *node)
|
|
||||||
: LilvNodePtr(node)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
float as_float(float defaultValue = 0)
|
|
||||||
{
|
|
||||||
return nodeAsFloat(this->node, defaultValue);
|
|
||||||
}
|
|
||||||
int as_int()
|
|
||||||
{
|
|
||||||
if (node == nullptr)
|
|
||||||
return 0;
|
|
||||||
if (lilv_node_is_int(node))
|
|
||||||
return lilv_node_as_int(node);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
bool as_bool()
|
|
||||||
{
|
|
||||||
if (node == nullptr)
|
|
||||||
return false;
|
|
||||||
if (lilv_node_is_int(node))
|
|
||||||
return lilv_node_as_bool(node);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
std::string as_string()
|
|
||||||
{
|
|
||||||
return nodeAsString(this->node);
|
|
||||||
}
|
|
||||||
|
|
||||||
~NodeAutoFree()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
PiPedalHost::PiPedalHost()
|
PiPedalHost::PiPedalHost()
|
||||||
{
|
{
|
||||||
pWorld = nullptr;
|
pWorld = nullptr;
|
||||||
@@ -418,7 +382,7 @@ void PiPedalHost::Load(const char *lv2Path)
|
|||||||
{
|
{
|
||||||
const LilvPlugin *lilvPlugin = lilv_plugins_get(plugins, iPlugin);
|
const LilvPlugin *lilvPlugin = lilv_plugins_get(plugins, iPlugin);
|
||||||
|
|
||||||
std::shared_ptr<Lv2PluginInfo> pluginInfo = std::make_shared<Lv2PluginInfo>(this, lilvPlugin);
|
std::shared_ptr<Lv2PluginInfo> pluginInfo = std::make_shared<Lv2PluginInfo>(this, pWorld, lilvPlugin);
|
||||||
|
|
||||||
Lv2Log::debug("Plugin: " + pluginInfo->name());
|
Lv2Log::debug("Plugin: " + pluginInfo->name());
|
||||||
|
|
||||||
@@ -573,7 +537,7 @@ const char *PiPedalHost::RDFS_COMMENT_URI = "http://www.w3.org/2000/01/rdf-schem
|
|||||||
|
|
||||||
LilvNode *PiPedalHost::get_comment(const std::string &uri)
|
LilvNode *PiPedalHost::get_comment(const std::string &uri)
|
||||||
{
|
{
|
||||||
NodeAutoFree uriNode = lilv_new_uri(pWorld, uri.c_str());
|
AutoLilvNode uriNode = lilv_new_uri(pWorld, uri.c_str());
|
||||||
LilvNode *result = lilv_world_get(pWorld, uriNode, lilvUris.rdfsComment, nullptr);
|
LilvNode *result = lilv_world_get(pWorld, uriNode, lilvUris.rdfsComment, nullptr);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -595,21 +559,19 @@ bool Lv2PluginInfo::HasFactoryPresets(PiPedalHost *lv2Host, const LilvPlugin *pl
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Lv2PluginInfo::Lv2PluginInfo(PiPedalHost *lv2Host, const LilvPlugin *pPlugin)
|
Lv2PluginInfo::Lv2PluginInfo(PiPedalHost *lv2Host, LilvWorld *pWorld, const LilvPlugin *pPlugin)
|
||||||
{
|
{
|
||||||
const LilvNode *pluginUri = lilv_plugin_get_uri(pPlugin);
|
|
||||||
|
|
||||||
this->has_factory_presets_ = HasFactoryPresets(lv2Host, pPlugin);
|
this->has_factory_presets_ = HasFactoryPresets(lv2Host, pPlugin);
|
||||||
|
|
||||||
this->uri_ = nodeAsString(lilv_plugin_get_uri(pPlugin));
|
this->uri_ = nodeAsString(lilv_plugin_get_uri(pPlugin));
|
||||||
|
|
||||||
NodeAutoFree name = (lilv_plugin_get_name(pPlugin));
|
AutoLilvNode name = (lilv_plugin_get_name(pPlugin));
|
||||||
this->name_ = nodeAsString(name);
|
this->name_ = nodeAsString(name);
|
||||||
|
|
||||||
NodeAutoFree author_name = (lilv_plugin_get_author_name(pPlugin));
|
AutoLilvNode author_name = (lilv_plugin_get_author_name(pPlugin));
|
||||||
this->author_name_ = nodeAsString(author_name);
|
this->author_name_ = nodeAsString(author_name);
|
||||||
|
|
||||||
NodeAutoFree author_homepage = (lilv_plugin_get_author_homepage(pPlugin));
|
AutoLilvNode author_homepage = (lilv_plugin_get_author_homepage(pPlugin));
|
||||||
this->author_homepage_ = nodeAsString(author_homepage);
|
this->author_homepage_ = nodeAsString(author_homepage);
|
||||||
|
|
||||||
const LilvPluginClass *pClass = lilv_plugin_get_class(pPlugin);
|
const LilvPluginClass *pClass = lilv_plugin_get_class(pPlugin);
|
||||||
@@ -627,7 +589,7 @@ Lv2PluginInfo::Lv2PluginInfo(PiPedalHost *lv2Host, const LilvPlugin *pPlugin)
|
|||||||
NodesAutoFree extensions = lilv_plugin_get_extension_data(pPlugin);
|
NodesAutoFree extensions = lilv_plugin_get_extension_data(pPlugin);
|
||||||
this->extensions_ = nodeAsStringArray(extensions);
|
this->extensions_ = nodeAsStringArray(extensions);
|
||||||
|
|
||||||
NodeAutoFree comment = lv2Host->get_comment(this->uri_);
|
AutoLilvNode comment = lv2Host->get_comment(this->uri_);
|
||||||
this->comment_ = nodeAsString(comment);
|
this->comment_ = nodeAsString(comment);
|
||||||
|
|
||||||
uint32_t ports = lilv_plugin_get_num_ports(pPlugin);
|
uint32_t ports = lilv_plugin_get_num_ports(pPlugin);
|
||||||
@@ -671,6 +633,26 @@ Lv2PluginInfo::Lv2PluginInfo(PiPedalHost *lv2Host, const LilvPlugin *pPlugin)
|
|||||||
|
|
||||||
std::sort(ports_.begin(), ports_.end(), ports_sort_compare);
|
std::sort(ports_.begin(), ports_.end(), ports_sort_compare);
|
||||||
|
|
||||||
|
// Fetch pipedal plugin UI
|
||||||
|
|
||||||
|
AutoLilvNode pipedalUINode = lilv_world_get(
|
||||||
|
pWorld,
|
||||||
|
lilv_plugin_get_uri(pPlugin),
|
||||||
|
lv2Host->lilvUris.pipedalUI__ui,
|
||||||
|
nullptr);
|
||||||
|
if (pipedalUINode)
|
||||||
|
{
|
||||||
|
this->piPedalUI_ = std::make_shared<PiPedalUI>(lv2Host, pipedalUINode);
|
||||||
|
}
|
||||||
|
// xxx lilv_world_get(pWorld,pluginUri,);
|
||||||
|
// for (auto&portInfo: ports_)
|
||||||
|
// {
|
||||||
|
// if (portInfo->is_control_port() && portInfo->is_output())
|
||||||
|
// {
|
||||||
|
// std::cout << "Dbg: " << "Has an output control port. " << this->uri_ << std::endl;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
this->is_valid_ = isValid;
|
this->is_valid_ = isValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -684,6 +666,7 @@ std::vector<std::string> supportedFeatures = {
|
|||||||
LV2_BUF_SIZE__fixedBlockLength,
|
LV2_BUF_SIZE__fixedBlockLength,
|
||||||
LV2_BUF_SIZE__powerOf2BlockLength,
|
LV2_BUF_SIZE__powerOf2BlockLength,
|
||||||
LV2_CORE__isLive,
|
LV2_CORE__isLive,
|
||||||
|
LV2_STATE__loadDefaultState
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -728,16 +711,16 @@ Lv2PortInfo::Lv2PortInfo(PiPedalHost *host, const LilvPlugin *plugin, const Lilv
|
|||||||
index_ = lilv_port_get_index(plugin, pPort);
|
index_ = lilv_port_get_index(plugin, pPort);
|
||||||
symbol_ = nodeAsString(lilv_port_get_symbol(plugin, pPort));
|
symbol_ = nodeAsString(lilv_port_get_symbol(plugin, pPort));
|
||||||
|
|
||||||
NodeAutoFree name = lilv_port_get_name(plugin, pPort);
|
AutoLilvNode name = lilv_port_get_name(plugin, pPort);
|
||||||
name_ = nodeAsString(name);
|
name_ = nodeAsString(name);
|
||||||
|
|
||||||
classes_ = host->GetPluginPortClass(plugin, pPort);
|
classes_ = host->GetPluginPortClass(plugin, pPort);
|
||||||
|
|
||||||
NodeAutoFree minNode, maxNode, defaultNode;
|
AutoLilvNode minNode, maxNode, defaultNode;
|
||||||
min_value_ = 0;
|
min_value_ = 0;
|
||||||
max_value_ = 1;
|
max_value_ = 1;
|
||||||
default_value_ = 0;
|
default_value_ = 0;
|
||||||
lilv_port_get_range(plugin, pPort, &defaultNode, &minNode, &maxNode);
|
lilv_port_get_range(plugin, pPort, &defaultNode.Get(), &minNode.Get(), &maxNode.Get());
|
||||||
if (defaultNode)
|
if (defaultNode)
|
||||||
{
|
{
|
||||||
default_value_ = getFloat(defaultNode);
|
default_value_ = getFloat(defaultNode);
|
||||||
@@ -812,19 +795,19 @@ Lv2PortInfo::Lv2PortInfo(PiPedalHost *host, const LilvPlugin *plugin, const Lilv
|
|||||||
supports_midi_ = lilv_port_supports_event(plugin, pPort, host->lilvUris.midiEventNode);
|
supports_midi_ = lilv_port_supports_event(plugin, pPort, host->lilvUris.midiEventNode);
|
||||||
supports_time_position_ = lilv_port_supports_event(plugin, pPort, host->lilvUris.time_Position);
|
supports_time_position_ = lilv_port_supports_event(plugin, pPort, host->lilvUris.time_Position);
|
||||||
|
|
||||||
NodeAutoFree designationValue = lilv_port_get(plugin, pPort, host->lilvUris.designationNode);
|
AutoLilvNode designationValue = lilv_port_get(plugin, pPort, host->lilvUris.designationNode);
|
||||||
designation_ = nodeAsString(designationValue);
|
designation_ = nodeAsString(designationValue);
|
||||||
|
|
||||||
NodeAutoFree portGroup_value = lilv_port_get(plugin, pPort, host->lilvUris.portGroupUri);
|
AutoLilvNode portGroup_value = lilv_port_get(plugin, pPort, host->lilvUris.portGroupUri);
|
||||||
port_group_ = nodeAsString(portGroup_value);
|
port_group_ = nodeAsString(portGroup_value);
|
||||||
|
|
||||||
NodeAutoFree unitsValueUri = lilv_port_get(plugin, pPort, host->lilvUris.unitsUri);
|
AutoLilvNode unitsValueUri = lilv_port_get(plugin, pPort, host->lilvUris.unitsUri);
|
||||||
this->units_ = UriToUnits(nodeAsString(unitsValueUri));
|
this->units_ = UriToUnits(nodeAsString(unitsValueUri));
|
||||||
|
|
||||||
NodeAutoFree commentNode = lilv_port_get(plugin, pPort, host->lilvUris.rdfsComment);
|
AutoLilvNode commentNode = lilv_port_get(plugin, pPort, host->lilvUris.rdfsComment);
|
||||||
this->comment_ = nodeAsString(commentNode);
|
this->comment_ = nodeAsString(commentNode);
|
||||||
|
|
||||||
NodeAutoFree bufferType = lilv_port_get(plugin, pPort, host->lilvUris.bufferType_uri);
|
AutoLilvNode bufferType = lilv_port_get(plugin, pPort, host->lilvUris.bufferType_uri);
|
||||||
|
|
||||||
this->buffer_type_ = "";
|
this->buffer_type_ = "";
|
||||||
if (bufferType)
|
if (bufferType)
|
||||||
@@ -953,6 +936,12 @@ Lv2PluginUiInfo::Lv2PluginUiInfo(PiPedalHost *pHost, const Lv2PluginInfo *plugin
|
|||||||
{
|
{
|
||||||
this->port_groups_.push_back(Lv2PluginUiPortGroup(portGroup.get()));
|
this->port_groups_.push_back(Lv2PluginUiPortGroup(portGroup.get()));
|
||||||
}
|
}
|
||||||
|
auto &piPedalUI = plugin->piPedalUI();
|
||||||
|
|
||||||
|
if (piPedalUI)
|
||||||
|
{
|
||||||
|
this->fileProperties_ = piPedalUI->fileProperties();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Lv2PluginClass> PiPedalHost::GetLv2PluginClass() const
|
std::shared_ptr<Lv2PluginClass> PiPedalHost::GetLv2PluginClass() const
|
||||||
@@ -1018,7 +1007,7 @@ std::vector<ControlValue> PiPedalHost::LoadFactoryPluginPreset(
|
|||||||
|
|
||||||
const LilvPlugins *plugins = lilv_world_get_all_plugins(this->pWorld);
|
const LilvPlugins *plugins = lilv_world_get_all_plugins(this->pWorld);
|
||||||
|
|
||||||
NodeAutoFree uriNode = lilv_new_uri(pWorld, pedalBoardItem->uri().c_str());
|
AutoLilvNode uriNode = lilv_new_uri(pWorld, pedalBoardItem->uri().c_str());
|
||||||
|
|
||||||
lilv_world_load_resource(pWorld, uriNode);
|
lilv_world_load_resource(pWorld, uriNode);
|
||||||
|
|
||||||
@@ -1040,7 +1029,7 @@ std::vector<ControlValue> PiPedalHost::LoadFactoryPluginPreset(
|
|||||||
|
|
||||||
/*********************************/
|
/*********************************/
|
||||||
|
|
||||||
// NodeAutoFree uriNode = lilv_new_uri(pWorld, presetUri.c_str());
|
// AutoLilvNode uriNode = lilv_new_uri(pWorld, presetUri.c_str());
|
||||||
|
|
||||||
auto lilvState = lilv_state_new_from_world(pWorld, GetLv2UridMap(), preset);
|
auto lilvState = lilv_state_new_from_world(pWorld, GetLv2UridMap(), preset);
|
||||||
|
|
||||||
@@ -1091,7 +1080,7 @@ PluginPresets PiPedalHost::GetFactoryPluginPresets(const std::string &pluginUri)
|
|||||||
{
|
{
|
||||||
const LilvPlugins *plugins = lilv_world_get_all_plugins(this->pWorld);
|
const LilvPlugins *plugins = lilv_world_get_all_plugins(this->pWorld);
|
||||||
|
|
||||||
NodeAutoFree uriNode = lilv_new_uri(pWorld, pluginUri.c_str());
|
AutoLilvNode uriNode = lilv_new_uri(pWorld, pluginUri.c_str());
|
||||||
|
|
||||||
lilv_world_load_resource(pWorld, uriNode);
|
lilv_world_load_resource(pWorld, uriNode);
|
||||||
|
|
||||||
@@ -1165,13 +1154,19 @@ Lv2PortGroup::Lv2PortGroup(PiPedalHost *lv2Host, const std::string &groupUri)
|
|||||||
LilvWorld *pWorld = lv2Host->pWorld;
|
LilvWorld *pWorld = lv2Host->pWorld;
|
||||||
|
|
||||||
this->uri_ = groupUri;
|
this->uri_ = groupUri;
|
||||||
NodeAutoFree uri = lilv_new_uri(pWorld, groupUri.c_str());
|
AutoLilvNode uri = lilv_new_uri(pWorld, groupUri.c_str());
|
||||||
LilvNode *symbolNode = lilv_world_get(pWorld, uri, lv2Host->lilvUris.symbolUri, nullptr);
|
LilvNode *symbolNode = lilv_world_get(pWorld, uri, lv2Host->lilvUris.symbolUri, nullptr);
|
||||||
symbol_ = nodeAsString(symbolNode);
|
symbol_ = nodeAsString(symbolNode);
|
||||||
LilvNode *nameNode = lilv_world_get(pWorld, uri, lv2Host->lilvUris.nameUri, nullptr);
|
LilvNode *nameNode = lilv_world_get(pWorld, uri, lv2Host->lilvUris.nameUri, nullptr);
|
||||||
name_ = nodeAsString(nameNode);
|
name_ = nodeAsString(nameNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PiPedalHostLogError(const std::string&errror)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#define MAP_REF(class, name) \
|
#define MAP_REF(class, name) \
|
||||||
json_map::reference(#name, &class ::name##_)
|
json_map::reference(#name, &class ::name##_)
|
||||||
|
|
||||||
@@ -1188,7 +1183,9 @@ json_map::storage_type<Lv2ScalePoint> Lv2ScalePoint::jmap{{
|
|||||||
json_map::reference("label", &Lv2ScalePoint::label_),
|
json_map::reference("label", &Lv2ScalePoint::label_),
|
||||||
}};
|
}};
|
||||||
|
|
||||||
json_map::storage_type<Lv2PortInfo> Lv2PortInfo::jmap{{json_map::reference("index", &Lv2PortInfo::index_),
|
json_map::storage_type<Lv2PortInfo> Lv2PortInfo::jmap{
|
||||||
|
{json_map::reference("index", &Lv2PortInfo::index_),
|
||||||
|
|
||||||
json_map::reference("symbol", &Lv2PortInfo::symbol_),
|
json_map::reference("symbol", &Lv2PortInfo::symbol_),
|
||||||
json_map::reference("index", &Lv2PortInfo::index_),
|
json_map::reference("index", &Lv2PortInfo::index_),
|
||||||
json_map::reference("name", &Lv2PortInfo::name_),
|
json_map::reference("name", &Lv2PortInfo::name_),
|
||||||
@@ -1308,5 +1305,5 @@ json_map::storage_type<Lv2PluginUiInfo> Lv2PluginUiInfo::jmap{{
|
|||||||
json_map::reference("controls", &Lv2PluginUiInfo::controls_),
|
json_map::reference("controls", &Lv2PluginUiInfo::controls_),
|
||||||
json_map::reference("port_groups", &Lv2PluginUiInfo::port_groups_),
|
json_map::reference("port_groups", &Lv2PluginUiInfo::port_groups_),
|
||||||
json_map::reference("is_vst3", &Lv2PluginUiInfo::is_vst3_),
|
json_map::reference("is_vst3", &Lv2PluginUiInfo::is_vst3_),
|
||||||
|
json_map::reference("fileProperties",&Lv2PluginUiInfo::fileProperties_)
|
||||||
}};
|
}};
|
||||||
|
|||||||
+164
-134
@@ -38,10 +38,11 @@
|
|||||||
|
|
||||||
#include "IEffect.hpp"
|
#include "IEffect.hpp"
|
||||||
#include "PiPedalConfiguration.hpp"
|
#include "PiPedalConfiguration.hpp"
|
||||||
|
#include "AutoLilvNode.hpp"
|
||||||
|
#include "PiPedalUI.hpp"
|
||||||
|
|
||||||
|
namespace pipedal
|
||||||
namespace pipedal {
|
{
|
||||||
|
|
||||||
|
|
||||||
// forward declarations
|
// forward declarations
|
||||||
class Lv2Effect;
|
class Lv2Effect;
|
||||||
@@ -52,25 +53,37 @@ class JackChannelSelection;
|
|||||||
|
|
||||||
#ifndef LV2_PROPERTY_GETSET
|
#ifndef LV2_PROPERTY_GETSET
|
||||||
#define LV2_PROPERTY_GETSET(name) \
|
#define LV2_PROPERTY_GETSET(name) \
|
||||||
const decltype(name##_) & name() const { return name##_; }; \
|
const decltype(name##_) &name() const \
|
||||||
decltype(name##_) & name() { return name##_; }; \
|
{ \
|
||||||
void name(const decltype(name##_) &value) { name##_ = value; };
|
return name##_; \
|
||||||
|
}; \
|
||||||
|
decltype(name##_) &name() \
|
||||||
|
{ \
|
||||||
|
return name##_; \
|
||||||
|
}; \
|
||||||
|
void name(const decltype(name##_) &value) \
|
||||||
|
{ \
|
||||||
|
name##_ = value; \
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#ifndef LV2_PROPERTY_GETSET_SCALAR
|
#ifndef LV2_PROPERTY_GETSET_SCALAR
|
||||||
#define LV2_PROPERTY_GETSET_SCALAR(name) \
|
#define LV2_PROPERTY_GETSET_SCALAR(name) \
|
||||||
decltype(name##_) name() const { return name##_;}; \
|
decltype(name##_) name() const \
|
||||||
void name(decltype(name##_) value) { name##_ = value; };
|
{ \
|
||||||
|
return name##_; \
|
||||||
|
}; \
|
||||||
|
void name(decltype(name##_) value) \
|
||||||
|
{ \
|
||||||
|
name##_ = value; \
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
class Lv2PluginClass
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
class Lv2PluginClass {
|
|
||||||
public:
|
public:
|
||||||
friend class PiPedalHost;
|
friend class PiPedalHost;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Lv2PluginClass *parent_ = nullptr; // NOT SERIALIZED!
|
Lv2PluginClass *parent_ = nullptr; // NOT SERIALIZED!
|
||||||
std::string parent_uri_;
|
std::string parent_uri_;
|
||||||
@@ -79,18 +92,17 @@ private:
|
|||||||
PluginType plugin_type_;
|
PluginType plugin_type_;
|
||||||
std::vector<std::shared_ptr<Lv2PluginClass>> children_;
|
std::vector<std::shared_ptr<Lv2PluginClass>> children_;
|
||||||
|
|
||||||
|
|
||||||
friend class ::pipedal::PiPedalHost;
|
friend class ::pipedal::PiPedalHost;
|
||||||
// hide copy constructor.
|
// hide copy constructor.
|
||||||
Lv2PluginClass(const Lv2PluginClass &other)
|
Lv2PluginClass(const Lv2PluginClass &other)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
void set_parent(std::shared_ptr<Lv2PluginClass> &parent)
|
void set_parent(std::shared_ptr<Lv2PluginClass> &parent)
|
||||||
{
|
{
|
||||||
this->parent_ = parent.get();
|
this->parent_ = parent.get();
|
||||||
}
|
}
|
||||||
void add_child(std::shared_ptr<Lv2PluginClass> &child) {
|
void add_child(std::shared_ptr<Lv2PluginClass> &child)
|
||||||
|
{
|
||||||
for (size_t i = 0; i < children_.size(); ++i)
|
for (size_t i = 0; i < children_.size(); ++i)
|
||||||
{
|
{
|
||||||
if (children_[i]->uri_ == child->uri_)
|
if (children_[i]->uri_ == child->uri_)
|
||||||
@@ -100,15 +112,12 @@ private:
|
|||||||
}
|
}
|
||||||
children_.push_back(child);
|
children_.push_back(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Lv2PluginClass(
|
Lv2PluginClass(
|
||||||
const char *display_name, const char *uri, const char *parent_uri)
|
const char *display_name, const char *uri, const char *parent_uri)
|
||||||
: parent_uri_(parent_uri)
|
: parent_uri_(parent_uri), display_name_(display_name), uri_(uri), plugin_type_(uri_to_plugin_type(uri))
|
||||||
, display_name_(display_name)
|
|
||||||
, uri_(uri)
|
|
||||||
, plugin_type_(uri_to_plugin_type(uri))
|
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
Lv2PluginClass() {}
|
Lv2PluginClass() {}
|
||||||
LV2_PROPERTY_GETSET(uri)
|
LV2_PROPERTY_GETSET(uri)
|
||||||
@@ -124,20 +133,19 @@ public:
|
|||||||
bool is_a(const std::string &classUri) const;
|
bool is_a(const std::string &classUri) const;
|
||||||
|
|
||||||
static json_map::storage_type<Lv2PluginClass> jmap;
|
static json_map::storage_type<Lv2PluginClass> jmap;
|
||||||
|
|
||||||
};
|
};
|
||||||
class Lv2PluginClasses {
|
class Lv2PluginClasses
|
||||||
|
{
|
||||||
private:
|
private:
|
||||||
std::vector<std::string> classes_;
|
std::vector<std::string> classes_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Lv2PluginClasses()
|
Lv2PluginClasses()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
Lv2PluginClasses(std::vector<std::string> classes)
|
Lv2PluginClasses(std::vector<std::string> classes)
|
||||||
: classes_(classes)
|
: classes_(classes)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
const std::vector<std::string> &classes() const
|
const std::vector<std::string> &classes() const
|
||||||
{
|
{
|
||||||
@@ -146,21 +154,19 @@ public:
|
|||||||
bool is_a(PiPedalHost *lv2Plugins, const char *classUri) const;
|
bool is_a(PiPedalHost *lv2Plugins, const char *classUri) const;
|
||||||
|
|
||||||
static json_map::storage_type<Lv2PluginClasses> jmap;
|
static json_map::storage_type<Lv2PluginClasses> jmap;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Lv2ScalePoint
|
||||||
class Lv2ScalePoint {
|
{
|
||||||
private:
|
private:
|
||||||
float value_;
|
float value_;
|
||||||
std::string label_;
|
std::string label_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Lv2ScalePoint() {}
|
Lv2ScalePoint() {}
|
||||||
Lv2ScalePoint(float value, std::string label)
|
Lv2ScalePoint(float value, std::string label)
|
||||||
: value_(value)
|
: value_(value), label_(label)
|
||||||
, label_(label)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LV2_PROPERTY_GETSET_SCALAR(value);
|
LV2_PROPERTY_GETSET_SCALAR(value);
|
||||||
@@ -169,17 +175,19 @@ public:
|
|||||||
static json_map::storage_type<Lv2ScalePoint> jmap;
|
static json_map::storage_type<Lv2ScalePoint> jmap;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class Lv2BufferType
|
||||||
enum class Lv2BufferType {
|
{
|
||||||
None,
|
None,
|
||||||
Event,
|
Event,
|
||||||
Sequence,
|
Sequence,
|
||||||
Unknown
|
Unknown
|
||||||
};
|
};
|
||||||
|
|
||||||
class Lv2PortInfo {
|
class Lv2PortInfo
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
Lv2PortInfo(PiPedalHost *lv2Host, const LilvPlugin *pPlugin, const LilvPort *pPort);
|
Lv2PortInfo(PiPedalHost *lv2Host, const LilvPlugin *pPlugin, const LilvPort *pPort);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class Lv2PluginInfo;
|
friend class Lv2PluginInfo;
|
||||||
|
|
||||||
@@ -214,24 +222,30 @@ private:
|
|||||||
std::string designation_;
|
std::string designation_;
|
||||||
Units units_ = Units::none;
|
Units units_ = Units::none;
|
||||||
std::string comment_;
|
std::string comment_;
|
||||||
|
PiPedalUI::ptr piPedalUI_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool IsSwitch() const {
|
bool IsSwitch() const
|
||||||
return min_value_ == 0 && max_value_ == 1
|
{
|
||||||
&& (integer_property_ || toggled_property_ || enumeration_property_);
|
return min_value_ == 0 && max_value_ == 1 && (integer_property_ || toggled_property_ || enumeration_property_);
|
||||||
}
|
}
|
||||||
|
const PiPedalUI::ptr &piPedalUI() const { return piPedalUI_; }
|
||||||
float rangeToValue(float range) const
|
float rangeToValue(float range) const
|
||||||
{
|
{
|
||||||
float value;
|
float value;
|
||||||
if (is_logarithmic_)
|
if (is_logarithmic_)
|
||||||
{
|
{
|
||||||
value = std::pow(max_value_ / min_value_, range) * min_value_;
|
value = std::pow(max_value_ / min_value_, range) * min_value_;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
value = (max_value_ - min_value_) * range + min_value_;
|
value = (max_value_ - min_value_) * range + min_value_;
|
||||||
}
|
}
|
||||||
if (integer_property_ || enumeration_property_)
|
if (integer_property_ || enumeration_property_)
|
||||||
{
|
{
|
||||||
value = std::round(value);
|
value = std::round(value);
|
||||||
} else if (range_steps_ >= 2)
|
}
|
||||||
|
else if (range_steps_ >= 2)
|
||||||
{
|
{
|
||||||
value = std::round(value * (range_steps_ - 1)) / (range_steps_ - 1);
|
value = std::round(value * (range_steps_ - 1)) / (range_steps_ - 1);
|
||||||
}
|
}
|
||||||
@@ -239,8 +253,10 @@ public:
|
|||||||
{
|
{
|
||||||
value = value == 0 ? 0 : max_value_;
|
value = value == 0 ? 0 : max_value_;
|
||||||
}
|
}
|
||||||
if (value > max_value_) value = max_value_;
|
if (value > max_value_)
|
||||||
if (value < min_value_) value = min_value_;
|
value = max_value_;
|
||||||
|
if (value < min_value_)
|
||||||
|
value = min_value_;
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
LV2_PROPERTY_GETSET(symbol);
|
LV2_PROPERTY_GETSET(symbol);
|
||||||
@@ -275,9 +291,12 @@ public:
|
|||||||
|
|
||||||
LV2_PROPERTY_GETSET(buffer_type);
|
LV2_PROPERTY_GETSET(buffer_type);
|
||||||
|
|
||||||
Lv2BufferType GetBufferType() {
|
Lv2BufferType GetBufferType()
|
||||||
if (buffer_type_ == "") return Lv2BufferType::None;
|
{
|
||||||
if (buffer_type_ == LV2_ATOM__Sequence) return Lv2BufferType::Sequence;
|
if (buffer_type_ == "")
|
||||||
|
return Lv2BufferType::None;
|
||||||
|
if (buffer_type_ == LV2_ATOM__Sequence)
|
||||||
|
return Lv2BufferType::Sequence;
|
||||||
return Lv2BufferType::Unknown;
|
return Lv2BufferType::Unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,10 +306,10 @@ public:
|
|||||||
bool is_a(PiPedalHost *lv2Plugins, const char *classUri);
|
bool is_a(PiPedalHost *lv2Plugins, const char *classUri);
|
||||||
|
|
||||||
static json_map::storage_type<Lv2PortInfo> jmap;
|
static json_map::storage_type<Lv2PortInfo> jmap;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Lv2PortGroup {
|
class Lv2PortGroup
|
||||||
|
{
|
||||||
private:
|
private:
|
||||||
std::string uri_;
|
std::string uri_;
|
||||||
std::string symbol_;
|
std::string symbol_;
|
||||||
@@ -307,12 +326,15 @@ public:
|
|||||||
static json_map::storage_type<Lv2PortGroup> jmap;
|
static json_map::storage_type<Lv2PortGroup> jmap;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Lv2PluginInfo {
|
class Lv2PluginInfo
|
||||||
|
{
|
||||||
private:
|
private:
|
||||||
friend class PiPedalHost;
|
friend class PiPedalHost;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Lv2PluginInfo(PiPedalHost*lv2Host,const LilvPlugin*);
|
Lv2PluginInfo(PiPedalHost *lv2Host, LilvWorld *pWorld, const LilvPlugin *);
|
||||||
Lv2PluginInfo() {}
|
Lv2PluginInfo() {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool HasFactoryPresets(PiPedalHost *lv2Host, const LilvPlugin *plugin);
|
bool HasFactoryPresets(PiPedalHost *lv2Host, const LilvPlugin *plugin);
|
||||||
std::string uri_;
|
std::string uri_;
|
||||||
@@ -331,6 +353,7 @@ private:
|
|||||||
std::vector<std::shared_ptr<Lv2PortInfo>> ports_;
|
std::vector<std::shared_ptr<Lv2PortInfo>> ports_;
|
||||||
std::vector<std::shared_ptr<Lv2PortGroup>> port_groups_;
|
std::vector<std::shared_ptr<Lv2PortGroup>> port_groups_;
|
||||||
bool is_valid_ = false;
|
bool is_valid_ = false;
|
||||||
|
PiPedalUI::ptr piPedalUI_;
|
||||||
|
|
||||||
bool IsSupportedFeature(const std::string &feature) const;
|
bool IsSupportedFeature(const std::string &feature) const;
|
||||||
|
|
||||||
@@ -349,6 +372,7 @@ public:
|
|||||||
LV2_PROPERTY_GETSET(is_valid)
|
LV2_PROPERTY_GETSET(is_valid)
|
||||||
LV2_PROPERTY_GETSET(port_groups)
|
LV2_PROPERTY_GETSET(port_groups)
|
||||||
LV2_PROPERTY_GETSET(has_factory_presets)
|
LV2_PROPERTY_GETSET(has_factory_presets)
|
||||||
|
LV2_PROPERTY_GETSET(piPedalUI)
|
||||||
|
|
||||||
const Lv2PortInfo &getPort(const std::string &symbol)
|
const Lv2PortInfo &getPort(const std::string &symbol)
|
||||||
{
|
{
|
||||||
@@ -365,11 +389,13 @@ public:
|
|||||||
{
|
{
|
||||||
for (int i = 0; i < extensions_.size(); ++i)
|
for (int i = 0; i < extensions_.size(); ++i)
|
||||||
{
|
{
|
||||||
if (extensions_[i] == uri) return true;
|
if (extensions_[i] == uri)
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool hasCvPorts() const {
|
bool hasCvPorts() const
|
||||||
|
{
|
||||||
for (size_t i = 0; i < ports_.size(); ++i)
|
for (size_t i = 0; i < ports_.size(); ++i)
|
||||||
{
|
{
|
||||||
if (ports_[i]->is_cv_port())
|
if (ports_[i]->is_cv_port())
|
||||||
@@ -379,7 +405,8 @@ public:
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool hasMidiInput() const {
|
bool hasMidiInput() const
|
||||||
|
{
|
||||||
for (size_t i = 0; i < ports_.size(); ++i)
|
for (size_t i = 0; i < ports_.size(); ++i)
|
||||||
{
|
{
|
||||||
if (ports_[i]->is_atom_port() && ports_[i]->supports_midi() && ports_[i]->is_input())
|
if (ports_[i]->is_atom_port() && ports_[i]->supports_midi() && ports_[i]->is_input())
|
||||||
@@ -388,7 +415,8 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool hasMidiOutput() const {
|
bool hasMidiOutput() const
|
||||||
|
{
|
||||||
for (size_t i = 0; i < ports_.size(); ++i)
|
for (size_t i = 0; i < ports_.size(); ++i)
|
||||||
{
|
{
|
||||||
if (ports_[i]->is_atom_port() && ports_[i]->supports_midi() && ports_[i]->is_output())
|
if (ports_[i]->is_atom_port() && ports_[i]->supports_midi() && ports_[i]->is_output())
|
||||||
@@ -398,13 +426,10 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
virtual ~Lv2PluginInfo();
|
virtual ~Lv2PluginInfo();
|
||||||
|
|
||||||
static json_map::storage_type<Lv2PluginInfo> jmap;
|
static json_map::storage_type<Lv2PluginInfo> jmap;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Lv2PluginUiPortGroup
|
class Lv2PluginUiPortGroup
|
||||||
@@ -432,7 +457,6 @@ public:
|
|||||||
const std::string &parent_group, int32_t programListId)
|
const std::string &parent_group, int32_t programListId)
|
||||||
: symbol_(symbol), name_(name), parent_group_(parent_group), program_list_id_(programListId)
|
: symbol_(symbol), name_(name), parent_group_(parent_group), program_list_id_(programListId)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -531,6 +555,7 @@ public:
|
|||||||
|
|
||||||
std::vector<Lv2PluginUiControlPort> controls_;
|
std::vector<Lv2PluginUiControlPort> controls_;
|
||||||
std::vector<Lv2PluginUiPortGroup> port_groups_;
|
std::vector<Lv2PluginUiPortGroup> port_groups_;
|
||||||
|
std::vector<PiPedalFileProperty::ptr> fileProperties_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LV2_PROPERTY_GETSET(uri)
|
LV2_PROPERTY_GETSET(uri)
|
||||||
@@ -547,13 +572,13 @@ public:
|
|||||||
LV2_PROPERTY_GETSET(controls)
|
LV2_PROPERTY_GETSET(controls)
|
||||||
LV2_PROPERTY_GETSET(port_groups)
|
LV2_PROPERTY_GETSET(port_groups)
|
||||||
LV2_PROPERTY_GETSET_SCALAR(is_vst3)
|
LV2_PROPERTY_GETSET_SCALAR(is_vst3)
|
||||||
|
LV2_PROPERTY_GETSET(fileProperties)
|
||||||
|
|
||||||
static json_map::storage_type<Lv2PluginUiInfo> jmap;
|
static json_map::storage_type<Lv2PluginUiInfo> jmap;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class IHost
|
||||||
|
{
|
||||||
class IHost {
|
|
||||||
public:
|
public:
|
||||||
virtual LilvWorld *getWorld() = 0;
|
virtual LilvWorld *getWorld() = 0;
|
||||||
virtual LV2_URID_Map *GetLv2UridMap() = 0;
|
virtual LV2_URID_Map *GetLv2UridMap() = 0;
|
||||||
@@ -571,96 +596,83 @@ public:
|
|||||||
virtual int GetNumberOfOutputAudioChannels() const = 0;
|
virtual int GetNumberOfOutputAudioChannels() const = 0;
|
||||||
virtual std::shared_ptr<Lv2PluginInfo> GetPluginInfo(const std::string &uri) const = 0;
|
virtual std::shared_ptr<Lv2PluginInfo> GetPluginInfo(const std::string &uri) const = 0;
|
||||||
|
|
||||||
|
|
||||||
virtual IEffect *CreateEffect(PedalBoardItem &pedalBoard) = 0;
|
virtual IEffect *CreateEffect(PedalBoardItem &pedalBoard) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class LilvNodePtr {
|
|
||||||
protected:
|
|
||||||
LilvNode *node = nullptr;
|
|
||||||
LilvNodePtr(const LilvNodePtr&) {}; // no copy.
|
|
||||||
LilvNodePtr(const LilvNode*) {}; // const LilvNodes are owned by lilv by convention.
|
|
||||||
public:
|
|
||||||
LilvNodePtr() { }
|
|
||||||
LilvNodePtr(LilvNode*node) { this->node = node; }
|
|
||||||
~LilvNodePtr() { Free(); }
|
|
||||||
void Free() { if (node != nullptr) lilv_node_free(node); node = nullptr;}
|
|
||||||
|
|
||||||
operator const LilvNode *()
|
|
||||||
{
|
|
||||||
return this->node;
|
|
||||||
}
|
|
||||||
|
|
||||||
LilvNode **operator&()
|
|
||||||
{
|
|
||||||
return &(this->node);
|
|
||||||
}
|
|
||||||
operator bool()
|
|
||||||
{
|
|
||||||
return this->node != nullptr;
|
|
||||||
}
|
|
||||||
LilvNodePtr&operator=(LilvNode*node) { Free(); this->node = node; return *this;}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_VST3
|
#if ENABLE_VST3
|
||||||
#include "vst3/Vst3Host.hpp"
|
#include "vst3/Vst3Host.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace pipedal
|
||||||
|
{
|
||||||
|
|
||||||
namespace pipedal{
|
class PiPedalHost : private IHost
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PiPedalHost: private IHost {
|
|
||||||
private:
|
private:
|
||||||
#if ENABLE_VST3
|
#if ENABLE_VST3
|
||||||
Vst3Host::Ptr vst3Host;
|
Vst3Host::Ptr vst3Host;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
friend class pipedal::AutoLilvNode;
|
||||||
|
friend class pipedal::PiPedalUI;
|
||||||
static const char *RDFS_COMMENT_URI;
|
static const char *RDFS_COMMENT_URI;
|
||||||
class LilvUris {
|
public:
|
||||||
|
class LilvUris
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
void Initialize(LilvWorld *pWorld);
|
void Initialize(LilvWorld *pWorld);
|
||||||
void Free();
|
void Free();
|
||||||
|
|
||||||
LilvNodePtr rdfsComment;
|
AutoLilvNode rdfsComment;
|
||||||
LilvNodePtr logarithic_uri;
|
AutoLilvNode logarithic_uri;
|
||||||
LilvNodePtr display_priority_uri;
|
AutoLilvNode display_priority_uri;
|
||||||
LilvNodePtr range_steps_uri;
|
AutoLilvNode range_steps_uri;
|
||||||
LilvNodePtr integer_property_uri;
|
AutoLilvNode integer_property_uri;
|
||||||
LilvNodePtr enumeration_property_uri;
|
AutoLilvNode enumeration_property_uri;
|
||||||
LilvNodePtr toggle_property_uri;
|
AutoLilvNode toggle_property_uri;
|
||||||
LilvNodePtr not_on_gui_property_uri;
|
AutoLilvNode not_on_gui_property_uri;
|
||||||
LilvNodePtr midiEventNode;
|
AutoLilvNode midiEventNode;
|
||||||
LilvNodePtr designationNode;
|
AutoLilvNode designationNode;
|
||||||
LilvNodePtr portGroupUri;
|
AutoLilvNode portGroupUri;
|
||||||
LilvNodePtr unitsUri;
|
AutoLilvNode unitsUri;
|
||||||
LilvNodePtr bufferType_uri;
|
AutoLilvNode bufferType_uri;
|
||||||
LilvNodePtr pset_Preset;
|
AutoLilvNode pset_Preset;
|
||||||
LilvNodePtr rdfs_label;
|
AutoLilvNode rdfs_label;
|
||||||
LilvNodePtr symbolUri;
|
AutoLilvNode symbolUri;
|
||||||
LilvNodePtr nameUri;
|
AutoLilvNode nameUri;
|
||||||
|
|
||||||
LilvNodePtr time_Position;
|
AutoLilvNode lv2Core__name;
|
||||||
LilvNodePtr time_barBeat;
|
AutoLilvNode pipedalUI__ui;
|
||||||
LilvNodePtr time_beatsPerMinute;
|
AutoLilvNode pipedalUI__fileProperties;
|
||||||
LilvNodePtr time_speed;
|
AutoLilvNode pipedalUI__directory;
|
||||||
|
AutoLilvNode pipedalUI__patchProperty;
|
||||||
|
AutoLilvNode pipedalUI__defaultFile;
|
||||||
|
|
||||||
LilvNodePtr appliesTo;
|
AutoLilvNode pipedalUI__fileProperty;
|
||||||
LilvNodePtr isA;
|
|
||||||
|
|
||||||
|
AutoLilvNode pipedalUI__fileTypes;
|
||||||
|
AutoLilvNode pipedalUI__fileExtension;
|
||||||
|
|
||||||
|
AutoLilvNode pipedalUI__outputPorts;
|
||||||
|
AutoLilvNode pipedalUI__text;
|
||||||
|
|
||||||
|
AutoLilvNode time_Position;
|
||||||
|
AutoLilvNode time_barBeat;
|
||||||
|
AutoLilvNode time_beatsPerMinute;
|
||||||
|
AutoLilvNode time_speed;
|
||||||
|
|
||||||
|
AutoLilvNode appliesTo;
|
||||||
|
AutoLilvNode isA;
|
||||||
};
|
};
|
||||||
bool vst3Enabled = true;
|
|
||||||
|
|
||||||
LilvUris lilvUris;
|
LilvUris lilvUris;
|
||||||
|
|
||||||
LilvNode *get_comment(const std::string &uri);
|
private:
|
||||||
|
|
||||||
|
bool vst3Enabled = true;
|
||||||
|
|
||||||
|
LilvNode *get_comment(const std::string &uri);
|
||||||
|
|
||||||
size_t maxBufferSize = 1024;
|
size_t maxBufferSize = 1024;
|
||||||
size_t maxAtomBufferSize = 16 * 1024;
|
size_t maxAtomBufferSize = 16 * 1024;
|
||||||
@@ -685,7 +697,6 @@ private:
|
|||||||
LilvWorld *pWorld;
|
LilvWorld *pWorld;
|
||||||
void free_world();
|
void free_world();
|
||||||
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Lv2PluginInfo>> plugins_;
|
std::vector<std::shared_ptr<Lv2PluginInfo>> plugins_;
|
||||||
std::vector<Lv2PluginUiInfo> ui_plugins_;
|
std::vector<Lv2PluginUiInfo> ui_plugins_;
|
||||||
|
|
||||||
@@ -700,12 +711,31 @@ private:
|
|||||||
Lv2PluginClasses GetPluginPortClass(const LilvPlugin *lilvPlugin, const LilvPort *lilvPort);
|
Lv2PluginClasses GetPluginPortClass(const LilvPlugin *lilvPlugin, const LilvPort *lilvPort);
|
||||||
|
|
||||||
bool classesLoaded = false;
|
bool classesLoaded = false;
|
||||||
|
|
||||||
private:
|
|
||||||
// IHost implementation
|
// IHost implementation
|
||||||
virtual LilvWorld*getWorld() {
|
|
||||||
|
public:
|
||||||
|
void LogError(const std::string&message)
|
||||||
|
{
|
||||||
|
logFeature.LogError("%s",message.c_str());
|
||||||
|
}
|
||||||
|
void LogWarning(const std::string&message)
|
||||||
|
{
|
||||||
|
logFeature.LogWarning("%s",message.c_str());
|
||||||
|
}
|
||||||
|
void LogNote(const std::string&message)
|
||||||
|
{
|
||||||
|
logFeature.LogNote("%s",message.c_str());
|
||||||
|
}
|
||||||
|
void LogTrace(const std::string&message)
|
||||||
|
{
|
||||||
|
logFeature.LogTrace("%s",message.c_str());
|
||||||
|
}
|
||||||
|
virtual LilvWorld *getWorld()
|
||||||
|
{
|
||||||
return pWorld;
|
return pWorld;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
virtual void SetMaxAudioBufferSize(size_t size) { maxBufferSize = size; }
|
virtual void SetMaxAudioBufferSize(size_t size) { maxBufferSize = size; }
|
||||||
virtual size_t GetMaxAudioBufferSize() const { return maxBufferSize; }
|
virtual size_t GetMaxAudioBufferSize() const { return maxBufferSize; }
|
||||||
virtual size_t GetAtomBufferSize() const { return maxAtomBufferSize; }
|
virtual size_t GetAtomBufferSize() const { return maxAtomBufferSize; }
|
||||||
@@ -713,7 +743,8 @@ private:
|
|||||||
virtual int GetNumberOfInputAudioChannels() const { return numberOfAudioInputChannels; }
|
virtual int GetNumberOfInputAudioChannels() const { return numberOfAudioInputChannels; }
|
||||||
virtual int GetNumberOfOutputAudioChannels() const { return numberOfAudioOutputChannels; }
|
virtual int GetNumberOfOutputAudioChannels() const { return numberOfAudioOutputChannels; }
|
||||||
virtual LV2_Feature *const *GetLv2Features() const { return this->lv2Features; }
|
virtual LV2_Feature *const *GetLv2Features() const { return this->lv2Features; }
|
||||||
virtual LV2_URID_Map* GetLv2UridMap() {
|
virtual LV2_URID_Map *GetLv2UridMap()
|
||||||
|
{
|
||||||
return this->mapFeature.GetMap();
|
return this->mapFeature.GetMap();
|
||||||
}
|
}
|
||||||
static void PortValueCallback(const char *symbol, void *user_data, const void *value, uint32_t size, uint32_t type);
|
static void PortValueCallback(const char *symbol, void *user_data, const void *value, uint32_t size, uint32_t type);
|
||||||
@@ -737,7 +768,8 @@ public:
|
|||||||
{
|
{
|
||||||
this->sampleRate = sampleRate;
|
this->sampleRate = sampleRate;
|
||||||
}
|
}
|
||||||
double GetSampleRate() const {
|
double GetSampleRate() const
|
||||||
|
{
|
||||||
return sampleRate;
|
return sampleRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -757,25 +789,23 @@ public:
|
|||||||
|
|
||||||
virtual std::shared_ptr<Lv2PluginInfo> GetPluginInfo(const std::string &uri) const;
|
virtual std::shared_ptr<Lv2PluginInfo> GetPluginInfo(const std::string &uri) const;
|
||||||
|
|
||||||
|
|
||||||
static constexpr const char *DEFAULT_LV2_PATH = "/usr/lib/lv2";
|
static constexpr const char *DEFAULT_LV2_PATH = "/usr/lib/lv2";
|
||||||
|
|
||||||
|
|
||||||
void LoadPluginClassesFromJson(std::filesystem::path jsonFile);
|
void LoadPluginClassesFromJson(std::filesystem::path jsonFile);
|
||||||
void Load(const char *lv2Path = PiPedalHost::DEFAULT_LV2_PATH);
|
void Load(const char *lv2Path = PiPedalHost::DEFAULT_LV2_PATH);
|
||||||
|
|
||||||
virtual LV2_URID GetLv2Urid(const char*uri) {
|
virtual LV2_URID GetLv2Urid(const char *uri)
|
||||||
|
{
|
||||||
return this->mapFeature.GetUrid(uri);
|
return this->mapFeature.GetUrid(uri);
|
||||||
}
|
}
|
||||||
virtual std::string Lv2UriudToString(LV2_URID urid) {
|
virtual std::string Lv2UriudToString(LV2_URID urid)
|
||||||
|
{
|
||||||
return this->mapFeature.UridToString(urid);
|
return this->mapFeature.UridToString(urid);
|
||||||
}
|
}
|
||||||
|
|
||||||
PluginPresets GetFactoryPluginPresets(const std::string &pluginUri);
|
PluginPresets GetFactoryPluginPresets(const std::string &pluginUri);
|
||||||
std::vector<ControlValue> LoadFactoryPluginPreset(PedalBoardItem *pedalBoardItem,
|
std::vector<ControlValue> LoadFactoryPluginPreset(PedalBoardItem *pedalBoardItem,
|
||||||
const std::string &presetUri);
|
const std::string &presetUri);
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#undef LV2_PROPERTY_GETSET
|
#undef LV2_PROPERTY_GETSET
|
||||||
|
|||||||
@@ -1467,6 +1467,21 @@ void PiPedalModel::UpdateDefaults(PedalBoardItem *pedalBoardItem)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (pPlugin->piPedalUI())
|
||||||
|
{
|
||||||
|
auto&piPedalUi = pPlugin->piPedalUI();
|
||||||
|
for (auto &fileProperty : piPedalUi->fileProperties())
|
||||||
|
{
|
||||||
|
PropertyValue *pValue = pedalBoardItem->GetPropertyValue(fileProperty->patchProperty());
|
||||||
|
if (pValue == nullptr)
|
||||||
|
{
|
||||||
|
// missing? set it to default value
|
||||||
|
pedalBoardItem->propertyValues().push_back(
|
||||||
|
PropertyValue(fileProperty->patchProperty(),fileProperty->defaultFile())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < pedalBoardItem->topChain().size(); ++i)
|
for (size_t i = 0; i < pedalBoardItem->topChain().size(); ++i)
|
||||||
{
|
{
|
||||||
@@ -1717,3 +1732,8 @@ void PiPedalModel::SetSystemMidiBindings(std::vector<MidiBinding> &bindings)
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> PiPedalModel::GetFileList(const PiPedalFileProperty&fileProperty)
|
||||||
|
{
|
||||||
|
return this->storage.GetFileList(PiPedalFilesProperty&fileProperty);
|
||||||
|
}
|
||||||
|
|||||||
@@ -281,6 +281,8 @@ namespace pipedal
|
|||||||
|
|
||||||
std::map<std::string, bool> GetFavorites() const;
|
std::map<std::string, bool> GetFavorites() const;
|
||||||
void SetFavorites(const std::map<std::string, bool> &favorites);
|
void SetFavorites(const std::map<std::string, bool> &favorites);
|
||||||
|
|
||||||
|
std::vector<std::string> GetFileList(const PiPedalFileProperty&fileProperty);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace pipedal.
|
} // namespace pipedal.
|
||||||
@@ -1246,6 +1246,12 @@ public:
|
|||||||
std::vector<MidiBinding> bindings = this->model.GetSystemMidiBidings();
|
std::vector<MidiBinding> bindings = this->model.GetSystemMidiBidings();
|
||||||
this->Reply(replyTo,"getSystemMidiBindings",bindings);
|
this->Reply(replyTo,"getSystemMidiBindings",bindings);
|
||||||
}
|
}
|
||||||
|
else if (message == "requestFileList")
|
||||||
|
{
|
||||||
|
PiPedalFileProperty fileProperty;
|
||||||
|
std::vector<std::string> list = this->model.GetFileList(fileProperty);
|
||||||
|
this->Reply(replyTo,"requestFileList",list);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Lv2Log::error("Unknown message received: %s", message.c_str());
|
Lv2Log::error("Unknown message received: %s", message.c_str());
|
||||||
|
|||||||
@@ -0,0 +1,170 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 Robin E. R. Davies
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "PiPedalUI.hpp"
|
||||||
|
#include "PiPedalHost.hpp"
|
||||||
|
|
||||||
|
using namespace pipedal;
|
||||||
|
|
||||||
|
PiPedalUI::PiPedalUI(PiPedalHost *pHost, const LilvNode *uiNode)
|
||||||
|
{
|
||||||
|
auto pWorld = pHost->getWorld();
|
||||||
|
|
||||||
|
LilvNodes *fileNodes = lilv_world_find_nodes(pWorld, uiNode, pHost->lilvUris.pipedalUI__fileProperties, nullptr);
|
||||||
|
LILV_FOREACH(nodes, i, fileNodes)
|
||||||
|
{
|
||||||
|
const LilvNode *fileNode = lilv_nodes_get(fileNodes, i);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PiPedalFileProperty::ptr fileUI = std::make_shared<PiPedalFileProperty>(pHost, fileNode);
|
||||||
|
this->fileProperites_.push_back(std::move(fileUI));
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
pHost->LogError(e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lilv_nodes_free(fileNodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
PiPedalFileType::PiPedalFileType(PiPedalHost*pHost, const LilvNode*node) {
|
||||||
|
auto pWorld = pHost->getWorld();
|
||||||
|
|
||||||
|
AutoLilvNode name = lilv_world_get(
|
||||||
|
pWorld,
|
||||||
|
node,
|
||||||
|
pHost->lilvUris.lv2Core__name,
|
||||||
|
nullptr);
|
||||||
|
if (name)
|
||||||
|
{
|
||||||
|
this->name_ = name.AsString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::logic_error("pipedal_ui:fileType is missing name property.");
|
||||||
|
}
|
||||||
|
AutoLilvNode fileExtension = lilv_world_get(
|
||||||
|
pWorld,
|
||||||
|
node,
|
||||||
|
pHost->lilvUris.pipedalUI__fileExtension,
|
||||||
|
nullptr);
|
||||||
|
if (fileExtension)
|
||||||
|
{
|
||||||
|
this->fileExtension_ = fileExtension.AsString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::logic_error("pipedal_ui:fileType is missing fileExtension property.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
PiPedalFileProperty::PiPedalFileProperty(PiPedalHost *pHost, const LilvNode *node)
|
||||||
|
{
|
||||||
|
auto pWorld = pHost->getWorld();
|
||||||
|
|
||||||
|
AutoLilvNode name = lilv_world_get(
|
||||||
|
pWorld,
|
||||||
|
node,
|
||||||
|
pHost->lilvUris.lv2Core__name,
|
||||||
|
nullptr);
|
||||||
|
if (name)
|
||||||
|
{
|
||||||
|
this->name_ = name.AsString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->name_ = "File";
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoLilvNode directory = lilv_world_get(
|
||||||
|
pWorld,
|
||||||
|
node,
|
||||||
|
pHost->lilvUris.pipedalUI__directory,
|
||||||
|
nullptr);
|
||||||
|
if (directory)
|
||||||
|
{
|
||||||
|
this->directory_ = name.AsString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::logic_error("PiPedal FileProperty is missing a pipedalui:directory value.");
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoLilvNode patchProperty = lilv_world_get(
|
||||||
|
pWorld,
|
||||||
|
node,
|
||||||
|
pHost->lilvUris.pipedalUI__patchProperty,
|
||||||
|
nullptr);
|
||||||
|
if (patchProperty)
|
||||||
|
{
|
||||||
|
this->patchProperty_ = patchProperty.AsUri();
|
||||||
|
} else {
|
||||||
|
throw std::logic_error("PiPedal FileProperty is missing pipedalui:patchProperty value.");
|
||||||
|
}
|
||||||
|
AutoLilvNode defaultFile = lilv_world_get(
|
||||||
|
pWorld,
|
||||||
|
node,
|
||||||
|
pHost->lilvUris.pipedalUI__defaultFile,
|
||||||
|
nullptr);
|
||||||
|
this->defaultFile_ = defaultFile.AsString();
|
||||||
|
|
||||||
|
|
||||||
|
this->fileTypes_ = PiPedalFileType::GetArray(pHost,node,pHost->lilvUris.pipedalUI__fileTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<PiPedalFileType> PiPedalFileType::GetArray(PiPedalHost*pHost, const LilvNode*node,const LilvNode*uri)
|
||||||
|
{
|
||||||
|
std::vector<PiPedalFileType> result;
|
||||||
|
LilvWorld* pWorld = pHost->getWorld();
|
||||||
|
|
||||||
|
LilvNodes *fileTypeNodes = lilv_world_find_nodes(pWorld, node, pHost->lilvUris.pipedalUI__fileTypes, nullptr);
|
||||||
|
LILV_FOREACH(nodes, i, fileTypeNodes)
|
||||||
|
{
|
||||||
|
const LilvNode *fileTypeNode = lilv_nodes_get(fileTypeNodes, i);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PiPedalFileType fileType = PiPedalFileType(pHost, fileTypeNode);
|
||||||
|
result.push_back(std::move(fileType));
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
pHost->LogError(e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lilv_nodes_free(fileTypeNodes);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
JSON_MAP_BEGIN(PiPedalFileType)
|
||||||
|
JSON_MAP_REFERENCE(PiPedalFileType,name)
|
||||||
|
JSON_MAP_REFERENCE(PiPedalFileType,fileExtension)
|
||||||
|
JSON_MAP_END()
|
||||||
|
|
||||||
|
JSON_MAP_BEGIN(PiPedalFileProperty)
|
||||||
|
JSON_MAP_REFERENCE(PiPedalFileProperty,patchProperty)
|
||||||
|
JSON_MAP_REFERENCE(PiPedalFileProperty,name)
|
||||||
|
JSON_MAP_REFERENCE(PiPedalFileProperty,defaultFile)
|
||||||
|
JSON_MAP_REFERENCE(PiPedalFileProperty,fileTypes)
|
||||||
|
JSON_MAP_END()
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 Robin E. R. Davies
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* 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 <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <lilv/lilv.h>
|
||||||
|
#include "json.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
#define PIPEDAL_UI "http://github.com/rerdavies/pipedal/ui"
|
||||||
|
#define PIPEDAL_UI_PREFIX PIPEDAL_UI "#"
|
||||||
|
|
||||||
|
#define PIPEDAL_UI__ui PIPEDAL_UI_PREFIX "ui"
|
||||||
|
|
||||||
|
#define PIPEDAL_UI__fileProperties PIPEDAL_UI_PREFIX "fileProperties"
|
||||||
|
|
||||||
|
#define PIPEDAL_UI__fileProperty PIPEDAL_UI_PREFIX "fileProperty"
|
||||||
|
#define PIPEDAL_UI__defaultFile PIPEDAL_UI_PREFIX "defaultFile"
|
||||||
|
#define PIPEDAL_UI__patchProperty PIPEDAL_UI_PREFIX "patchProperty"
|
||||||
|
#define PIPEDAL_UI__directory PIPEDAL_UI_PREFIX "directory"
|
||||||
|
#define PIPEDAL_UI__fileTypes PIPEDAL_UI_PREFIX "fileTypes"
|
||||||
|
|
||||||
|
#define PIPEDAL_UI__fileType PIPEDAL_UI_PREFIX "fileType"
|
||||||
|
#define PIPEDAL_UI__fileExtension PIPEDAL_UI_PREFIX "fileExtension"
|
||||||
|
|
||||||
|
#define PIPEDAL_UI__outputPorts PIPEDAL_UI_PREFIX "outputPorts"
|
||||||
|
#define PIPEDAL_UI__text PIPEDAL_UI_PREFIX "text"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace pipedal {
|
||||||
|
|
||||||
|
class PiPedalHost;
|
||||||
|
|
||||||
|
class PiPedalFileType {
|
||||||
|
private:
|
||||||
|
std::string name_;
|
||||||
|
std::string fileExtension_;
|
||||||
|
public:
|
||||||
|
PiPedalFileType() { }
|
||||||
|
PiPedalFileType(PiPedalHost*pHost, const LilvNode*node);
|
||||||
|
|
||||||
|
static std::vector<PiPedalFileType> GetArray(PiPedalHost*pHost, const LilvNode*node,const LilvNode*uri);
|
||||||
|
|
||||||
|
const std::string& name() const { return name_;}
|
||||||
|
const std::string &fileExtension() const { return fileExtension_; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
DECLARE_JSON_MAP(PiPedalFileType);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class PiPedalFileProperty {
|
||||||
|
private:
|
||||||
|
std::string name_;
|
||||||
|
std::string directory_;
|
||||||
|
std::vector<PiPedalFileType> fileTypes_;
|
||||||
|
std::string patchProperty_;
|
||||||
|
std::string defaultFile_;
|
||||||
|
public:
|
||||||
|
using ptr = std::shared_ptr<PiPedalFileProperty>;
|
||||||
|
PiPedalFileProperty() { }
|
||||||
|
PiPedalFileProperty(PiPedalHost*pHost, const LilvNode*node);
|
||||||
|
|
||||||
|
|
||||||
|
const std::string &name() const { return name_; }
|
||||||
|
const std::string &directory() const { return directory_; }
|
||||||
|
const std::string &defaultFile() const { return defaultFile_; }
|
||||||
|
|
||||||
|
const std::vector<PiPedalFileType> &fileTypes() const { return fileTypes_; }
|
||||||
|
|
||||||
|
const std::string &patchProperty() const { return patchProperty_; }
|
||||||
|
public:
|
||||||
|
DECLARE_JSON_MAP(PiPedalFileProperty);
|
||||||
|
};
|
||||||
|
|
||||||
|
class PiPedalUI {
|
||||||
|
public:
|
||||||
|
using ptr = std::shared_ptr<PiPedalUI>;
|
||||||
|
PiPedalUI(PiPedalHost*pHost, const LilvNode*uiNode);
|
||||||
|
const std::vector<PiPedalFileProperty::ptr>& fileProperties() const
|
||||||
|
{
|
||||||
|
return fileProperites_;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::vector<PiPedalFileProperty::ptr> fileProperites_;
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -27,6 +27,7 @@
|
|||||||
#include "Lv2Log.hpp"
|
#include "Lv2Log.hpp"
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include "PiPedalUI.hpp"
|
||||||
|
|
||||||
using namespace pipedal;
|
using namespace pipedal;
|
||||||
|
|
||||||
@@ -254,6 +255,11 @@ std::filesystem::path Storage::GetPluginPresetsDirectory() const
|
|||||||
{
|
{
|
||||||
return this->dataRoot / "plugin_presets";
|
return this->dataRoot / "plugin_presets";
|
||||||
}
|
}
|
||||||
|
std::filesystem::path Storage::GetAudioFilesDirectory() const
|
||||||
|
{
|
||||||
|
return this->dataRoot / "audio_uploads";
|
||||||
|
}
|
||||||
|
|
||||||
std::filesystem::path Storage::GetCurrentPresetPath() const
|
std::filesystem::path Storage::GetCurrentPresetPath() const
|
||||||
{
|
{
|
||||||
return this->dataRoot / "currentPreset.json";
|
return this->dataRoot / "currentPreset.json";
|
||||||
@@ -1383,6 +1389,54 @@ std::vector<MidiBinding> Storage::GetSystemMidiBindings()
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool containsDotDot(const std::string&value)
|
||||||
|
{
|
||||||
|
std::size_t offset = value.find("..");
|
||||||
|
return offset != std::string::npos;
|
||||||
|
}
|
||||||
|
static bool containsDirectorySeparator(const std::string&value)
|
||||||
|
{
|
||||||
|
if (value.find("/") != std::string::npos) return true; //linux
|
||||||
|
if (value.find("\\") != std::string::npos) return true; // windows
|
||||||
|
if (value.find("::") != std::string::npos) return true; // mac
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool containsNonAlphaNumericCharacter(const std::string&value)
|
||||||
|
{
|
||||||
|
for (char c:value)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
(c >= '0' && c <= '9')
|
||||||
|
|| (c >= 'a' && c <= 'z')
|
||||||
|
|| (c >= 'A' && c <= 'Z)
|
||||||
|
|| (c == '_')
|
||||||
|
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sanityCheckDirectory(const std::string&directory)
|
||||||
|
{
|
||||||
|
// we can afford to be highly restrictive here. Alpha-numeric only.
|
||||||
|
if (containsNonAlphaNumericCharacter(directory))
|
||||||
|
{
|
||||||
|
throw std::logic_error("Invalid directory name.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> Storage::GetFileList(const PiPedalFileProperty&fileProperty)
|
||||||
|
{
|
||||||
|
sanityCheckDirectory(fileProperty.directory());
|
||||||
|
std::filesystem::path path = this->GetAudioFilesDirectory() / fileProperty.directory();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
JSON_MAP_BEGIN(UserSettings)
|
JSON_MAP_BEGIN(UserSettings)
|
||||||
JSON_MAP_REFERENCE(UserSettings, governor)
|
JSON_MAP_REFERENCE(UserSettings, governor)
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
|
|
||||||
namespace pipedal {
|
namespace pipedal {
|
||||||
|
|
||||||
|
class PiPedalFileProperty;
|
||||||
|
|
||||||
class CurrentPreset {
|
class CurrentPreset {
|
||||||
public:
|
public:
|
||||||
@@ -67,6 +68,7 @@ private:
|
|||||||
static std::string SafeDecodeName(const std::string& name);
|
static std::string SafeDecodeName(const std::string& name);
|
||||||
std::filesystem::path GetPresetsDirectory() const;
|
std::filesystem::path GetPresetsDirectory() const;
|
||||||
std::filesystem::path GetPluginPresetsDirectory() const;
|
std::filesystem::path GetPluginPresetsDirectory() const;
|
||||||
|
std::filesystem::path GetAudioFilesDirectory() const
|
||||||
std::filesystem::path GetIndexFileName() const;
|
std::filesystem::path GetIndexFileName() const;
|
||||||
std::filesystem::path GetBankFileName(const std::string & name) const;
|
std::filesystem::path GetBankFileName(const std::string & name) const;
|
||||||
std::filesystem::path GetChannelSelectionFileName();
|
std::filesystem::path GetChannelSelectionFileName();
|
||||||
@@ -134,6 +136,8 @@ public:
|
|||||||
void MoveBank(int from, int to);
|
void MoveBank(int from, int to);
|
||||||
int64_t DeleteBank(int64_t bankId);
|
int64_t DeleteBank(int64_t bankId);
|
||||||
|
|
||||||
|
std::vector<std::string> GetFileList(const PiPedalFileProperty&fileProperty);
|
||||||
|
|
||||||
|
|
||||||
void SetJackChannelSelection(const JackChannelSelection&channelSelection);
|
void SetJackChannelSelection(const JackChannelSelection&channelSelection);
|
||||||
const JackChannelSelection&GetJackChannelSelection(const JackConfiguration &jackConfiguration);
|
const JackChannelSelection&GetJackChannelSelection(const JackConfiguration &jackConfiguration);
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <lv2/lv2plug.in/ns/ext/worker/worker.h>
|
#include <lv2/lv2plug.in/ns/ext/worker/worker.h>
|
||||||
#include "Lv2Log.hpp"
|
#include "Lv2Log.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <unistd.h> // for nice()
|
||||||
|
|
||||||
using namespace pipedal;
|
using namespace pipedal;
|
||||||
|
|
||||||
@@ -103,6 +105,14 @@ void Worker::EmitResponses()
|
|||||||
}
|
}
|
||||||
void Worker::ThreadProc()
|
void Worker::ThreadProc()
|
||||||
{
|
{
|
||||||
|
// run nice +1 (priority -1 on Windows)
|
||||||
|
errno = 0;
|
||||||
|
nice(1);
|
||||||
|
if (errno != 0)
|
||||||
|
{
|
||||||
|
std::cout << "Warning: Unable to run Lv2 schedule thread at nice +1" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
|
|||||||
@@ -8,5 +8,6 @@
|
|||||||
"http://two-play.com/plugins/toob-power-stage-2": true,
|
"http://two-play.com/plugins/toob-power-stage-2": true,
|
||||||
"http://two-play.com/plugins/toob-spectrum": true,
|
"http://two-play.com/plugins/toob-spectrum": true,
|
||||||
"http://two-play.com/plugins/toob-tone-stack": true,
|
"http://two-play.com/plugins/toob-tone-stack": true,
|
||||||
"http://two-play.com/plugins/toob-tuner": true
|
"http://two-play.com/plugins/toob-tuner": true,
|
||||||
|
"http://two-play.com/plugins/toob-convolution-reverb": true
|
||||||
}
|
}
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include "PiPedalException.hpp"
|
#include "PiPedalException.hpp"
|
||||||
|
#include "json_variant.hpp"
|
||||||
|
|
||||||
using namespace pipedal;
|
using namespace pipedal;
|
||||||
|
|
||||||
@@ -631,3 +632,8 @@ void json_reader::throw_format_error(const char*error)
|
|||||||
throw PiPedalException(message);
|
throw PiPedalException(message);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// void json_writer::write(const json_variant &value)
|
||||||
|
// {
|
||||||
|
// ((JsonSerializable *)&value)->write_json(*this);
|
||||||
|
// }
|
||||||
|
|||||||
+177
-128
@@ -30,14 +30,16 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include "PiPedalException.hpp"
|
#include "PiPedalException.hpp"
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <variant>
|
||||||
|
#include <concepts>
|
||||||
|
|
||||||
#define DECLARE_JSON_MAP(CLASSNAME) \
|
#define DECLARE_JSON_MAP(CLASSNAME) \
|
||||||
static json_map::storage_type<CLASSNAME> jmap
|
static json_map::storage_type<CLASSNAME> jmap
|
||||||
|
|
||||||
#define JSON_MAP_BEGIN(CLASSNAME) \
|
#define JSON_MAP_BEGIN(CLASSNAME) \
|
||||||
json_map::storage_type<CLASSNAME> CLASSNAME::jmap { {
|
json_map::storage_type<CLASSNAME> CLASSNAME::jmap \
|
||||||
|
{ \
|
||||||
|
{
|
||||||
|
|
||||||
#define JSON_MAP_REFERENCE(class, name) \
|
#define JSON_MAP_REFERENCE(class, name) \
|
||||||
json_map::reference(#name, &class ::name##_),
|
json_map::reference(#name, &class ::name##_),
|
||||||
@@ -45,40 +47,56 @@
|
|||||||
#define JSON_MAP_REFERENCE_CONDITIONAL(class, name, conditionFn) \
|
#define JSON_MAP_REFERENCE_CONDITIONAL(class, name, conditionFn) \
|
||||||
json_map::conditional_reference(#name, &class ::name##_, conditionFn),
|
json_map::conditional_reference(#name, &class ::name##_, conditionFn),
|
||||||
|
|
||||||
|
|
||||||
#define JSON_MAP_END() \
|
#define JSON_MAP_END() \
|
||||||
}};
|
} \
|
||||||
|
} \
|
||||||
|
;
|
||||||
|
|
||||||
namespace pipedal
|
namespace pipedal
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
// a function pointer of type bool (*)(const CLASS*,const MEMBER_TYPE&value) (because pre-C++ 20 templated function pointers are *hard*)
|
// a function pointer of type bool (*)(const CLASS*,const MEMBER_TYPE&value) (because pre-C++ 20 templated function pointers are *hard*)
|
||||||
template<typename CLASS,typename MEMBER_TYPE> class JsonConditionFunction {
|
template <typename CLASS, typename MEMBER_TYPE>
|
||||||
|
class JsonConditionFunction
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
using Pointer = bool (*)(const CLASS *self, const MEMBER_TYPE &value);
|
using Pointer = bool (*)(const CLASS *self, const MEMBER_TYPE &value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
class json_reader;
|
class json_reader;
|
||||||
class json_writer;
|
class json_writer;
|
||||||
|
|
||||||
class JsonWritable {
|
class JsonSerializable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void write_json(json_writer &writer) const = 0;
|
||||||
|
virtual void read_json(json_reader &reader) = 0;
|
||||||
|
};
|
||||||
|
template <typename T>
|
||||||
|
concept IsJsonSerializable = std::derived_from<T,JsonSerializable>;
|
||||||
|
|
||||||
|
|
||||||
|
class JsonMemberWritable
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
virtual void write_members(json_writer &writer) const = 0;
|
virtual void write_members(json_writer &writer) const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename CLASS>
|
template <typename CLASS>
|
||||||
class json_member_reference_base {
|
class json_member_reference_base
|
||||||
|
{
|
||||||
private:
|
private:
|
||||||
const char *name_;
|
const char *name_;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
json_member_reference_base(const char *name)
|
json_member_reference_base(const char *name)
|
||||||
: name_(name)
|
: name_(name)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual ~json_member_reference_base() {}
|
virtual ~json_member_reference_base() {}
|
||||||
const char *name() { return this->name_; }
|
const char *name() { return this->name_; }
|
||||||
@@ -87,30 +105,27 @@ public:
|
|||||||
virtual void write_value(json_writer &writer, const CLASS *self) = 0;
|
virtual void write_value(json_writer &writer, const CLASS *self) = 0;
|
||||||
};
|
};
|
||||||
template <typename CLASS, typename MEMBER_TYPE>
|
template <typename CLASS, typename MEMBER_TYPE>
|
||||||
class json_member_reference: public json_member_reference_base<CLASS> {
|
class json_member_reference : public json_member_reference_base<CLASS>
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~json_member_reference() {}
|
virtual ~json_member_reference() {}
|
||||||
json_member_reference(const char *name, MEMBER_TYPE CLASS::*member_pointer)
|
json_member_reference(const char *name, MEMBER_TYPE CLASS::*member_pointer)
|
||||||
: json_member_reference_base<CLASS>(name)
|
: json_member_reference_base<CLASS>(name), member_pointer(member_pointer)
|
||||||
, member_pointer(member_pointer)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
MEMBER_TYPE CLASS::*member_pointer;
|
MEMBER_TYPE CLASS::*member_pointer;
|
||||||
|
|
||||||
void read_value(json_reader &reader, CLASS *self);
|
void read_value(json_reader &reader, CLASS *self);
|
||||||
void write_value(json_writer &writer, const CLASS *self);
|
void write_value(json_writer &writer, const CLASS *self);
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
template <typename CLASS, typename MEMBER_TYPE>
|
template <typename CLASS, typename MEMBER_TYPE>
|
||||||
class json_conditional_member_reference: public json_member_reference_base<CLASS> {
|
class json_conditional_member_reference : public json_member_reference_base<CLASS>
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~json_conditional_member_reference() {}
|
virtual ~json_conditional_member_reference() {}
|
||||||
|
|
||||||
json_conditional_member_reference(const char *name, MEMBER_TYPE CLASS::*member_pointer, typename JsonConditionFunction<CLASS, MEMBER_TYPE>::Pointer condition)
|
json_conditional_member_reference(const char *name, MEMBER_TYPE CLASS::*member_pointer, typename JsonConditionFunction<CLASS, MEMBER_TYPE>::Pointer condition)
|
||||||
: json_member_reference_base<CLASS>(name)
|
: json_member_reference_base<CLASS>(name), member_pointer(member_pointer), condition_(condition)
|
||||||
, member_pointer(member_pointer)
|
|
||||||
, condition_(condition)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
MEMBER_TYPE CLASS::*member_pointer;
|
MEMBER_TYPE CLASS::*member_pointer;
|
||||||
@@ -123,42 +138,37 @@ public:
|
|||||||
void write_value(json_writer &writer, const CLASS *self);
|
void write_value(json_writer &writer, const CLASS *self);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template <typename CLASS>
|
template <typename CLASS>
|
||||||
class json_enum_converter {
|
class json_enum_converter
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
virtual CLASS fromString(const std::string &value) const = 0;
|
virtual CLASS fromString(const std::string &value) const = 0;
|
||||||
virtual const std::string &toString(CLASS value) const = 0;
|
virtual const std::string &toString(CLASS value) const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename CLASS, typename MEMBER_TYPE>
|
template <typename CLASS, typename MEMBER_TYPE>
|
||||||
class json_enum_member_reference: public json_member_reference_base<CLASS> {
|
class json_enum_member_reference : public json_member_reference_base<CLASS>
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
json_enum_member_reference(const char *name, MEMBER_TYPE CLASS::*member_pointer, const json_enum_converter<MEMBER_TYPE> *converter)
|
json_enum_member_reference(const char *name, MEMBER_TYPE CLASS::*member_pointer, const json_enum_converter<MEMBER_TYPE> *converter)
|
||||||
: json_member_reference_base<CLASS>(name)
|
: json_member_reference_base<CLASS>(name), member_pointer(member_pointer), converter_(converter)
|
||||||
, member_pointer(member_pointer)
|
|
||||||
, converter_(converter)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
MEMBER_TYPE CLASS::*member_pointer;
|
MEMBER_TYPE CLASS::*member_pointer;
|
||||||
const json_enum_converter<MEMBER_TYPE> *converter_;
|
const json_enum_converter<MEMBER_TYPE> *converter_;
|
||||||
|
|
||||||
|
|
||||||
void read_value(json_reader &reader, CLASS *self);
|
void read_value(json_reader &reader, CLASS *self);
|
||||||
void write_value(json_writer &writer, const CLASS *self);
|
void write_value(json_writer &writer, const CLASS *self);
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
class json_map_base {
|
class json_map_base
|
||||||
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename CLASS>
|
template <typename CLASS>
|
||||||
class json_map_impl: public json_map_base {
|
class json_map_impl : public json_map_base
|
||||||
|
{
|
||||||
private:
|
private:
|
||||||
std::vector<json_member_reference_base<CLASS> *> members_;
|
std::vector<json_member_reference_base<CLASS> *> members_;
|
||||||
json_map_impl(const json_map_impl &) {} // disable copy constructor.
|
json_map_impl(const json_map_impl &) {} // disable copy constructor.
|
||||||
@@ -168,12 +178,10 @@ public:
|
|||||||
json_map_impl(const members_t &members)
|
json_map_impl(const members_t &members)
|
||||||
: members_(members)
|
: members_(members)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
json_map_impl(members_t &&members)
|
json_map_impl(members_t &&members)
|
||||||
: members_(std::forward<members_t>(members))
|
: members_(std::forward<members_t>(members))
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
virtual ~json_map_impl()
|
virtual ~json_map_impl()
|
||||||
{
|
{
|
||||||
@@ -185,18 +193,15 @@ public:
|
|||||||
}
|
}
|
||||||
void read_property(json_reader *reader, const char *memberName, CLASS *pObject);
|
void read_property(json_reader *reader, const char *memberName, CLASS *pObject);
|
||||||
void write_members(json_writer *writer, const CLASS *pObject);
|
void write_members(json_writer *writer, const CLASS *pObject);
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class json_map
|
||||||
|
{
|
||||||
class json_map {
|
|
||||||
public:
|
public:
|
||||||
|
template <typename CLASS>
|
||||||
template <typename CLASS> class storage_type : public json_map_impl<CLASS> {
|
class storage_type : public json_map_impl<CLASS>
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
using members_t = std::vector<json_member_reference_base<CLASS> *>;
|
using members_t = std::vector<json_member_reference_base<CLASS> *>;
|
||||||
|
|
||||||
storage_type(const members_t &members)
|
storage_type(const members_t &members)
|
||||||
@@ -230,13 +235,13 @@ public:
|
|||||||
{
|
{
|
||||||
return new json_conditional_member_reference<CLASS, MEMBER_TYPE>(name, member_pointer, condition);
|
return new json_conditional_member_reference<CLASS, MEMBER_TYPE>(name, member_pointer, condition);
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class HasJsonPropertyMap {
|
class HasJsonPropertyMap
|
||||||
|
{
|
||||||
|
|
||||||
template <class TYPE>
|
template <class TYPE>
|
||||||
static std::true_type test(decltype(TYPE::jmap) *) { return std::true_type(); };
|
static std::true_type test(decltype(TYPE::jmap) *) { return std::true_type(); };
|
||||||
@@ -248,17 +253,29 @@ public:
|
|||||||
static constexpr bool value = decltype(test<T>(nullptr))::value;
|
static constexpr bool value = decltype(test<T>(nullptr))::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class HasExplicitJsonWrite
|
||||||
|
{
|
||||||
|
|
||||||
|
template <class TYPE>
|
||||||
|
static std::true_type test() { return std::true_type(); };
|
||||||
|
|
||||||
|
template <class TYPE>
|
||||||
|
static std::false_type test(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr bool value = decltype(test<T>(nullptr))::value;
|
||||||
|
};
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class json_writer;
|
class json_writer;
|
||||||
|
|
||||||
|
class json_writer
|
||||||
class json_writer {
|
{
|
||||||
public:
|
public:
|
||||||
const char *CRLF;
|
const char *CRLF;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool allowNaN_ = true;
|
bool allowNaN_ = true;
|
||||||
std::ostream &os;
|
std::ostream &os;
|
||||||
@@ -281,7 +298,6 @@ private:
|
|||||||
static const uint32_t UTF8_CONTINUATION_MASK = 0xC0;
|
static const uint32_t UTF8_CONTINUATION_MASK = 0xC0;
|
||||||
static const uint32_t UTF8_CONTINUATION_BITS = 0x80;
|
static const uint32_t UTF8_CONTINUATION_BITS = 0x80;
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static std::string encode_string(const std::string &text)
|
static std::string encode_string(const std::string &text)
|
||||||
{
|
{
|
||||||
@@ -290,15 +306,13 @@ public:
|
|||||||
writer.write(text);
|
writer.write(text);
|
||||||
return s.str();
|
return s.str();
|
||||||
}
|
}
|
||||||
void write_raw(const char*text) {
|
void write_raw(const char *text)
|
||||||
|
{
|
||||||
os << text;
|
os << text;
|
||||||
}
|
}
|
||||||
using string_view = boost::string_view;
|
using string_view = boost::string_view;
|
||||||
json_writer(std::ostream &os, bool compressed = true, bool allowNaN = true)
|
json_writer(std::ostream &os, bool compressed = true, bool allowNaN = true)
|
||||||
: os(os)
|
: os(os), compressed(compressed), allowNaN_(allowNaN), indent_level(0)
|
||||||
, compressed(compressed)
|
|
||||||
, allowNaN_(allowNaN)
|
|
||||||
, indent_level(0)
|
|
||||||
{
|
{
|
||||||
this->CRLF = compressed ? "" : "\r\n";
|
this->CRLF = compressed ? "" : "\r\n";
|
||||||
}
|
}
|
||||||
@@ -330,8 +344,6 @@ public:
|
|||||||
os << value;
|
os << value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void throw_encoding_error();
|
static void throw_encoding_error();
|
||||||
|
|
||||||
@@ -342,7 +354,8 @@ public:
|
|||||||
void indent();
|
void indent();
|
||||||
std::ostream &output_stream() { return os; }
|
std::ostream &output_stream() { return os; }
|
||||||
|
|
||||||
void write(bool value) {
|
void write(bool value)
|
||||||
|
{
|
||||||
os << (value ? "true" : "false");
|
os << (value ? "true" : "false");
|
||||||
}
|
}
|
||||||
void write(
|
void write(
|
||||||
@@ -367,7 +380,9 @@ public:
|
|||||||
if (allowNaN_ && (std::isnan(f) || std::isinf(f)))
|
if (allowNaN_ && (std::isnan(f) || std::isinf(f)))
|
||||||
{
|
{
|
||||||
os << "NaN";
|
os << "NaN";
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
os << std::setprecision(std::numeric_limits<float>::max_digits10) << f; // round-trip format
|
os << std::setprecision(std::numeric_limits<float>::max_digits10) << f; // round-trip format
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -376,12 +391,16 @@ public:
|
|||||||
if (allowNaN_ && (std::isnan(f) || std::isinf(f)))
|
if (allowNaN_ && (std::isnan(f) || std::isinf(f)))
|
||||||
{
|
{
|
||||||
os << "NaN";
|
os << "NaN";
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
os << std::setprecision(std::numeric_limits<double>::max_digits10) << f; // round-trip format
|
os << std::setprecision(std::numeric_limits<double>::max_digits10) << f; // round-trip format
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> void write(const std::vector<T> &value)
|
|
||||||
|
template <typename T>
|
||||||
|
void write(const std::vector<T> &value)
|
||||||
{
|
{
|
||||||
if (std::is_fundamental<T>() || std::is_assignable<T, const char *>() || value.size() == 0)
|
if (std::is_fundamental<T>() || std::is_assignable<T, const char *>() || value.size() == 0)
|
||||||
{
|
{
|
||||||
@@ -398,7 +417,9 @@ public:
|
|||||||
write(value[i]);
|
write(value[i]);
|
||||||
}
|
}
|
||||||
os << "]";
|
os << "]";
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
// complex types: one line per entry.
|
// complex types: one line per entry.
|
||||||
os << "[" << CRLF;
|
os << "[" << CRLF;
|
||||||
indent_level += TAB_SIZE;
|
indent_level += TAB_SIZE;
|
||||||
@@ -417,7 +438,6 @@ public:
|
|||||||
os << CRLF;
|
os << CRLF;
|
||||||
indent();
|
indent();
|
||||||
os << "]";
|
os << "]";
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template <
|
template <
|
||||||
@@ -425,8 +445,9 @@ public:
|
|||||||
class T,
|
class T,
|
||||||
class Distance = std::ptrdiff_t,
|
class Distance = std::ptrdiff_t,
|
||||||
class Pointer = T *,
|
class Pointer = T *,
|
||||||
class Reference = T&
|
class Reference = T &>
|
||||||
> void write(std::iterator<Category,T,Distance,Pointer,Reference> &it) {
|
void write(std::iterator<Category, T, Distance, Pointer, Reference> &it)
|
||||||
|
{
|
||||||
start_array();
|
start_array();
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (Reference ref : it)
|
for (Reference ref : it)
|
||||||
@@ -449,7 +470,8 @@ public:
|
|||||||
os << json_text;
|
os << json_text;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T> void write_member(const char*name,const T&value)
|
template <typename T>
|
||||||
|
void write_member(const char *name, const T &value)
|
||||||
{
|
{
|
||||||
write(name);
|
write(name);
|
||||||
os << ": ";
|
os << ": ";
|
||||||
@@ -460,7 +482,6 @@ public:
|
|||||||
void start_array();
|
void start_array();
|
||||||
void end_array();
|
void end_array();
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
void write(const json_writable *obj)
|
void write(const json_writable *obj)
|
||||||
{
|
{
|
||||||
@@ -488,9 +509,22 @@ private:
|
|||||||
// Type T must have public static propery jmap.
|
// Type T must have public static propery jmap.
|
||||||
return T::jmap;
|
return T::jmap;
|
||||||
}
|
}
|
||||||
public:
|
|
||||||
|
|
||||||
void write(const JsonWritable*pWritable)
|
public:
|
||||||
|
void write(const JsonSerializable *pWritable)
|
||||||
|
{
|
||||||
|
pWritable->write_json(*this);
|
||||||
|
}
|
||||||
|
void write(const JsonSerializable &writable)
|
||||||
|
{
|
||||||
|
writable.write_json(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeRawWritable(const JsonSerializable &writable)
|
||||||
|
{
|
||||||
|
writable.write_json(*this);
|
||||||
|
}
|
||||||
|
void write(const JsonMemberWritable *pWritable)
|
||||||
{
|
{
|
||||||
start_object();
|
start_object();
|
||||||
pWritable->write_members(*this);
|
pWritable->write_members(*this);
|
||||||
@@ -520,11 +554,13 @@ public:
|
|||||||
if (obj == nullptr)
|
if (obj == nullptr)
|
||||||
{
|
{
|
||||||
write_raw("null");
|
write_raw("null");
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
write(*obj);
|
write(*obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void write(T *obj)
|
void write(T *obj)
|
||||||
{
|
{
|
||||||
@@ -532,10 +568,18 @@ public:
|
|||||||
if (obj == nullptr)
|
if (obj == nullptr)
|
||||||
{
|
{
|
||||||
write_raw("null");
|
write_raw("null");
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
write(*obj);
|
write(*obj);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
requires IsJsonSerializable<T>
|
||||||
|
void write(const T&obj)
|
||||||
|
{
|
||||||
|
writeRawWritable(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -569,15 +613,10 @@ public:
|
|||||||
}
|
}
|
||||||
end_object();
|
end_object();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class json_reader
|
||||||
class json_reader {
|
{
|
||||||
private:
|
private:
|
||||||
std::istream &is_;
|
std::istream &is_;
|
||||||
|
|
||||||
@@ -586,8 +625,6 @@ private:
|
|||||||
const uint16_t UTF16_SURROGATE_MASK = 0x3FFU;
|
const uint16_t UTF16_SURROGATE_MASK = 0x3FFU;
|
||||||
bool allowNaN_ = true;
|
bool allowNaN_ = true;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
json_reader(std::istream &input, bool allowNaN = true)
|
json_reader(std::istream &input, bool allowNaN = true)
|
||||||
: is_(input)
|
: is_(input)
|
||||||
@@ -606,8 +643,6 @@ private:
|
|||||||
throw_format_error("Invalid file format");
|
throw_format_error("Invalid file format");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
inline bool is_whitespace(char c)
|
inline bool is_whitespace(char c)
|
||||||
{
|
{
|
||||||
switch (c)
|
switch (c)
|
||||||
@@ -620,11 +655,12 @@ private:
|
|||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
char get() {
|
char get()
|
||||||
|
{
|
||||||
int ic = is_.get();
|
int ic = is_.get();
|
||||||
if (ic == -1) throw_format_error("Unexpected end of file");
|
if (ic == -1)
|
||||||
|
throw_format_error("Unexpected end of file");
|
||||||
return (char)ic;
|
return (char)ic;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -640,6 +676,7 @@ private:
|
|||||||
// Type T must have public static propery map.
|
// Type T must have public static propery map.
|
||||||
return T::jmap;
|
return T::jmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void consume(char expected);
|
void consume(char expected);
|
||||||
void consumeToken(const char *expectedToken, const char *errorMessage);
|
void consumeToken(const char *expectedToken, const char *errorMessage);
|
||||||
@@ -648,12 +685,19 @@ public:
|
|||||||
skip_whitespace();
|
skip_whitespace();
|
||||||
return is_.peek();
|
return is_.peek();
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::string read_string();
|
std::string read_string();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool is_complete();
|
bool is_complete();
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
requires IsJsonSerializable<T>
|
||||||
|
void read(T*pObject)
|
||||||
|
{
|
||||||
|
dynamic_cast<JsonSerializable*>(pObject)->read_json(*this);
|
||||||
|
}
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void read(T *pObject)
|
void read(T *pObject)
|
||||||
{
|
{
|
||||||
@@ -721,7 +765,7 @@ public:
|
|||||||
|
|
||||||
U u;
|
U u;
|
||||||
this->read(&u);
|
this->read(&u);
|
||||||
(*pMap)[key] = u;
|
(*pMap)[key] = std::move(u);
|
||||||
skip_whitespace();
|
skip_whitespace();
|
||||||
|
|
||||||
if (peek() == ',')
|
if (peek() == ',')
|
||||||
@@ -731,7 +775,6 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void read(std::unique_ptr<T> *pUniquePtr)
|
void read(std::unique_ptr<T> *pUniquePtr)
|
||||||
{
|
{
|
||||||
@@ -761,12 +804,14 @@ private:
|
|||||||
void skip_object();
|
void skip_object();
|
||||||
std::string readToken();
|
std::string readToken();
|
||||||
bool read_boolean();
|
bool read_boolean();
|
||||||
void read_null();
|
|
||||||
void skip_token();
|
void skip_token();
|
||||||
|
public:
|
||||||
|
void read_null();
|
||||||
public:
|
public:
|
||||||
void skip_property();
|
void skip_property();
|
||||||
|
|
||||||
void read(std::string*value) {
|
void read(std::string *value)
|
||||||
|
{
|
||||||
skip_whitespace();
|
skip_whitespace();
|
||||||
*value = read_string();
|
*value = read_string();
|
||||||
}
|
}
|
||||||
@@ -776,38 +821,48 @@ public:
|
|||||||
skip_whitespace();
|
skip_whitespace();
|
||||||
*value = read_boolean();
|
*value = read_boolean();
|
||||||
}
|
}
|
||||||
void read(int *value) {
|
void read(int *value)
|
||||||
|
{
|
||||||
skip_whitespace();
|
skip_whitespace();
|
||||||
is_ >> *value;
|
is_ >> *value;
|
||||||
if (is_.fail()) throw PiPedalException("Invalid format.");
|
if (is_.fail())
|
||||||
|
throw PiPedalException("Invalid format.");
|
||||||
}
|
}
|
||||||
void read(long *value)
|
void read(long *value)
|
||||||
{
|
{
|
||||||
is_ >> *value;
|
is_ >> *value;
|
||||||
if (is_.fail()) throw PiPedalException("Invalid format.");
|
if (is_.fail())
|
||||||
|
throw PiPedalException("Invalid format.");
|
||||||
}
|
}
|
||||||
void read(long long*value) {
|
void read(long long *value)
|
||||||
|
{
|
||||||
skip_whitespace();
|
skip_whitespace();
|
||||||
is_ >> *value;
|
is_ >> *value;
|
||||||
if (is_.fail()) throw PiPedalException("Invalid format.");
|
if (is_.fail())
|
||||||
|
throw PiPedalException("Invalid format.");
|
||||||
}
|
}
|
||||||
void read(unsigned int *value)
|
void read(unsigned int *value)
|
||||||
{
|
{
|
||||||
is_ >> *value;
|
is_ >> *value;
|
||||||
if (is_.fail()) throw PiPedalException("Invalid format.");
|
if (is_.fail())
|
||||||
|
throw PiPedalException("Invalid format.");
|
||||||
}
|
}
|
||||||
void read(unsigned long *value)
|
void read(unsigned long *value)
|
||||||
{
|
{
|
||||||
is_ >> *value;
|
is_ >> *value;
|
||||||
if (is_.fail()) throw PiPedalException("Invalid format.");
|
if (is_.fail())
|
||||||
|
throw PiPedalException("Invalid format.");
|
||||||
}
|
}
|
||||||
void read(unsigned long long*value) {
|
void read(unsigned long long *value)
|
||||||
|
{
|
||||||
skip_whitespace();
|
skip_whitespace();
|
||||||
is_ >> *value;
|
is_ >> *value;
|
||||||
if (is_.fail()) throw PiPedalException("Invalid format.");
|
if (is_.fail())
|
||||||
|
throw PiPedalException("Invalid format.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void read(float*value) {
|
void read(float *value)
|
||||||
|
{
|
||||||
skip_whitespace();
|
skip_whitespace();
|
||||||
if (allowNaN_)
|
if (allowNaN_)
|
||||||
{
|
{
|
||||||
@@ -819,9 +874,11 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is_ >> *value;
|
is_ >> *value;
|
||||||
if (is_.fail()) throw PiPedalException("Invalid format.");
|
if (is_.fail())
|
||||||
|
throw PiPedalException("Invalid format.");
|
||||||
}
|
}
|
||||||
void read(double*value) {
|
void read(double *value)
|
||||||
|
{
|
||||||
skip_whitespace();
|
skip_whitespace();
|
||||||
if (allowNaN_)
|
if (allowNaN_)
|
||||||
{
|
{
|
||||||
@@ -834,9 +891,9 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
is_ >> *value;
|
is_ >> *value;
|
||||||
if (is_.fail()) throw PiPedalException("Invalid format.");
|
if (is_.fail())
|
||||||
|
throw PiPedalException("Invalid format.");
|
||||||
}
|
}
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void read(std::vector<T> *value)
|
void read(std::vector<T> *value)
|
||||||
@@ -862,13 +919,11 @@ public:
|
|||||||
}
|
}
|
||||||
*value = std::move(result);
|
*value = std::move(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class HasJsonRead {
|
class HasJsonRead
|
||||||
|
{
|
||||||
|
|
||||||
template <typename TYPE, typename ARG>
|
template <typename TYPE, typename ARG>
|
||||||
static std::true_type test(decltype(json_reader(std::cin).read((ARG *)nullptr)) *v) { return std::true_type(); };
|
static std::true_type test(decltype(json_reader(std::cin).read((ARG *)nullptr)) *v) { return std::true_type(); };
|
||||||
@@ -880,35 +935,36 @@ public:
|
|||||||
static constexpr bool value = decltype(test<json_reader, T>(nullptr))::value;
|
static constexpr bool value = decltype(test<json_reader, T>(nullptr))::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template <typename CLASS, typename MEMBER_TYPE>
|
template <typename CLASS, typename MEMBER_TYPE>
|
||||||
void json_member_reference<CLASS, MEMBER_TYPE>::read_value(json_reader&reader, CLASS*self) {
|
void json_member_reference<CLASS, MEMBER_TYPE>::read_value(json_reader &reader, CLASS *self)
|
||||||
|
{
|
||||||
static_assert(HasJsonRead<MEMBER_TYPE>::value, "Type not supported for Json serialization.");
|
static_assert(HasJsonRead<MEMBER_TYPE>::value, "Type not supported for Json serialization.");
|
||||||
MEMBER_TYPE *ref = &((*self).*member_pointer);
|
MEMBER_TYPE *ref = &((*self).*member_pointer);
|
||||||
reader.read(ref);
|
reader.read(ref);
|
||||||
}
|
}
|
||||||
template <typename CLASS, typename MEMBER_TYPE>
|
template <typename CLASS, typename MEMBER_TYPE>
|
||||||
void json_member_reference<CLASS,MEMBER_TYPE>::write_value(json_writer&writer, const CLASS*self) {
|
void json_member_reference<CLASS, MEMBER_TYPE>::write_value(json_writer &writer, const CLASS *self)
|
||||||
|
{
|
||||||
const MEMBER_TYPE &ref = (*self).*member_pointer;
|
const MEMBER_TYPE &ref = (*self).*member_pointer;
|
||||||
writer.write_member(this->name(), ref);
|
writer.write_member(this->name(), ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename CLASS, typename MEMBER_TYPE>
|
template <typename CLASS, typename MEMBER_TYPE>
|
||||||
void json_enum_member_reference<CLASS, MEMBER_TYPE>::read_value(json_reader&reader, CLASS*self) {
|
void json_enum_member_reference<CLASS, MEMBER_TYPE>::read_value(json_reader &reader, CLASS *self)
|
||||||
|
{
|
||||||
MEMBER_TYPE *ref = &((*self).*member_pointer);
|
MEMBER_TYPE *ref = &((*self).*member_pointer);
|
||||||
std::string val = reader.read_string();
|
std::string val = reader.read_string();
|
||||||
*ref = converter_->fromString(val);
|
*ref = converter_->fromString(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename CLASS, typename MEMBER_TYPE>
|
template <typename CLASS, typename MEMBER_TYPE>
|
||||||
void json_enum_member_reference<CLASS,MEMBER_TYPE>::write_value(json_writer&writer, const CLASS*self) {
|
void json_enum_member_reference<CLASS, MEMBER_TYPE>::write_value(json_writer &writer, const CLASS *self)
|
||||||
|
{
|
||||||
const MEMBER_TYPE &ref = (*self).*member_pointer;
|
const MEMBER_TYPE &ref = (*self).*member_pointer;
|
||||||
std::string val = converter_->toString(ref);
|
std::string val = converter_->toString(ref);
|
||||||
writer.write_member(this->name(), val);
|
writer.write_member(this->name(), val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template <typename CLASS>
|
template <typename CLASS>
|
||||||
void json_map_impl<CLASS>::read_property(json_reader *reader, const char *memberName, CLASS *pObject)
|
void json_map_impl<CLASS>::read_property(json_reader *reader, const char *memberName, CLASS *pObject)
|
||||||
{
|
{
|
||||||
@@ -919,33 +975,32 @@ void json_map_impl<CLASS>::read_property(json_reader* reader,const char*memberNa
|
|||||||
member->read_value(*reader, pObject);
|
member->read_value(*reader, pObject);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
reader->skip_property();
|
reader->skip_property();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename CLASS, typename MEMBER_TYPE>
|
template <typename CLASS, typename MEMBER_TYPE>
|
||||||
void json_conditional_member_reference<CLASS, MEMBER_TYPE>::read_value(json_reader&reader, CLASS*self) {
|
void json_conditional_member_reference<CLASS, MEMBER_TYPE>::read_value(json_reader &reader, CLASS *self)
|
||||||
|
{
|
||||||
static_assert(HasJsonRead<MEMBER_TYPE>::value, "Type not supported for Json serialization.");
|
static_assert(HasJsonRead<MEMBER_TYPE>::value, "Type not supported for Json serialization.");
|
||||||
MEMBER_TYPE *ref = &((*self).*member_pointer);
|
MEMBER_TYPE *ref = &((*self).*member_pointer);
|
||||||
reader.read(ref);
|
reader.read(ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename CLASS, typename MEMBER_TYPE>
|
template <typename CLASS, typename MEMBER_TYPE>
|
||||||
bool json_conditional_member_reference<CLASS,MEMBER_TYPE>::canWrite(const CLASS*self) {
|
bool json_conditional_member_reference<CLASS, MEMBER_TYPE>::canWrite(const CLASS *self)
|
||||||
|
{
|
||||||
const MEMBER_TYPE &ref = (*self).*member_pointer;
|
const MEMBER_TYPE &ref = (*self).*member_pointer;
|
||||||
return this->condition_(self, ref);
|
return this->condition_(self, ref);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template <typename CLASS, typename MEMBER_TYPE>
|
template <typename CLASS, typename MEMBER_TYPE>
|
||||||
void json_conditional_member_reference<CLASS,MEMBER_TYPE>::write_value(json_writer&writer, const CLASS*self) {
|
void json_conditional_member_reference<CLASS, MEMBER_TYPE>::write_value(json_writer &writer, const CLASS *self)
|
||||||
|
{
|
||||||
const MEMBER_TYPE &ref = (*self).*member_pointer;
|
const MEMBER_TYPE &ref = (*self).*member_pointer;
|
||||||
writer.write_member(this->name(), ref);
|
writer.write_member(this->name(), ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template <typename CLASS>
|
template <typename CLASS>
|
||||||
void json_map_impl<CLASS>::write_members(json_writer *writer, const CLASS *pObject)
|
void json_map_impl<CLASS>::write_members(json_writer *writer, const CLASS *pObject)
|
||||||
{
|
{
|
||||||
@@ -964,12 +1019,6 @@ void json_map_impl<CLASS>::write_members(json_writer* writer, const CLASS*pObjec
|
|||||||
member->write_value(*writer, pObject);
|
member->write_value(*writer, pObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace pipedal
|
} // namespace pipedal
|
||||||
+126
-1
@@ -25,6 +25,9 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
|
#include "json_variant.hpp"
|
||||||
|
#include <concepts>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
using namespace pipedal;
|
using namespace pipedal;
|
||||||
|
|
||||||
@@ -94,6 +97,7 @@ json_map::storage_type<JsonTestTarget> JsonTestTarget::jmap {{
|
|||||||
|
|
||||||
|
|
||||||
TEST_CASE( "json write", "[json_write_test]" ) {
|
TEST_CASE( "json write", "[json_write_test]" ) {
|
||||||
|
std::cout << "== json write ==" << std::endl;
|
||||||
std::stringstream os;
|
std::stringstream os;
|
||||||
json_writer writer { os };
|
json_writer writer { os };
|
||||||
|
|
||||||
@@ -101,7 +105,7 @@ TEST_CASE( "json write", "[json_write_test]" ) {
|
|||||||
|
|
||||||
writer.write(testTarget);
|
writer.write(testTarget);
|
||||||
|
|
||||||
std::cout << os.str();
|
std::cout << os.str() << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string get_json()
|
static std::string get_json()
|
||||||
@@ -167,3 +171,124 @@ TEST_CASE( "json smart ptrs", "[json_smart_ptrs][Build][Dev]" ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void TestVariantRoundTrip(const T &value)
|
||||||
|
{
|
||||||
|
json_variant variant(value);
|
||||||
|
T out = variant.get<T>();
|
||||||
|
REQUIRE(out == value);
|
||||||
|
|
||||||
|
std::string output;
|
||||||
|
{
|
||||||
|
std::stringstream s;
|
||||||
|
json_writer writer(s);
|
||||||
|
writer.write(variant);
|
||||||
|
output = s.str();
|
||||||
|
}
|
||||||
|
std::cout << output << std::endl;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::stringstream s(output);
|
||||||
|
json_reader reader(s);
|
||||||
|
|
||||||
|
json_variant outputVariant;
|
||||||
|
reader.read(&outputVariant);
|
||||||
|
REQUIRE(outputVariant == variant);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void TestVariantRoundTrip(json_variant &value)
|
||||||
|
{
|
||||||
|
json_variant variant(value);
|
||||||
|
REQUIRE(variant == value);
|
||||||
|
|
||||||
|
std::string output;
|
||||||
|
{
|
||||||
|
std::stringstream s;
|
||||||
|
json_writer writer(s);
|
||||||
|
writer.write(variant);
|
||||||
|
output = s.str();
|
||||||
|
}
|
||||||
|
std::cout << output << std::endl;
|
||||||
|
|
||||||
|
json_variant outputVariant;
|
||||||
|
{
|
||||||
|
std::stringstream s(output);
|
||||||
|
json_reader reader(s);
|
||||||
|
|
||||||
|
json_variant outputVariant;
|
||||||
|
reader.read(&outputVariant);
|
||||||
|
REQUIRE(outputVariant == variant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class X{
|
||||||
|
public:
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
requires std::derived_from<T,JsonSerializable>
|
||||||
|
bool write(T &v)
|
||||||
|
{
|
||||||
|
(void)v;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
bool write(T &v)
|
||||||
|
{
|
||||||
|
(void)v;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void TestVariantSFINAE()
|
||||||
|
{
|
||||||
|
X x;
|
||||||
|
|
||||||
|
|
||||||
|
json_variant v;
|
||||||
|
dynamic_cast<JsonSerializable&>(v);
|
||||||
|
|
||||||
|
REQUIRE(x.write(v) == true);
|
||||||
|
|
||||||
|
int i;
|
||||||
|
REQUIRE(x.write(i) == false);
|
||||||
|
}
|
||||||
|
TEST_CASE( "json variants", "[json_variants][Build][Dev]" ) {
|
||||||
|
TestVariantSFINAE();
|
||||||
|
json_variant v(0);
|
||||||
|
|
||||||
|
TestVariantRoundTrip(json_null());
|
||||||
|
|
||||||
|
TestVariantRoundTrip(0.0);
|
||||||
|
TestVariantRoundTrip(std::string("abc"));
|
||||||
|
|
||||||
|
json_array array;
|
||||||
|
array.push_back(json_null());
|
||||||
|
array.push_back(3.25E19);
|
||||||
|
array.push_back(std::string("abc"));
|
||||||
|
|
||||||
|
json_variant variantArray { std::move(array) };
|
||||||
|
TestVariantRoundTrip(variantArray);
|
||||||
|
|
||||||
|
{
|
||||||
|
json_object obj;
|
||||||
|
obj["a"] = json_null();
|
||||||
|
obj["b"] = 0.25;
|
||||||
|
obj["c"] = std::move(variantArray);
|
||||||
|
json_variant variantObj { std::move(obj)};
|
||||||
|
TestVariantRoundTrip(variantObj);
|
||||||
|
|
||||||
|
variantObj["a"] = std::string("abc");
|
||||||
|
TestVariantRoundTrip(variantObj);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
json_variant x = json_variant::MakeArray();
|
||||||
|
x.resize(3);
|
||||||
|
x[0] = "def";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 Robin E. R. Davies
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "json_variant.hpp"
|
||||||
|
#include <limits>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
using namespace pipedal;
|
||||||
|
|
||||||
|
void concrete_json_variant_base::write_double_value(json_writer &writer,double value) const
|
||||||
|
{
|
||||||
|
if (value < std::numeric_limits<int32_t>::max() && value > std::numeric_limits<int32_t>::min())
|
||||||
|
{
|
||||||
|
double frac = value-(int32_t)value;
|
||||||
|
if (value == 0)
|
||||||
|
{
|
||||||
|
writer.write((int32_t)value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writer.write(value);
|
||||||
|
}
|
||||||
@@ -0,0 +1,325 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 Robin E. R. Davies
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* 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 <vector>
|
||||||
|
#include <variant>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include "json.hpp"
|
||||||
|
|
||||||
|
namespace pipedal
|
||||||
|
{
|
||||||
|
class json_null
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool operator==(const json_null&other) const { return true;}
|
||||||
|
private:
|
||||||
|
int value = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <class T> // avoid ordering problem in declarations.
|
||||||
|
class json_object_base: public JsonSerializable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using json_variant = T;
|
||||||
|
json_object_base() {}
|
||||||
|
|
||||||
|
T &operator[](const std::string &index) { return values[index]; }
|
||||||
|
const T &operator[](const std::string &index) const { return values[index]; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool operator==(const json_object_base<T> &other) const
|
||||||
|
{
|
||||||
|
for (const auto &pair: this->values)
|
||||||
|
{
|
||||||
|
auto index = other.values.find(pair.first);
|
||||||
|
if (index == other.values.end()) return false;
|
||||||
|
if (!(index->second == pair.second)) return false;
|
||||||
|
}
|
||||||
|
for (const auto &pair: other.values)
|
||||||
|
{
|
||||||
|
auto index = this->values.find(pair.first);
|
||||||
|
if (index == this->values.end()) return false;
|
||||||
|
if (!(index->second == pair.second)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
virtual void read_json(json_reader&reader) {
|
||||||
|
reader.read(&(this->values));
|
||||||
|
}
|
||||||
|
virtual void write_json(json_writer&writer) const {
|
||||||
|
writer.start_object();
|
||||||
|
bool first = true;
|
||||||
|
for (auto&value: values)
|
||||||
|
{
|
||||||
|
if (!first)
|
||||||
|
{
|
||||||
|
writer.write_raw(",");
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
writer.write(value.first);
|
||||||
|
writer.write_raw(": ");
|
||||||
|
writer.writeRawWritable(value.second);
|
||||||
|
}
|
||||||
|
writer.end_object();
|
||||||
|
}
|
||||||
|
std::map<std::string, T> values;
|
||||||
|
};
|
||||||
|
template <class T> // avoid ordering problem in declarations.
|
||||||
|
class json_array_base: public JsonSerializable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
json_array_base() {}
|
||||||
|
|
||||||
|
T &operator[](size_t index) {
|
||||||
|
check_index(index);
|
||||||
|
return values[index]; }
|
||||||
|
const T &operator[](size_t &index) const {
|
||||||
|
check_index(index);
|
||||||
|
return values[index];
|
||||||
|
}
|
||||||
|
void resize(size_t size)
|
||||||
|
{
|
||||||
|
values.resize(size);
|
||||||
|
}
|
||||||
|
size_t size() const { return values.size(); }
|
||||||
|
template <typename U>
|
||||||
|
void push_back(const U&value) { values.push_back(value); }
|
||||||
|
template <typename U>
|
||||||
|
void push_back(U&&value) { values.push_back(value); }
|
||||||
|
bool operator==(const json_array_base<T>&other) const
|
||||||
|
{
|
||||||
|
if (!(this->size() == other.size())) return false;
|
||||||
|
for (size_t i = 0; i < this->size(); ++i)
|
||||||
|
{
|
||||||
|
if (!((*this)[i] == other[i])) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
virtual void read_json(json_reader&reader) {
|
||||||
|
reader.read(&(this->values));
|
||||||
|
}
|
||||||
|
virtual void write_json(json_writer&writer) const {
|
||||||
|
writer.start_array();
|
||||||
|
bool first = true;
|
||||||
|
for (auto&value: values)
|
||||||
|
{
|
||||||
|
if (!first) writer.write_raw(",");
|
||||||
|
first = false;
|
||||||
|
writer.writeRawWritable(value);
|
||||||
|
}
|
||||||
|
writer.end_array();
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_index(size_t size) const
|
||||||
|
{
|
||||||
|
if (size >= values.size())
|
||||||
|
{
|
||||||
|
throw std::out_of_range("index out of range.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::vector<T> values;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class concrete_json_variant_base {
|
||||||
|
protected:
|
||||||
|
void write_double_value(json_writer &writer,double value) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename DUMMY = void>
|
||||||
|
class json_variant_base
|
||||||
|
: public std::variant<json_null, bool, double, std::string, json_object_base<json_variant_base<DUMMY>>, json_array_base<json_variant_base<DUMMY>>>,
|
||||||
|
public JsonSerializable,
|
||||||
|
private concrete_json_variant_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using base = std::variant<json_null, bool, double, std::string, json_object_base<json_variant_base<DUMMY>>, json_array_base<json_variant_base<DUMMY>>>;
|
||||||
|
using json_object = json_object_base<json_variant_base<DUMMY>>;
|
||||||
|
using json_array = json_array_base<json_variant_base<DUMMY>>;
|
||||||
|
using json_variant = json_variant_base<void>;
|
||||||
|
|
||||||
|
|
||||||
|
json_variant_base(json_null value)
|
||||||
|
:base(value)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
json_variant_base(double value)
|
||||||
|
: base(value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
json_variant_base(int value)
|
||||||
|
: base((double)value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
json_variant_base(const std::string &value)
|
||||||
|
: base(value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
json_variant_base(const char*value)
|
||||||
|
:base(std::string(value))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
json_variant_base()
|
||||||
|
: base(json_null())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
json_variant_base(json_object &&value)
|
||||||
|
: base(std::forward<json_object>(value))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
json_variant_base(json_array &&value)
|
||||||
|
: base(std::forward<json_array>(value))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
bool holds_alternative() const { return std::holds_alternative<U>(*this);}
|
||||||
|
|
||||||
|
bool IsNull() const { return holds_alternative<json_null>(); }
|
||||||
|
bool IsBool() const { return holds_alternative<bool>(); }
|
||||||
|
bool IsNumber() const { return holds_alternative<double>(); }
|
||||||
|
bool IsString() const { return holds_alternative<std::string>(); }
|
||||||
|
bool IsObject() const { return holds_alternative<json_object>(); }
|
||||||
|
bool IsArray() const { return holds_alternative<json_array>(); }
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
const U &get() const
|
||||||
|
{
|
||||||
|
return std::get<U>(*this);
|
||||||
|
}
|
||||||
|
template <typename U>
|
||||||
|
U &get()
|
||||||
|
{
|
||||||
|
return std::get<U>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool &AsBool() { return get<bool>(); }
|
||||||
|
bool AsBool() const { return get<bool>(); }
|
||||||
|
|
||||||
|
double &AsNumber() { return get<double>(); }
|
||||||
|
double AsNumber() const { return get<double>(); }
|
||||||
|
std::string &AsString() { return get<std::string>(); }
|
||||||
|
const std::string &AsString() const { return get<std::string>(); }
|
||||||
|
|
||||||
|
json_object &AsObject() { return get<json_object>(); }
|
||||||
|
const json_object &AsObject() const { return get<json_object>(); }
|
||||||
|
|
||||||
|
std::vector<float> AsFloatArray() { return get<json_object>().AsFloatArray(); }
|
||||||
|
std::vector<double> AsDoubleArray() { return get<json_object>().AsDoubleArray(); }
|
||||||
|
|
||||||
|
json_array &AsArray() { return get<json_array>(); }
|
||||||
|
const json_array &AsArray() const { return get<json_array>(); }
|
||||||
|
|
||||||
|
// convenience methods for object and array manipulation.
|
||||||
|
static json_variant MakeObject() { return json_variant{ json_object()};};
|
||||||
|
static json_variant MakeArray() { return json_variant{ json_array()};};
|
||||||
|
|
||||||
|
void resize(size_t size) { AsArray().resize(size); }
|
||||||
|
size_t size() const { return AsArray().size(); }
|
||||||
|
|
||||||
|
json_variant&operator[](size_t index) { return AsArray()[index];}
|
||||||
|
const json_variant&operator[](size_t index) const { return AsArray()[index];}
|
||||||
|
|
||||||
|
const json_variant&operator[](const std::string& index) const { return AsObject()[index];}
|
||||||
|
json_variant&operator[](const std::string& index) { return AsObject()[index];}
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void read_json(json_reader &reader)
|
||||||
|
{
|
||||||
|
int v = reader.peek();
|
||||||
|
if (v == '[')
|
||||||
|
{
|
||||||
|
json_array array;
|
||||||
|
reader.read(&array);
|
||||||
|
(*this) = std::move(array);
|
||||||
|
} else if (v == '{')
|
||||||
|
{
|
||||||
|
json_object object;
|
||||||
|
reader.read(&object);
|
||||||
|
(*this) = std::move(object);
|
||||||
|
}
|
||||||
|
else if (v == '\"') {
|
||||||
|
std::string s;
|
||||||
|
reader.read(&s);
|
||||||
|
(*this) = std::move(s);
|
||||||
|
} else if (v == 'n')
|
||||||
|
{
|
||||||
|
reader.read_null();
|
||||||
|
(*this) = json_null();
|
||||||
|
|
||||||
|
} else if (v == 't' || v == 'f')
|
||||||
|
{
|
||||||
|
bool b;
|
||||||
|
reader.read(&b);
|
||||||
|
(*this) = b;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// it's a number.
|
||||||
|
double v;
|
||||||
|
reader.read(&v);
|
||||||
|
(*this) = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virtual void write_json(json_writer&writer) const
|
||||||
|
{
|
||||||
|
switch (this->index())
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
writer.write_raw("null");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
writer.write(this->get<bool>());
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
write_double_value(writer,this->get<double>());
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
writer.write(get<std::string>());
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
writer.writeRawWritable(get<json_object>());
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
writer.writeRawWritable(get<json_array>());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw std::logic_error("Invalid variant index");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using json_variant = json_variant_base<void>;
|
||||||
|
using json_object = json_variant::json_object;
|
||||||
|
using json_array = json_variant::json_array;
|
||||||
|
|
||||||
|
} // namespace pipedal
|
||||||
Reference in New Issue
Block a user