This commit is contained in:
Robin Davies
2022-02-01 18:51:38 -05:00
parent dddd6a85d5
commit caa2aa1312
26 changed files with 1245 additions and 114 deletions
+1
View File
@@ -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
+3 -1
View File
@@ -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
View File
@@ -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;
+1 -1
View File
@@ -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;
}
});
}
+5 -5
View File
@@ -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();
}
);
+185
View File
@@ -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;
+413
View File
@@ -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;