Precached images. ToobTuner. key additions.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"socket_server_port": 8080,
|
"socket_server_port": 80,
|
||||||
"socket_server_address": "*",
|
"socket_server_address": "*",
|
||||||
|
|
||||||
"max_upload_size": 1048576,
|
"max_upload_size": 1048576,
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ import ToobToneStackViewFactory from './ToobToneStackView';
|
|||||||
import ToobCabSimViewFactory from './ToobCabSimView';
|
import ToobCabSimViewFactory from './ToobCabSimView';
|
||||||
import ToobPowerStage2Factory from './ToobPowerStage2View';
|
import ToobPowerStage2Factory from './ToobPowerStage2View';
|
||||||
import ToobSpectrumAnalyzerViewFactory from './ToobSpectrumAnalyzerView';
|
import ToobSpectrumAnalyzerViewFactory from './ToobSpectrumAnalyzerView';
|
||||||
|
import ToobMLViewFactory from './ToobMLView';
|
||||||
|
import {ToobTunerViewFactory,GxTunerViewFactory> from './GxTunerView';
|
||||||
|
|
||||||
|
|
||||||
let pluginFactories: IControlViewFactory[] = [
|
let pluginFactories: IControlViewFactory[] = [
|
||||||
@@ -38,7 +40,10 @@ let pluginFactories: IControlViewFactory[] = [
|
|||||||
new ToobToneStackViewFactory(),
|
new ToobToneStackViewFactory(),
|
||||||
new ToobCabSimViewFactory(),
|
new ToobCabSimViewFactory(),
|
||||||
new ToobPowerStage2Factory(),
|
new ToobPowerStage2Factory(),
|
||||||
new ToobSpectrumAnalyzerViewFactory()
|
new ToobSpectrumAnalyzerViewFactory(),
|
||||||
|
new ToobMLViewFactory(),
|
||||||
|
new GxTunerViewFactory(),
|
||||||
|
new ToobTunerViewFactory()
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
// Copyright (c) 2021 Robin Davies
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to
|
||||||
|
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
import React 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 IControlViewFactory from './IControlViewFactory';
|
||||||
|
import { PiPedalModelFactory, PiPedalModel } from "./PiPedalModel";
|
||||||
|
import { PedalBoardItem } from './PedalBoard';
|
||||||
|
import PluginControlView, { ControlGroup,ControlViewCustomization } from './PluginControlView';
|
||||||
|
import GxTunerControl from './GxTunerControl';
|
||||||
|
|
||||||
|
const GXTUNER_URI = "http://guitarix.sourceforge.net/plugins/gxtuner#tuner";
|
||||||
|
const TOOBTUNER_URI = "http://two-play.com/plugins/toob-tuner";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const styles = (theme: Theme) => createStyles({
|
||||||
|
});
|
||||||
|
|
||||||
|
interface GxTunerProps extends WithStyles<typeof styles> {
|
||||||
|
instanceId: number;
|
||||||
|
item: PedalBoardItem;
|
||||||
|
|
||||||
|
}
|
||||||
|
interface GxTunerState {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const GxTunerView =
|
||||||
|
withStyles(styles, { withTheme: true })(
|
||||||
|
class extends React.Component<GxTunerProps, GxTunerState>
|
||||||
|
implements ControlViewCustomization
|
||||||
|
{
|
||||||
|
model: PiPedalModel;
|
||||||
|
|
||||||
|
customizationId: number = 1;
|
||||||
|
|
||||||
|
constructor(props: GxTunerProps) {
|
||||||
|
super(props);
|
||||||
|
this.model = PiPedalModelFactory.getInstance();
|
||||||
|
this.state = {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getControlIndex(key: string): number {
|
||||||
|
let item = this.props.item;
|
||||||
|
for (let i = 0; i < item.controlValues.length; ++i)
|
||||||
|
{
|
||||||
|
if (item.controlValues[i].key === key)
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error("GxTuner: Control '" + key + "' not found.");
|
||||||
|
}
|
||||||
|
ModifyControls(controls: (React.ReactNode| ControlGroup)[]): (React.ReactNode| ControlGroup)[]
|
||||||
|
{
|
||||||
|
let refFreqIndex = this.getControlIndex("REFFREQ");
|
||||||
|
let thresholdIndex = this.getControlIndex("THRESHOLD");
|
||||||
|
|
||||||
|
let result: (React.ReactNode | ControlGroup)[] = [];
|
||||||
|
|
||||||
|
let tunerControl = (<GxTunerControl instanceId={this.props.instanceId} />);
|
||||||
|
result.push(tunerControl);
|
||||||
|
|
||||||
|
result.push(controls[refFreqIndex]);
|
||||||
|
result.push(controls[thresholdIndex]);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
return (<PluginControlView
|
||||||
|
instanceId={this.props.instanceId}
|
||||||
|
item={this.props.item}
|
||||||
|
customization={this}
|
||||||
|
customizationId={this.customizationId}
|
||||||
|
/>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class GxTunerViewFactory implements IControlViewFactory {
|
||||||
|
uri: string = GXTUNER_URI;
|
||||||
|
|
||||||
|
Create(model: PiPedalModel, pedalBoardItem: PedalBoardItem): React.ReactNode {
|
||||||
|
return (<GxTunerView instanceId={pedalBoardItem.instanceId} item={pedalBoardItem} />);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToobTuner uses the same controls and control semantics.
|
||||||
|
class ToobTunerViewFactory implements IControlViewFactory {
|
||||||
|
uri: string = TOOBTUNER_URI;
|
||||||
|
|
||||||
|
Create(model: PiPedalModel, pedalBoardItem: PedalBoardItem): React.ReactNode {
|
||||||
|
return (<GxTunerView instanceId={pedalBoardItem.instanceId} item={pedalBoardItem} />);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export ToobTunerViewFactory;
|
||||||
|
export GxTunerViewFactory;
|
||||||
@@ -332,7 +332,7 @@ const JackServerSettingsDialog = withStyles(styles)(
|
|||||||
>
|
>
|
||||||
{bufferSizes.map((buffSize) =>
|
{bufferSizes.map((buffSize) =>
|
||||||
(
|
(
|
||||||
<MenuItem value={buffSize}>{buffSize}</MenuItem>
|
<MenuItem key={"b"+buffSize} value={buffSize}>{buffSize}</MenuItem>
|
||||||
|
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -55,6 +55,9 @@ const NARROW_DISPLAY_THRESHOLD = 600;
|
|||||||
const FILTER_STORAGE_KEY = "com.twoplay.pipedal.load_dlg.filter";
|
const FILTER_STORAGE_KEY = "com.twoplay.pipedal.load_dlg.filter";
|
||||||
|
|
||||||
|
|
||||||
|
let lastClickTime: number = 0;
|
||||||
|
|
||||||
|
|
||||||
const pluginGridStyles = (theme: Theme) => createStyles({
|
const pluginGridStyles = (theme: Theme) => createStyles({
|
||||||
frame: {
|
frame: {
|
||||||
|
|
||||||
@@ -338,8 +341,26 @@ export const LoadPluginDialog =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
onClick(e: SyntheticEvent, uri: string): void {
|
onClick(e: SyntheticEvent, uri: string): void {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
let currentTime = Date.now();
|
||||||
|
let dt = currentTime-lastClickTime;
|
||||||
|
let DOUBLE_CLICK_TIME = 500;
|
||||||
|
let isDoubleClick = dt < DOUBLE_CLICK_TIME;
|
||||||
|
lastClickTime = currentTime;
|
||||||
|
|
||||||
|
|
||||||
this.setState({ selected_uri: uri });
|
this.setState({ selected_uri: uri });
|
||||||
|
|
||||||
|
// we have to synthesize double clicks because
|
||||||
|
// DOM rewrites interfere with natural double click.
|
||||||
|
if (isDoubleClick)
|
||||||
|
{
|
||||||
|
this.props.onOk(uri);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
handleMouseEnter(e: SyntheticEvent, uri: string): void {
|
handleMouseEnter(e: SyntheticEvent, uri: string): void {
|
||||||
// this.setHoverUri(uri);
|
// this.setHoverUri(uri);
|
||||||
@@ -373,7 +394,7 @@ export const LoadPluginDialog =
|
|||||||
for (let i = 0; i < classNode.children.length; ++i) {
|
for (let i = 0; i < classNode.children.length; ++i) {
|
||||||
let child = classNode.children[i];
|
let child = classNode.children[i];
|
||||||
let name = "\u00A0".repeat(level * 3 + 1) + child.display_name;
|
let name = "\u00A0".repeat(level * 3 + 1) + child.display_name;
|
||||||
result.push((<MenuItem value={child.plugin_type}>{name}</MenuItem>));
|
result.push((<MenuItem key={child.plugin_type} value={child.plugin_type}>{name}</MenuItem>));
|
||||||
if (child.children.length !== 0) {
|
if (child.children.length !== 0) {
|
||||||
this.createFilterChildren(result, child, level + 1);
|
this.createFilterChildren(result, child, level + 1);
|
||||||
}
|
}
|
||||||
@@ -383,7 +404,7 @@ export const LoadPluginDialog =
|
|||||||
let classes = this.model.plugin_classes.get();
|
let classes = this.model.plugin_classes.get();
|
||||||
let result: ReactNode[] = [];
|
let result: ReactNode[] = [];
|
||||||
|
|
||||||
result.push((<MenuItem value={PluginType.Plugin}> All</MenuItem>));
|
result.push((<MenuItem key={PluginType.Plugin} value={PluginType.Plugin}> All</MenuItem>));
|
||||||
this.createFilterChildren(result, classes, 1);
|
this.createFilterChildren(result, classes, 1);
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ export class Port implements Deserializable<Port> {
|
|||||||
this.is_atom_port = input.is_atom_port;
|
this.is_atom_port = input.is_atom_port;
|
||||||
this.is_valid = input.is_valid;
|
this.is_valid = input.is_valid;
|
||||||
this.supports_midi = input.supports_midi;
|
this.supports_midi = input.supports_midi;
|
||||||
|
this.supports_time_position = input.supports_time_position;
|
||||||
this.port_group = input.port_group;
|
this.port_group = input.port_group;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -70,6 +71,7 @@ export class Port implements Deserializable<Port> {
|
|||||||
is_atom_port: boolean = false;
|
is_atom_port: boolean = false;
|
||||||
is_valid: boolean = false;
|
is_valid: boolean = false;
|
||||||
supports_midi: boolean = false;
|
supports_midi: boolean = false;
|
||||||
|
supports_time_position: boolean = false;
|
||||||
port_group: string = "";
|
port_group: string = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+48
-50
@@ -311,7 +311,7 @@ export interface PiPedalModel {
|
|||||||
presets: ObservableProperty<PresetIndex>;
|
presets: ObservableProperty<PresetIndex>;
|
||||||
|
|
||||||
|
|
||||||
zoomedUiControl: ObservableProperty< ZoomedControlInfo| undefined >;
|
zoomedUiControl: ObservableProperty<ZoomedControlInfo | undefined>;
|
||||||
|
|
||||||
setJackServerSettings(jackServerSettings: JackServerSettings): void;
|
setJackServerSettings(jackServerSettings: JackServerSettings): void;
|
||||||
setMidiBinding(instanceId: number, midiBinding: MidiBinding): void;
|
setMidiBinding(instanceId: number, midiBinding: MidiBinding): void;
|
||||||
@@ -382,7 +382,7 @@ export interface PiPedalModel {
|
|||||||
cancelListenForMidiEvent(listenHandle: ListenHandle): void;
|
cancelListenForMidiEvent(listenHandle: ListenHandle): void;
|
||||||
|
|
||||||
addControlValueChangeListener(instanceId: number, onValueChanged: ControlValueChangedHandler): ControlValueChangedHandle
|
addControlValueChangeListener(instanceId: number, onValueChanged: ControlValueChangedHandler): ControlValueChangedHandle
|
||||||
removeControlValueChangeListener(handle: ControlValueChangedHandle): void;
|
removeControlValueChangeListener(handle: ControlValueChangedHandle): void;
|
||||||
|
|
||||||
listenForAtomOutput(instanceId: number, onComplete: (instanceId: number, atomOutput: any) => void): ListenHandle;
|
listenForAtomOutput(instanceId: number, onComplete: (instanceId: number, atomOutput: any) => void): ListenHandle;
|
||||||
cancelListenForAtomOutput(listenHandle: ListenHandle): void;
|
cancelListenForAtomOutput(listenHandle: ListenHandle): void;
|
||||||
@@ -398,9 +398,9 @@ export interface PiPedalModel {
|
|||||||
|
|
||||||
getWifiChannels(countryIso3661: string): Promise<WifiChannel[]>;
|
getWifiChannels(countryIso3661: string): Promise<WifiChannel[]>;
|
||||||
|
|
||||||
getAlsaDevices() : Promise<AlsaDeviceInfo[]>;
|
getAlsaDevices(): Promise<AlsaDeviceInfo[]>;
|
||||||
|
|
||||||
zoomUiControl(sourceElement: HTMLElement,instanceId: number,uiControl: UiControl): void;
|
zoomUiControl(sourceElement: HTMLElement, instanceId: number, uiControl: UiControl): void;
|
||||||
clearZoomedControl(): void;
|
clearZoomedControl(): void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -438,7 +438,7 @@ class PiPedalModelImpl implements PiPedalModel {
|
|||||||
new PresetIndex()
|
new PresetIndex()
|
||||||
);
|
);
|
||||||
|
|
||||||
zoomedUiControl: ObservableProperty<ZoomedControlInfo | undefined> = new ObservableProperty<ZoomedControlInfo | undefined>(undefined);
|
zoomedUiControl: ObservableProperty<ZoomedControlInfo | undefined> = new ObservableProperty<ZoomedControlInfo | undefined>(undefined);
|
||||||
|
|
||||||
svgImgUrl(svgImage: string): string {
|
svgImgUrl(svgImage: string): string {
|
||||||
//return this.varServerUrl + "img/" + svgImage;
|
//return this.varServerUrl + "img/" + svgImage;
|
||||||
@@ -466,7 +466,7 @@ class PiPedalModelImpl implements PiPedalModel {
|
|||||||
onSocketReconnecting(retry: number, maxRetries: number): void {
|
onSocketReconnecting(retry: number, maxRetries: number): void {
|
||||||
if (this.visibilityState.get() === VisibilityState.Hidden) return;
|
if (this.visibilityState.get() === VisibilityState.Hidden) return;
|
||||||
//if (retry !== 0) {
|
//if (retry !== 0) {
|
||||||
this.setState(State.Reconnecting);
|
this.setState(State.Reconnecting);
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -536,11 +536,11 @@ class PiPedalModelImpl implements PiPedalModel {
|
|||||||
this.handleNotifyMidiListener(clientHandle, isNote, noteOrControl);
|
this.handleNotifyMidiListener(clientHandle, isNote, noteOrControl);
|
||||||
} else if (message === "onNotifyAtomOut") {
|
} else if (message === "onNotifyAtomOut") {
|
||||||
let clientHandle = body.clientHandle as number;
|
let clientHandle = body.clientHandle as number;
|
||||||
let instanceId = body.instanceId as number;
|
let instanceId = body.instanceId as number;
|
||||||
let atomJson = body.atomJson as string;
|
let atomJson = body.atomJson as string;
|
||||||
this.handleNotifyAtomOutput(clientHandle, instanceId,atomJson);
|
this.handleNotifyAtomOutput(clientHandle, instanceId, atomJson);
|
||||||
if (header.replyTo) {
|
if (header.replyTo) {
|
||||||
this.webSocket?.reply(header.replyTo,"onNotifyAtomOut",true);
|
this.webSocket?.reply(header.replyTo, "onNotifyAtomOut", true);
|
||||||
}
|
}
|
||||||
} else if (message === "onControlChanged") {
|
} else if (message === "onControlChanged") {
|
||||||
let controlChangedBody = body as ControlChangedBody;
|
let controlChangedBody = body as ControlChangedBody;
|
||||||
@@ -733,6 +733,10 @@ class PiPedalModelImpl implements PiPedalModel {
|
|||||||
})
|
})
|
||||||
.then((clientId) => {
|
.then((clientId) => {
|
||||||
this.clientId = clientId;
|
this.clientId = clientId;
|
||||||
|
return this.getWebSocket().request<string>("imageList");
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
this.preloadImages(data);
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
@@ -842,20 +846,16 @@ class PiPedalModelImpl implements PiPedalModel {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exitBackgroundState()
|
exitBackgroundState() {
|
||||||
{
|
if (this.state.get() === State.Background) {
|
||||||
if (this.state.get() === State.Background)
|
|
||||||
{
|
|
||||||
console.log("Exiting background state.");
|
console.log("Exiting background state.");
|
||||||
this.visibilityState.set(VisibilityState.Visible);
|
this.visibilityState.set(VisibilityState.Visible);
|
||||||
this.webSocket?.exitBackgroundState();
|
this.webSocket?.exitBackgroundState();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
enterBackgroundState()
|
enterBackgroundState() {
|
||||||
{
|
if (this.state.get() !== State.Background) {
|
||||||
if (this.state.get() !== State.Background)
|
|
||||||
{
|
|
||||||
console.log("Entering background state.");
|
console.log("Entering background state.");
|
||||||
this.visibilityState.set(VisibilityState.Hidden);
|
this.visibilityState.set(VisibilityState.Hidden);
|
||||||
this.setState(State.Background);
|
this.setState(State.Background);
|
||||||
@@ -864,12 +864,10 @@ class PiPedalModelImpl implements PiPedalModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
onVisibilityChanged(doc: Document, event: Event) : any
|
onVisibilityChanged(doc: Document, event: Event): any {
|
||||||
{
|
|
||||||
if (document.visibilityState) {
|
if (document.visibilityState) {
|
||||||
|
|
||||||
switch (document.visibilityState)
|
switch (document.visibilityState) {
|
||||||
{
|
|
||||||
case "visible":
|
case "visible":
|
||||||
this.visibilityState.set(VisibilityState.Visible);
|
this.visibilityState.set(VisibilityState.Visible);
|
||||||
this.exitBackgroundState();
|
this.exitBackgroundState();
|
||||||
@@ -898,8 +896,8 @@ class PiPedalModelImpl implements PiPedalModel {
|
|||||||
});
|
});
|
||||||
let t = this.onVisibilityChanged;
|
let t = this.onVisibilityChanged;
|
||||||
|
|
||||||
(document as any).addEventListener("visibilitychange",(doc: Document,event: Event) => {
|
(document as any).addEventListener("visibilitychange", (doc: Document, event: Event) => {
|
||||||
return t(doc,event);
|
return t(doc, event);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -958,22 +956,18 @@ class PiPedalModelImpl implements PiPedalModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_controlValueChangeItems: ControlValueChangeItem[] = [];
|
_controlValueChangeItems: ControlValueChangeItem[] = [];
|
||||||
|
|
||||||
addControlValueChangeListener(instanceId: number, onValueChanged: ControlValueChangedHandler): ControlValueChangedHandle
|
addControlValueChangeListener(instanceId: number, onValueChanged: ControlValueChangedHandler): ControlValueChangedHandle {
|
||||||
{
|
|
||||||
let handle = ++this.nextListenHandle;
|
let handle = ++this.nextListenHandle;
|
||||||
this._controlValueChangeItems.push({ handle: handle, instanceId: instanceId,onValueChanged: onValueChanged});
|
this._controlValueChangeItems.push({ handle: handle, instanceId: instanceId, onValueChanged: onValueChanged });
|
||||||
return { _ControlValueChangedHandle: handle};
|
return { _ControlValueChangedHandle: handle };
|
||||||
}
|
}
|
||||||
removeControlValueChangeListener(handle: ControlValueChangedHandle)
|
removeControlValueChangeListener(handle: ControlValueChangedHandle) {
|
||||||
{
|
for (let i = 0; i < this._controlValueChangeItems.length; ++i) {
|
||||||
for (let i = 0; i < this._controlValueChangeItems.length; ++i)
|
if (this._controlValueChangeItems[i].handle === handle._ControlValueChangedHandle) {
|
||||||
{
|
this._controlValueChangeItems.splice(i, 1);
|
||||||
if (this._controlValueChangeItems[i].handle === handle._ControlValueChangedHandle)
|
|
||||||
{
|
|
||||||
this._controlValueChangeItems.splice(i,1);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -992,12 +986,10 @@ class PiPedalModelImpl implements PiPedalModel {
|
|||||||
if (notifyServer) {
|
if (notifyServer) {
|
||||||
this._setServerControl("setControl", instanceId, key, value);
|
this._setServerControl("setControl", instanceId, key, value);
|
||||||
}
|
}
|
||||||
for (let i = 0; i < this._controlValueChangeItems.length; ++i)
|
for (let i = 0; i < this._controlValueChangeItems.length; ++i) {
|
||||||
{
|
|
||||||
let item = this._controlValueChangeItems[i];
|
let item = this._controlValueChangeItems[i];
|
||||||
if (instanceId === item.instanceId)
|
if (instanceId === item.instanceId) {
|
||||||
{
|
item.onValueChanged(key, value);
|
||||||
item.onValueChanged(key,value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1557,7 +1549,7 @@ class PiPedalModelImpl implements PiPedalModel {
|
|||||||
listenForAtomOutput(instanceId: number, onComplete: (instanceId: number, atomOutput: any) => void): ListenHandle {
|
listenForAtomOutput(instanceId: number, onComplete: (instanceId: number, atomOutput: any) => void): ListenHandle {
|
||||||
let handle = this.nextListenHandle++;
|
let handle = this.nextListenHandle++;
|
||||||
|
|
||||||
this.atomOutputListeners.push(new AtomOutputListener(handle,instanceId, onComplete));
|
this.atomOutputListeners.push(new AtomOutputListener(handle, instanceId, onComplete));
|
||||||
|
|
||||||
this.webSocket?.send("listenForAtomOutput", { instanceId: instanceId, handle: handle });
|
this.webSocket?.send("listenForAtomOutput", { instanceId: instanceId, handle: handle });
|
||||||
return {
|
return {
|
||||||
@@ -1570,7 +1562,7 @@ class PiPedalModelImpl implements PiPedalModel {
|
|||||||
for (let i = 0; i < this.atomOutputListeners.length; ++i) {
|
for (let i = 0; i < this.atomOutputListeners.length; ++i) {
|
||||||
let listener = this.atomOutputListeners[i];
|
let listener = this.atomOutputListeners[i];
|
||||||
if (listener.handle === clientHandle && listener.instanceId === instanceId) {
|
if (listener.handle === clientHandle && listener.instanceId === instanceId) {
|
||||||
listener.callback(instanceId,jsonObject);
|
listener.callback(instanceId, jsonObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1760,8 +1752,7 @@ class PiPedalModelImpl implements PiPedalModel {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
getAlsaDevices() : Promise<AlsaDeviceInfo[]>
|
getAlsaDevices(): Promise<AlsaDeviceInfo[]> {
|
||||||
{
|
|
||||||
let result = new Promise<AlsaDeviceInfo[]>((resolve, reject) => {
|
let result = new Promise<AlsaDeviceInfo[]>((resolve, reject) => {
|
||||||
if (!this.webSocket) {
|
if (!this.webSocket) {
|
||||||
reject("Connection closed.");
|
reject("Connection closed.");
|
||||||
@@ -1776,15 +1767,22 @@ class PiPedalModelImpl implements PiPedalModel {
|
|||||||
return result;
|
return result;
|
||||||
|
|
||||||
}
|
}
|
||||||
zoomUiControl(sourceElement: HTMLElement,instanceId: number,uiControl: UiControl): void
|
zoomUiControl(sourceElement: HTMLElement, instanceId: number, uiControl: UiControl): void {
|
||||||
{
|
this.zoomedUiControl.set({ source: sourceElement, instanceId: instanceId, uiControl: uiControl });
|
||||||
this.zoomedUiControl.set({source: sourceElement,instanceId: instanceId,uiControl: uiControl});
|
|
||||||
}
|
}
|
||||||
clearZoomedControl(): void
|
clearZoomedControl(): void {
|
||||||
{
|
|
||||||
this.zoomedUiControl.set(undefined);
|
this.zoomedUiControl.set(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
preloadImages(imageList: string): void {
|
||||||
|
let imageNames = imageList.split(';');
|
||||||
|
for (let i = 0; i < imageNames.length; ++i)
|
||||||
|
{
|
||||||
|
let imageName = imageNames[i];
|
||||||
|
let img = new Image();
|
||||||
|
img.src = "/img/" + imageName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import PluginControl from './PluginControl';
|
|||||||
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
||||||
import VuMeter from './VuMeter';
|
import VuMeter from './VuMeter';
|
||||||
import { nullCast } from './Utility'
|
import { nullCast } from './Utility'
|
||||||
import GxTunerControl from './GxTunerControl';
|
|
||||||
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';
|
||||||
@@ -40,7 +39,6 @@ import FullScreenIME from './FullScreenIME';
|
|||||||
export const StandardItemSize = { width: 80, height: 110 };
|
export const StandardItemSize = { width: 80, height: 110 };
|
||||||
|
|
||||||
|
|
||||||
const GXTUNER_URI = "http://guitarix.sourceforge.net/plugins/gxtuner#tuner";
|
|
||||||
|
|
||||||
const LANDSCAPE_HEIGHT_BREAK = 500;
|
const LANDSCAPE_HEIGHT_BREAK = 500;
|
||||||
|
|
||||||
@@ -127,7 +125,7 @@ const styles = (theme: Theme) => createStyles({
|
|||||||
paddingTop: 0,
|
paddingTop: 0,
|
||||||
paddingBottom: 0,
|
paddingBottom: 0,
|
||||||
border: "2pt #AAA solid",
|
border: "2pt #AAA solid",
|
||||||
borderRadius: 4,
|
borderRadius: 8,
|
||||||
elevation: 12,
|
elevation: 12,
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "row", flexWrap: "wrap",
|
flexDirection: "row", flexWrap: "wrap",
|
||||||
@@ -144,7 +142,7 @@ const styles = (theme: Theme) => createStyles({
|
|||||||
paddingTop: 0,
|
paddingTop: 0,
|
||||||
paddingBottom: 0,
|
paddingBottom: 0,
|
||||||
border: "2pt #AAA solid",
|
border: "2pt #AAA solid",
|
||||||
borderRadius: 10,
|
borderRadius: 8,
|
||||||
elevation: 12,
|
elevation: 12,
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "row", flexWrap: "nowrap",
|
flexDirection: "row", flexWrap: "nowrap",
|
||||||
@@ -152,7 +150,7 @@ const styles = (theme: Theme) => createStyles({
|
|||||||
},
|
},
|
||||||
portGroupTitle: {
|
portGroupTitle: {
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
top: -10,
|
top: -15,
|
||||||
background: "white",
|
background: "white",
|
||||||
marginLeft: 20,
|
marginLeft: 20,
|
||||||
paddingLeft: 8,
|
paddingLeft: 8,
|
||||||
@@ -344,35 +342,6 @@ const PluginControlView =
|
|||||||
throw new Error("Not found.");
|
throw new Error("Not found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
getTunerControls(plugin: UiPlugin, controlValues: ControlValue[]): ControlNodes {
|
|
||||||
// display standard version of just a few controls.
|
|
||||||
let shortList: ControlValue[] = [];
|
|
||||||
// for (let i = 0; i < controlValues.length; ++i)
|
|
||||||
// {
|
|
||||||
// shortList.push(controlValues[i]);
|
|
||||||
// }
|
|
||||||
shortList.push(this.getControl(controlValues, "REFFREQ"));
|
|
||||||
shortList.push(this.getControl(controlValues, "THRESHOLD"));
|
|
||||||
|
|
||||||
|
|
||||||
let controls = shortList.map((controlValue) => (
|
|
||||||
<PluginControl instanceId={this.props.instanceId} uiControl={plugin.getControl(controlValue.key)} value={controlValue.value}
|
|
||||||
onChange={(value: number) => { this.onValueChanged(controlValue.key, value) }}
|
|
||||||
onPreviewChange={(value: number) => { this.onPreviewChange(controlValue.key, value) }}
|
|
||||||
requestIMEEdit={(uiControl, value) => this.requestImeEdit(uiControl, value)}
|
|
||||||
/>
|
|
||||||
));
|
|
||||||
let tunerControl = (<GxTunerControl instanceId={this.props.instanceId} />);
|
|
||||||
let result: ControlNodes = [];
|
|
||||||
result.push(tunerControl);
|
|
||||||
|
|
||||||
for (let i = 0; i < controls.length; ++i)
|
|
||||||
{
|
|
||||||
result.push(controls[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
onImeValueChange(key: string, value: number) {
|
onImeValueChange(key: string, value: number) {
|
||||||
this.model.setPedalBoardControlValue(this.props.instanceId, key, value);
|
this.model.setPedalBoardControlValue(this.props.instanceId, key, value);
|
||||||
this.onImeClose();
|
this.onImeClose();
|
||||||
@@ -464,11 +433,8 @@ const PluginControlView =
|
|||||||
let vuMeterRClass = this.state.landscapeGrid ? classes.vuMeterRLandscape : classes.vuMeterR;
|
let vuMeterRClass = this.state.landscapeGrid ? classes.vuMeterRLandscape : classes.vuMeterR;
|
||||||
let controlNodes: ControlNodes;
|
let controlNodes: ControlNodes;
|
||||||
|
|
||||||
if (plugin.uri === GXTUNER_URI) {
|
controlNodes = this.getStandardControlNodes(plugin, controlValues);
|
||||||
controlNodes = this.getTunerControls(plugin, controlValues);
|
|
||||||
} else {
|
|
||||||
controlNodes = this.getStandardControlNodes(plugin, controlValues);
|
|
||||||
}
|
|
||||||
if (this.props.customization)
|
if (this.props.customization)
|
||||||
{
|
{
|
||||||
// allow wrapper class to insert/remove/rebuild controls.
|
// allow wrapper class to insert/remove/rebuild controls.
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ function makeControls(controls: UiControl[]) {
|
|||||||
<Grid container direction="row" justifyContent="flex-start" alignItems="flex-start" spacing={1} style={{ paddingLeft: "24px" }}>
|
<Grid container direction="row" justifyContent="flex-start" alignItems="flex-start" spacing={1} style={{ paddingLeft: "24px" }}>
|
||||||
{
|
{
|
||||||
controls.map((control) => (
|
controls.map((control) => (
|
||||||
<Grid item spacing={2} xs={6} sm={4} key={control.symbol} >
|
<Grid xs={6} sm={4} key={control.symbol} >
|
||||||
<Typography variant="body2">
|
<Typography variant="body2">
|
||||||
{control.name}
|
{control.name}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|||||||
@@ -279,7 +279,8 @@ const PluginPresetSelector =
|
|||||||
<Typography variant="caption" color="textSecondary" style={{marginLeft: 16,marginTop:4,marginBottom: 4}}>Plugin presets</Typography>
|
<Typography variant="caption" color="textSecondary" style={{marginLeft: 16,marginTop:4,marginBottom: 4}}>Plugin presets</Typography>
|
||||||
{
|
{
|
||||||
this.state.presets.map((preset) => {
|
this.state.presets.map((preset) => {
|
||||||
return (<MenuItem onClick={(e) => this.handleLoadPluginPreset(preset.presetUri)}>{preset.name}</MenuItem>);
|
return (<MenuItem key={preset.presetUri}
|
||||||
|
onClick={(e) => this.handleLoadPluginPreset(preset.presetUri)}>{preset.name}</MenuItem>);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
{/*
|
{/*
|
||||||
|
|||||||
@@ -46,6 +46,10 @@ const styles = (theme: Theme) => createStyles({
|
|||||||
|
|
||||||
interface ToobFrequencyResponseProps extends WithStyles<typeof styles> {
|
interface ToobFrequencyResponseProps extends WithStyles<typeof styles> {
|
||||||
instanceId: number;
|
instanceId: number;
|
||||||
|
maxDb?: number;
|
||||||
|
minDb?: number;
|
||||||
|
minFrequency?: number;
|
||||||
|
maxFrequency?: number;
|
||||||
|
|
||||||
}
|
}
|
||||||
interface ToobFrequencyResponseState {
|
interface ToobFrequencyResponseState {
|
||||||
@@ -104,8 +108,8 @@ const ToobFrequencyResponseView =
|
|||||||
requestOutstanding: boolean = false;
|
requestOutstanding: boolean = false;
|
||||||
requestDeferred: boolean = false;
|
requestDeferred: boolean = false;
|
||||||
|
|
||||||
dbMinOpt: number = -35;
|
dbMinDefault: number = -35;
|
||||||
dbMaxOpt: number = 5;
|
dbMaxDefault: number = 5;
|
||||||
dbTickSpacingOpt: number = 10;
|
dbTickSpacingOpt: number = 10;
|
||||||
|
|
||||||
|
|
||||||
@@ -120,8 +124,6 @@ const ToobFrequencyResponseView =
|
|||||||
xMax: number = PLOT_WIDTH+4;
|
xMax: number = PLOT_WIDTH+4;
|
||||||
yMin: number = 0;
|
yMin: number = 0;
|
||||||
yMax: number = PLOT_HEIGHT;
|
yMax: number = PLOT_HEIGHT;
|
||||||
dbMin: number = this.dbMaxOpt; // deliberately reversed to flip up and down.
|
|
||||||
dbMax: number = this.dbMinOpt;
|
|
||||||
dbTickSpacing: number = this.dbTickSpacingOpt;
|
dbTickSpacing: number = this.dbTickSpacingOpt;
|
||||||
|
|
||||||
toX(frequency: number): number
|
toX(frequency: number): number
|
||||||
@@ -189,6 +191,10 @@ const ToobFrequencyResponseView =
|
|||||||
|
|
||||||
currentPath: string = "";
|
currentPath: string = "";
|
||||||
|
|
||||||
|
majorGridLine(x0: number, y0: number, x1: number, y1: number): React.ReactNode {
|
||||||
|
return (<line x1={x0} y1={y0} x2={x1} y2={y1} fill='none' stroke="#FFF" strokeWidth="0.75" opacity="1" />);
|
||||||
|
}
|
||||||
|
|
||||||
gridLine(x0: number, y0: number, x1: number, y1: number): React.ReactNode {
|
gridLine(x0: number, y0: number, x1: number, y1: number): React.ReactNode {
|
||||||
return (<line x1={x0} y1={y0} x2={x1} y2={y1} fill='none' stroke="#FFF" strokeWidth="0.25" opacity="1" />);
|
return (<line x1={x0} y1={y0} x2={x1} y2={y1} fill='none' stroke="#FFF" strokeWidth="0.25" opacity="1" />);
|
||||||
}
|
}
|
||||||
@@ -199,27 +205,54 @@ const ToobFrequencyResponseView =
|
|||||||
for (var db = Math.ceil(this.dbMax/this.dbTickSpacing)*this.dbTickSpacing; db < this.dbMin; db += this.dbTickSpacing )
|
for (var db = Math.ceil(this.dbMax/this.dbTickSpacing)*this.dbTickSpacing; db < this.dbMin; db += this.dbTickSpacing )
|
||||||
{
|
{
|
||||||
var y = (db-this.dbMin)/(this.dbMax-this.dbMin)*(this.yMax-this.yMin);
|
var y = (db-this.dbMin)/(this.dbMax-this.dbMin)*(this.yMax-this.yMin);
|
||||||
result.push(
|
if (db === 0)
|
||||||
this.gridLine(this.xMin,y,this.xMax,y)
|
{
|
||||||
);
|
result.push(
|
||||||
|
this.majorGridLine(this.xMin,y,this.xMax,y)
|
||||||
|
);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
result.push(
|
||||||
|
this.gridLine(this.xMin,y,this.xMax,y)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let decade0 = Math.pow(10,Math.floor(Math.log10(this.fMin)));
|
let decade0 = Math.pow(10,Math.floor(Math.log10(this.fMin)));
|
||||||
for (var decade = decade0; decade < this.fMax; decade *= 10)
|
for (var decade = decade0; decade < this.fMax; decade *= 10)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < 10; ++i)
|
for (var i = 1; i <= 10; ++i)
|
||||||
{
|
{
|
||||||
var f = decade*i;
|
var f = decade*i;
|
||||||
if (f > this.fMin && f < this.fMax)
|
if (f > this.fMin && f < this.fMax)
|
||||||
{
|
{
|
||||||
var x = this.toX(f);
|
var x = this.toX(f);
|
||||||
result.push(this.gridLine(x,this.yMin,x,this.yMax));
|
if (i === 10)
|
||||||
|
{
|
||||||
|
result.push(this.majorGridLine(x,this.yMin,x,this.yMax));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
result.push(this.gridLine(x,this.yMin,x,this.yMax));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dbMin: number = 5;
|
||||||
|
dbMax: number = -35;
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
// deliberately reversed to flip up and down.
|
||||||
|
this.dbMax = this.props.minDb ?? this.dbMinDefault;
|
||||||
|
this.dbMin = this.props.maxDb ?? this.dbMaxDefault;
|
||||||
|
|
||||||
|
this.fMin = this.props.minFrequency ?? 30;
|
||||||
|
this.fMax = this.props.maxFrequency ?? 20000;
|
||||||
|
this.logMin = Math.log(this.fMin);
|
||||||
|
this.logMax = Math.log(this.fMax);
|
||||||
|
|
||||||
|
|
||||||
let classes = this.props.classes;
|
let classes = this.props.classes;
|
||||||
return (
|
return (
|
||||||
<div className={classes.frame} >
|
<div className={classes.frame} >
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
// Copyright (c) 2021 Robin Davies
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
// this software and associated documentation files (the "Software"), to deal in
|
||||||
|
// the Software without restriction, including without limitation the rights to
|
||||||
|
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
// subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
import React 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 IControlViewFactory from './IControlViewFactory';
|
||||||
|
import { PiPedalModelFactory, PiPedalModel } from "./PiPedalModel";
|
||||||
|
import { PedalBoardItem } from './PedalBoard';
|
||||||
|
import PluginControlView, { ControlGroup,ControlViewCustomization } from './PluginControlView';
|
||||||
|
import ToobFrequencyResponseView from './ToobFrequencyResponseView';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const styles = (theme: Theme) => createStyles({
|
||||||
|
});
|
||||||
|
|
||||||
|
interface ToobMLProps extends WithStyles<typeof styles> {
|
||||||
|
instanceId: number;
|
||||||
|
item: PedalBoardItem;
|
||||||
|
|
||||||
|
}
|
||||||
|
interface ToobMLState {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const ToobMLView =
|
||||||
|
withStyles(styles, { withTheme: true })(
|
||||||
|
class extends React.Component<ToobMLProps, ToobMLState>
|
||||||
|
implements ControlViewCustomization
|
||||||
|
{
|
||||||
|
model: PiPedalModel;
|
||||||
|
|
||||||
|
customizationId: number = 1;
|
||||||
|
|
||||||
|
constructor(props: ToobMLProps) {
|
||||||
|
super(props);
|
||||||
|
this.model = PiPedalModelFactory.getInstance();
|
||||||
|
this.state = {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ModifyControls(controls: (React.ReactNode| ControlGroup)[]): (React.ReactNode| ControlGroup)[]
|
||||||
|
{
|
||||||
|
let group = controls[1] as ControlGroup;
|
||||||
|
group.controls.splice(0,0,
|
||||||
|
( <ToobFrequencyResponseView instanceId={this.props.instanceId} minDb={-20} maxDb={20} />)
|
||||||
|
);
|
||||||
|
return controls;
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
return (<PluginControlView
|
||||||
|
instanceId={this.props.instanceId}
|
||||||
|
item={this.props.item}
|
||||||
|
customization={this}
|
||||||
|
customizationId={this.customizationId}
|
||||||
|
/>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ToobMLViewFactory implements IControlViewFactory {
|
||||||
|
uri: string = "http://two-play.com/plugins/toob-ml";
|
||||||
|
|
||||||
|
Create(model: PiPedalModel, pedalBoardItem: PedalBoardItem): React.ReactNode {
|
||||||
|
return (<ToobMLView instanceId={pedalBoardItem.instanceId} item={pedalBoardItem} />);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
export default ToobMLViewFactory;
|
||||||
@@ -25,7 +25,7 @@ import createStyles from '@mui/styles/createStyles';
|
|||||||
import withStyles from '@mui/styles/withStyles';
|
import withStyles from '@mui/styles/withStyles';
|
||||||
|
|
||||||
import IControlViewFactory from './IControlViewFactory';
|
import IControlViewFactory from './IControlViewFactory';
|
||||||
import { PiPedalModelFactory, PiPedalModel } from "./PiPedalModel";
|
import { PiPedalModelFactory, PiPedalModel,ControlValueChangedHandle } from "./PiPedalModel";
|
||||||
import { PedalBoardItem } from './PedalBoard';
|
import { PedalBoardItem } from './PedalBoard';
|
||||||
import PluginControlView, { ControlGroup,ControlViewCustomization } from './PluginControlView';
|
import PluginControlView, { ControlGroup,ControlViewCustomization } from './PluginControlView';
|
||||||
import ToobFrequencyResponseView from './ToobFrequencyResponseView';
|
import ToobFrequencyResponseView from './ToobFrequencyResponseView';
|
||||||
@@ -41,7 +41,7 @@ interface ToobToneStackProps extends WithStyles<typeof styles> {
|
|||||||
|
|
||||||
}
|
}
|
||||||
interface ToobToneStackState {
|
interface ToobToneStackState {
|
||||||
|
isBaxandall: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ToobToneStackView =
|
const ToobToneStackView =
|
||||||
@@ -57,14 +57,48 @@ const ToobToneStackView =
|
|||||||
super(props);
|
super(props);
|
||||||
this.model = PiPedalModelFactory.getInstance();
|
this.model = PiPedalModelFactory.getInstance();
|
||||||
this.state = {
|
this.state = {
|
||||||
|
isBaxandall: this.IsBaxandall()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IsBaxandall() : boolean {
|
||||||
|
return this.props.item.getControl("ampmodel").value === 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
controlValueChangedHandle?: ControlValueChangedHandle;
|
||||||
|
componentDidMount()
|
||||||
|
{
|
||||||
|
this.controlValueChangedHandle = this.model.addControlValueChangeListener(
|
||||||
|
this.props.instanceId,
|
||||||
|
(key,value) => {
|
||||||
|
if (key === "ampmodel")
|
||||||
|
{
|
||||||
|
this.setState({isBaxandall: value === 2.0});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this.controlValueChangedHandle)
|
||||||
|
{
|
||||||
|
this.model.removeControlValueChangeListener(this.controlValueChangedHandle);
|
||||||
|
this.controlValueChangedHandle = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ModifyControls(controls: (React.ReactNode| ControlGroup)[]): (React.ReactNode| ControlGroup)[]
|
ModifyControls(controls: (React.ReactNode| ControlGroup)[]): (React.ReactNode| ControlGroup)[]
|
||||||
{
|
{
|
||||||
controls.splice(0,0,
|
if (this.state.isBaxandall)
|
||||||
( <ToobFrequencyResponseView instanceId={this.props.instanceId} />)
|
{
|
||||||
);
|
controls.splice(0,0,
|
||||||
|
( <ToobFrequencyResponseView instanceId={this.props.instanceId}
|
||||||
|
minDb={-20} maxDb={20} />)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
controls.splice(0,0,
|
||||||
|
( <ToobFrequencyResponseView instanceId={this.props.instanceId} />)
|
||||||
|
);
|
||||||
|
}
|
||||||
return controls;
|
return controls;
|
||||||
}
|
}
|
||||||
render() {
|
render() {
|
||||||
|
|||||||
+7
-3
@@ -754,7 +754,7 @@ public:
|
|||||||
#else
|
#else
|
||||||
xxx; // TODO!
|
xxx; // TODO!
|
||||||
#endif
|
#endif
|
||||||
|
int underrunMessagesGiven = 0;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -787,8 +787,12 @@ public:
|
|||||||
uint64_t underruns = this->underruns;
|
uint64_t underruns = this->underruns;
|
||||||
if (underruns != lastUnderrunCount)
|
if (underruns != lastUnderrunCount)
|
||||||
{
|
{
|
||||||
Lv2Log::info("Jack - Underrun count: %lu", (unsigned long)underruns);
|
if (underrunMessagesGiven < 60) // limit how much log file clutter we generate.
|
||||||
lastUnderrunCount = underruns;
|
{
|
||||||
|
Lv2Log::info("Jack - Underrun count: %lu", (unsigned long)underruns);
|
||||||
|
lastUnderrunCount = underruns;
|
||||||
|
++underrunMessagesGiven;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
clock_gettime(CLOCK_REALTIME, &ts);
|
clock_gettime(CLOCK_REALTIME, &ts);
|
||||||
ts.tv_sec += pollRateS;
|
ts.tv_sec += pollRateS;
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
#include "lv2/urid.lv2/urid.h"
|
#include "lv2/urid.lv2/urid.h"
|
||||||
#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/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"
|
||||||
@@ -40,8 +41,10 @@
|
|||||||
#include "lv2/lv2plug.in/ns/ext/port-groups/port-groups.h"
|
#include "lv2/lv2plug.in/ns/ext/port-groups/port-groups.h"
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include "PiPedalException.hpp"
|
#include "PiPedalException.hpp"
|
||||||
|
|
||||||
#include "Locale.hpp"
|
#include "Locale.hpp"
|
||||||
|
|
||||||
|
|
||||||
using namespace pipedal;
|
using namespace pipedal;
|
||||||
|
|
||||||
#define MAP_CHECK() \
|
#define MAP_CHECK() \
|
||||||
@@ -115,6 +118,12 @@ void Lv2Host::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);
|
||||||
|
|
||||||
|
time_Position = lilv_new_uri(pWorld, LV2_TIME__Position);
|
||||||
|
time_barBeat = lilv_new_uri(pWorld, LV2_TIME__barBeat);
|
||||||
|
time_beatsPerMinute = lilv_new_uri(pWorld, LV2_TIME__beatsPerMinute);
|
||||||
|
time_speed = lilv_new_uri(pWorld, LV2_TIME__speed);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Lv2Host::LilvUris::Free()
|
void Lv2Host::LilvUris::Free()
|
||||||
@@ -137,6 +146,11 @@ void Lv2Host::LilvUris::Free()
|
|||||||
symbolUri.Free();
|
symbolUri.Free();
|
||||||
nameUri.Free();
|
nameUri.Free();
|
||||||
|
|
||||||
|
time_Position.Free();
|
||||||
|
time_barBeat.Free();
|
||||||
|
time_beatsPerMinute.Free();
|
||||||
|
time_speed.Free();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string nodeAsString(const LilvNode *node)
|
static std::string nodeAsString(const LilvNode *node)
|
||||||
@@ -398,10 +412,12 @@ void Lv2Host::Load(const char *lv2Path)
|
|||||||
{
|
{
|
||||||
Lv2Log::debug("Plugin %s (%s) skipped. (Has CV ports).", pluginInfo->name().c_str(), pluginInfo->uri().c_str());
|
Lv2Log::debug("Plugin %s (%s) skipped. (Has CV ports).", pluginInfo->name().c_str(), pluginInfo->uri().c_str());
|
||||||
}
|
}
|
||||||
|
#if !SUPPORT_MIDI
|
||||||
else if (pluginInfo->plugin_class() == LV2_MIDI_PLUGIN)
|
else if (pluginInfo->plugin_class() == LV2_MIDI_PLUGIN)
|
||||||
{
|
{
|
||||||
Lv2Log::debug("Plugin %s (%s) skipped. (MIDI Plugin).", pluginInfo->name().c_str(), pluginInfo->uri().c_str());
|
Lv2Log::debug("Plugin %s (%s) skipped. (MIDI Plugin).", pluginInfo->name().c_str(), pluginInfo->uri().c_str());
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
else if (!pluginInfo->is_valid())
|
else if (!pluginInfo->is_valid())
|
||||||
{
|
{
|
||||||
auto &ports = pluginInfo->ports();
|
auto &ports = pluginInfo->ports();
|
||||||
@@ -439,6 +455,16 @@ void Lv2Host::Load(const char *lv2Path)
|
|||||||
Lv2PluginUiInfo info(this, plugin.get());
|
Lv2PluginUiInfo info(this, plugin.get());
|
||||||
if (plugin->is_valid())
|
if (plugin->is_valid())
|
||||||
{
|
{
|
||||||
|
#if SUPPORT_MIDI
|
||||||
|
if (info.audio_inputs() == 0 && !info.has_midi_input())
|
||||||
|
{
|
||||||
|
Lv2Log::debug("Plugin %s (%s) skipped. No inputs.", plugin->name().c_str(), plugin->uri().c_str());
|
||||||
|
}
|
||||||
|
else if (info.audio_outputs() == 0 && !info.has_midi_output())
|
||||||
|
{
|
||||||
|
Lv2Log::debug("Plugin %s (%s) skipped. No audio outputs.", plugin->name().c_str(), plugin->uri().c_str());
|
||||||
|
}
|
||||||
|
#else
|
||||||
if (info.audio_inputs() == 0)
|
if (info.audio_inputs() == 0)
|
||||||
{
|
{
|
||||||
Lv2Log::debug("Plugin %s (%s) skipped. No audio inputs.", plugin->name().c_str(), plugin->uri().c_str());
|
Lv2Log::debug("Plugin %s (%s) skipped. No audio inputs.", plugin->name().c_str(), plugin->uri().c_str());
|
||||||
@@ -447,6 +473,7 @@ void Lv2Host::Load(const char *lv2Path)
|
|||||||
{
|
{
|
||||||
Lv2Log::debug("Plugin %s (%s) skipped. No audio outputs.", plugin->name().c_str(), plugin->uri().c_str());
|
Lv2Log::debug("Plugin %s (%s) skipped. No audio outputs.", plugin->name().c_str(), plugin->uri().c_str());
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ui_plugins_.push_back(std::move(info));
|
ui_plugins_.push_back(std::move(info));
|
||||||
@@ -718,6 +745,7 @@ Lv2PortInfo::Lv2PortInfo(Lv2Host *host, const LilvPlugin *plugin, const LilvPort
|
|||||||
is_cv_port_ = is_a(host, LV2_CORE__CVPort);
|
is_cv_port_ = is_a(host, LV2_CORE__CVPort);
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
NodeAutoFree designationValue = lilv_port_get(plugin, pPort, host->lilvUris.designationNode);
|
NodeAutoFree designationValue = lilv_port_get(plugin, pPort, host->lilvUris.designationNode);
|
||||||
designation_ = nodeAsString(designationValue);
|
designation_ = nodeAsString(designationValue);
|
||||||
@@ -1096,6 +1124,7 @@ json_map::storage_type<Lv2PortInfo> Lv2PortInfo::jmap{{
|
|||||||
json_map::reference("is_valid", &Lv2PortInfo::is_valid_),
|
json_map::reference("is_valid", &Lv2PortInfo::is_valid_),
|
||||||
|
|
||||||
json_map::reference("supports_midi", &Lv2PortInfo::supports_midi_),
|
json_map::reference("supports_midi", &Lv2PortInfo::supports_midi_),
|
||||||
|
json_map::reference("supports_time_position", &Lv2PortInfo::supports_time_position_),
|
||||||
json_map::reference("port_group", &Lv2PortInfo::port_group_),
|
json_map::reference("port_group", &Lv2PortInfo::port_group_),
|
||||||
json_map::reference("is_logarithmic", &Lv2PortInfo::is_logarithmic_),
|
json_map::reference("is_logarithmic", &Lv2PortInfo::is_logarithmic_),
|
||||||
|
|
||||||
|
|||||||
@@ -208,6 +208,7 @@ private:
|
|||||||
|
|
||||||
bool is_valid_ = false;
|
bool is_valid_ = false;
|
||||||
bool supports_midi_ = false;
|
bool supports_midi_ = false;
|
||||||
|
bool supports_time_position_ = false;
|
||||||
bool is_logarithmic_ = false;
|
bool is_logarithmic_ = false;
|
||||||
int display_priority_ = -1;
|
int display_priority_ = -1;
|
||||||
int range_steps_ = 0;
|
int range_steps_ = 0;
|
||||||
@@ -267,6 +268,7 @@ public:
|
|||||||
LV2_PROPERTY_GETSET_SCALAR(is_cv_port);
|
LV2_PROPERTY_GETSET_SCALAR(is_cv_port);
|
||||||
LV2_PROPERTY_GETSET_SCALAR(is_valid);
|
LV2_PROPERTY_GETSET_SCALAR(is_valid);
|
||||||
LV2_PROPERTY_GETSET_SCALAR(supports_midi);
|
LV2_PROPERTY_GETSET_SCALAR(supports_midi);
|
||||||
|
LV2_PROPERTY_GETSET_SCALAR(supports_time_position);
|
||||||
LV2_PROPERTY_GETSET_SCALAR(is_logarithmic);
|
LV2_PROPERTY_GETSET_SCALAR(is_logarithmic);
|
||||||
LV2_PROPERTY_GETSET_SCALAR(display_priority);
|
LV2_PROPERTY_GETSET_SCALAR(display_priority);
|
||||||
LV2_PROPERTY_GETSET_SCALAR(range_steps);
|
LV2_PROPERTY_GETSET_SCALAR(range_steps);
|
||||||
@@ -381,6 +383,24 @@ public:
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
bool hasMidiInput() const {
|
||||||
|
for (size_t i = 0; i < ports_.size(); ++i)
|
||||||
|
{
|
||||||
|
if (ports_[i]->is_atom_port() && ports_[i]->supports_midi() && ports_[i]->is_input())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool hasMidiOutput() const {
|
||||||
|
for (size_t i = 0; i < ports_.size(); ++i)
|
||||||
|
{
|
||||||
|
if (ports_[i]->is_atom_port() && ports_[i]->supports_midi() && ports_[i]->is_output())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -598,6 +618,12 @@ private:
|
|||||||
LilvNodePtr symbolUri;
|
LilvNodePtr symbolUri;
|
||||||
LilvNodePtr nameUri;
|
LilvNodePtr nameUri;
|
||||||
|
|
||||||
|
LilvNodePtr time_Position;
|
||||||
|
LilvNodePtr time_barBeat;
|
||||||
|
LilvNodePtr time_beatsPerMinute;
|
||||||
|
LilvNodePtr time_speed;
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
LilvUris lilvUris;
|
LilvUris lilvUris;
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ class PiPedalConfiguration
|
|||||||
private:
|
private:
|
||||||
std::string lv2_path_ = "/usr/lib/lv2:/usr/local/lib/lv2";
|
std::string lv2_path_ = "/usr/lib/lv2:/usr/local/lib/lv2";
|
||||||
std::filesystem::path docRoot_;
|
std::filesystem::path docRoot_;
|
||||||
|
std::filesystem::path webRoot_;
|
||||||
std::string local_storage_path_;
|
std::string local_storage_path_;
|
||||||
bool mlock_ = true;
|
bool mlock_ = true;
|
||||||
std::vector<std::string> reactServerAddresses_ = {"*:5000"};
|
std::vector<std::string> reactServerAddresses_ = {"*:5000"};
|
||||||
@@ -52,7 +53,10 @@ public:
|
|||||||
std::filesystem::path GetConfigFilePath() const {
|
std::filesystem::path GetConfigFilePath() const {
|
||||||
return docRoot_ / "config.jason";
|
return docRoot_ / "config.jason";
|
||||||
}
|
}
|
||||||
void Load(std::filesystem::path path) {
|
const std::filesystem::path& GetWebRoot() const {
|
||||||
|
return webRoot_;
|
||||||
|
}
|
||||||
|
void Load(const std::filesystem::path& path, const std::filesystem::path&webRoot) {
|
||||||
std::filesystem::path configPath = path / "config.json";
|
std::filesystem::path configPath = path / "config.json";
|
||||||
if (!std::filesystem::exists(configPath))
|
if (!std::filesystem::exists(configPath))
|
||||||
{
|
{
|
||||||
@@ -68,6 +72,8 @@ public:
|
|||||||
json_reader reader(f);
|
json_reader reader(f);
|
||||||
reader.read(this);
|
reader.read(this);
|
||||||
docRoot_ = path;
|
docRoot_ = path;
|
||||||
|
|
||||||
|
webRoot_ = webRoot;
|
||||||
|
|
||||||
}
|
}
|
||||||
const std::filesystem::path &GetDocRoot() const { return docRoot_; }
|
const std::filesystem::path &GetDocRoot() const { return docRoot_; }
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ void PiPedalModel::LoadLv2PluginInfo(const PiPedalConfiguration&configuration)
|
|||||||
|
|
||||||
void PiPedalModel::Load(const PiPedalConfiguration &configuration)
|
void PiPedalModel::Load(const PiPedalConfiguration &configuration)
|
||||||
{
|
{
|
||||||
|
this->webRoot = configuration.GetWebRoot();
|
||||||
this->jackServerSettings.ReadJackConfiguration();
|
this->jackServerSettings.ReadJackConfiguration();
|
||||||
|
|
||||||
|
|
||||||
@@ -1164,4 +1164,9 @@ void PiPedalModel::cancelListenForAtomOutputs(int64_t clientId, int64_t clientHa
|
|||||||
std::vector<AlsaDeviceInfo> PiPedalModel::GetAlsaDevices()
|
std::vector<AlsaDeviceInfo> PiPedalModel::GetAlsaDevices()
|
||||||
{
|
{
|
||||||
return this->alsaDevices.GetAlsaDevices();
|
return this->alsaDevices.GetAlsaDevices();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::filesystem::path& PiPedalModel::GetWebRoot() const
|
||||||
|
{
|
||||||
|
return webRoot;
|
||||||
|
}
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ private:
|
|||||||
std::unique_ptr<JackHost> jackHost;
|
std::unique_ptr<JackHost> jackHost;
|
||||||
JackConfiguration jackConfiguration;
|
JackConfiguration jackConfiguration;
|
||||||
std::shared_ptr<Lv2PedalBoard> lv2PedalBoard;
|
std::shared_ptr<Lv2PedalBoard> lv2PedalBoard;
|
||||||
|
std::filesystem::path webRoot;
|
||||||
|
|
||||||
|
|
||||||
std::vector<IPiPedalModelSubscriber*> subscribers;
|
std::vector<IPiPedalModelSubscriber*> subscribers;
|
||||||
@@ -219,7 +220,7 @@ public:
|
|||||||
void cancelListenForAtomOutputs(int64_t clientId, int64_t clientHandle);
|
void cancelListenForAtomOutputs(int64_t clientId, int64_t clientHandle);
|
||||||
|
|
||||||
std::vector<AlsaDeviceInfo> GetAlsaDevices();
|
std::vector<AlsaDeviceInfo> GetAlsaDevices();
|
||||||
|
const std::filesystem::path& GetWebRoot() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace pipedal.
|
} // namespace pipedal.
|
||||||
+21
-1
@@ -34,6 +34,7 @@
|
|||||||
#include "WifiChannels.hpp"
|
#include "WifiChannels.hpp"
|
||||||
#include "SysExec.hpp"
|
#include "SysExec.hpp"
|
||||||
#include "PiPedalAlsa.hpp"
|
#include "PiPedalAlsa.hpp"
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace pipedal;
|
using namespace pipedal;
|
||||||
@@ -344,6 +345,7 @@ private:
|
|||||||
std::recursive_mutex writeMutex;
|
std::recursive_mutex writeMutex;
|
||||||
PiPedalModel &model;
|
PiPedalModel &model;
|
||||||
static std::atomic<uint64_t> nextClientId;
|
static std::atomic<uint64_t> nextClientId;
|
||||||
|
std::string imageList;
|
||||||
|
|
||||||
uint64_t clientId;
|
uint64_t clientId;
|
||||||
// pedalboard is mutable and not thread-safe.
|
// pedalboard is mutable and not thread-safe.
|
||||||
@@ -390,6 +392,19 @@ public:
|
|||||||
PiPedalSocketHandler(PiPedalModel &model)
|
PiPedalSocketHandler(PiPedalModel &model)
|
||||||
: model(model), clientId(++nextClientId)
|
: model(model), clientId(++nextClientId)
|
||||||
{
|
{
|
||||||
|
std::stringstream imageList;
|
||||||
|
const std::filesystem::path& webRoot = model.GetWebRoot() / "img";
|
||||||
|
bool firstTime = true;
|
||||||
|
for (const auto&entry: std::filesystem::directory_iterator(webRoot))
|
||||||
|
{
|
||||||
|
if (!firstTime)
|
||||||
|
{
|
||||||
|
imageList << ";";
|
||||||
|
}
|
||||||
|
firstTime = false;
|
||||||
|
imageList << entry.path().filename().string();
|
||||||
|
}
|
||||||
|
this->imageList = imageList.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -1067,7 +1082,7 @@ public:
|
|||||||
std::lock_guard<std::recursive_mutex> guard(subscriptionMutex);
|
std::lock_guard<std::recursive_mutex> guard(subscriptionMutex);
|
||||||
activeVuSubscriptions.push_back(VuSubscription{subscriptionHandle, instanceId});
|
activeVuSubscriptions.push_back(VuSubscription{subscriptionHandle, instanceId});
|
||||||
}
|
}
|
||||||
this->Reply(replyTo, "addVuSubscriptin", subscriptionHandle);
|
this->Reply(replyTo, "addVuSubscription", subscriptionHandle);
|
||||||
}
|
}
|
||||||
else if (message == "removeVuSubscription")
|
else if (message == "removeVuSubscription")
|
||||||
{
|
{
|
||||||
@@ -1087,6 +1102,11 @@ public:
|
|||||||
}
|
}
|
||||||
model.removeVuSubscription(subscriptionHandle);
|
model.removeVuSubscription(subscriptionHandle);
|
||||||
}
|
}
|
||||||
|
else if (message == "imageList")
|
||||||
|
{
|
||||||
|
|
||||||
|
this->Reply(replyTo, "imageList", imageList);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Lv2Log::error("Unknown message received: %s", message.c_str());
|
Lv2Log::error("Unknown message received: %s", message.c_str());
|
||||||
|
|||||||
+47
-13
@@ -416,6 +416,9 @@ void json_reader::skip_property()
|
|||||||
int c = is_.peek();
|
int c = is_.peek();
|
||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
|
case -1:
|
||||||
|
throw_format_error("Premature end of file.");
|
||||||
|
break;
|
||||||
case '[':
|
case '[':
|
||||||
skip_array();
|
skip_array();
|
||||||
break;
|
break;
|
||||||
@@ -442,8 +445,10 @@ void json_reader::skip_string()
|
|||||||
consume('"');
|
consume('"');
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
char c;
|
int c;
|
||||||
c = get();
|
c = get();
|
||||||
|
if (c == -1) throw_format_error("Premature end of file.");
|
||||||
|
|
||||||
if (c == '\"')
|
if (c == '\"')
|
||||||
{
|
{
|
||||||
if (peek() == '\"')
|
if (peek() == '\"')
|
||||||
@@ -463,27 +468,54 @@ void json_reader::skip_string()
|
|||||||
void json_reader::skip_number()
|
void json_reader::skip_number()
|
||||||
{
|
{
|
||||||
skip_whitespace();
|
skip_whitespace();
|
||||||
char c;
|
int c;
|
||||||
while (true)
|
if (is_.peek() == '-')
|
||||||
{
|
{
|
||||||
int ic = is_.peek();
|
get();
|
||||||
if (ic == -1)
|
}
|
||||||
break;
|
if (!std::isdigit(is_.peek()))
|
||||||
if (is_whitespace((char)ic))
|
{
|
||||||
|
throw_format_error("Expecting a number.");
|
||||||
|
}
|
||||||
|
while (std::isdigit(is_.peek()))
|
||||||
|
{
|
||||||
|
get();
|
||||||
|
}
|
||||||
|
if (is_.peek() == '.')
|
||||||
|
{
|
||||||
|
get();
|
||||||
|
}
|
||||||
|
while (std::isdigit(is_.peek()))
|
||||||
|
{
|
||||||
|
get();
|
||||||
|
}
|
||||||
|
c = is_.peek();
|
||||||
|
if (c == 'e' || c == 'E')
|
||||||
|
{
|
||||||
|
get();
|
||||||
|
c = is_.peek();
|
||||||
|
if (c == '+' || c == 'i')
|
||||||
{
|
{
|
||||||
break;
|
get();
|
||||||
}
|
}
|
||||||
c = get();
|
while (std::isdigit(is_.peek()))
|
||||||
|
{
|
||||||
|
get();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void json_reader::skip_array()
|
void json_reader::skip_array()
|
||||||
{
|
{
|
||||||
char c;
|
int c;
|
||||||
consume('[');
|
consume('[');
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (peek() == ']')
|
c = peek();
|
||||||
|
if (c == -1) throw_format_error("Premature end of file.");
|
||||||
|
|
||||||
|
if (c == ']')
|
||||||
{
|
{
|
||||||
c = get();
|
c = get();
|
||||||
break;
|
break;
|
||||||
@@ -499,11 +531,13 @@ void json_reader::skip_array()
|
|||||||
|
|
||||||
void json_reader::skip_object()
|
void json_reader::skip_object()
|
||||||
{
|
{
|
||||||
char c;
|
int c;
|
||||||
consume('{');
|
consume('{');
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (peek() == '}')
|
c = peek();
|
||||||
|
if (c == -1) throw_format_error("Premature end of file.");
|
||||||
|
if (c == '}')
|
||||||
{
|
{
|
||||||
c = get();
|
c = get();
|
||||||
break;
|
break;
|
||||||
|
|||||||
+48
-5
@@ -27,6 +27,7 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include "HtmlHelper.hpp"
|
#include "HtmlHelper.hpp"
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
#include <cmath>
|
||||||
#include "PiPedalException.hpp"
|
#include "PiPedalException.hpp"
|
||||||
|
|
||||||
|
|
||||||
@@ -243,6 +244,7 @@ class json_writer {
|
|||||||
public:
|
public:
|
||||||
const char*CRLF;
|
const char*CRLF;
|
||||||
private:
|
private:
|
||||||
|
bool allowNaN_ = true;
|
||||||
std::ostream &os;
|
std::ostream &os;
|
||||||
int indent_level;
|
int indent_level;
|
||||||
bool compressed;
|
bool compressed;
|
||||||
@@ -276,13 +278,17 @@ public:
|
|||||||
os << text;
|
os << text;
|
||||||
}
|
}
|
||||||
using string_view = boost::string_view;
|
using string_view = boost::string_view;
|
||||||
json_writer(std::ostream &os, bool compressed = false)
|
json_writer(std::ostream &os, bool compressed = false, bool allowNaN = true)
|
||||||
: os(os)
|
: os(os)
|
||||||
, compressed(compressed)
|
, compressed(compressed)
|
||||||
|
, allowNaN_(allowNaN)
|
||||||
, indent_level(0)
|
, indent_level(0)
|
||||||
{
|
{
|
||||||
this->CRLF = compressed? "" : "\r\n";
|
this->CRLF = compressed? "" : "\r\n";
|
||||||
}
|
}
|
||||||
|
bool allowNaN() const { return allowNaN_; }
|
||||||
|
void allowNaN(bool allow) { allowNaN_ = allow; }
|
||||||
|
|
||||||
void write(long long value)
|
void write(long long value)
|
||||||
{
|
{
|
||||||
os << value;
|
os << value;
|
||||||
@@ -342,11 +348,21 @@ public:
|
|||||||
}
|
}
|
||||||
void write(float f)
|
void write(float f)
|
||||||
{
|
{
|
||||||
os << std::setprecision(std::numeric_limits<float>::max_digits10) << f; // round-trip format
|
if (allowNaN_ && (std::isnan(f) || std::isinf(f)))
|
||||||
|
{
|
||||||
|
os << "NaN";
|
||||||
|
} else {
|
||||||
|
os << std::setprecision(std::numeric_limits<float>::max_digits10) << f; // round-trip format
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void write(double f)
|
void write(double f)
|
||||||
{
|
{
|
||||||
os << std::setprecision(std::numeric_limits<double>::max_digits10) << f; // round-trip format
|
if (allowNaN_ && (std::isnan(f) || std::isinf(f)))
|
||||||
|
{
|
||||||
|
os << "NaN";
|
||||||
|
} else {
|
||||||
|
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)
|
||||||
@@ -526,20 +542,26 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
class json_reader {
|
class json_reader {
|
||||||
|
private:
|
||||||
std::istream &is_;
|
std::istream &is_;
|
||||||
|
|
||||||
const uint16_t UTF16_SURROGATE_1_BASE = 0xD800U;
|
const uint16_t UTF16_SURROGATE_1_BASE = 0xD800U;
|
||||||
const uint16_t UTF16_SURROGATE_2_BASE = 0xDC00U;
|
const uint16_t UTF16_SURROGATE_2_BASE = 0xDC00U;
|
||||||
const uint16_t UTF16_SURROGATE_MASK = 0x3FFU;
|
const uint16_t UTF16_SURROGATE_MASK = 0x3FFU;
|
||||||
|
bool allowNaN_ = true;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
json_reader(std::istream &input)
|
json_reader(std::istream &input, bool allowNaN = true)
|
||||||
: is_(input)
|
: is_(input)
|
||||||
{
|
{
|
||||||
|
this->allowNaN_ = allowNaN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool allowNaN() const { return allowNaN_;}
|
||||||
|
void allowNaN(bool allow) { allowNaN_ = allow; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void throw_format_error(const char*error);
|
void throw_format_error(const char*error);
|
||||||
|
|
||||||
@@ -719,11 +741,32 @@ public:
|
|||||||
|
|
||||||
void read(float*value) {
|
void read(float*value) {
|
||||||
skip_whitespace();
|
skip_whitespace();
|
||||||
|
if (allowNaN_)
|
||||||
|
{
|
||||||
|
if (peek() == 'N')
|
||||||
|
{
|
||||||
|
consumeToken("NaN","Expecting a number.");
|
||||||
|
*value = std::nanf("");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
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 (peek() == 'N')
|
||||||
|
{
|
||||||
|
consumeToken("NaN","Expecting a number.");
|
||||||
|
|
||||||
|
*value = std::nan("");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
is_ >> *value;
|
is_ >> *value;
|
||||||
if (is_.fail()) throw PiPedalException("Invalid format.");
|
if (is_.fail()) throw PiPedalException("Invalid format.");
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -480,7 +480,7 @@ int main(int argc, char *argv[])
|
|||||||
PiPedalConfiguration configuration;
|
PiPedalConfiguration configuration;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
configuration.Load(doc_root);
|
configuration.Load(doc_root,web_root);
|
||||||
}
|
}
|
||||||
catch (const std::exception &e)
|
catch (const std::exception &e)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -22,6 +22,10 @@
|
|||||||
// Diagnostic type used to report calculated type T in an error message.
|
// Diagnostic type used to report calculated type T in an error message.
|
||||||
template <typename T> class TypeDisplay;
|
template <typename T> class TypeDisplay;
|
||||||
|
|
||||||
|
#ifndef SUPPORT_MIDI // currently, only whether midi plugins can be loaded. No routing or handling implemented (yet).
|
||||||
|
#define SUPPORT_MIDI 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|||||||
Reference in New Issue
Block a user