CPU Use
This commit is contained in:
@@ -74,6 +74,7 @@ add_custom_command(
|
||||
src/TemporaryDrawer.tsx
|
||||
src/ToobCabSimView.tsx
|
||||
src/ToobFrequencyResponseView.tsx
|
||||
src/ToobPowerStage2View.tsx
|
||||
src/ToobInputStageView.tsx
|
||||
src/ToobToneStackView.tsx
|
||||
src/Units.tsx
|
||||
|
||||
@@ -29,12 +29,14 @@ import IControlViewFactory from './IControlViewFactory';
|
||||
import ToobInputStageViewFactory from './ToobInputStageView';
|
||||
import ToobToneStackViewFactory from './ToobToneStackView';
|
||||
import ToobCabSimViewFactory from './ToobCabSimView';
|
||||
import ToobPowerStage2Factory from './ToobPowerStage2View';
|
||||
|
||||
|
||||
let pluginFactories: IControlViewFactory[] = [
|
||||
new ToobInputStageViewFactory(),
|
||||
new ToobToneStackViewFactory(),
|
||||
new ToobCabSimViewFactory()
|
||||
new ToobCabSimViewFactory(),
|
||||
new ToobPowerStage2Factory()
|
||||
];
|
||||
|
||||
|
||||
|
||||
+102
-1
@@ -45,6 +45,8 @@ export enum State {
|
||||
Reconnecting
|
||||
};
|
||||
|
||||
export type ControlValueChangedHandler = (key: string, value: number) => void;
|
||||
|
||||
export interface ZoomedControlInfo {
|
||||
source: HTMLElement;
|
||||
instanceId: number;
|
||||
@@ -65,6 +67,17 @@ export interface VuUpdateInfo {
|
||||
|
||||
export interface MonitorPortHandle {
|
||||
|
||||
};
|
||||
export interface ControlValueChangedHandle {
|
||||
_ControlValueChangedHandle: number;
|
||||
|
||||
};
|
||||
|
||||
interface ControlValueChangeItem {
|
||||
handle: number;
|
||||
instanceId: number;
|
||||
onValueChanged: ControlValueChangedHandler;
|
||||
|
||||
};
|
||||
|
||||
class MidiEventListener {
|
||||
@@ -75,6 +88,18 @@ class MidiEventListener {
|
||||
handle: number;
|
||||
callback: (isNote: boolean, noteOrControl: number) => void;
|
||||
};
|
||||
class AtomOutputListener {
|
||||
constructor(handle: number, instanceId: number, callback: (instanceId: number, atomObject: any) => void) {
|
||||
this.handle = handle;
|
||||
this.instanceId = instanceId;
|
||||
this.callback = callback;
|
||||
}
|
||||
handle: number;
|
||||
instanceId: number;
|
||||
callback: (instanceId: number, atomObject: any) => void;
|
||||
};
|
||||
|
||||
|
||||
|
||||
export interface ListenHandle {
|
||||
_handle: number;
|
||||
@@ -356,6 +381,14 @@ export interface PiPedalModel {
|
||||
listenForMidiEvent(listenForControlsOnly: boolean, onComplete: (isNote: boolean, noteOrControl: number) => void): ListenHandle;
|
||||
cancelListenForMidiEvent(listenHandle: ListenHandle): void;
|
||||
|
||||
addControlValueChangeListener(instanceId: number, onValueChanged: ControlValueChangedHandler): ControlValueChangedHandle
|
||||
removeControlValueChangeListener(handle: ControlValueChangedHandle): void;
|
||||
|
||||
listenForAtomOutput(instanceId: number, onComplete: (instanceId: number, atomOutput: any) => void): ListenHandle;
|
||||
cancelListenForAtomOutput(listenHandle: ListenHandle): void;
|
||||
|
||||
|
||||
|
||||
download(targetType: string, isntanceId: number): void;
|
||||
|
||||
uploadPreset(file: File, uploadAfter: number): Promise<number>;
|
||||
@@ -501,6 +534,14 @@ class PiPedalModelImpl implements PiPedalModel {
|
||||
let isNote = body.isNote as boolean;
|
||||
let noteOrControl = body.noteOrControl as number;
|
||||
this.handleNotifyMidiListener(clientHandle, isNote, noteOrControl);
|
||||
} else if (message === "onNotifyAtomOut") {
|
||||
let clientHandle = body.clientHandle as number;
|
||||
let instanceId = body.instanceId as number;
|
||||
let atomJson = body.atomJson as string;
|
||||
this.handleNotifyAtomOutput(clientHandle, instanceId,atomJson);
|
||||
if (header.replyTo) {
|
||||
this.webSocket?.reply(header.replyTo,"onNotifyAtomOut",true);
|
||||
}
|
||||
} else if (message === "onControlChanged") {
|
||||
let controlChangedBody = body as ControlChangedBody;
|
||||
this._setPedalBoardControlValue(
|
||||
@@ -917,6 +958,26 @@ class PiPedalModelImpl implements PiPedalModel {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_controlValueChangeItems: ControlValueChangeItem[] = [];
|
||||
|
||||
addControlValueChangeListener(instanceId: number, onValueChanged: ControlValueChangedHandler): ControlValueChangedHandle
|
||||
{
|
||||
let handle = ++this.nextListenHandle;
|
||||
this._controlValueChangeItems.push({ handle: handle, instanceId: instanceId,onValueChanged: onValueChanged});
|
||||
return { _ControlValueChangedHandle: handle};
|
||||
}
|
||||
removeControlValueChangeListener(handle: ControlValueChangedHandle)
|
||||
{
|
||||
for (let i = 0; i < this._controlValueChangeItems.length; ++i)
|
||||
{
|
||||
if (this._controlValueChangeItems[i].handle === handle._ControlValueChangedHandle)
|
||||
{
|
||||
this._controlValueChangeItems.splice(i,1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_setPedalBoardControlValue(instanceId: number, key: string, value: number, notifyServer: boolean): void {
|
||||
@@ -931,6 +992,14 @@ class PiPedalModelImpl implements PiPedalModel {
|
||||
if (notifyServer) {
|
||||
this._setServerControl("setControl", instanceId, key, value);
|
||||
}
|
||||
for (let i = 0; i < this._controlValueChangeItems.length; ++i)
|
||||
{
|
||||
let item = this._controlValueChangeItems[i];
|
||||
if (instanceId === item.instanceId)
|
||||
{
|
||||
item.onValueChanged(key,value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1470,6 +1539,7 @@ class PiPedalModelImpl implements PiPedalModel {
|
||||
}
|
||||
}
|
||||
midiListeners: MidiEventListener[] = [];
|
||||
atomOutputListeners: AtomOutputListener[] = [];
|
||||
|
||||
nextListenHandle = 1;
|
||||
listenForMidiEvent(listenForControlsOnly: boolean, onComplete: (isNote: boolean, noteOrControl: number) => void): ListenHandle {
|
||||
@@ -1484,13 +1554,34 @@ class PiPedalModelImpl implements PiPedalModel {
|
||||
|
||||
}
|
||||
|
||||
listenForAtomOutput(instanceId: number, onComplete: (instanceId: number, atomOutput: any) => void): ListenHandle {
|
||||
let handle = this.nextListenHandle++;
|
||||
|
||||
this.atomOutputListeners.push(new AtomOutputListener(handle,instanceId, onComplete));
|
||||
|
||||
this.webSocket?.send("listenForAtomOutput", { instanceId: instanceId, handle: handle });
|
||||
return {
|
||||
_handle: handle
|
||||
};
|
||||
}
|
||||
|
||||
handleNotifyAtomOutput(clientHandle: number, instanceId: number, atomJson: string) {
|
||||
let jsonObject: any = JSON.parse(atomJson);
|
||||
for (let i = 0; i < this.atomOutputListeners.length; ++i) {
|
||||
let listener = this.atomOutputListeners[i];
|
||||
if (listener.handle === clientHandle && listener.instanceId === instanceId) {
|
||||
listener.callback(instanceId,jsonObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
handleNotifyMidiListener(clientHandle: number, isNote: boolean, noteOrControl: number) {
|
||||
for (let i = 0; i < this.midiListeners.length; ++i) {
|
||||
let listener = this.midiListeners[i];
|
||||
if (listener.handle === clientHandle) {
|
||||
listener.callback(isNote, noteOrControl);
|
||||
|
||||
this.midiListeners.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1498,10 +1589,20 @@ class PiPedalModelImpl implements PiPedalModel {
|
||||
for (let i = 0; i < this.midiListeners.length; ++i) {
|
||||
if (this.midiListeners[i].handle === listenHandle._handle) {
|
||||
this.midiListeners.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.webSocket?.send("cancelListenForMidiEvent", listenHandle._handle);
|
||||
}
|
||||
cancelListenForAtomOutput(listenHandle: ListenHandle): void {
|
||||
for (let i = 0; i < this.midiListeners.length; ++i) {
|
||||
if (this.midiListeners[i].handle === listenHandle._handle) {
|
||||
this.midiListeners.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.webSocket?.send("cancelListenForAtomOutput", listenHandle._handle);
|
||||
}
|
||||
|
||||
download(targetType: string, instanceId: number): void {
|
||||
if (instanceId === -1) return;
|
||||
|
||||
@@ -241,7 +241,7 @@ class PiPedalSocket {
|
||||
this.totalRetryDelay += this.retryDelay;
|
||||
Utility.delay(this.retryDelay).then(() => this.reconnect());
|
||||
this.retryDelay *= 2;
|
||||
if (this.retryDelay > 5000) this.retryDelay = 5000;
|
||||
if (this.retryDelay > 3000) this.retryDelay = 3000;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -75,18 +75,18 @@ const ToobFrequencyResponseView =
|
||||
{
|
||||
this.requestDeferred = false;
|
||||
this.requestOutstanding = false;
|
||||
this.updateFrequencyResponse(); // after a reconnect.
|
||||
this.updateAllWaveShapes(); // after a reconnect.
|
||||
}
|
||||
}
|
||||
onPedalBoardChanged()
|
||||
{
|
||||
this.updateFrequencyResponse();
|
||||
this.updateAllWaveShapes();
|
||||
}
|
||||
componentDidMount()
|
||||
{
|
||||
this.model.state.addOnChangedHandler(this.onStateChanged);
|
||||
this.model.pedalBoard.addOnChangedHandler(this.onPedalBoardChanged);
|
||||
this.updateFrequencyResponse();
|
||||
this.updateAllWaveShapes();
|
||||
}
|
||||
componentWillUnmount()
|
||||
{
|
||||
@@ -154,7 +154,7 @@ const ToobFrequencyResponseView =
|
||||
|
||||
}
|
||||
|
||||
updateFrequencyResponse() {
|
||||
updateAllWaveShapes() {
|
||||
if (this.requestOutstanding) { // throttling.
|
||||
this.requestDeferred = true;
|
||||
return;
|
||||
@@ -169,7 +169,7 @@ const ToobFrequencyResponseView =
|
||||
() => {
|
||||
this.requestOutstanding = false;
|
||||
this.requestDeferred = false;
|
||||
this.updateFrequencyResponse();
|
||||
this.updateAllWaveShapes();
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
// 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 { createStyles, withStyles, WithStyles, Theme } from '@material-ui/core/styles';
|
||||
|
||||
import IControlViewFactory from './IControlViewFactory';
|
||||
import { PiPedalModelFactory, PiPedalModel, State,ListenHandle } from "./PiPedalModel";
|
||||
import { PedalBoardItem } from './PedalBoard';
|
||||
import PluginControlView, { ControlGroup,ControlViewCustomization } from './PluginControlView';
|
||||
import ToobWaveShapeView, {BidirectionalVuPeak} from './ToobWaveShapeView';
|
||||
|
||||
|
||||
|
||||
const styles = (theme: Theme) => createStyles({
|
||||
});
|
||||
|
||||
interface ToobPowerstage2Props extends WithStyles<typeof styles> {
|
||||
instanceId: number;
|
||||
item: PedalBoardItem;
|
||||
|
||||
};
|
||||
interface ToobPowerstage2State {
|
||||
uiState: number[];
|
||||
};
|
||||
|
||||
const ToobPowerstage2View =
|
||||
withStyles(styles, { withTheme: true })(
|
||||
class extends React.Component<ToobPowerstage2Props, ToobPowerstage2State>
|
||||
implements ControlViewCustomization
|
||||
{
|
||||
model: PiPedalModel;
|
||||
|
||||
customizationId: number = 5;
|
||||
|
||||
constructor(props: ToobPowerstage2Props) {
|
||||
super(props);
|
||||
this.model = PiPedalModelFactory.getInstance();
|
||||
this.state = {
|
||||
uiState: [0,0,0,0,0,0,0,0]
|
||||
}
|
||||
this.onStateChanged = this.onStateChanged.bind(this);
|
||||
this.onAtomOutput = this.onAtomOutput.bind(this);
|
||||
}
|
||||
|
||||
listeningForOutput: boolean = false;
|
||||
atomOutputHandle?: ListenHandle;
|
||||
|
||||
onAtomOutput(instanceId: number, atomOutput: any): void {
|
||||
if (atomOutput.lv2Type === "http://two-play.com/plugins/toob-power-stage-2#uiState" )
|
||||
{
|
||||
this.setState({uiState: atomOutput.data as number[]});
|
||||
}
|
||||
}
|
||||
maybeListenForAtomOutput(): void {
|
||||
let listenForOutput = this.isReady && this.isControlMounted;
|
||||
|
||||
if (listenForOutput !== this.listeningForOutput)
|
||||
{
|
||||
this.listeningForOutput = listenForOutput;
|
||||
if (listenForOutput)
|
||||
{
|
||||
this.atomOutputHandle = this.model.listenForAtomOutput(this.props.instanceId,this.onAtomOutput);
|
||||
} else {
|
||||
if (this.atomOutputHandle)
|
||||
{
|
||||
this.model.cancelListenForAtomOutput(this.atomOutputHandle);
|
||||
this.atomOutputHandle = undefined;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
isReady: boolean = false;
|
||||
|
||||
gain1Peak: BidirectionalVuPeak = new BidirectionalVuPeak();
|
||||
gain2Peak: BidirectionalVuPeak = new BidirectionalVuPeak();
|
||||
gain3Peak: BidirectionalVuPeak = new BidirectionalVuPeak();
|
||||
gain1OutPeak: BidirectionalVuPeak = new BidirectionalVuPeak();
|
||||
gain2OutPeak: BidirectionalVuPeak = new BidirectionalVuPeak();
|
||||
gain3OutPeak: BidirectionalVuPeak = new BidirectionalVuPeak();
|
||||
|
||||
onStateChanged() {
|
||||
this.isReady = this.model.state.get() === State.Ready;
|
||||
this.maybeListenForAtomOutput();
|
||||
}
|
||||
|
||||
isControlMounted: boolean = false;
|
||||
|
||||
componentDidMount() {
|
||||
this.model.state.addOnChangedHandler(
|
||||
this.onStateChanged);
|
||||
this.isControlMounted = true;
|
||||
this.isReady = this.model.state.get() === State.Ready;
|
||||
this.maybeListenForAtomOutput();
|
||||
}
|
||||
componentWillUnmount()
|
||||
{
|
||||
this.isControlMounted = false;
|
||||
this.maybeListenForAtomOutput();
|
||||
}
|
||||
|
||||
ModifyControls(controls: (React.ReactNode| ControlGroup)[]): (React.ReactNode| ControlGroup)[]
|
||||
{
|
||||
let time = Date.now()*0.001;
|
||||
|
||||
this.gain1Peak.update(time,this.state.uiState[0],this.state.uiState[1]);
|
||||
this.gain1OutPeak.update(time,this.state.uiState[2],this.state.uiState[3]);
|
||||
|
||||
this.gain2Peak.update(time,this.state.uiState[4],this.state.uiState[5]);
|
||||
this.gain2OutPeak.update(time,this.state.uiState[6],this.state.uiState[7]);
|
||||
|
||||
this.gain3Peak.update(time,this.state.uiState[8],this.state.uiState[9]);
|
||||
this.gain3OutPeak.update(time,this.state.uiState[10],this.state.uiState[11]);
|
||||
|
||||
var gain1 = (
|
||||
<ToobWaveShapeView instanceId={this.props.instanceId} controlNumber={1} controlKeys={["gain1","shape1","bias1"]}
|
||||
vuMin={this.gain1Peak.minValue} vuMax={this.gain1Peak.maxValue}
|
||||
vuMinPeak={this.gain1Peak.minPeak} vuMaxPeak={this.gain1Peak.maxPeak}
|
||||
vuOutMin={this.gain1OutPeak.minValue} vuOutMax={this.gain1OutPeak.maxValue}
|
||||
vuOutMinPeak={this.gain1OutPeak.minPeak} vuOutMaxPeak={this.gain1OutPeak.maxPeak}
|
||||
/>
|
||||
);
|
||||
var gain2 = (
|
||||
<ToobWaveShapeView instanceId={this.props.instanceId} controlNumber={2} controlKeys={["gain2","shape2","bias2"]}
|
||||
vuMin={this.gain2Peak.minValue} vuMax={this.gain2Peak.maxValue}
|
||||
vuMinPeak={this.gain2Peak.minPeak} vuMaxPeak={this.gain2Peak.maxPeak}
|
||||
vuOutMin={this.gain2OutPeak.minValue} vuOutMax={this.gain2OutPeak.maxValue}
|
||||
vuOutMinPeak={this.gain2OutPeak.minPeak} vuOutMaxPeak={this.gain2OutPeak.maxPeak}
|
||||
/>
|
||||
);
|
||||
var gain3 = (
|
||||
<ToobWaveShapeView instanceId={this.props.instanceId} controlNumber={3} controlKeys={["gain3","shape3","bias3"]}
|
||||
vuMin={this.gain3Peak.minValue} vuMax={this.gain3Peak.maxValue}
|
||||
vuMinPeak={this.gain3Peak.minPeak} vuMaxPeak={this.gain3Peak.maxPeak}
|
||||
vuOutMin={this.gain3OutPeak.minValue} vuOutMax={this.gain3OutPeak.maxValue}
|
||||
vuOutMinPeak={this.gain3OutPeak.minPeak} vuOutMaxPeak={this.gain3OutPeak.maxPeak}
|
||||
/>
|
||||
);
|
||||
((controls[0]) as ControlGroup).controls.splice(3,0,gain1);
|
||||
((controls[1]) as ControlGroup).controls.splice(3,0,gain2);
|
||||
((controls[2]) as ControlGroup).controls.splice(3,0,gain3);
|
||||
|
||||
return controls;
|
||||
}
|
||||
render() {
|
||||
return (<PluginControlView
|
||||
instanceId={this.props.instanceId}
|
||||
item={this.props.item}
|
||||
customization={this}
|
||||
customizationId={this.customizationId}
|
||||
/>);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
class ToobPowerstage2ViewFactory implements IControlViewFactory {
|
||||
uri: string = "http://two-play.com/plugins/toob-power-stage-2";
|
||||
|
||||
Create(model: PiPedalModel, pedalBoardItem: PedalBoardItem): React.ReactNode {
|
||||
return (<ToobPowerstage2View instanceId={pedalBoardItem.instanceId} item={pedalBoardItem} />);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
export default ToobPowerstage2ViewFactory;
|
||||
@@ -0,0 +1,413 @@
|
||||
// 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 { createStyles, withStyles, WithStyles, Theme } from '@material-ui/core/styles';
|
||||
|
||||
import { PiPedalModelFactory, PiPedalModel, State, ControlValueChangedHandle } from "./PiPedalModel";
|
||||
import { StandardItemSize } from './PluginControlView';
|
||||
import Utility from './Utility';
|
||||
import SvgPathBuilder from './SvgPathBuilder';
|
||||
|
||||
|
||||
const WAVESHAPE_VECTOR_URI = "http://two-play.com/plugins/toob#waveShape";
|
||||
|
||||
const PLOT_WIDTH = StandardItemSize.height - 12;
|
||||
const PLOT_HEIGHT = StandardItemSize.height - 12;
|
||||
|
||||
const styles = (theme: Theme) => createStyles({
|
||||
frame: {
|
||||
width: PLOT_WIDTH, height: PLOT_HEIGHT, background: "#444",
|
||||
borderRadius: 6,
|
||||
marginTop: 12, marginLeft: 8, marginRight: 8,
|
||||
boxShadow: "1px 4px 8px #000 inset"
|
||||
},
|
||||
});
|
||||
|
||||
interface SvgRect
|
||||
{
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
color: string;
|
||||
|
||||
}
|
||||
|
||||
interface ToobWaveShapeProps extends WithStyles<typeof styles> {
|
||||
instanceId: number;
|
||||
controlNumber: number;
|
||||
controlKeys: string[];
|
||||
|
||||
vuMin: number;
|
||||
vuMax: number;
|
||||
vuMinPeak: number;
|
||||
vuMaxPeak: number;
|
||||
|
||||
vuOutMin: number;
|
||||
vuOutMax: number;
|
||||
vuOutMinPeak: number;
|
||||
vuOutMaxPeak: number;
|
||||
|
||||
};
|
||||
interface ToobWaveShapeState {
|
||||
data: number[];
|
||||
};
|
||||
|
||||
const HOLD_TIME: number = 1.6; // seconds.
|
||||
const DECAY_RATE = 1.0; // full range/second.
|
||||
|
||||
export class BidirectionalVuPeak {
|
||||
minValue: number = 0;
|
||||
maxValue: number = 0;
|
||||
minPeak: number = 0;
|
||||
maxPeak: number = 0;
|
||||
holdTime: number = 1E120; // don't decay until we have an actual peak.
|
||||
lastTime: number = 0;
|
||||
|
||||
update(time: number, minValue: number, maxValue: number)
|
||||
{
|
||||
// decay peak.
|
||||
if (time > this.holdTime)
|
||||
{
|
||||
let dt = time-this.lastTime;
|
||||
let decay = dt*DECAY_RATE;
|
||||
this.maxPeak -= decay;
|
||||
if (this.maxPeak < 0) this.maxPeak = 0;
|
||||
|
||||
this.minPeak += decay;
|
||||
if (this.minPeak > 0) this.minPeak = 0;
|
||||
this.lastTime = time;
|
||||
|
||||
}
|
||||
this.minValue = minValue;
|
||||
this.maxValue = maxValue;
|
||||
let resetPeakHold = false;
|
||||
if (minValue < this.minPeak)
|
||||
{
|
||||
this.minPeak = minValue;
|
||||
resetPeakHold = true;
|
||||
}
|
||||
if (maxValue > this.maxPeak)
|
||||
{
|
||||
this.maxPeak = maxValue;
|
||||
resetPeakHold = true;
|
||||
}
|
||||
if (resetPeakHold)
|
||||
{
|
||||
this.holdTime = time+HOLD_TIME;
|
||||
this.lastTime = this.holdTime;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
const ToobWaveShapeView =
|
||||
withStyles(styles, { withTheme: true })(
|
||||
class extends React.Component<ToobWaveShapeProps, ToobWaveShapeState>
|
||||
{
|
||||
model: PiPedalModel;
|
||||
|
||||
customizationId: number = 1;
|
||||
|
||||
constructor(props: ToobWaveShapeProps) {
|
||||
super(props);
|
||||
this.model = PiPedalModelFactory.getInstance();
|
||||
this.state = {
|
||||
data: []
|
||||
};
|
||||
|
||||
this.onPedalBoardChanged = this.onPedalBoardChanged.bind(this);
|
||||
this.onStateChanged = this.onStateChanged.bind(this);
|
||||
this.onControlValueChanged = this.onControlValueChanged.bind(this);
|
||||
this.isReady = this.model.state.get() === State.Ready;
|
||||
}
|
||||
|
||||
isReady: boolean;
|
||||
onStateChanged() {
|
||||
let isReady = this.model.state.get() === State.Ready;
|
||||
if (this.isReady !== isReady) {
|
||||
this.isReady = isReady;
|
||||
|
||||
if (isReady) {
|
||||
this.requestDeferred = false;
|
||||
this.requestOutstanding = false;
|
||||
this.updateWaveShape(); // after a reconnect.
|
||||
}
|
||||
}
|
||||
}
|
||||
onPedalBoardChanged() {
|
||||
this.updateWaveShape();
|
||||
}
|
||||
_valueChangedHandle?: ControlValueChangedHandle;
|
||||
|
||||
|
||||
mounted: boolean = false;
|
||||
|
||||
componentDidMount() {
|
||||
this.mounted = true;
|
||||
this.model.state.addOnChangedHandler(this.onStateChanged);
|
||||
this.model.pedalBoard.addOnChangedHandler(this.onPedalBoardChanged);
|
||||
this._valueChangedHandle = this.model.addControlValueChangeListener(
|
||||
this.props.instanceId,
|
||||
this.onControlValueChanged);
|
||||
|
||||
this.isReady = this.model.state.get() === State.Ready;
|
||||
this.updateWaveShape();
|
||||
}
|
||||
|
||||
lastVuMin: number = -1000;
|
||||
lastVuMax: number = -1000;
|
||||
|
||||
componentDidUpdate() {
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.mounted = false;
|
||||
if (this._valueChangedHandle) {
|
||||
this.model.removeControlValueChangeListener(this._valueChangedHandle);
|
||||
this._valueChangedHandle = undefined;
|
||||
}
|
||||
this.model.state.removeOnChangedHandler(this.onStateChanged);
|
||||
this.model.pedalBoard.removeOnChangedHandler(this.onPedalBoardChanged);
|
||||
}
|
||||
|
||||
onControlValueChanged(key: string, value: number) {
|
||||
for (let i = 0; i < this.props.controlKeys.length; ++i) {
|
||||
if (this.props.controlKeys[i] === (key)) {
|
||||
this.updateWaveShape();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
requestOutstanding: boolean = false;
|
||||
requestDeferred: boolean = false;
|
||||
|
||||
// Size of the SVG element.
|
||||
xMin: number = 4;
|
||||
xMax: number = PLOT_WIDTH - 10;
|
||||
yMin: number = 2;
|
||||
yMax: number = PLOT_HEIGHT - 10;
|
||||
vuHeight = 4;
|
||||
nPoints: number = 0;
|
||||
|
||||
indexToX(value: number): number {
|
||||
return (this.xMax - this.xMin) * value / this.nPoints + this.xMin;
|
||||
}
|
||||
toY(value: number): number {
|
||||
let yMid = (this.yMin + this.yMax) / 2;
|
||||
return yMid - yMid * value;
|
||||
}
|
||||
toX(value: number): number {
|
||||
let xMid = (this.xMin + this.xMax)/2;
|
||||
return xMid+ (this.xMax-this.xMin)*0.5*value;
|
||||
}
|
||||
|
||||
|
||||
onWaveShapeUpdated(data: number[]) {
|
||||
if (!this.mounted) {
|
||||
return;
|
||||
}
|
||||
this.setState({data: data});
|
||||
}
|
||||
|
||||
updateWaveShape() {
|
||||
if (!this.isReady)
|
||||
return;
|
||||
if (this.requestOutstanding) { // throttling.
|
||||
this.requestDeferred = true;
|
||||
return;
|
||||
}
|
||||
this.requestOutstanding = true;
|
||||
this.model.getLv2Parameter<number[]>(this.props.instanceId, WAVESHAPE_VECTOR_URI + this.props.controlNumber)
|
||||
.then((data) => {
|
||||
this.onWaveShapeUpdated(data);
|
||||
if (this.requestDeferred) {
|
||||
Utility.delay(10) // take breath
|
||||
.then(
|
||||
() => {
|
||||
this.requestOutstanding = false;
|
||||
this.requestDeferred = false;
|
||||
this.updateWaveShape();
|
||||
}
|
||||
|
||||
);
|
||||
} else {
|
||||
this.requestOutstanding = false;
|
||||
}
|
||||
}).catch(error => {
|
||||
// assume the connection was lost. We'll get saved by a reconnect.
|
||||
this.requestOutstanding = false;
|
||||
this.requestDeferred = false;
|
||||
});
|
||||
}
|
||||
|
||||
currentPath: string = "";
|
||||
|
||||
redColor: string = "#F00";
|
||||
yellowColor: string = "#FF0";
|
||||
greenColor: string = "#0F0";
|
||||
|
||||
horizontalVuMeter(minValue: number, maxValue: number, minPeak: number, maxPeak: number): React.ReactNode[]
|
||||
{
|
||||
let result: React.ReactNode[] = [];
|
||||
let greenXMin = this.toX(-0.5);
|
||||
let greenXMax = this.toX(0.5);
|
||||
if (minValue < -1.0) minValue = -1.0;
|
||||
if (maxValue > 1.0) maxValue = 1.0;
|
||||
if (minPeak < -1.0) minPeak = -1;
|
||||
if (maxPeak > 1.0) maxPeak = 1.0;
|
||||
let nKey = 0;
|
||||
|
||||
let yTop = this.yMax+2;
|
||||
|
||||
let minX = this.toX(minValue);
|
||||
let maxX = this.toX(maxValue);
|
||||
let minPeakX = this.toX(minPeak);
|
||||
let maxPeakX = this.toX(maxPeak);
|
||||
if (minX < greenXMin)
|
||||
{
|
||||
result.push( ( <rect x={minX} y={yTop}
|
||||
width={greenXMin-minX} height={this.vuHeight}
|
||||
fill={this.yellowColor} stroke="none" key={"hr"+ nKey++} />));
|
||||
minX = greenXMin;
|
||||
}
|
||||
if (maxX > greenXMax)
|
||||
{
|
||||
result.push( ( <rect x={greenXMax} y={yTop}
|
||||
width={maxX-greenXMax} height={this.vuHeight}
|
||||
fill={this.yellowColor} stroke="none" key={"hr"+ nKey++} />))
|
||||
maxX = greenXMax;
|
||||
|
||||
}
|
||||
if (maxX > minX)
|
||||
{
|
||||
result.push( ( <rect x={minX} y={yTop}
|
||||
width={maxX-minX} height={this.vuHeight}
|
||||
fill={this.greenColor} stroke="none" key={"hr"+ nKey++} />))
|
||||
}
|
||||
if (minPeakX+4 < maxPeakX)
|
||||
{
|
||||
let minColor = this.peakColor(minPeak);
|
||||
let maxColor = this.peakColor(maxPeak);
|
||||
result.push( ( <rect x={minPeakX} y={yTop}
|
||||
width={2} height={this.vuHeight}
|
||||
fill={minColor} stroke="none" key={"hr"+ nKey++} />))
|
||||
result.push( ( <rect x={maxPeakX-2} y={yTop}
|
||||
width={2} height={this.vuHeight}
|
||||
fill={maxColor} stroke="none" key={"hr"+ nKey++} />))
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
verticalVuMeter(minValue: number, maxValue: number, minPeak: number, maxPeak: number): React.ReactNode[]
|
||||
{
|
||||
let result: React.ReactNode[] = [];
|
||||
let greenYMin = this.toY(-0.5);
|
||||
let greenYMax = this.toY(0.5);
|
||||
if (minValue < -1.0) minValue = -1.0;
|
||||
if (maxValue > 1.0) maxValue = 1.0;
|
||||
if (minPeak < -1.0) minPeak = -1;
|
||||
if (maxPeak > 1.0) maxPeak = 1.0;
|
||||
let nKey = 0;
|
||||
|
||||
let xLeft = this.xMax+2;
|
||||
|
||||
let minY = this.toY(minValue);
|
||||
let maxY = this.toY(maxValue);
|
||||
let minPeakY = this.toY(minPeak);
|
||||
let maxPeakY = this.toY(maxPeak);
|
||||
if (minY > greenYMin)
|
||||
{
|
||||
result.push( ( <rect y={greenYMin} x={xLeft}
|
||||
height={minY-greenYMin} width={4}
|
||||
fill={this.yellowColor} stroke="none" key={"vr"+ nKey++} />));
|
||||
minY = greenYMin;
|
||||
}
|
||||
if (maxY < greenYMax)
|
||||
{
|
||||
result.push( ( <rect y={maxY} x={xLeft}
|
||||
height={greenYMax-maxY} width={4}
|
||||
fill={this.yellowColor} stroke="none" key={"vr"+ nKey++} />))
|
||||
maxY = greenYMax;
|
||||
}
|
||||
if (minY > maxY)
|
||||
{
|
||||
result.push( ( <rect y={maxY} x={xLeft}
|
||||
height={minY-maxY} width={4}
|
||||
fill={this.greenColor} stroke="none" key={"vr"+ nKey++} />))
|
||||
}
|
||||
if (minPeakY-4 > maxPeakY)
|
||||
{
|
||||
let minColor = this.peakColor(minPeak);
|
||||
let maxColor = this.peakColor(maxPeak);
|
||||
result.push( ( <rect y={minPeakY-4} x={xLeft}
|
||||
width={4} height={2}
|
||||
fill={minColor} stroke="none" key={"vr"+ nKey++} />))
|
||||
result.push( ( <rect y={maxPeakY} x={xLeft}
|
||||
width={4} height={2}
|
||||
fill={maxColor} stroke="none" key={`vr${nKey++}`} />))
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
peakColor(value: number): string {
|
||||
if (value >= 1.0 || value <= -1.0) return this.redColor;
|
||||
if (value >= 0.5 || value <= -0.5) return this.yellowColor;
|
||||
return this.greenColor;
|
||||
}
|
||||
|
||||
render() {
|
||||
let classes = this.props.classes;
|
||||
|
||||
|
||||
let data = this.state.data;
|
||||
let pathBuilder = new SvgPathBuilder();
|
||||
this.nPoints = data.length;
|
||||
if (data.length > 2) {
|
||||
pathBuilder.moveTo(this.indexToX(0), this.toY(data[0]));
|
||||
for (let i = 1; i < data.length; ++i) {
|
||||
pathBuilder.lineTo(this.indexToX(i), this.toY(data[i]));
|
||||
}
|
||||
}
|
||||
let currentPath = pathBuilder.toString();
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className={classes.frame} >
|
||||
<svg width={PLOT_WIDTH} height={PLOT_HEIGHT} viewBox={"0 0 " + PLOT_WIDTH + " " + PLOT_HEIGHT} >
|
||||
{ this.horizontalVuMeter(this.props.vuMin,this.props.vuMax,
|
||||
this.props.vuMinPeak,this.props.vuMaxPeak)
|
||||
}
|
||||
{ this.verticalVuMeter(this.props.vuOutMin,this.props.vuOutMax,
|
||||
this.props.vuOutMinPeak,this.props.vuOutMaxPeak)
|
||||
}
|
||||
<path d={currentPath} stroke="#0F8" fill="none" strokeWidth="2.5" opacity="0.6" />
|
||||
</svg>
|
||||
|
||||
</div>);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
export default ToobWaveShapeView;
|
||||
+2
-1
@@ -212,6 +212,7 @@ set (REACT_BUILD_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../build/react/build/)
|
||||
set (REACT_NOTICES_FILE ${CMAKE_CURRENT_SOURCE_DIR}/../build/src/notices.txt)
|
||||
set (DEBIAN_COPYRIGHT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/../debian/copyright)
|
||||
|
||||
|
||||
# generate Copyright section of settings page.
|
||||
# warning: there may be multiple versions. Pick the latest.
|
||||
if(EXISTS "/usr/share/doc/libboost1.74-dev")
|
||||
@@ -222,7 +223,7 @@ elseif(EXISTS "/usr/share/doc/libboost1.67-dev")
|
||||
set (BOOST_COPYRIGHT_DIR "libboost1.67-dev")
|
||||
else()
|
||||
message(ERROR "Boost libary version has changed. Please update me.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
add_custom_command(OUTPUT ${REACT_NOTICES_FILE}
|
||||
|
||||
@@ -44,6 +44,7 @@ namespace pipedal {
|
||||
virtual void GatherParameter(RealtimeParameterRequest*pRequest) = 0;
|
||||
|
||||
virtual std::string AtomToJson(uint8_t*pAtom) = 0;
|
||||
virtual std::string GetAtomObjectType(uint8_t*pData) = 0;
|
||||
|
||||
|
||||
virtual void SetAudioOutputBuffer(int index, float*buffer) = 0;
|
||||
|
||||
+44
-11
@@ -130,10 +130,12 @@ private:
|
||||
RingBuffer<true, false> inputRingBuffer;
|
||||
RingBuffer<false, true> outputRingBuffer;
|
||||
|
||||
RingBufferReader<true, false> realtimeReader;
|
||||
RingBufferWriter<false, true> realtimeWriter;
|
||||
RingBufferReader<false, true> hostReader;
|
||||
RingBufferWriter<true, false> hostWriter;
|
||||
RingBufferWriter<true,false> x;
|
||||
|
||||
RealtimeRingBufferReader realtimeReader;
|
||||
RealtimeRingBufferWriter realtimeWriter;
|
||||
HostRingBufferReader hostReader;
|
||||
HostRingBufferWriter hostWriter;
|
||||
|
||||
JackChannelSelection channelSelection;
|
||||
bool active = false;
|
||||
@@ -577,7 +579,7 @@ private:
|
||||
pedalBoard->ResetAtomBuffers();
|
||||
pedalBoard->ProcessParameterRequests(pParameterRequests);
|
||||
|
||||
processed = pedalBoard->Run(inputBuffers, outputBuffers, (uint32_t)nframes);
|
||||
processed = pedalBoard->Run(inputBuffers, outputBuffers, (uint32_t)nframes,&realtimeWriter);
|
||||
if (processed)
|
||||
{
|
||||
if (this->realtimeVuBuffers != nullptr)
|
||||
@@ -723,6 +725,8 @@ public:
|
||||
Lv2Log::error("Audio processing terminated unexpectedly.");
|
||||
realtimeWriter.AudioStopped();
|
||||
}
|
||||
std::vector<uint8_t> atomBuffer;
|
||||
|
||||
bool terminateThread;
|
||||
void ThreadProc()
|
||||
{
|
||||
@@ -871,6 +875,26 @@ public:
|
||||
this->pNotifyCallbacks->OnNotifyVusSubscription(*updates);
|
||||
}
|
||||
this->hostWriter.AckVuUpdate(); // please sir, can I have some more?
|
||||
} else if (command == RingBufferCommand::AtomOutput)
|
||||
{
|
||||
uint64_t instanceId;
|
||||
hostReader.read(&instanceId);
|
||||
size_t extraBytes;
|
||||
hostReader.read(&extraBytes);
|
||||
if (atomBuffer.size() < extraBytes)
|
||||
{
|
||||
atomBuffer.resize(extraBytes);
|
||||
}
|
||||
hostReader.read(extraBytes,&(atomBuffer[0]));
|
||||
|
||||
|
||||
IEffect *pEffect = currentPedalBoard->GetEffect(instanceId);
|
||||
if (pEffect != nullptr &&this->pNotifyCallbacks && listenForAtomOutput)
|
||||
{
|
||||
std::string atomType = pEffect->GetAtomObjectType(&atomBuffer[0]);
|
||||
auto json = pEffect->AtomToJson(&(atomBuffer[0]));
|
||||
this->pNotifyCallbacks->OnNotifyAtomOutput(instanceId,atomType,json);
|
||||
}
|
||||
}
|
||||
else if (command == RingBufferCommand::FreeVuSubscriptions)
|
||||
{
|
||||
@@ -1175,7 +1199,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
virtual void SetBypass(long instanceId, bool enabled)
|
||||
virtual void SetBypass(uint64_t instanceId, bool enabled)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
if (active && this->currentPedalBoard)
|
||||
@@ -1189,7 +1213,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
virtual void SetPluginPreset(long instanceId, const std::vector<ControlValue> &values)
|
||||
virtual void SetPluginPreset(uint64_t instanceId, const std::vector<ControlValue> &values)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
if (active && this->currentPedalBoard)
|
||||
@@ -1210,7 +1234,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void SetControlValue(long instanceId, const std::string &symbol, float value)
|
||||
void SetControlValue(uint64_t instanceId, const std::string &symbol, float value)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
if (active && this->currentPedalBoard)
|
||||
@@ -1273,6 +1297,7 @@ public:
|
||||
result.subscriptionHandle = subscription.subscriptionHandle;
|
||||
result.instanceIndex = this->currentPedalBoard->GetIndexOfInstanceId(subscription.instanceid);
|
||||
IEffect *pEffect = this->currentPedalBoard->GetEffect(subscription.instanceid);
|
||||
|
||||
result.portIndex = pEffect->GetControlIndex(subscription.key);
|
||||
result.sampleRate = (int)(this->GetSampleRate() * subscription.updateInterval);
|
||||
result.samplesToNextCallback = result.sampleRate;
|
||||
@@ -1296,8 +1321,11 @@ public:
|
||||
|
||||
for (size_t i = 0; i < subscriptions.size(); ++i)
|
||||
{
|
||||
pSubscriptions->subscriptions.push_back(
|
||||
MakeRealtimeSubscription(subscriptions[i]));
|
||||
if (this->currentPedalBoard->GetEffect(subscriptions[i].instanceid) != nullptr)
|
||||
{
|
||||
pSubscriptions->subscriptions.push_back(
|
||||
MakeRealtimeSubscription(subscriptions[i]));
|
||||
}
|
||||
}
|
||||
this->hostWriter.SetMonitorPortSubscriptions(pSubscriptions);
|
||||
}
|
||||
@@ -1453,12 +1481,17 @@ public:
|
||||
|
||||
return result;
|
||||
}
|
||||
volatile bool listenForMidiEvent;
|
||||
volatile bool listenForMidiEvent = false;
|
||||
volatile bool listenForAtomOutput = false;
|
||||
|
||||
virtual void SetListenForMidiEvent(bool listen)
|
||||
{
|
||||
this->listenForMidiEvent = listen;
|
||||
}
|
||||
virtual void SetListenForAtomOutput(bool listen)
|
||||
{
|
||||
this->listenForAtomOutput = listen;
|
||||
}
|
||||
};
|
||||
|
||||
int JackHostImpl::jackInstanceId = 0;
|
||||
|
||||
+7
-4
@@ -23,8 +23,8 @@
|
||||
|
||||
#include "Lv2PedalBoard.hpp"
|
||||
#include "VuUpdate.hpp"
|
||||
#include "RingBuffer.hpp"
|
||||
#include "json.hpp"
|
||||
#include "JackHost.hpp"
|
||||
#include "JackServerSettings.hpp"
|
||||
#include <functional>
|
||||
#include "PiPedalAlsa.hpp"
|
||||
@@ -97,6 +97,7 @@ public:
|
||||
virtual void OnNotifyMonitorPort(const MonitorPortUpdate &update) = 0;
|
||||
virtual void OnNotifyMidiValueChanged(int64_t instanceId, int portIndex, float value) = 0;
|
||||
virtual void OnNotifyMidiListen(bool isNote, uint8_t noteOrControl) = 0;
|
||||
virtual void OnNotifyAtomOutput(uint64_t instanceId, const std::string&atomType,const std::string&atomJson) = 0;
|
||||
|
||||
|
||||
};
|
||||
@@ -119,6 +120,7 @@ public:
|
||||
|
||||
};
|
||||
|
||||
|
||||
class IHost;
|
||||
|
||||
class JackHost {
|
||||
@@ -134,6 +136,7 @@ public:
|
||||
virtual void SetNotificationCallbacks(IJackHostCallbacks *pNotifyCallbacks) = 0;
|
||||
|
||||
virtual void SetListenForMidiEvent(bool listen) = 0;
|
||||
virtual void SetListenForAtomOutput(bool listen) = 0;
|
||||
|
||||
|
||||
virtual void Open(const JackChannelSelection & channelSelection) = 0;
|
||||
@@ -145,9 +148,9 @@ public:
|
||||
|
||||
virtual void SetPedalBoard(const std::shared_ptr<Lv2PedalBoard> &pedalBoard) = 0;
|
||||
|
||||
virtual void SetControlValue(long instanceId,const std::string&symbol, float value) = 0;
|
||||
virtual void SetPluginPreset(long isntanceId, const std::vector<ControlValue> & values) = 0;
|
||||
virtual void SetBypass(long instanceId, bool enabled) = 0;
|
||||
virtual void SetControlValue(uint64_t instanceId,const std::string&symbol, float value) = 0;
|
||||
virtual void SetPluginPreset(uint64_t instanceId, const std::vector<ControlValue> & values) = 0;
|
||||
virtual void SetBypass(uint64_t instanceId, bool enabled) = 0;
|
||||
|
||||
virtual bool IsOpen() const = 0;
|
||||
|
||||
|
||||
+80
-11
@@ -38,6 +38,8 @@
|
||||
#include "lv2/units.lv2/units.h"
|
||||
#include "lv2/atom.lv2/util.h"
|
||||
#include "JackHost.hpp"
|
||||
#include <exception>
|
||||
#include "RingBufferReader.hpp"
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
@@ -93,6 +95,10 @@ Lv2Effect::Lv2Effect(
|
||||
|
||||
LilvInstance *pInstance = lilv_plugin_instantiate(pPlugin, pHost->GetSampleRate(), myFeatures);
|
||||
this->pInstance = pInstance;
|
||||
if (this->pInstance == nullptr)
|
||||
{
|
||||
throw PiPedalException("Failed to create plugin.");
|
||||
}
|
||||
|
||||
if (info_->hasExtension(LV2_WORKER__interface))
|
||||
{
|
||||
@@ -329,7 +335,7 @@ static inline void CopyBuffer(float *input, float *output, uint32_t frames)
|
||||
}
|
||||
}
|
||||
|
||||
void Lv2Effect::Run(uint32_t samples)
|
||||
void Lv2Effect::Run(uint32_t samples,uint64_t instanceId, RealtimeRingBufferWriter *realtimeRingBufferWriter)
|
||||
{
|
||||
// close off the atom input frame.
|
||||
if (this->inputAtomBuffers.size() != 0)
|
||||
@@ -425,7 +431,9 @@ void Lv2Effect::Run(uint32_t samples)
|
||||
this->currentBypassDx = currentBypassDx;
|
||||
this->bypassSamplesRemaining = bypassSamplesRemaining;
|
||||
}
|
||||
|
||||
}
|
||||
RelayOutputMessages(instanceId,realtimeRingBufferWriter);
|
||||
}
|
||||
|
||||
LV2_Worker_Status Lv2Effect::worker_schedule_fn(LV2_Worker_Schedule_Handle handle,
|
||||
@@ -510,6 +518,30 @@ void Lv2Effect::RequestParameter(LV2_URID uridUri)
|
||||
lv2_atom_forge_pop(&inputForgeRt, &objectFrame);
|
||||
}
|
||||
|
||||
void Lv2Effect::RelayOutputMessages(uint64_t instanceId,RealtimeRingBufferWriter *realtimeRingBufferWriter)
|
||||
{
|
||||
LV2_Atom_Sequence*controlOutput = (LV2_Atom_Sequence*)GetAtomOutputBuffer();
|
||||
if (controlOutput == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LV2_ATOM_SEQUENCE_FOREACH(controlOutput, ev)
|
||||
{
|
||||
|
||||
// frame_offset = ev->time.frames; // not really interested.
|
||||
|
||||
if (lv2_atom_forge_is_object_type(&this->outputForgeRt, ev->body.type))
|
||||
{
|
||||
const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
|
||||
if (obj->body.otype != uris.patch_Set) // patch_Set is handled elsewhere.
|
||||
{
|
||||
realtimeRingBufferWriter->AtomOutput(instanceId,obj->atom.size +sizeof(obj->atom),(uint8_t*)obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Lv2Effect::GatherParameter(RealtimeParameterRequest *pRequest)
|
||||
{
|
||||
LV2_Atom_Sequence*controlInput = (LV2_Atom_Sequence*)GetAtomOutputBuffer();
|
||||
@@ -544,12 +576,13 @@ void Lv2Effect::GatherParameter(RealtimeParameterRequest *pRequest)
|
||||
LV2_URID key = ((const LV2_Atom_URID *)property)->body;
|
||||
if (key == pRequest->uridUri)
|
||||
{
|
||||
if (value->size > sizeof(pRequest->response))
|
||||
int atom_size = value->size + sizeof(LV2_Atom);
|
||||
if (atom_size > sizeof(pRequest->response))
|
||||
{
|
||||
pRequest->errorMessage = "Response is too large.";
|
||||
} else {
|
||||
pRequest->responseLength = value->size;
|
||||
memcpy(pRequest->response,value,value->size);
|
||||
pRequest->responseLength = atom_size;
|
||||
memcpy(pRequest->response,value,atom_size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -559,6 +592,16 @@ void Lv2Effect::GatherParameter(RealtimeParameterRequest *pRequest)
|
||||
}
|
||||
}
|
||||
|
||||
std::string Lv2Effect::GetAtomObjectType(uint8_t*pData)
|
||||
{
|
||||
LV2_Atom_Object *pAtom = (LV2_Atom_Object*)pData;
|
||||
if (pAtom->atom.type != uris.atom_Object)
|
||||
{
|
||||
throw std::invalid_argument("Not an Lv2 Object");
|
||||
}
|
||||
return pHost->Lv2UriudToString(pAtom->body.otype);
|
||||
|
||||
}
|
||||
std::string Lv2Effect::AtomToJson(uint8_t*pData)
|
||||
{
|
||||
std::stringstream s;
|
||||
@@ -569,6 +612,18 @@ std::string Lv2Effect::AtomToJson(uint8_t*pData)
|
||||
return s.str();
|
||||
}
|
||||
|
||||
static std::string UriToFieldName(const std::string&uri){
|
||||
int pos;
|
||||
for (pos = uri.length(); pos >= 0; --pos)
|
||||
{
|
||||
char c = uri[pos];
|
||||
if (c == '#' || c == '/' || c == ':')
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return uri.substr(pos+1);
|
||||
}
|
||||
void Lv2Effect::WriteAtom(json_writer &writer, LV2_Atom*pAtom)
|
||||
{
|
||||
if (pAtom->type == uris.atom_Blank)
|
||||
@@ -612,7 +667,7 @@ void Lv2Effect::WriteAtom(json_writer &writer, LV2_Atom*pAtom)
|
||||
LV2_Atom_Vector *pVector = (LV2_Atom_Vector*)pAtom;
|
||||
writer.start_array();
|
||||
{
|
||||
size_t n = (pAtom->size-sizeof(LV2_Atom_Vector))/pVector->body.child_size;
|
||||
size_t n = (pAtom->size-sizeof(pVector->body))/pVector->body.child_size;
|
||||
char *pItems = ((char*)pAtom) + sizeof(LV2_Atom_Vector);
|
||||
if (pVector->body.child_type == uris.atom_float)
|
||||
{
|
||||
@@ -662,22 +717,36 @@ void Lv2Effect::WriteAtom(json_writer &writer, LV2_Atom*pAtom)
|
||||
writer.start_object();
|
||||
|
||||
const LV2_Atom_Object *obj = (const LV2_Atom_Object *)pAtom;
|
||||
std::string id = pHost->Lv2UriudToString(obj->body.id);
|
||||
std::string type = pHost->Lv2UriudToString(obj->body.otype);
|
||||
|
||||
writer.write_json_member("id",id.c_str());
|
||||
writer.write_json_member("lv2Type",type.c_str());
|
||||
|
||||
bool firstMember = true;
|
||||
if (obj->body.id != 0)
|
||||
{
|
||||
std::string id = pHost->Lv2UriudToString(obj->body.id);
|
||||
writer.write_member("id",id.c_str());
|
||||
firstMember = false;
|
||||
}
|
||||
|
||||
if (obj->body.otype != 0)
|
||||
{
|
||||
std::string type = pHost->Lv2UriudToString(obj->body.otype);
|
||||
writer.write_member("lv2Type",type.c_str());
|
||||
if (!firstMember)
|
||||
{
|
||||
writer.write_raw(",");
|
||||
}
|
||||
firstMember = false;
|
||||
}
|
||||
|
||||
LV2_ATOM_OBJECT_FOREACH (obj, prop) {
|
||||
if (!firstMember) {
|
||||
writer.write_raw(",");
|
||||
}
|
||||
firstMember = false;
|
||||
std::string key = pHost->Lv2UriudToString(prop->key);
|
||||
key = UriToFieldName(key);
|
||||
writer.write(key);
|
||||
writer.write_raw(": ");
|
||||
LV2_Atom *value = &(prop->value);
|
||||
WriteAtom(writer,value);
|
||||
}
|
||||
writer.end_object();
|
||||
|
||||
|
||||
+9
-2
@@ -33,9 +33,12 @@
|
||||
#include "lv2/lv2plug.in/ns/extensions/units/units.h"
|
||||
#include "lv2/atom.lv2/forge.h"
|
||||
|
||||
|
||||
namespace pipedal
|
||||
{
|
||||
|
||||
class RealtimeRingBufferWriter;
|
||||
|
||||
class Lv2Effect : public IEffect
|
||||
{
|
||||
private:
|
||||
@@ -144,7 +147,7 @@ namespace pipedal
|
||||
|
||||
Uris uris;
|
||||
|
||||
long instanceId;
|
||||
uint64_t instanceId;
|
||||
BufferPool bufferPool;
|
||||
|
||||
static LV2_Worker_Status worker_schedule_fn(LV2_Worker_Schedule_Handle handle,
|
||||
@@ -167,6 +170,8 @@ namespace pipedal
|
||||
virtual void RequestParameter(LV2_URID uridUri);
|
||||
virtual void GatherParameter(RealtimeParameterRequest*pRequest);
|
||||
|
||||
virtual void RelayOutputMessages(uint64_t instanceId,RealtimeRingBufferWriter *realtimeRingBufferWriter);
|
||||
|
||||
virtual uint8_t*GetAtomInputBuffer() {
|
||||
if (this->inputAtomBuffers.size() == 0) return nullptr;
|
||||
return (uint8_t*)this->inputAtomBuffers[0];
|
||||
@@ -178,6 +183,8 @@ namespace pipedal
|
||||
|
||||
}
|
||||
virtual std::string AtomToJson(uint8_t*pAtom);
|
||||
virtual std::string GetAtomObjectType(uint8_t*pData);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -261,7 +268,7 @@ namespace pipedal
|
||||
}
|
||||
|
||||
void Activate();
|
||||
void Run(uint32_t samples);
|
||||
void Run(uint32_t samples, uint64_t instanceId,RealtimeRingBufferWriter *realtimeRingBufferWriter);
|
||||
void Deactivate();
|
||||
};
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ std::vector<float *> Lv2PedalBoard::AllocateAudioBuffers(int nChannels)
|
||||
return result;
|
||||
}
|
||||
|
||||
int Lv2PedalBoard::GetControlIndex(long instanceId, const std::string &symbol)
|
||||
int Lv2PedalBoard::GetControlIndex(uint64_t instanceId, const std::string &symbol)
|
||||
{
|
||||
for (int i = 0; i < realtimeEffects.size(); ++i)
|
||||
{
|
||||
@@ -109,7 +109,7 @@ std::vector<float *> Lv2PedalBoard::PrepareItems(
|
||||
{
|
||||
pEffect = pLv2Effect;
|
||||
|
||||
long instanceId = pEffect->GetInstanceId();
|
||||
uint64_t instanceId = pEffect->GetInstanceId();
|
||||
|
||||
if (inputBuffers.size() == 1)
|
||||
{
|
||||
@@ -142,9 +142,9 @@ std::vector<float *> Lv2PedalBoard::PrepareItems(
|
||||
}
|
||||
|
||||
this->processActions.push_back(
|
||||
[pLv2Effect](uint32_t frames)
|
||||
[pLv2Effect,this,instanceId](uint32_t frames)
|
||||
{
|
||||
pLv2Effect->Run(frames);
|
||||
pLv2Effect->Run(frames,instanceId,this->ringBufferWriter);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -356,9 +356,9 @@ static void Copy(float *input, float *output, uint32_t samples)
|
||||
output[i] = input[i];
|
||||
}
|
||||
}
|
||||
bool Lv2PedalBoard::Run(float **inputBuffers, float **outputBuffers, uint32_t samples)
|
||||
bool Lv2PedalBoard::Run(float **inputBuffers, float **outputBuffers, uint32_t samples, RealtimeRingBufferWriter*ringBufferWriter)
|
||||
{
|
||||
|
||||
this->ringBufferWriter = ringBufferWriter;
|
||||
for (int i = 0; i < this->pedalBoardInputBuffers.size(); ++i)
|
||||
{
|
||||
if (inputBuffers[i] == nullptr)
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace pipedal {
|
||||
|
||||
class RealtimeVuBuffers;
|
||||
class RealtimeParameterRequest;
|
||||
class RealtimeRingBufferWriter;
|
||||
|
||||
class Lv2PedalBoard {
|
||||
IHost *pHost = nullptr;
|
||||
@@ -54,6 +55,8 @@ class Lv2PedalBoard {
|
||||
|
||||
float *CreateNewAudioBuffer();
|
||||
|
||||
RealtimeRingBufferWriter *ringBufferWriter;
|
||||
|
||||
enum class MappingType {
|
||||
Linear,
|
||||
Circular,
|
||||
@@ -94,14 +97,14 @@ public:
|
||||
|
||||
void Prepare(IHost *pHost,const PedalBoard&pedalBoard);
|
||||
|
||||
int GetIndexOfInstanceId(long instanceId) {
|
||||
int GetIndexOfInstanceId(uint64_t instanceId) {
|
||||
for (int i = 0; i < this->realtimeEffects.size(); ++i)
|
||||
{
|
||||
if (this->realtimeEffects[i]->GetInstanceId() == instanceId) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
IEffect*GetEffect(long instanceId) {
|
||||
IEffect*GetEffect(uint64_t instanceId) {
|
||||
for (int i = 0; i < realtimeEffects.size(); ++i) {
|
||||
if (realtimeEffects[i]->GetInstanceId() == instanceId)
|
||||
{
|
||||
@@ -112,7 +115,7 @@ public:
|
||||
}
|
||||
void Activate();
|
||||
void Deactivate();
|
||||
bool Run(float**inputBuffers, float**outputBuffers,uint32_t samples);
|
||||
bool Run(float**inputBuffers, float**outputBuffers,uint32_t samples, RealtimeRingBufferWriter*realtimeWriter);
|
||||
|
||||
void ResetAtomBuffers();
|
||||
|
||||
@@ -126,7 +129,7 @@ public:
|
||||
|
||||
|
||||
|
||||
int GetControlIndex(long instanceId,const std::string&symbol);
|
||||
int GetControlIndex(uint64_t instanceId,const std::string&symbol);
|
||||
void SetControlValue(int effectIndex, int portIndex, float value);
|
||||
void SetBypass(int effectIndex,bool enabled);
|
||||
|
||||
|
||||
+2
-2
@@ -96,7 +96,7 @@ bool PedalBoard::SetControlValue(long pedalItemId, const std::string &symbol, fl
|
||||
|
||||
PedalBoardItem PedalBoard::MakeEmptyItem()
|
||||
{
|
||||
long instanceId = NextInstanceId();
|
||||
uint64_t instanceId = NextInstanceId();
|
||||
|
||||
PedalBoardItem result;
|
||||
result.instanceId(instanceId);
|
||||
@@ -109,7 +109,7 @@ PedalBoardItem PedalBoard::MakeEmptyItem()
|
||||
|
||||
PedalBoardItem PedalBoard::MakeSplit()
|
||||
{
|
||||
long instanceId = NextInstanceId();
|
||||
uint64_t instanceId = NextInstanceId();
|
||||
|
||||
PedalBoardItem result;
|
||||
result.instanceId(instanceId);
|
||||
|
||||
+2
-2
@@ -122,8 +122,8 @@ public:
|
||||
class PedalBoard {
|
||||
std::string name_;
|
||||
std::vector<PedalBoardItem> items_;
|
||||
long nextInstanceId_ = 0;
|
||||
long NextInstanceId() { return ++nextInstanceId_; }
|
||||
uint64_t nextInstanceId_ = 0;
|
||||
uint64_t NextInstanceId() { return ++nextInstanceId_; }
|
||||
|
||||
public:
|
||||
bool SetControlValue(long pedalItemId, const std::string &symbol, float value);
|
||||
|
||||
+114
-40
@@ -53,7 +53,7 @@ PiPedalModel::PiPedalModel()
|
||||
|
||||
void PiPedalModel::Close()
|
||||
{
|
||||
std::lock_guard lock(this->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
|
||||
// take a snapshot incase a client unsusbscribes in the notification handler (in which case the mutex won't protect us)
|
||||
IPiPedalModelSubscriber **t = new IPiPedalModelSubscriber *[this->subscribers.size()];
|
||||
@@ -186,13 +186,13 @@ IPiPedalModelSubscriber *PiPedalModel::GetNotificationSubscriber(int64_t clientI
|
||||
|
||||
void PiPedalModel::AddNotificationSubscription(IPiPedalModelSubscriber *pSubscriber)
|
||||
{
|
||||
std::lock_guard lock(this->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
this->subscribers.push_back(pSubscriber);
|
||||
}
|
||||
void PiPedalModel::RemoveNotificationSubsription(IPiPedalModelSubscriber *pSubscriber)
|
||||
{
|
||||
{
|
||||
std::lock_guard lock(this->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
|
||||
for (auto it = this->subscribers.begin(); it != this->subscribers.end(); ++it)
|
||||
{
|
||||
@@ -205,6 +205,7 @@ void PiPedalModel::RemoveNotificationSubsription(IPiPedalModelSubscriber *pSubsc
|
||||
int64_t clientId = pSubscriber->GetClientId();
|
||||
|
||||
this->deleteMidiListeners(clientId);
|
||||
this->deleteAtomOutputListeners(clientId);
|
||||
|
||||
for (int i = 0; i < this->outstandingParameterRequests.size(); ++i)
|
||||
{
|
||||
@@ -226,7 +227,7 @@ void PiPedalModel::previewControl(int64_t clientId, int64_t pedalItemId, const s
|
||||
void PiPedalModel::setControl(int64_t clientId, int64_t pedalItemId, const std::string &symbol, float value)
|
||||
{
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
previewControl(clientId, pedalItemId, symbol, value);
|
||||
this->pedalBoard.SetControlValue(pedalItemId, symbol, value);
|
||||
{
|
||||
@@ -309,7 +310,7 @@ void PiPedalModel::firePedalBoardChanged(int64_t clientId)
|
||||
void PiPedalModel::setPedalBoard(int64_t clientId, PedalBoard &pedalBoard)
|
||||
{
|
||||
{
|
||||
std::lock_guard guard(this->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
this->pedalBoard = pedalBoard;
|
||||
updateDefaults(&this->pedalBoard);
|
||||
|
||||
@@ -345,7 +346,7 @@ void PiPedalModel::setPedalBoardItemEnable(int64_t clientId, int64_t pedalItemId
|
||||
|
||||
void PiPedalModel::getPresets(PresetIndex *pResult)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
|
||||
this->storage.GetPresetIndex(pResult);
|
||||
pResult->presetChanged(this->hasPresetChanged);
|
||||
@@ -353,12 +354,12 @@ void PiPedalModel::getPresets(PresetIndex *pResult)
|
||||
|
||||
PedalBoard PiPedalModel::getPreset(int64_t instanceId)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
return this->storage.GetPreset(instanceId);
|
||||
}
|
||||
void PiPedalModel::getBank(int64_t instanceId, BankFile *pResult)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
this->storage.GetBankFile(instanceId, pResult);
|
||||
}
|
||||
|
||||
@@ -501,7 +502,7 @@ int64_t PiPedalModel::deletePreset(int64_t clientId, int64_t instanceId)
|
||||
}
|
||||
bool PiPedalModel::renamePreset(int64_t clientId, int64_t instanceId, const std::string &name)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
if (storage.RenamePreset(instanceId, name))
|
||||
{
|
||||
this->firePresetsChanged(clientId);
|
||||
@@ -515,7 +516,7 @@ bool PiPedalModel::renamePreset(int64_t clientId, int64_t instanceId, const std:
|
||||
|
||||
void PiPedalModel::setWifiConfigSettings(const WifiConfigSettings &wifiConfigSettings)
|
||||
{
|
||||
std::lock_guard lock(this->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
|
||||
ShutdownClient::SetWifiConfig(wifiConfigSettings);
|
||||
|
||||
@@ -540,19 +541,19 @@ void PiPedalModel::setWifiConfigSettings(const WifiConfigSettings &wifiConfigSet
|
||||
}
|
||||
WifiConfigSettings PiPedalModel::getWifiConfigSettings()
|
||||
{
|
||||
std::lock_guard lock(this->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
return this->storage.GetWifiConfigSettings();
|
||||
}
|
||||
|
||||
JackConfiguration PiPedalModel::getJackConfiguration()
|
||||
{
|
||||
std::lock_guard lock(this->mutex); // copy atomically.
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex); // copy atomically.
|
||||
return this->jackConfiguration;
|
||||
}
|
||||
|
||||
void PiPedalModel::setJackChannelSelection(int64_t clientId, const JackChannelSelection &channelSelection)
|
||||
{
|
||||
std::lock_guard lock(this->mutex); // copy atomically.
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex); // copy atomically.
|
||||
this->storage.SetJackChannelSelection(channelSelection);
|
||||
this->fireChannelSelectionChanged(clientId);
|
||||
|
||||
@@ -595,7 +596,7 @@ void PiPedalModel::fireChannelSelectionChanged(int64_t clientId)
|
||||
|
||||
JackChannelSelection PiPedalModel::getJackChannelSelection()
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
JackChannelSelection t = this->storage.GetJackChannelSelection(this->jackConfiguration);
|
||||
if (this->jackConfiguration.isValid())
|
||||
{
|
||||
@@ -606,7 +607,7 @@ JackChannelSelection PiPedalModel::getJackChannelSelection()
|
||||
|
||||
int64_t PiPedalModel::addVuSubscription(int64_t instanceId)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
int64_t subscriptionId = ++nextSubscriptionId;
|
||||
activeVuSubscriptions.push_back(VuSubscription{subscriptionId, instanceId});
|
||||
|
||||
@@ -616,7 +617,7 @@ int64_t PiPedalModel::addVuSubscription(int64_t instanceId)
|
||||
}
|
||||
void PiPedalModel::removeVuSubscription(int64_t subscriptionHandle)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
for (auto i = activeVuSubscriptions.begin(); i != activeVuSubscriptions.end(); ++i)
|
||||
{
|
||||
if ((*i).subscriptionHandle == subscriptionHandle)
|
||||
@@ -630,7 +631,7 @@ void PiPedalModel::removeVuSubscription(int64_t subscriptionHandle)
|
||||
|
||||
void PiPedalModel::OnNotifyMidiValueChanged(int64_t instanceId, int portIndex, float value)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
PedalBoardItem *item = this->pedalBoard.GetItem(instanceId);
|
||||
if (item)
|
||||
{
|
||||
@@ -702,7 +703,7 @@ void PiPedalModel::OnNotifyMidiValueChanged(int64_t instanceId, int portIndex, f
|
||||
}
|
||||
void PiPedalModel::OnNotifyVusSubscription(const std::vector<VuUpdate> &updates)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
for (size_t i = 0; i < updates.size(); ++i)
|
||||
{
|
||||
// take a snapshot incase a client unsusbscribes in the notification handler (in which case the mutex won't protect us)
|
||||
@@ -743,7 +744,7 @@ void PiPedalModel::updateRealtimeMonitorPortSubscriptions()
|
||||
|
||||
int64_t PiPedalModel::monitorPort(int64_t instanceId, const std::string &key, float updateInterval, PortMonitorCallback onUpdate)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
int64_t subscriptionId = ++nextSubscriptionId;
|
||||
activeMonitorPortSubscriptions.push_back(
|
||||
MonitorPortSubscription{subscriptionId, instanceId, key, updateInterval, onUpdate});
|
||||
@@ -754,7 +755,7 @@ int64_t PiPedalModel::monitorPort(int64_t instanceId, const std::string &key, fl
|
||||
}
|
||||
void PiPedalModel::unmonitorPort(int64_t subscriptionHandle)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
|
||||
for (auto i = activeMonitorPortSubscriptions.begin(); i != activeMonitorPortSubscriptions.end(); ++i)
|
||||
{
|
||||
@@ -766,9 +767,11 @@ void PiPedalModel::unmonitorPort(int64_t subscriptionHandle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PiPedalModel::OnNotifyMonitorPort(const MonitorPortUpdate &update)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
|
||||
for (auto i = activeMonitorPortSubscriptions.begin(); i != activeMonitorPortSubscriptions.end(); ++i)
|
||||
{
|
||||
@@ -793,7 +796,7 @@ void PiPedalModel::getLv2Parameter(
|
||||
[this](RealtimeParameterRequest *pParameter)
|
||||
{
|
||||
{
|
||||
std::lock_guard guard(this->mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
bool cancelled = true;
|
||||
for (auto i = this->outstandingParameterRequests.begin();
|
||||
i != this->outstandingParameterRequests.end(); ++i)
|
||||
@@ -829,27 +832,27 @@ void PiPedalModel::getLv2Parameter(
|
||||
onRequestComplete,
|
||||
clientId, instanceId, urid, onSuccess, onError);
|
||||
|
||||
std::lock_guard guard(constMutex(mutex));
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
outstandingParameterRequests.push_back(request);
|
||||
this->jackHost->getRealtimeParameter(request);
|
||||
}
|
||||
|
||||
BankIndex PiPedalModel::getBankIndex() const
|
||||
{
|
||||
std::lock_guard guard(constMutex(mutex));
|
||||
std::lock_guard<std::recursive_mutex> guard(const_cast<std::recursive_mutex&>(mutex));
|
||||
return storage.GetBanks();
|
||||
}
|
||||
|
||||
void PiPedalModel::renameBank(int64_t clientId, int64_t bankId, const std::string &newName)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
storage.RenameBank(bankId, newName);
|
||||
fireBanksChanged(clientId);
|
||||
}
|
||||
|
||||
int64_t PiPedalModel::saveBankAs(int64_t clientId, int64_t bankId, const std::string &newName)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
int64_t newId = storage.SaveBankAs(bankId, newName);
|
||||
fireBanksChanged(clientId);
|
||||
return newId;
|
||||
@@ -857,7 +860,7 @@ int64_t PiPedalModel::saveBankAs(int64_t clientId, int64_t bankId, const std::st
|
||||
|
||||
void PiPedalModel::openBank(int64_t clientId, int64_t bankId)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
|
||||
storage.LoadBank(bankId);
|
||||
fireBanksChanged(clientId);
|
||||
@@ -872,13 +875,13 @@ void PiPedalModel::openBank(int64_t clientId, int64_t bankId)
|
||||
|
||||
JackServerSettings PiPedalModel::GetJackServerSettings()
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
return this->jackServerSettings;
|
||||
}
|
||||
|
||||
void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSettings)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
|
||||
if (!ShutdownClient::CanUseShutdownClient())
|
||||
{
|
||||
@@ -916,7 +919,7 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet
|
||||
jackServerSettings,
|
||||
[this](bool success, const std::string &errorMessage)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
if (!success)
|
||||
{
|
||||
std::stringstream s;
|
||||
@@ -998,14 +1001,14 @@ void PiPedalModel::updateDefaults(PedalBoard *pedalBoard)
|
||||
|
||||
std::vector<Lv2PluginPreset> PiPedalModel::GetPluginPresets(std::string pluginUri)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
|
||||
return lv2Host.GetPluginPresets(pluginUri);
|
||||
}
|
||||
|
||||
void PiPedalModel::LoadPluginPreset(int64_t instanceId, const std::string &presetUri)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
|
||||
PedalBoardItem *pedalBoardItem = this->pedalBoard.GetItem(instanceId);
|
||||
if (pedalBoardItem != nullptr)
|
||||
@@ -1033,9 +1036,23 @@ void PiPedalModel::LoadPluginPreset(int64_t instanceId, const std::string &prese
|
||||
}
|
||||
}
|
||||
|
||||
void PiPedalModel::deleteAtomOutputListeners(int64_t clientId)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
for (size_t i = 0; i < atomOutputListeners.size(); ++i)
|
||||
{
|
||||
if (atomOutputListeners[i].clientId == clientId)
|
||||
{
|
||||
atomOutputListeners.erase(atomOutputListeners.begin() + i);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
jackHost->SetListenForAtomOutput(atomOutputListeners.size() != 0);
|
||||
}
|
||||
|
||||
void PiPedalModel::deleteMidiListeners(int64_t clientId)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
for (size_t i = 0; i < midiEventListeners.size(); ++i)
|
||||
{
|
||||
if (midiEventListeners[i].clientId == clientId)
|
||||
@@ -1047,9 +1064,34 @@ void PiPedalModel::deleteMidiListeners(int64_t clientId)
|
||||
jackHost->SetListenForMidiEvent(midiEventListeners.size() != 0);
|
||||
}
|
||||
|
||||
void PiPedalModel::OnNotifyAtomOutput(uint64_t instanceId, const std::string&atomType,const std::string&atomJson)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
|
||||
for (int i = 0; i < atomOutputListeners.size(); ++i)
|
||||
{
|
||||
auto &listener = atomOutputListeners[i];
|
||||
if (listener.instanceId == instanceId)
|
||||
{
|
||||
auto subscriber = this->GetNotificationSubscriber(listener.clientId);
|
||||
if (subscriber)
|
||||
{
|
||||
subscriber->OnNotifyAtomOutput(listener.clientHandle,instanceId,atomType,atomJson);
|
||||
} else {
|
||||
atomOutputListeners.erase(atomOutputListeners.begin() + i);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
jackHost->SetListenForAtomOutput(atomOutputListeners.size() != 0);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PiPedalModel::OnNotifyMidiListen(bool isNote, uint8_t noteOrControl)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
|
||||
for (int i = 0; i < midiEventListeners.size(); ++i)
|
||||
{
|
||||
@@ -1060,9 +1102,10 @@ void PiPedalModel::OnNotifyMidiListen(bool isNote, uint8_t noteOrControl)
|
||||
if (subscriber)
|
||||
{
|
||||
subscriber->OnNotifyMidiListener(listener.clientHandle, isNote, noteOrControl);
|
||||
} else {
|
||||
midiEventListeners.erase(midiEventListeners.begin() + i);
|
||||
--i;
|
||||
}
|
||||
midiEventListeners.erase(midiEventListeners.begin() + i);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
jackHost->SetListenForMidiEvent(midiEventListeners.size() != 0);
|
||||
@@ -1070,17 +1113,48 @@ void PiPedalModel::OnNotifyMidiListen(bool isNote, uint8_t noteOrControl)
|
||||
|
||||
void PiPedalModel::listenForMidiEvent(int64_t clientId, int64_t clientHandle, bool listenForControlsOnly)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
MidiListener listener{clientId, clientHandle, listenForControlsOnly};
|
||||
midiEventListeners.push_back(listener);
|
||||
jackHost->SetListenForMidiEvent(true);
|
||||
}
|
||||
void PiPedalModel::cancelListenForMidiEvent(int64_t clientId, int64_t clientHandled)
|
||||
|
||||
void PiPedalModel::listenForAtomOutputs(int64_t clientId, int64_t clientHandle, uint64_t instanceId)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
AtomOutputListener listener{clientId, clientHandle, instanceId};
|
||||
atomOutputListeners.push_back(listener);
|
||||
jackHost->SetListenForAtomOutput(true);
|
||||
}
|
||||
|
||||
void PiPedalModel::cancelListenForMidiEvent(int64_t clientId, int64_t clientHandle)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
for (size_t i = 0; i < midiEventListeners.size(); ++i)
|
||||
{
|
||||
midiEventListeners.erase(midiEventListeners.begin() + i);
|
||||
const auto& listener = midiEventListeners[i];
|
||||
if (listener.clientId == clientId && listener.clientHandle == clientHandle)
|
||||
{
|
||||
midiEventListeners.erase(midiEventListeners.begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (midiEventListeners.size() == 0)
|
||||
{
|
||||
jackHost->SetListenForMidiEvent(false);
|
||||
}
|
||||
}
|
||||
void PiPedalModel::cancelListenForAtomOutputs(int64_t clientId, int64_t clientHandle)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
for (size_t i = 0; i < atomOutputListeners.size(); ++i)
|
||||
{
|
||||
const auto&listener = atomOutputListeners[i];
|
||||
if (listener.clientId == clientId && listener.clientHandle == clientHandle)
|
||||
{
|
||||
atomOutputListeners.erase(atomOutputListeners.begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (midiEventListeners.size() == 0)
|
||||
{
|
||||
|
||||
+18
-4
@@ -54,6 +54,7 @@ public:
|
||||
virtual void OnLoadPluginPreset(int64_t instanceId,const std::vector<ControlValue>&controlValues) = 0;
|
||||
virtual void OnMidiValueChanged(int64_t instanceId, const std::string&symbol, float value) = 0;
|
||||
virtual void OnNotifyMidiListener(int64_t clientHandle, bool isNote, uint8_t noteOrControl) = 0;
|
||||
virtual void OnNotifyAtomOutput(int64_t clientModel,uint64_t instanceId,const std::string&atomType, const std::string&atomJson) = 0;
|
||||
virtual void OnWifiConfigSettingsChanged(const WifiConfigSettings&wifiConfigSettings) = 0;
|
||||
virtual void Close() = 0;
|
||||
};
|
||||
@@ -61,6 +62,7 @@ public:
|
||||
class PiPedalModel: private IJackHostCallbacks {
|
||||
private:
|
||||
PiPedalAlsaDevices alsaDevices;
|
||||
std::recursive_mutex mutex;
|
||||
|
||||
|
||||
class MidiListener {
|
||||
@@ -69,13 +71,20 @@ private:
|
||||
int64_t clientHandle;
|
||||
bool listenForControlsOnly;
|
||||
};
|
||||
class AtomOutputListener {
|
||||
public:
|
||||
int64_t clientId;
|
||||
int64_t clientHandle;
|
||||
uint64_t instanceId;
|
||||
};
|
||||
void deleteMidiListeners(int64_t clientId);
|
||||
void deleteAtomOutputListeners(int64_t clientId);
|
||||
|
||||
std::vector<MidiListener> midiEventListeners;
|
||||
std::vector<AtomOutputListener> atomOutputListeners;
|
||||
|
||||
JackServerSettings jackServerSettings;
|
||||
Lv2Host lv2Host;
|
||||
std::recursive_mutex mutex;
|
||||
PedalBoard pedalBoard;
|
||||
Storage storage;
|
||||
bool hasPresetChanged = false;
|
||||
@@ -120,7 +129,7 @@ private: // IJackHostCallbacks
|
||||
virtual void OnNotifyMonitorPort(const MonitorPortUpdate &update);
|
||||
virtual void OnNotifyMidiValueChanged(int64_t instanceId, int portIndex, float value);
|
||||
virtual void OnNotifyMidiListen(bool isNote, uint8_t noteOrControl);
|
||||
|
||||
virtual void OnNotifyAtomOutput(uint64_t instanceId, const std::string&atomType,const std::string&atomJson);
|
||||
|
||||
public:
|
||||
PiPedalModel();
|
||||
@@ -132,7 +141,7 @@ public:
|
||||
|
||||
const Lv2Host& getPlugins() const { return lv2Host; }
|
||||
PedalBoard getCurrentPedalBoardCopy() {
|
||||
std::lock_guard guard(mutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(mutex);
|
||||
return pedalBoard;
|
||||
}
|
||||
std::vector<Lv2PluginPreset> GetPluginPresets(std::string pluginUri);
|
||||
@@ -204,8 +213,13 @@ public:
|
||||
void SetJackServerSettings(const JackServerSettings& jackServerSettings);
|
||||
|
||||
void listenForMidiEvent(int64_t clientId, int64_t clientHandle, bool listenForControlsOnly);
|
||||
void cancelListenForMidiEvent(int64_t clientId, int64_t clientHandled);
|
||||
void cancelListenForMidiEvent(int64_t clientId, int64_t clientHandle);
|
||||
|
||||
void listenForAtomOutputs(int64_t clientId, int64_t clientHandle, uint64_t instanceId);
|
||||
void cancelListenForAtomOutputs(int64_t clientId, int64_t clientHandle);
|
||||
|
||||
std::vector<AlsaDeviceInfo> GetAlsaDevices();
|
||||
|
||||
};
|
||||
|
||||
} // namespace pipedal.
|
||||
+124
-14
@@ -53,6 +53,21 @@ JSON_MAP_REFERENCE(NotifyMidiListenerBody, isNote)
|
||||
JSON_MAP_REFERENCE(NotifyMidiListenerBody, noteOrControl)
|
||||
JSON_MAP_END()
|
||||
|
||||
class NotifyAtomOutputBody {
|
||||
public:
|
||||
int64_t clientHandle_;
|
||||
uint64_t instanceId_;
|
||||
|
||||
std::string atomJson_;
|
||||
|
||||
DECLARE_JSON_MAP(NotifyAtomOutputBody);
|
||||
};
|
||||
JSON_MAP_BEGIN(NotifyAtomOutputBody)
|
||||
JSON_MAP_REFERENCE(NotifyAtomOutputBody, clientHandle)
|
||||
JSON_MAP_REFERENCE(NotifyAtomOutputBody, instanceId)
|
||||
JSON_MAP_REFERENCE(NotifyAtomOutputBody, atomJson)
|
||||
JSON_MAP_END()
|
||||
|
||||
class ListenForMidiEventBody {
|
||||
public:
|
||||
bool listenForControlsOnly_;
|
||||
@@ -65,6 +80,18 @@ JSON_MAP_REFERENCE(ListenForMidiEventBody, listenForControlsOnly)
|
||||
JSON_MAP_REFERENCE(ListenForMidiEventBody, handle)
|
||||
JSON_MAP_END()
|
||||
|
||||
class ListenForAtomOutputBody {
|
||||
public:
|
||||
uint64_t instanceId_;
|
||||
int64_t handle_;
|
||||
DECLARE_JSON_MAP(ListenForAtomOutputBody);
|
||||
};
|
||||
|
||||
JSON_MAP_BEGIN(ListenForAtomOutputBody)
|
||||
JSON_MAP_REFERENCE(ListenForAtomOutputBody, instanceId)
|
||||
JSON_MAP_REFERENCE(ListenForAtomOutputBody, handle)
|
||||
JSON_MAP_END()
|
||||
|
||||
|
||||
class OnLoadPluginPresetBody {
|
||||
public:
|
||||
@@ -390,7 +417,7 @@ private:
|
||||
writer.end_array();
|
||||
|
||||
{
|
||||
std::lock_guard guard(this->writeMutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(this->writeMutex);
|
||||
this->send(s.str());
|
||||
}
|
||||
}
|
||||
@@ -421,7 +448,7 @@ private:
|
||||
}
|
||||
writer.end_array();
|
||||
{
|
||||
std::lock_guard guard(this->writeMutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(this->writeMutex);
|
||||
this->send(s.str());
|
||||
}
|
||||
}
|
||||
@@ -448,7 +475,7 @@ private:
|
||||
writer.end_array();
|
||||
|
||||
{
|
||||
std::lock_guard guard(this->writeMutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(this->writeMutex);
|
||||
this->send(s.str());
|
||||
}
|
||||
}
|
||||
@@ -526,7 +553,7 @@ private:
|
||||
{
|
||||
}
|
||||
};
|
||||
std::mutex requestMutex;
|
||||
std::recursive_mutex requestMutex;
|
||||
std::vector<IRequestReservation *> requestReservations;
|
||||
std::atomic<int> nextRequestId{1};
|
||||
|
||||
@@ -543,7 +570,7 @@ public:
|
||||
onSuccess,
|
||||
onError);
|
||||
{
|
||||
std::lock_guard lock(requestMutex);
|
||||
std::lock_guard<std::recursive_mutex> lock(requestMutex);
|
||||
requestReservations.push_back(reservation);
|
||||
}
|
||||
std::stringstream s(ios_base::out);
|
||||
@@ -563,7 +590,7 @@ public:
|
||||
}
|
||||
writer.end_array();
|
||||
{
|
||||
std::lock_guard guard(this->writeMutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(this->writeMutex);
|
||||
this->send(s.str());
|
||||
}
|
||||
}
|
||||
@@ -595,7 +622,7 @@ public:
|
||||
bool waitingForPortMonitorAck(uint64_t subscriptionId)
|
||||
{
|
||||
{
|
||||
std::lock_guard guard(subscriptionMutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(subscriptionMutex);
|
||||
for (int i = 0; i < this->activePortMonitors.size(); ++i)
|
||||
{
|
||||
auto &portMonitor = activePortMonitors[i];
|
||||
@@ -613,7 +640,7 @@ public:
|
||||
void portMonitorAck(uint64_t subscriptionId)
|
||||
{
|
||||
{
|
||||
std::lock_guard guard(subscriptionMutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(subscriptionMutex);
|
||||
for (int i = 0; i < this->activePortMonitors.size(); ++i)
|
||||
{
|
||||
auto &portMonitor = activePortMonitors[i];
|
||||
@@ -632,7 +659,7 @@ public:
|
||||
{
|
||||
IRequestReservation *reservation = nullptr;
|
||||
{
|
||||
std::lock_guard guard(this->requestMutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(this->requestMutex);
|
||||
for (auto i = this->requestReservations.begin(); i != this->requestReservations.end(); ++i)
|
||||
{
|
||||
if ((*i)->GetReservationid() == reply)
|
||||
@@ -686,6 +713,19 @@ public:
|
||||
pReader->read(&handle);
|
||||
this->model.cancelListenForMidiEvent(this->clientId,handle);
|
||||
}
|
||||
else if (message == "listenForAtomOutput")
|
||||
{
|
||||
ListenForAtomOutputBody body;
|
||||
pReader->read(&body);
|
||||
this->model.listenForAtomOutputs(this->clientId,body.handle_, body.instanceId_);
|
||||
}
|
||||
else if (message == "cancelListenForAtomOutput")
|
||||
{
|
||||
int64_t handle;
|
||||
pReader->read(&handle);
|
||||
this->model.cancelListenForMidiEvent(this->clientId,handle);
|
||||
}
|
||||
|
||||
else if (message == "getJackStatus")
|
||||
{
|
||||
JackHostStatus status = model.getJackStatus();
|
||||
@@ -989,7 +1029,7 @@ public:
|
||||
}
|
||||
});
|
||||
{
|
||||
std::lock_guard guard(subscriptionMutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(subscriptionMutex);
|
||||
activePortMonitors.push_back(
|
||||
PortMonitorSubscription{subscriptionHandle, body.instanceId_,false, body.key_});
|
||||
}
|
||||
@@ -1002,7 +1042,7 @@ public:
|
||||
pReader->read(&subscriptionHandle);
|
||||
{
|
||||
{
|
||||
std::lock_guard guard(subscriptionMutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(subscriptionMutex);
|
||||
for (auto i = this->activePortMonitors.begin(); i != this->activePortMonitors.end(); ++i)
|
||||
{
|
||||
if ((*i).subscriptionHandle == subscriptionHandle)
|
||||
@@ -1024,7 +1064,7 @@ public:
|
||||
int64_t subscriptionHandle = model.addVuSubscription(instanceId);
|
||||
|
||||
{
|
||||
std::lock_guard guard(subscriptionMutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(subscriptionMutex);
|
||||
activeVuSubscriptions.push_back(VuSubscription{subscriptionHandle, instanceId});
|
||||
}
|
||||
this->Reply(replyTo, "addVuSubscriptin", subscriptionHandle);
|
||||
@@ -1034,7 +1074,7 @@ public:
|
||||
int64_t subscriptionHandle = -1;
|
||||
pReader->read(&subscriptionHandle);
|
||||
{
|
||||
std::lock_guard guard(subscriptionMutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(subscriptionMutex);
|
||||
|
||||
for (auto i = activeVuSubscriptions.begin(); i != activeVuSubscriptions.end(); ++i)
|
||||
{
|
||||
@@ -1152,7 +1192,7 @@ public:
|
||||
|
||||
virtual void OnVuMeterUpdate(const std::vector<VuUpdate> &updates)
|
||||
{
|
||||
std::lock_guard guard(subscriptionMutex);
|
||||
std::lock_guard<std::recursive_mutex> guard(subscriptionMutex);
|
||||
if (updateRequestOutstanding < 5) // throttle to accomodate a web page that can't keep up.
|
||||
{
|
||||
vuUpdateDropped = false;
|
||||
@@ -1255,6 +1295,76 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
int outstandingNotifyAtomOutputs = 0;
|
||||
|
||||
class PendingNotifyAtomOutput {
|
||||
public:
|
||||
int64_t clientHandle;
|
||||
uint64_t instanceId;
|
||||
std::string atomType;
|
||||
std::string json;
|
||||
};
|
||||
|
||||
std::vector<PendingNotifyAtomOutput> pendingNotifyAtomOutputs;
|
||||
|
||||
|
||||
void OnAckNotifyAtomOutput() {
|
||||
std::lock_guard<std::recursive_mutex> guard(subscriptionMutex);
|
||||
if (--outstandingNotifyAtomOutputs <= 0)
|
||||
{
|
||||
outstandingNotifyAtomOutputs = 0;
|
||||
if (pendingNotifyAtomOutputs.size() != 0)
|
||||
{
|
||||
PendingNotifyAtomOutput t = pendingNotifyAtomOutputs[0];
|
||||
pendingNotifyAtomOutputs.erase(pendingNotifyAtomOutputs.begin());
|
||||
OnNotifyAtomOutput(
|
||||
t.clientHandle,
|
||||
t.instanceId,
|
||||
t.atomType,
|
||||
t.json);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnNotifyAtomOutput(int64_t clientHandle,uint64_t instanceId,const std::string&atomType, const std::string&atomJson)
|
||||
{
|
||||
NotifyAtomOutputBody body;
|
||||
body.clientHandle_ = clientHandle;
|
||||
body.instanceId_ = instanceId;
|
||||
body.atomJson_ = atomJson;
|
||||
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(subscriptionMutex);
|
||||
|
||||
if (outstandingNotifyAtomOutputs != 0)
|
||||
{
|
||||
for (size_t i = 0; i < pendingNotifyAtomOutputs.size(); ++i)
|
||||
{
|
||||
auto &output = pendingNotifyAtomOutputs[i];
|
||||
if (output.clientHandle == clientHandle && output.instanceId == instanceId && output.atomType == atomType)
|
||||
{
|
||||
output.json = atomJson;
|
||||
return;
|
||||
}
|
||||
}
|
||||
pendingNotifyAtomOutputs.push_back(PendingNotifyAtomOutput{ clientHandle, instanceId,atomType,atomJson });
|
||||
return;
|
||||
}
|
||||
++outstandingNotifyAtomOutputs;
|
||||
}
|
||||
|
||||
Request<bool>("onNotifyAtomOut",body,
|
||||
[this] (const bool& value)
|
||||
{
|
||||
this->OnAckNotifyAtomOutput();
|
||||
},
|
||||
[] (const std::exception &e) {
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
virtual void OnNotifyMidiListener(int64_t clientHandle, bool isNote, uint8_t noteOrControl)
|
||||
{
|
||||
NotifyMidiListenerBody body;
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ public:
|
||||
};
|
||||
|
||||
class PedalBoardPreset {
|
||||
long instanceId_;
|
||||
uint64_t instanceId_;
|
||||
std::string displayName_;
|
||||
std::vector<std::unique_ptr<PedalPreset> > values_;
|
||||
public:
|
||||
|
||||
@@ -163,6 +163,69 @@ namespace pipedal
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Write two disjoint areas of memory atomically.
|
||||
bool write(size_t bytes, uint8_t *data, size_t bytes2, uint8_t*data2)
|
||||
{
|
||||
if (MULTI_WRITER)
|
||||
{
|
||||
std::lock_guard guard(write_mutex);
|
||||
if (writeSpace() <= bytes+sizeof(bytes2)+bytes2) {
|
||||
return false;
|
||||
}
|
||||
size_t index = this->writePosition;
|
||||
for (size_t i = 0; i < bytes; ++i)
|
||||
{
|
||||
buffer[(index+i) & ringBufferMask] = data[i];
|
||||
}
|
||||
index = (index+bytes) & ringBufferMask;
|
||||
|
||||
for (size_t i = 0; i < sizeof(bytes2); ++i)
|
||||
{
|
||||
buffer[(index+i) & ringBufferMask] = ((char*)&bytes2)[i];
|
||||
}
|
||||
index = (index+sizeof(bytes2)) & ringBufferMask;
|
||||
|
||||
for (size_t i = 0; i < bytes2; ++i)
|
||||
{
|
||||
buffer[(index+i) & ringBufferMask] = data2[i];
|
||||
}
|
||||
this->writePosition = (index+bytes2) & ringBufferMask;
|
||||
if (SEMAPHORE_READER)
|
||||
{
|
||||
sem_post(&readSemaphore);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
if (writeSpace() <= bytes+sizeof(bytes2)+bytes2) {
|
||||
return false;
|
||||
}
|
||||
size_t index = this->writePosition;
|
||||
for (size_t i = 0; i < bytes; ++i)
|
||||
{
|
||||
buffer[(index+i) & ringBufferMask] = data[i];
|
||||
}
|
||||
index = (index+bytes) & ringBufferMask;
|
||||
|
||||
for (size_t i = 0; i < sizeof(bytes2); ++i)
|
||||
{
|
||||
buffer[(index+i) & ringBufferMask] = ((char*)&bytes2)[i];
|
||||
}
|
||||
index = (index+sizeof(bytes2)) & ringBufferMask;
|
||||
|
||||
for (size_t i = 0; i < bytes2; ++i)
|
||||
{
|
||||
buffer[(index+i) & ringBufferMask] = data2[i];
|
||||
}
|
||||
this->writePosition = (index+bytes2) & ringBufferMask;
|
||||
|
||||
if (SEMAPHORE_READER)
|
||||
{
|
||||
sem_post(&readSemaphore);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool read(size_t bytes, uint8_t*data)
|
||||
{
|
||||
if (readSpace() < bytes) return false;
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "Lv2Log.hpp"
|
||||
#include "VuUpdate.hpp"
|
||||
#include "JackHost.hpp"
|
||||
|
||||
namespace pipedal
|
||||
{
|
||||
|
||||
@@ -50,6 +49,8 @@ namespace pipedal
|
||||
|
||||
OnMidiListen = 16,
|
||||
|
||||
AtomOutput = 17,
|
||||
|
||||
};
|
||||
|
||||
class RealtimeMonitorPortSubscription
|
||||
@@ -186,6 +187,16 @@ namespace pipedal
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool read(size_t size, uint8_t*data)
|
||||
{
|
||||
if (!ringBuffer->read(size,data))
|
||||
{
|
||||
throw PiPedalStateException("Ringbuffer read failed. Did you forget to check for space?");
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
template <typename T>
|
||||
void readComplete(T *output)
|
||||
{
|
||||
@@ -251,6 +262,24 @@ namespace pipedal
|
||||
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
void write(RingBufferCommand command, const T &value, size_t dataLength, uint8_t*variableData)
|
||||
{
|
||||
|
||||
// the goal: to atomically write the command and associated data.
|
||||
CommandBuffer<T> buffer(command, value);
|
||||
|
||||
if (!ringBuffer->write(buffer.size(),(uint8_t *)&buffer,dataLength,variableData))
|
||||
{
|
||||
Lv2Log::error("No space in audio service ringbuffer.");
|
||||
return;
|
||||
|
||||
}
|
||||
}
|
||||
void AtomOutput(uint64_t instanceId, size_t bytes, uint8_t*data)
|
||||
{
|
||||
write(RingBufferCommand::AtomOutput,instanceId,bytes,data);
|
||||
}
|
||||
void ParameterRequest(RealtimeParameterRequest *pRequest)
|
||||
{
|
||||
write(RingBufferCommand::ParameterRequest,pRequest);
|
||||
@@ -354,4 +383,25 @@ namespace pipedal
|
||||
}
|
||||
};
|
||||
|
||||
typedef RingBufferReader<true, false> RealtimeRingBufferReader;
|
||||
typedef RingBufferReader<false, true> HostRingBufferReader;
|
||||
typedef RingBufferWriter<true, false> HostRingBufferWriter;
|
||||
|
||||
// cures a forward-declaration problem.
|
||||
class RealtimeRingBufferWriter: public RingBufferWriter<false, true>
|
||||
{
|
||||
public:
|
||||
RealtimeRingBufferWriter()
|
||||
{
|
||||
}
|
||||
RealtimeRingBufferWriter(RingBuffer<false,true>*ringBuffer)
|
||||
: RingBufferWriter<false, true> (ringBuffer)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
} //namespace
|
||||
+3
-2
@@ -274,17 +274,18 @@ namespace pipedal
|
||||
}
|
||||
}
|
||||
}
|
||||
long instanceId;
|
||||
uint64_t instanceId;
|
||||
|
||||
virtual uint8_t *GetAtomInputBuffer() { return nullptr; }
|
||||
virtual uint8_t *GetAtomOutputBuffer() { return nullptr; }
|
||||
virtual void RequestParameter(LV2_URID uridUri) {}
|
||||
virtual void GatherParameter(RealtimeParameterRequest *pRequest) {}
|
||||
virtual std::string AtomToJson(uint8_t *pAtom) { return ""; }
|
||||
virtual std::string GetAtomObjectType(uint8_t*pData) { return "not implemented";}
|
||||
|
||||
public:
|
||||
SplitEffect(
|
||||
long instanceId,
|
||||
uint64_t instanceId,
|
||||
double sampleRate,
|
||||
const std::vector<float *> &inputs)
|
||||
: instanceId(instanceId), inputs(inputs), sampleRate(sampleRate)
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ namespace pipedal
|
||||
class VuUpdate
|
||||
{
|
||||
public:
|
||||
long instanceId_ = 0;
|
||||
uint64_t instanceId_ = 0;
|
||||
long sampleTime_ = 0;
|
||||
bool isStereoInput_ = false;
|
||||
bool isStereoOutput_ = false;
|
||||
|
||||
Reference in New Issue
Block a user