Precached images. ToobTuner. key additions.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"socket_server_port": 8080,
|
||||
"socket_server_port": 80,
|
||||
"socket_server_address": "*",
|
||||
|
||||
"max_upload_size": 1048576,
|
||||
|
||||
@@ -31,6 +31,8 @@ import ToobToneStackViewFactory from './ToobToneStackView';
|
||||
import ToobCabSimViewFactory from './ToobCabSimView';
|
||||
import ToobPowerStage2Factory from './ToobPowerStage2View';
|
||||
import ToobSpectrumAnalyzerViewFactory from './ToobSpectrumAnalyzerView';
|
||||
import ToobMLViewFactory from './ToobMLView';
|
||||
import {ToobTunerViewFactory,GxTunerViewFactory> from './GxTunerView';
|
||||
|
||||
|
||||
let pluginFactories: IControlViewFactory[] = [
|
||||
@@ -38,7 +40,10 @@ let pluginFactories: IControlViewFactory[] = [
|
||||
new ToobToneStackViewFactory(),
|
||||
new ToobCabSimViewFactory(),
|
||||
new ToobPowerStage2Factory(),
|
||||
new ToobSpectrumAnalyzerViewFactory()
|
||||
new ToobSpectrumAnalyzerViewFactory(),
|
||||
new ToobMLViewFactory(),
|
||||
new GxTunerViewFactory(),
|
||||
new ToobTunerViewFactory()
|
||||
];
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
// Copyright (c) 2021 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
import React from 'react';
|
||||
import { Theme } from '@mui/material/styles';
|
||||
|
||||
import { WithStyles } from '@mui/styles';
|
||||
import createStyles from '@mui/styles/createStyles';
|
||||
import withStyles from '@mui/styles/withStyles';
|
||||
|
||||
import IControlViewFactory from './IControlViewFactory';
|
||||
import { PiPedalModelFactory, PiPedalModel } from "./PiPedalModel";
|
||||
import { PedalBoardItem } from './PedalBoard';
|
||||
import PluginControlView, { ControlGroup,ControlViewCustomization } from './PluginControlView';
|
||||
import GxTunerControl from './GxTunerControl';
|
||||
|
||||
const GXTUNER_URI = "http://guitarix.sourceforge.net/plugins/gxtuner#tuner";
|
||||
const TOOBTUNER_URI = "http://two-play.com/plugins/toob-tuner";
|
||||
|
||||
|
||||
|
||||
const styles = (theme: Theme) => createStyles({
|
||||
});
|
||||
|
||||
interface GxTunerProps extends WithStyles<typeof styles> {
|
||||
instanceId: number;
|
||||
item: PedalBoardItem;
|
||||
|
||||
}
|
||||
interface GxTunerState {
|
||||
|
||||
}
|
||||
|
||||
const GxTunerView =
|
||||
withStyles(styles, { withTheme: true })(
|
||||
class extends React.Component<GxTunerProps, GxTunerState>
|
||||
implements ControlViewCustomization
|
||||
{
|
||||
model: PiPedalModel;
|
||||
|
||||
customizationId: number = 1;
|
||||
|
||||
constructor(props: GxTunerProps) {
|
||||
super(props);
|
||||
this.model = PiPedalModelFactory.getInstance();
|
||||
this.state = {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
getControlIndex(key: string): number {
|
||||
let item = this.props.item;
|
||||
for (let i = 0; i < item.controlValues.length; ++i)
|
||||
{
|
||||
if (item.controlValues[i].key === key)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw new Error("GxTuner: Control '" + key + "' not found.");
|
||||
}
|
||||
ModifyControls(controls: (React.ReactNode| ControlGroup)[]): (React.ReactNode| ControlGroup)[]
|
||||
{
|
||||
let refFreqIndex = this.getControlIndex("REFFREQ");
|
||||
let thresholdIndex = this.getControlIndex("THRESHOLD");
|
||||
|
||||
let result: (React.ReactNode | ControlGroup)[] = [];
|
||||
|
||||
let tunerControl = (<GxTunerControl instanceId={this.props.instanceId} />);
|
||||
result.push(tunerControl);
|
||||
|
||||
result.push(controls[refFreqIndex]);
|
||||
result.push(controls[thresholdIndex]);
|
||||
return result;
|
||||
}
|
||||
render() {
|
||||
return (<PluginControlView
|
||||
instanceId={this.props.instanceId}
|
||||
item={this.props.item}
|
||||
customization={this}
|
||||
customizationId={this.customizationId}
|
||||
/>);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
class GxTunerViewFactory implements IControlViewFactory {
|
||||
uri: string = GXTUNER_URI;
|
||||
|
||||
Create(model: PiPedalModel, pedalBoardItem: PedalBoardItem): React.ReactNode {
|
||||
return (<GxTunerView instanceId={pedalBoardItem.instanceId} item={pedalBoardItem} />);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// ToobTuner uses the same controls and control semantics.
|
||||
class ToobTunerViewFactory implements IControlViewFactory {
|
||||
uri: string = TOOBTUNER_URI;
|
||||
|
||||
Create(model: PiPedalModel, pedalBoardItem: PedalBoardItem): React.ReactNode {
|
||||
return (<GxTunerView instanceId={pedalBoardItem.instanceId} item={pedalBoardItem} />);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export ToobTunerViewFactory;
|
||||
export GxTunerViewFactory;
|
||||
@@ -332,7 +332,7 @@ const JackServerSettingsDialog = withStyles(styles)(
|
||||
>
|
||||
{bufferSizes.map((buffSize) =>
|
||||
(
|
||||
<MenuItem value={buffSize}>{buffSize}</MenuItem>
|
||||
<MenuItem key={"b"+buffSize} value={buffSize}>{buffSize}</MenuItem>
|
||||
|
||||
)
|
||||
)}
|
||||
|
||||
@@ -55,6 +55,9 @@ const NARROW_DISPLAY_THRESHOLD = 600;
|
||||
const FILTER_STORAGE_KEY = "com.twoplay.pipedal.load_dlg.filter";
|
||||
|
||||
|
||||
let lastClickTime: number = 0;
|
||||
|
||||
|
||||
const pluginGridStyles = (theme: Theme) => createStyles({
|
||||
frame: {
|
||||
|
||||
@@ -338,8 +341,26 @@ export const LoadPluginDialog =
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onClick(e: SyntheticEvent, uri: string): void {
|
||||
e.preventDefault();
|
||||
|
||||
let currentTime = Date.now();
|
||||
let dt = currentTime-lastClickTime;
|
||||
let DOUBLE_CLICK_TIME = 500;
|
||||
let isDoubleClick = dt < DOUBLE_CLICK_TIME;
|
||||
lastClickTime = currentTime;
|
||||
|
||||
|
||||
this.setState({ selected_uri: uri });
|
||||
|
||||
// we have to synthesize double clicks because
|
||||
// DOM rewrites interfere with natural double click.
|
||||
if (isDoubleClick)
|
||||
{
|
||||
this.props.onOk(uri);
|
||||
}
|
||||
|
||||
}
|
||||
handleMouseEnter(e: SyntheticEvent, uri: string): void {
|
||||
// this.setHoverUri(uri);
|
||||
@@ -373,7 +394,7 @@ export const LoadPluginDialog =
|
||||
for (let i = 0; i < classNode.children.length; ++i) {
|
||||
let child = classNode.children[i];
|
||||
let name = "\u00A0".repeat(level * 3 + 1) + child.display_name;
|
||||
result.push((<MenuItem value={child.plugin_type}>{name}</MenuItem>));
|
||||
result.push((<MenuItem key={child.plugin_type} value={child.plugin_type}>{name}</MenuItem>));
|
||||
if (child.children.length !== 0) {
|
||||
this.createFilterChildren(result, child, level + 1);
|
||||
}
|
||||
@@ -383,7 +404,7 @@ export const LoadPluginDialog =
|
||||
let classes = this.model.plugin_classes.get();
|
||||
let result: ReactNode[] = [];
|
||||
|
||||
result.push((<MenuItem value={PluginType.Plugin}> All</MenuItem>));
|
||||
result.push((<MenuItem key={PluginType.Plugin} value={PluginType.Plugin}> All</MenuItem>));
|
||||
this.createFilterChildren(result, classes, 1);
|
||||
return result;
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ export class Port implements Deserializable<Port> {
|
||||
this.is_atom_port = input.is_atom_port;
|
||||
this.is_valid = input.is_valid;
|
||||
this.supports_midi = input.supports_midi;
|
||||
this.supports_time_position = input.supports_time_position;
|
||||
this.port_group = input.port_group;
|
||||
return this;
|
||||
}
|
||||
@@ -70,6 +71,7 @@ export class Port implements Deserializable<Port> {
|
||||
is_atom_port: boolean = false;
|
||||
is_valid: boolean = false;
|
||||
supports_midi: boolean = false;
|
||||
supports_time_position: boolean = false;
|
||||
port_group: string = "";
|
||||
}
|
||||
|
||||
|
||||
+48
-50
@@ -311,7 +311,7 @@ export interface PiPedalModel {
|
||||
presets: ObservableProperty<PresetIndex>;
|
||||
|
||||
|
||||
zoomedUiControl: ObservableProperty< ZoomedControlInfo| undefined >;
|
||||
zoomedUiControl: ObservableProperty<ZoomedControlInfo | undefined>;
|
||||
|
||||
setJackServerSettings(jackServerSettings: JackServerSettings): void;
|
||||
setMidiBinding(instanceId: number, midiBinding: MidiBinding): void;
|
||||
@@ -382,7 +382,7 @@ export interface PiPedalModel {
|
||||
cancelListenForMidiEvent(listenHandle: ListenHandle): void;
|
||||
|
||||
addControlValueChangeListener(instanceId: number, onValueChanged: ControlValueChangedHandler): ControlValueChangedHandle
|
||||
removeControlValueChangeListener(handle: ControlValueChangedHandle): void;
|
||||
removeControlValueChangeListener(handle: ControlValueChangedHandle): void;
|
||||
|
||||
listenForAtomOutput(instanceId: number, onComplete: (instanceId: number, atomOutput: any) => void): ListenHandle;
|
||||
cancelListenForAtomOutput(listenHandle: ListenHandle): void;
|
||||
@@ -398,9 +398,9 @@ export interface PiPedalModel {
|
||||
|
||||
getWifiChannels(countryIso3661: string): Promise<WifiChannel[]>;
|
||||
|
||||
getAlsaDevices() : Promise<AlsaDeviceInfo[]>;
|
||||
getAlsaDevices(): Promise<AlsaDeviceInfo[]>;
|
||||
|
||||
zoomUiControl(sourceElement: HTMLElement,instanceId: number,uiControl: UiControl): void;
|
||||
zoomUiControl(sourceElement: HTMLElement, instanceId: number, uiControl: UiControl): void;
|
||||
clearZoomedControl(): void;
|
||||
};
|
||||
|
||||
@@ -438,7 +438,7 @@ class PiPedalModelImpl implements PiPedalModel {
|
||||
new PresetIndex()
|
||||
);
|
||||
|
||||
zoomedUiControl: ObservableProperty<ZoomedControlInfo | undefined> = new ObservableProperty<ZoomedControlInfo | undefined>(undefined);
|
||||
zoomedUiControl: ObservableProperty<ZoomedControlInfo | undefined> = new ObservableProperty<ZoomedControlInfo | undefined>(undefined);
|
||||
|
||||
svgImgUrl(svgImage: string): string {
|
||||
//return this.varServerUrl + "img/" + svgImage;
|
||||
@@ -466,7 +466,7 @@ class PiPedalModelImpl implements PiPedalModel {
|
||||
onSocketReconnecting(retry: number, maxRetries: number): void {
|
||||
if (this.visibilityState.get() === VisibilityState.Hidden) return;
|
||||
//if (retry !== 0) {
|
||||
this.setState(State.Reconnecting);
|
||||
this.setState(State.Reconnecting);
|
||||
//}
|
||||
}
|
||||
|
||||
@@ -536,11 +536,11 @@ class PiPedalModelImpl implements PiPedalModel {
|
||||
this.handleNotifyMidiListener(clientHandle, isNote, noteOrControl);
|
||||
} else if (message === "onNotifyAtomOut") {
|
||||
let clientHandle = body.clientHandle as number;
|
||||
let instanceId = body.instanceId as number;
|
||||
let instanceId = body.instanceId as number;
|
||||
let atomJson = body.atomJson as string;
|
||||
this.handleNotifyAtomOutput(clientHandle, instanceId,atomJson);
|
||||
this.handleNotifyAtomOutput(clientHandle, instanceId, atomJson);
|
||||
if (header.replyTo) {
|
||||
this.webSocket?.reply(header.replyTo,"onNotifyAtomOut",true);
|
||||
this.webSocket?.reply(header.replyTo, "onNotifyAtomOut", true);
|
||||
}
|
||||
} else if (message === "onControlChanged") {
|
||||
let controlChangedBody = body as ControlChangedBody;
|
||||
@@ -733,6 +733,10 @@ class PiPedalModelImpl implements PiPedalModel {
|
||||
})
|
||||
.then((clientId) => {
|
||||
this.clientId = clientId;
|
||||
return this.getWebSocket().request<string>("imageList");
|
||||
})
|
||||
.then((data) => {
|
||||
this.preloadImages(data);
|
||||
return true;
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -842,20 +846,16 @@ class PiPedalModelImpl implements PiPedalModel {
|
||||
|
||||
}
|
||||
|
||||
exitBackgroundState()
|
||||
{
|
||||
if (this.state.get() === State.Background)
|
||||
{
|
||||
exitBackgroundState() {
|
||||
if (this.state.get() === State.Background) {
|
||||
console.log("Exiting background state.");
|
||||
this.visibilityState.set(VisibilityState.Visible);
|
||||
this.webSocket?.exitBackgroundState();
|
||||
}
|
||||
|
||||
}
|
||||
enterBackgroundState()
|
||||
{
|
||||
if (this.state.get() !== State.Background)
|
||||
{
|
||||
enterBackgroundState() {
|
||||
if (this.state.get() !== State.Background) {
|
||||
console.log("Entering background state.");
|
||||
this.visibilityState.set(VisibilityState.Hidden);
|
||||
this.setState(State.Background);
|
||||
@@ -864,12 +864,10 @@ class PiPedalModelImpl implements PiPedalModel {
|
||||
}
|
||||
|
||||
|
||||
onVisibilityChanged(doc: Document, event: Event) : any
|
||||
{
|
||||
onVisibilityChanged(doc: Document, event: Event): any {
|
||||
if (document.visibilityState) {
|
||||
|
||||
switch (document.visibilityState)
|
||||
{
|
||||
|
||||
switch (document.visibilityState) {
|
||||
case "visible":
|
||||
this.visibilityState.set(VisibilityState.Visible);
|
||||
this.exitBackgroundState();
|
||||
@@ -898,8 +896,8 @@ class PiPedalModelImpl implements PiPedalModel {
|
||||
});
|
||||
let t = this.onVisibilityChanged;
|
||||
|
||||
(document as any).addEventListener("visibilitychange",(doc: Document,event: Event) => {
|
||||
return t(doc,event);
|
||||
(document as any).addEventListener("visibilitychange", (doc: Document, event: Event) => {
|
||||
return t(doc, event);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -958,22 +956,18 @@ class PiPedalModelImpl implements PiPedalModel {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
_controlValueChangeItems: ControlValueChangeItem[] = [];
|
||||
|
||||
addControlValueChangeListener(instanceId: number, onValueChanged: ControlValueChangedHandler): ControlValueChangedHandle
|
||||
{
|
||||
addControlValueChangeListener(instanceId: number, onValueChanged: ControlValueChangedHandler): ControlValueChangedHandle {
|
||||
let handle = ++this.nextListenHandle;
|
||||
this._controlValueChangeItems.push({ handle: handle, instanceId: instanceId,onValueChanged: onValueChanged});
|
||||
return { _ControlValueChangedHandle: handle};
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -992,12 +986,10 @@ class PiPedalModelImpl implements PiPedalModel {
|
||||
if (notifyServer) {
|
||||
this._setServerControl("setControl", instanceId, key, value);
|
||||
}
|
||||
for (let i = 0; i < this._controlValueChangeItems.length; ++i)
|
||||
{
|
||||
for (let i = 0; i < this._controlValueChangeItems.length; ++i) {
|
||||
let item = this._controlValueChangeItems[i];
|
||||
if (instanceId === item.instanceId)
|
||||
{
|
||||
item.onValueChanged(key,value);
|
||||
if (instanceId === item.instanceId) {
|
||||
item.onValueChanged(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1557,7 +1549,7 @@ 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.atomOutputListeners.push(new AtomOutputListener(handle, instanceId, onComplete));
|
||||
|
||||
this.webSocket?.send("listenForAtomOutput", { instanceId: instanceId, handle: handle });
|
||||
return {
|
||||
@@ -1570,7 +1562,7 @@ class PiPedalModelImpl implements PiPedalModel {
|
||||
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);
|
||||
listener.callback(instanceId, jsonObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1760,8 +1752,7 @@ class PiPedalModelImpl implements PiPedalModel {
|
||||
return result;
|
||||
}
|
||||
|
||||
getAlsaDevices() : Promise<AlsaDeviceInfo[]>
|
||||
{
|
||||
getAlsaDevices(): Promise<AlsaDeviceInfo[]> {
|
||||
let result = new Promise<AlsaDeviceInfo[]>((resolve, reject) => {
|
||||
if (!this.webSocket) {
|
||||
reject("Connection closed.");
|
||||
@@ -1776,15 +1767,22 @@ class PiPedalModelImpl implements PiPedalModel {
|
||||
return result;
|
||||
|
||||
}
|
||||
zoomUiControl(sourceElement: HTMLElement,instanceId: number,uiControl: UiControl): void
|
||||
{
|
||||
this.zoomedUiControl.set({source: sourceElement,instanceId: instanceId,uiControl: uiControl});
|
||||
zoomUiControl(sourceElement: HTMLElement, instanceId: number, uiControl: UiControl): void {
|
||||
this.zoomedUiControl.set({ source: sourceElement, instanceId: instanceId, uiControl: uiControl });
|
||||
}
|
||||
clearZoomedControl(): void
|
||||
{
|
||||
clearZoomedControl(): void {
|
||||
this.zoomedUiControl.set(undefined);
|
||||
}
|
||||
|
||||
preloadImages(imageList: string): void {
|
||||
let imageNames = imageList.split(';');
|
||||
for (let i = 0; i < imageNames.length; ++i)
|
||||
{
|
||||
let imageName = imageNames[i];
|
||||
let img = new Image();
|
||||
img.src = "/img/" + imageName;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ import PluginControl from './PluginControl';
|
||||
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
||||
import VuMeter from './VuMeter';
|
||||
import { nullCast } from './Utility'
|
||||
import GxTunerControl from './GxTunerControl';
|
||||
import { PiPedalStateError } from './PiPedalError';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import FullScreenIME from './FullScreenIME';
|
||||
@@ -40,7 +39,6 @@ import FullScreenIME from './FullScreenIME';
|
||||
export const StandardItemSize = { width: 80, height: 110 };
|
||||
|
||||
|
||||
const GXTUNER_URI = "http://guitarix.sourceforge.net/plugins/gxtuner#tuner";
|
||||
|
||||
const LANDSCAPE_HEIGHT_BREAK = 500;
|
||||
|
||||
@@ -127,7 +125,7 @@ const styles = (theme: Theme) => createStyles({
|
||||
paddingTop: 0,
|
||||
paddingBottom: 0,
|
||||
border: "2pt #AAA solid",
|
||||
borderRadius: 4,
|
||||
borderRadius: 8,
|
||||
elevation: 12,
|
||||
display: "flex",
|
||||
flexDirection: "row", flexWrap: "wrap",
|
||||
@@ -144,7 +142,7 @@ const styles = (theme: Theme) => createStyles({
|
||||
paddingTop: 0,
|
||||
paddingBottom: 0,
|
||||
border: "2pt #AAA solid",
|
||||
borderRadius: 10,
|
||||
borderRadius: 8,
|
||||
elevation: 12,
|
||||
display: "flex",
|
||||
flexDirection: "row", flexWrap: "nowrap",
|
||||
@@ -152,7 +150,7 @@ const styles = (theme: Theme) => createStyles({
|
||||
},
|
||||
portGroupTitle: {
|
||||
position: "absolute",
|
||||
top: -10,
|
||||
top: -15,
|
||||
background: "white",
|
||||
marginLeft: 20,
|
||||
paddingLeft: 8,
|
||||
@@ -344,35 +342,6 @@ const PluginControlView =
|
||||
throw new Error("Not found.");
|
||||
}
|
||||
|
||||
getTunerControls(plugin: UiPlugin, controlValues: ControlValue[]): ControlNodes {
|
||||
// display standard version of just a few controls.
|
||||
let shortList: ControlValue[] = [];
|
||||
// for (let i = 0; i < controlValues.length; ++i)
|
||||
// {
|
||||
// shortList.push(controlValues[i]);
|
||||
// }
|
||||
shortList.push(this.getControl(controlValues, "REFFREQ"));
|
||||
shortList.push(this.getControl(controlValues, "THRESHOLD"));
|
||||
|
||||
|
||||
let controls = shortList.map((controlValue) => (
|
||||
<PluginControl instanceId={this.props.instanceId} uiControl={plugin.getControl(controlValue.key)} value={controlValue.value}
|
||||
onChange={(value: number) => { this.onValueChanged(controlValue.key, value) }}
|
||||
onPreviewChange={(value: number) => { this.onPreviewChange(controlValue.key, value) }}
|
||||
requestIMEEdit={(uiControl, value) => this.requestImeEdit(uiControl, value)}
|
||||
/>
|
||||
));
|
||||
let tunerControl = (<GxTunerControl instanceId={this.props.instanceId} />);
|
||||
let result: ControlNodes = [];
|
||||
result.push(tunerControl);
|
||||
|
||||
for (let i = 0; i < controls.length; ++i)
|
||||
{
|
||||
result.push(controls[i]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
onImeValueChange(key: string, value: number) {
|
||||
this.model.setPedalBoardControlValue(this.props.instanceId, key, value);
|
||||
this.onImeClose();
|
||||
@@ -464,11 +433,8 @@ const PluginControlView =
|
||||
let vuMeterRClass = this.state.landscapeGrid ? classes.vuMeterRLandscape : classes.vuMeterR;
|
||||
let controlNodes: ControlNodes;
|
||||
|
||||
if (plugin.uri === GXTUNER_URI) {
|
||||
controlNodes = this.getTunerControls(plugin, controlValues);
|
||||
} else {
|
||||
controlNodes = this.getStandardControlNodes(plugin, controlValues);
|
||||
}
|
||||
controlNodes = this.getStandardControlNodes(plugin, controlValues);
|
||||
|
||||
if (this.props.customization)
|
||||
{
|
||||
// allow wrapper class to insert/remove/rebuild controls.
|
||||
|
||||
@@ -138,7 +138,7 @@ function makeControls(controls: UiControl[]) {
|
||||
<Grid container direction="row" justifyContent="flex-start" alignItems="flex-start" spacing={1} style={{ paddingLeft: "24px" }}>
|
||||
{
|
||||
controls.map((control) => (
|
||||
<Grid item spacing={2} xs={6} sm={4} key={control.symbol} >
|
||||
<Grid xs={6} sm={4} key={control.symbol} >
|
||||
<Typography variant="body2">
|
||||
{control.name}
|
||||
</Typography>
|
||||
|
||||
@@ -279,7 +279,8 @@ const PluginPresetSelector =
|
||||
<Typography variant="caption" color="textSecondary" style={{marginLeft: 16,marginTop:4,marginBottom: 4}}>Plugin presets</Typography>
|
||||
{
|
||||
this.state.presets.map((preset) => {
|
||||
return (<MenuItem onClick={(e) => this.handleLoadPluginPreset(preset.presetUri)}>{preset.name}</MenuItem>);
|
||||
return (<MenuItem key={preset.presetUri}
|
||||
onClick={(e) => this.handleLoadPluginPreset(preset.presetUri)}>{preset.name}</MenuItem>);
|
||||
})
|
||||
}
|
||||
{/*
|
||||
|
||||
@@ -46,6 +46,10 @@ const styles = (theme: Theme) => createStyles({
|
||||
|
||||
interface ToobFrequencyResponseProps extends WithStyles<typeof styles> {
|
||||
instanceId: number;
|
||||
maxDb?: number;
|
||||
minDb?: number;
|
||||
minFrequency?: number;
|
||||
maxFrequency?: number;
|
||||
|
||||
}
|
||||
interface ToobFrequencyResponseState {
|
||||
@@ -104,8 +108,8 @@ const ToobFrequencyResponseView =
|
||||
requestOutstanding: boolean = false;
|
||||
requestDeferred: boolean = false;
|
||||
|
||||
dbMinOpt: number = -35;
|
||||
dbMaxOpt: number = 5;
|
||||
dbMinDefault: number = -35;
|
||||
dbMaxDefault: number = 5;
|
||||
dbTickSpacingOpt: number = 10;
|
||||
|
||||
|
||||
@@ -120,8 +124,6 @@ const ToobFrequencyResponseView =
|
||||
xMax: number = PLOT_WIDTH+4;
|
||||
yMin: number = 0;
|
||||
yMax: number = PLOT_HEIGHT;
|
||||
dbMin: number = this.dbMaxOpt; // deliberately reversed to flip up and down.
|
||||
dbMax: number = this.dbMinOpt;
|
||||
dbTickSpacing: number = this.dbTickSpacingOpt;
|
||||
|
||||
toX(frequency: number): number
|
||||
@@ -189,6 +191,10 @@ const ToobFrequencyResponseView =
|
||||
|
||||
currentPath: string = "";
|
||||
|
||||
majorGridLine(x0: number, y0: number, x1: number, y1: number): React.ReactNode {
|
||||
return (<line x1={x0} y1={y0} x2={x1} y2={y1} fill='none' stroke="#FFF" strokeWidth="0.75" opacity="1" />);
|
||||
}
|
||||
|
||||
gridLine(x0: number, y0: number, x1: number, y1: number): React.ReactNode {
|
||||
return (<line x1={x0} y1={y0} x2={x1} y2={y1} fill='none' stroke="#FFF" strokeWidth="0.25" opacity="1" />);
|
||||
}
|
||||
@@ -199,27 +205,54 @@ const ToobFrequencyResponseView =
|
||||
for (var db = Math.ceil(this.dbMax/this.dbTickSpacing)*this.dbTickSpacing; db < this.dbMin; db += this.dbTickSpacing )
|
||||
{
|
||||
var y = (db-this.dbMin)/(this.dbMax-this.dbMin)*(this.yMax-this.yMin);
|
||||
result.push(
|
||||
this.gridLine(this.xMin,y,this.xMax,y)
|
||||
);
|
||||
if (db === 0)
|
||||
{
|
||||
result.push(
|
||||
this.majorGridLine(this.xMin,y,this.xMax,y)
|
||||
);
|
||||
|
||||
} else {
|
||||
result.push(
|
||||
this.gridLine(this.xMin,y,this.xMax,y)
|
||||
);
|
||||
}
|
||||
}
|
||||
let decade0 = Math.pow(10,Math.floor(Math.log10(this.fMin)));
|
||||
for (var decade = decade0; decade < this.fMax; decade *= 10)
|
||||
{
|
||||
for (var i = 0; i < 10; ++i)
|
||||
for (var i = 1; i <= 10; ++i)
|
||||
{
|
||||
var f = decade*i;
|
||||
if (f > this.fMin && f < this.fMax)
|
||||
{
|
||||
var x = this.toX(f);
|
||||
result.push(this.gridLine(x,this.yMin,x,this.yMax));
|
||||
if (i === 10)
|
||||
{
|
||||
result.push(this.majorGridLine(x,this.yMin,x,this.yMax));
|
||||
|
||||
} else {
|
||||
result.push(this.gridLine(x,this.yMin,x,this.yMax));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
dbMin: number = 5;
|
||||
dbMax: number = -35;
|
||||
|
||||
render() {
|
||||
// deliberately reversed to flip up and down.
|
||||
this.dbMax = this.props.minDb ?? this.dbMinDefault;
|
||||
this.dbMin = this.props.maxDb ?? this.dbMaxDefault;
|
||||
|
||||
this.fMin = this.props.minFrequency ?? 30;
|
||||
this.fMax = this.props.maxFrequency ?? 20000;
|
||||
this.logMin = Math.log(this.fMin);
|
||||
this.logMax = Math.log(this.fMax);
|
||||
|
||||
|
||||
let classes = this.props.classes;
|
||||
return (
|
||||
<div className={classes.frame} >
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
// Copyright (c) 2021 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
import React from 'react';
|
||||
import { Theme } from '@mui/material/styles';
|
||||
|
||||
import { WithStyles } from '@mui/styles';
|
||||
import createStyles from '@mui/styles/createStyles';
|
||||
import withStyles from '@mui/styles/withStyles';
|
||||
|
||||
import IControlViewFactory from './IControlViewFactory';
|
||||
import { PiPedalModelFactory, PiPedalModel } from "./PiPedalModel";
|
||||
import { PedalBoardItem } from './PedalBoard';
|
||||
import PluginControlView, { ControlGroup,ControlViewCustomization } from './PluginControlView';
|
||||
import ToobFrequencyResponseView from './ToobFrequencyResponseView';
|
||||
|
||||
|
||||
|
||||
const styles = (theme: Theme) => createStyles({
|
||||
});
|
||||
|
||||
interface ToobMLProps extends WithStyles<typeof styles> {
|
||||
instanceId: number;
|
||||
item: PedalBoardItem;
|
||||
|
||||
}
|
||||
interface ToobMLState {
|
||||
|
||||
}
|
||||
|
||||
const ToobMLView =
|
||||
withStyles(styles, { withTheme: true })(
|
||||
class extends React.Component<ToobMLProps, ToobMLState>
|
||||
implements ControlViewCustomization
|
||||
{
|
||||
model: PiPedalModel;
|
||||
|
||||
customizationId: number = 1;
|
||||
|
||||
constructor(props: ToobMLProps) {
|
||||
super(props);
|
||||
this.model = PiPedalModelFactory.getInstance();
|
||||
this.state = {
|
||||
}
|
||||
}
|
||||
|
||||
ModifyControls(controls: (React.ReactNode| ControlGroup)[]): (React.ReactNode| ControlGroup)[]
|
||||
{
|
||||
let group = controls[1] as ControlGroup;
|
||||
group.controls.splice(0,0,
|
||||
( <ToobFrequencyResponseView instanceId={this.props.instanceId} minDb={-20} maxDb={20} />)
|
||||
);
|
||||
return controls;
|
||||
}
|
||||
render() {
|
||||
return (<PluginControlView
|
||||
instanceId={this.props.instanceId}
|
||||
item={this.props.item}
|
||||
customization={this}
|
||||
customizationId={this.customizationId}
|
||||
/>);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
class ToobMLViewFactory implements IControlViewFactory {
|
||||
uri: string = "http://two-play.com/plugins/toob-ml";
|
||||
|
||||
Create(model: PiPedalModel, pedalBoardItem: PedalBoardItem): React.ReactNode {
|
||||
return (<ToobMLView instanceId={pedalBoardItem.instanceId} item={pedalBoardItem} />);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
export default ToobMLViewFactory;
|
||||
@@ -25,7 +25,7 @@ import createStyles from '@mui/styles/createStyles';
|
||||
import withStyles from '@mui/styles/withStyles';
|
||||
|
||||
import IControlViewFactory from './IControlViewFactory';
|
||||
import { PiPedalModelFactory, PiPedalModel } from "./PiPedalModel";
|
||||
import { PiPedalModelFactory, PiPedalModel,ControlValueChangedHandle } from "./PiPedalModel";
|
||||
import { PedalBoardItem } from './PedalBoard';
|
||||
import PluginControlView, { ControlGroup,ControlViewCustomization } from './PluginControlView';
|
||||
import ToobFrequencyResponseView from './ToobFrequencyResponseView';
|
||||
@@ -41,7 +41,7 @@ interface ToobToneStackProps extends WithStyles<typeof styles> {
|
||||
|
||||
}
|
||||
interface ToobToneStackState {
|
||||
|
||||
isBaxandall: boolean;
|
||||
}
|
||||
|
||||
const ToobToneStackView =
|
||||
@@ -57,14 +57,48 @@ const ToobToneStackView =
|
||||
super(props);
|
||||
this.model = PiPedalModelFactory.getInstance();
|
||||
this.state = {
|
||||
isBaxandall: this.IsBaxandall()
|
||||
}
|
||||
}
|
||||
IsBaxandall() : boolean {
|
||||
return this.props.item.getControl("ampmodel").value === 2.0;
|
||||
}
|
||||
|
||||
|
||||
controlValueChangedHandle?: ControlValueChangedHandle;
|
||||
componentDidMount()
|
||||
{
|
||||
this.controlValueChangedHandle = this.model.addControlValueChangeListener(
|
||||
this.props.instanceId,
|
||||
(key,value) => {
|
||||
if (key === "ampmodel")
|
||||
{
|
||||
this.setState({isBaxandall: value === 2.0});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
if (this.controlValueChangedHandle)
|
||||
{
|
||||
this.model.removeControlValueChangeListener(this.controlValueChangedHandle);
|
||||
this.controlValueChangedHandle = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
ModifyControls(controls: (React.ReactNode| ControlGroup)[]): (React.ReactNode| ControlGroup)[]
|
||||
{
|
||||
controls.splice(0,0,
|
||||
( <ToobFrequencyResponseView instanceId={this.props.instanceId} />)
|
||||
);
|
||||
if (this.state.isBaxandall)
|
||||
{
|
||||
controls.splice(0,0,
|
||||
( <ToobFrequencyResponseView instanceId={this.props.instanceId}
|
||||
minDb={-20} maxDb={20} />)
|
||||
);
|
||||
} else {
|
||||
controls.splice(0,0,
|
||||
( <ToobFrequencyResponseView instanceId={this.props.instanceId} />)
|
||||
);
|
||||
}
|
||||
return controls;
|
||||
}
|
||||
render() {
|
||||
|
||||
Reference in New Issue
Block a user