Routing, in progress.
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
|
||||
import { ControlValue } from "../pipedal/Pedalboard.tsx";
|
||||
|
||||
|
||||
export class ChannelMixerStrip {
|
||||
inputVu: number = -100.0;
|
||||
inputVolume: number = 0;
|
||||
outputVu: number = -100.0;
|
||||
outputVolume: number = 0;
|
||||
};
|
||||
|
||||
export default class ChannelMixerSettings {
|
||||
configured: boolean = false;
|
||||
|
||||
guitarInputChannels: number[] = [1,1];
|
||||
|
||||
guitarOutputChannels: number[] = [0,1];
|
||||
|
||||
guitarMixerSettings: ChannelMixerStrip = new ChannelMixerStrip();
|
||||
|
||||
|
||||
auxInputChannels: number[] = [0,0];
|
||||
auxOutputChannels: number[] = [-1,-1];
|
||||
|
||||
auxMixerSettings: ChannelMixerStrip = new ChannelMixerStrip();
|
||||
|
||||
controls: ControlValue[] = [];
|
||||
// Inserts...
|
||||
|
||||
getControlValue(symbol: string): number | null {
|
||||
for (let control of this.controls) {
|
||||
if (control.key === symbol) {
|
||||
return control.value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
setControlValue(symbol: string, value: number): boolean {
|
||||
for (let control of this.controls) {
|
||||
if (control.key === symbol) {
|
||||
if (control.value === value) {
|
||||
return false;
|
||||
}
|
||||
control.value = value;
|
||||
this.controls = this.controls.slice(); // trigger observers.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
let newValue = new ControlValue(symbol,value);
|
||||
this.controls.push(newValue);
|
||||
this.controls = this.controls.slice(); // trigger observers.
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,524 @@
|
||||
// Copyright (c) 2026 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 { useState } from 'react';
|
||||
import HelpOutlineIcon from '@mui/icons-material/HelpOutline';
|
||||
import VuMeter from './VuMeter';
|
||||
import Toolbar from '@mui/material/Toolbar';
|
||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||
import IconButtonEx from './IconButtonEx';
|
||||
import Button from '@mui/material/Button';
|
||||
import useWindowSize from "./UseWindowSize";
|
||||
import DialIcon from './svg/fx_dial.svg?react';
|
||||
import Select from '@mui/material/Select';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
import ChannelMixerSettingsHelpDialog from './ChannelMixerSettingsHelpDialog';
|
||||
import PluginControl from './PluginControl';
|
||||
|
||||
import DialogTitle from '@mui/material/DialogTitle';
|
||||
import DialogContent from '@mui/material/DialogContent';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import ChannelMixerSettings from './ChannelMixerSettings';
|
||||
import { makeChanelMixerUiPlugin, CHANNEL_MIXER_INSTANCE_ID } from './ChannelMixerUiControl';
|
||||
import { ControlValue } from './Pedalboard';
|
||||
|
||||
import DialogEx from './DialogEx';
|
||||
|
||||
import { PiPedalModelFactory } from './PiPedalModel';
|
||||
|
||||
export interface ChannelMixerSettingsDialogProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
let channelMixerUiPlugin = makeChanelMixerUiPlugin();
|
||||
|
||||
enum RouteType {
|
||||
Main,
|
||||
Aux
|
||||
}
|
||||
let cellSectionHead: React.CSSProperties = {
|
||||
border: "0px",
|
||||
paddingTop: 4,
|
||||
paddingBottom: 4,
|
||||
paddingLeft: 0,
|
||||
paddingRight: 12,
|
||||
margin: "0px",
|
||||
textAlign: "left",
|
||||
verticalAlign: "middle"
|
||||
};
|
||||
|
||||
let cellPortraitControlStrip: React.CSSProperties = {
|
||||
border: "0px",
|
||||
paddingTop: 12,
|
||||
paddingBottom: 12,
|
||||
paddingLeft: 0,
|
||||
paddingRight: 0,
|
||||
margin: "0px",
|
||||
textAlign: "left",
|
||||
verticalAlign: "middle"
|
||||
};
|
||||
let cellLeft: React.CSSProperties = {
|
||||
border: "0px",
|
||||
paddingTop: 4,
|
||||
paddingBottom: 4,
|
||||
paddingLeft: 4,
|
||||
paddingRight: 4,
|
||||
margin: "0px",
|
||||
textAlign: "left",
|
||||
verticalAlign: "middle"
|
||||
};
|
||||
let cellLeftIndent: React.CSSProperties = {
|
||||
border: "0px",
|
||||
paddingTop: 4,
|
||||
paddingBottom: 4,
|
||||
paddingLeft: 12,
|
||||
paddingRight: 4,
|
||||
margin: "0px",
|
||||
textAlign: "right",
|
||||
verticalAlign: "middle"
|
||||
};
|
||||
let cellLeftH: React.CSSProperties = {
|
||||
border: "0px",
|
||||
paddingTop: 4,
|
||||
paddingBottom: 4,
|
||||
paddingLeft: 4,
|
||||
paddingRight: 4,
|
||||
margin: "0px",
|
||||
textAlign: "left",
|
||||
verticalAlign: "middle"
|
||||
};
|
||||
|
||||
|
||||
function MakeChannelMenu(channelCount: number): React.ReactElement[] {
|
||||
let items: React.ReactElement[] = [(<MenuItem key={-1} value={-1}>None</MenuItem>)]
|
||||
if (channelCount === 0) {
|
||||
return items;
|
||||
}
|
||||
if (channelCount === 2) {
|
||||
items.push((<MenuItem key={0} value={0}>Left</MenuItem>));
|
||||
items.push((<MenuItem key={1} value={1}>Right</MenuItem>));
|
||||
return items;
|
||||
}
|
||||
for (let i = 0; i < channelCount; ++i) {
|
||||
items.push((<MenuItem key={i} value={i}>Ch {i + 1}</MenuItem>));
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function ChannelMixerSettingsDialog(props: ChannelMixerSettingsDialogProps) {
|
||||
//const classes = useStyles();
|
||||
const { open, onClose } = props;
|
||||
let model = PiPedalModelFactory.getInstance();
|
||||
let config = model.jackConfiguration.get();
|
||||
|
||||
|
||||
const [settings, setSettings] = useState<ChannelMixerSettings>(model.channelMixerSettings.get());
|
||||
const [controlValues,setControlValues] = useState<ControlValue[]>(model.channelMixerControlValues.get());
|
||||
const [showHelp, setShowHelp] = useState<boolean>(false);
|
||||
|
||||
if (settings) {
|
||||
}
|
||||
if (controlValues) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
React.useEffect(() => {
|
||||
if (open) {
|
||||
let handleSettingsChanged = () => {
|
||||
setSettings(model.channelMixerSettings.get());
|
||||
};
|
||||
model.channelMixerSettings.addOnChangedHandler(handleSettingsChanged);
|
||||
let handleControlsChanged = () => {
|
||||
setControlValues(model.channelMixerControlValues.get());
|
||||
};
|
||||
model.channelMixerControlValues.addOnChangedHandler(handleControlsChanged);
|
||||
return () => {
|
||||
model.channelMixerSettings.removeOnChangedHandler(handleSettingsChanged);
|
||||
model.channelMixerControlValues.removeOnChangedHandler(handleControlsChanged);
|
||||
}
|
||||
} else {
|
||||
return () => { };
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
const handleClose = (): void => {
|
||||
onClose();
|
||||
};
|
||||
let [windowSize] = useWindowSize();
|
||||
let landscape = windowSize.width > windowSize.height;
|
||||
let fullScreen = windowSize.width < 450 || windowSize.height < 600;
|
||||
|
||||
let Dial = (symbol: string) => {
|
||||
let instanceId = CHANNEL_MIXER_INSTANCE_ID;
|
||||
let control = channelMixerUiPlugin.getControl(symbol);
|
||||
let value = settings.getControlValue(symbol);
|
||||
if (value === null) {
|
||||
value = control?.default_value ?? 0;
|
||||
}
|
||||
|
||||
return (<PluginControl
|
||||
instanceId={instanceId}
|
||||
uiControl={control}
|
||||
onPreviewChange={(value: number) => {
|
||||
model.previewPedalboardValue(instanceId, symbol, value);
|
||||
}}
|
||||
onChange={(value: number) => {
|
||||
model.setPedalboardControl(instanceId, symbol, value);
|
||||
}}
|
||||
requestIMEEdit={() => { }}
|
||||
value={value}
|
||||
/>);
|
||||
|
||||
return <DialIcon width={32} height={32} style={{ verticalAlign: "middle", fill: "white" }} />;
|
||||
}
|
||||
let ChannelSelect = (routeType: RouteType, channelIndex: number, input: boolean) => {
|
||||
let channelCount = input ? config.inputAudioPorts.length : config.outputAudioPorts.length;
|
||||
|
||||
let value: number;
|
||||
|
||||
switch (routeType) {
|
||||
case RouteType.Main:
|
||||
value = input ? settings.guitarInputChannels[channelIndex] : settings.guitarOutputChannels[channelIndex];
|
||||
break;
|
||||
case RouteType.Aux:
|
||||
value = input ? settings.auxInputChannels[channelIndex] : settings.auxOutputChannels[channelIndex];
|
||||
break;
|
||||
}
|
||||
if (value >= channelCount) {
|
||||
value = -1;
|
||||
}
|
||||
return (
|
||||
<Select variant="standard" style={{ width: 80 }} value={value}
|
||||
onChange={(event) => {
|
||||
let newValue = event.target.value as number;
|
||||
let newSettings = Object.assign(new ChannelMixerSettings(), settings);
|
||||
switch (routeType) {
|
||||
case RouteType.Main:
|
||||
if (input) {
|
||||
newSettings.guitarInputChannels[channelIndex] = newValue;
|
||||
} else {
|
||||
newSettings.guitarOutputChannels[channelIndex] = newValue;
|
||||
}
|
||||
break;
|
||||
case RouteType.Aux:
|
||||
if (input) {
|
||||
newSettings.auxInputChannels[channelIndex] = newValue;
|
||||
} else {
|
||||
newSettings.auxOutputChannels[channelIndex] = newValue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
model.channelMixerSettings.set(newSettings);
|
||||
}}
|
||||
>
|
||||
{MakeChannelMenu(channelCount)}
|
||||
</Select>
|
||||
)
|
||||
}
|
||||
let Vu = () => {
|
||||
return (
|
||||
<div style={{ }} >
|
||||
<VuMeter instanceId={CHANNEL_MIXER_INSTANCE_ID} display={"input"} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let LandscapeView = () => {
|
||||
return (
|
||||
<div style={{ width: "100%", height: "100%", flexGrow: 1, fontSize: "14px" }} >
|
||||
<table style={{ borderCollapse: "collapse", width: "100%" }}>
|
||||
<colgroup>
|
||||
<col style={{ width: "auto" }} />
|
||||
<col style={{ width: "auto" }} />
|
||||
<col style={{ width: "auto" }} />
|
||||
<col style={{ width: "auto" }} />
|
||||
<col style={{ width: "auto" }} />
|
||||
<col style={{ width: "auto" }} />
|
||||
<col style={{ width: "auto" }} />
|
||||
<col style={{ width: "auto" }} />
|
||||
<col style={{ width: "auto" }} />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th style={cellLeftH}><Typography variant="caption" >In</Typography></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th style={cellLeftH}><Typography variant="caption" >Out</Typography></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{/* Main */}
|
||||
<tr>
|
||||
<td rowSpan={1} style={cellLeft}>
|
||||
<Typography variant="body2" color="textSecondary">Main</Typography>
|
||||
</td>
|
||||
<td style={cellLeft}>
|
||||
{ChannelSelect(RouteType.Main, 0, true)}
|
||||
</td>
|
||||
<td rowSpan={2} style={cellLeft}>{Dial("mainIn")}</td>
|
||||
<td rowSpan={2} style={cellLeft}>{Vu()}</td>
|
||||
<td rowSpan={2} style={cellLeft}>
|
||||
<Button variant="outlined" style={{ textTransform: "none", borderRadius: 24 }}>
|
||||
Inserts
|
||||
</Button>
|
||||
</td>
|
||||
|
||||
<td rowSpan={2} style={cellLeft}>{Dial("mainOut")}</td>
|
||||
<td rowSpan={2} style={cellLeft}>{Vu()}</td>
|
||||
<td style={cellLeft}>
|
||||
{ChannelSelect(RouteType.Main, 0, false)}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td style={cellLeft}>
|
||||
{(ChannelSelect(RouteType.Main, 1, true))}
|
||||
</td>
|
||||
<td style={cellLeft}>
|
||||
{(ChannelSelect(RouteType.Main, 1, false))}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{/* Spacer */}
|
||||
<tr><td colSpan={9} style={{ height: 16 }}></td></tr>
|
||||
|
||||
{/* Aux */}
|
||||
<tr>
|
||||
<td rowSpan={1} style={cellLeft}>
|
||||
<Typography variant="body2" color="textSecondary">Aux</Typography>
|
||||
</td>
|
||||
<td style={cellLeft}>
|
||||
{(ChannelSelect(RouteType.Aux, 0, true))}
|
||||
</td>
|
||||
<td rowSpan={2} style={cellLeft}>{Dial("auxIn")}</td>
|
||||
<td rowSpan={2} style={cellLeft}>{Vu()}</td>
|
||||
<td rowSpan={2} style={cellLeft}>
|
||||
<Button variant="outlined" style={{ textTransform: "none", borderRadius: 24 }}>
|
||||
Inserts
|
||||
</Button>
|
||||
</td>
|
||||
|
||||
<td rowSpan={2} style={cellLeft}>{Dial("auxOut")}</td>
|
||||
<td rowSpan={2} style={cellLeft}>{Vu()}</td>
|
||||
<td style={cellLeft}>
|
||||
{(ChannelSelect(RouteType.Aux, 0, false))}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td style={cellLeft}>
|
||||
{(ChannelSelect(RouteType.Aux, 1, true))}
|
||||
</td>
|
||||
<td style={cellLeft}>
|
||||
{(ChannelSelect(RouteType.Aux, 1, false))}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
let PortraitView = () => {
|
||||
return (
|
||||
<div style={{ width: "100%", height: "100%", flexGrow: 1, fontSize: "14px" }} >
|
||||
<table style={{ borderCollapse: "collapse", width: "100%" }}>
|
||||
<colgroup>
|
||||
<col style={{ width: "auto" }} />
|
||||
<col style={{ width: "auto" }} />
|
||||
<col style={{ width: "auto" }} />
|
||||
<col style={{ width: "100%" }} />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
{/* Main */}
|
||||
<tr>
|
||||
<td colSpan={4} style={cellSectionHead}>
|
||||
<Typography variant="body2" color="textSecondary" >Main</Typography>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style={cellLeftIndent}>
|
||||
<Typography variant="body2" color="textSecondary" >In</Typography>
|
||||
</td>
|
||||
<td style={cellLeft}>
|
||||
{ChannelSelect(RouteType.Main, 0, true)}
|
||||
</td>
|
||||
<td style={cellLeft}>
|
||||
{ChannelSelect(RouteType.Main, 1, true)}
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td colSpan={3}>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style={cellPortraitControlStrip}>{Dial("mainIn")}</td>
|
||||
<td style={cellPortraitControlStrip}>{Vu()}</td>
|
||||
<td style={cellPortraitControlStrip}>
|
||||
<div style={{ marginLeft: 8, marginRight: 8 }}>
|
||||
<Button variant="outlined" style={{ textTransform: "none", borderRadius: 24 }}>
|
||||
Inserts
|
||||
</Button>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td style={cellPortraitControlStrip}>{Dial("mainOut")}</td>
|
||||
<td style={cellPortraitControlStrip}>{Vu()}</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style={cellLeftIndent}>
|
||||
<Typography variant="body2" color="textSecondary" >Out</Typography>
|
||||
</td>
|
||||
<td style={cellLeft}>
|
||||
{ChannelSelect(RouteType.Main, 0, false)}
|
||||
</td>
|
||||
<td style={cellLeft}>
|
||||
{ChannelSelect(RouteType.Main, 1, false)}
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
|
||||
{/* Spacer */}
|
||||
<tr><td colSpan={4} style={{ height: 16 }}></td></tr>
|
||||
|
||||
{/* Aux */}
|
||||
<tr>
|
||||
<td colSpan={4} style={cellSectionHead}>
|
||||
<Typography variant="body2" color="textSecondary" >Aux</Typography>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style={cellLeftIndent}>
|
||||
<Typography variant="body2" color="textSecondary" >In</Typography>
|
||||
</td>
|
||||
<td style={cellLeft}>
|
||||
{(ChannelSelect(RouteType.Aux, 0, true))}
|
||||
</td>
|
||||
<td style={cellLeft}>
|
||||
{(ChannelSelect(RouteType.Aux, 1, true))}
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td colSpan={3}>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style={cellPortraitControlStrip}>{Dial("auxIn")}</td>
|
||||
<td style={cellPortraitControlStrip}>{Vu()}</td>
|
||||
<td style={cellPortraitControlStrip}>
|
||||
<Button variant="outlined" style={{ textTransform: "none", borderRadius: 24 }}>
|
||||
Inserts
|
||||
</Button>
|
||||
</td>
|
||||
|
||||
<td style={cellPortraitControlStrip}>{Dial("auxOut")}</td>
|
||||
<td style={cellPortraitControlStrip}>{Vu()}</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style={cellLeftIndent}>
|
||||
<Typography variant="body2" color="textSecondary" >Out</Typography>
|
||||
</td>
|
||||
<td style={cellLeft}>
|
||||
{ChannelSelect(RouteType.Aux, 0, false)}
|
||||
</td>
|
||||
<td style={cellLeft}>
|
||||
{ChannelSelect(RouteType.Aux, 1, false)}
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<DialogEx tag="channelMixerSettings" onClose={handleClose} aria-labelledby="select-channel_mixer_settings"
|
||||
open={open}
|
||||
fullWidth maxWidth="sm"
|
||||
onEnterKey={handleClose}
|
||||
fullScreen={fullScreen}
|
||||
style={{ maxWidth: 1200 }}
|
||||
>
|
||||
<DialogTitle id="select-channel_mixer_settings" style={{ paddingTop: 0, paddingBottom: 0, marginBottom: 8 }}>
|
||||
<Toolbar style={{ padding: 0, margin: 0 }}>
|
||||
<IconButtonEx
|
||||
tooltip="Close"
|
||||
edge="start"
|
||||
color="inherit"
|
||||
aria-label="cancel"
|
||||
style={{ opacity: 0.6 }}
|
||||
onClick={() => { onClose(); }}
|
||||
>
|
||||
<ArrowBackIcon style={{ width: 24, height: 24 }} />
|
||||
</IconButtonEx>
|
||||
|
||||
<Typography noWrap component="div" sx={{ flexGrow: 1 }}>
|
||||
Channel Routing
|
||||
</Typography>
|
||||
|
||||
<IconButtonEx tooltip="Help"
|
||||
edge="end"
|
||||
aria-label="help"
|
||||
onClick={() => { setShowHelp(true); }}
|
||||
>
|
||||
<HelpOutlineIcon style={{ width: 24, height: 24, opacity: 0.6 }} />
|
||||
</IconButtonEx>
|
||||
|
||||
|
||||
</Toolbar>
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
{landscape ?
|
||||
LandscapeView()
|
||||
:
|
||||
PortraitView()
|
||||
}
|
||||
</DialogContent>
|
||||
{showHelp && (
|
||||
<ChannelMixerSettingsHelpDialog open={showHelp} onClose={() => setShowHelp(false)} />
|
||||
)}
|
||||
</DialogEx>
|
||||
);
|
||||
}
|
||||
|
||||
export default ChannelMixerSettingsDialog;
|
||||
@@ -0,0 +1,146 @@
|
||||
// Copyright (c) 2022 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 Button from '@mui/material/Button';
|
||||
import DialogEx from './DialogEx';
|
||||
import DialogActions from '@mui/material/DialogActions';
|
||||
import DialogContent from '@mui/material/DialogContent';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import DialogTitle from '@mui/material/DialogTitle';
|
||||
import Toolbar from '@mui/material/Toolbar';
|
||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||
import IconButtonEx from './IconButtonEx';
|
||||
|
||||
|
||||
export interface ChannelMixerSettingsHelpDialogProps {
|
||||
open: boolean,
|
||||
onClose: () => void,
|
||||
};
|
||||
|
||||
export interface ChannelMixerSettingsHelpDialogState {
|
||||
fullScreen: boolean;
|
||||
};
|
||||
|
||||
export default class ChannelMixerSettingsHelpDialog extends ResizeResponsiveComponent<ChannelMixerSettingsHelpDialogProps, ChannelMixerSettingsHelpDialogState> {
|
||||
|
||||
refText: React.RefObject<HTMLInputElement | null>;
|
||||
|
||||
constructor(props: ChannelMixerSettingsHelpDialogProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
fullScreen: false
|
||||
};
|
||||
this.refText = React.createRef<HTMLInputElement>();
|
||||
}
|
||||
mounted: boolean = false;
|
||||
|
||||
|
||||
|
||||
onWindowSizeChanged(width: number, height: number): void {
|
||||
this.setState({ fullScreen: height < 200 })
|
||||
}
|
||||
|
||||
|
||||
componentDidMount() {
|
||||
super.componentDidMount();
|
||||
this.mounted = true;
|
||||
}
|
||||
componentWillUnmount() {
|
||||
super.componentWillUnmount();
|
||||
this.mounted = false;
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
}
|
||||
render() {
|
||||
let props = this.props;
|
||||
let { open, onClose } = props;
|
||||
|
||||
const handleClose = () => {
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<DialogEx tag="bindingHelp" open={open} fullWidth maxWidth="sm"
|
||||
onClose={handleClose}
|
||||
aria-labelledby="mixer-settings-dialog-title"
|
||||
style={{ userSelect: "none" }}
|
||||
fullwidth
|
||||
onEnterKey={() => { }}
|
||||
>
|
||||
<DialogTitle id="select-channel_mixer_settings" style={{ paddingTop: 0, paddingBottom: 0, marginBottom: 8 }}>
|
||||
<Toolbar style={{ padding: 0, margin: 0 }}>
|
||||
<IconButtonEx
|
||||
tooltip="Close"
|
||||
edge="start"
|
||||
color="inherit"
|
||||
aria-label="cancel"
|
||||
style={{ opacity: 0.6 }}
|
||||
onClick={() => { onClose(); }}
|
||||
>
|
||||
<ArrowBackIcon style={{ width: 24, height: 24 }} />
|
||||
</IconButtonEx>
|
||||
|
||||
<Typography noWrap component="div" sx={{ flexGrow: 1 }}>
|
||||
Channel Routing Dialog Help
|
||||
</Typography>
|
||||
</Toolbar>
|
||||
</DialogTitle>
|
||||
|
||||
<DialogContent style={{ minHeight: 96 }}>
|
||||
<div style={{ fontSize: "0.9em", lineHeight: "18pt" }}>
|
||||
<p> The Mixer Routing Dialog determines how audio signals are processed and routed between input and output audio channels.
|
||||
</p>
|
||||
<p>Guitar effects processing occurs on the <i>Main</i> route only. Plugins in the main PiPedal window are
|
||||
applied before any insert plugins that have been added to the <i>Main</i> route. Main insert plugins are
|
||||
applied globally, regardless of the selected preset or effects in the main PiPedal window. You might
|
||||
want to add an EQ or reverb plugin here, to allow your presets to be globally adjusted to suit the room
|
||||
in which you are currently performing.
|
||||
</p>
|
||||
<p>Plugins in the main PiPedal window are not applied to the <i>Aux</i> route. The Aux route is intended to be
|
||||
used for
|
||||
</p>
|
||||
<ul>
|
||||
<li>Passing through a dry (unprocessed) guitar signal that can be recorded by a DAW and
|
||||
re-amped later during mixing.
|
||||
</li>
|
||||
<li>Passing through a vocal mic signal from one of the input channels, perhaps with compression, EQ or
|
||||
other processing performed by plugins in Aux inserts.
|
||||
</li>
|
||||
<li>
|
||||
Passing through a backing track or other external audio signal from one of the input channels, and then either
|
||||
mixing it with the processed guitar signal, or passing it out on a dedicated output channel.
|
||||
</li>
|
||||
</ul>
|
||||
<p>although you may find other creative uses for the Aux channel as well.</p>
|
||||
<p>If the Main and Aux routes share output channels, then the results of the Main and Output signals are
|
||||
summed together on those output channels. For stereo mixing, assign the same left and right output
|
||||
channels to both the Main and Aux routes. If you have a 2x4 or 4x4 audio interface with two guitar input channels
|
||||
you can also pass through stereo dry signals by configuring Main and Aux input and output channels appropriately.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</DialogContent>
|
||||
</DialogEx>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
// Copyright (c) 2026 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 Units from './Units';
|
||||
import {UiPlugin, UiControl, PluginType, ControlType, ScalePoint} from './Lv2Plugin';
|
||||
|
||||
export const ChannelMixerUiUri = "uri://two-play/pipedal/ppChannelMixer";
|
||||
export const CHANNEL_MIXER_INSTANCE_ID = -4;
|
||||
|
||||
export function makeChanelMixerUiPlugin(): UiPlugin {
|
||||
|
||||
return new UiPlugin().deserialize({
|
||||
uri: ChannelMixerUiUri,
|
||||
name: "Channel Mixer",
|
||||
brand: "",
|
||||
label: "",
|
||||
plugin_type: PluginType.MixerPlugin,
|
||||
plugin_display_type: "Mixer",
|
||||
author_name: "",
|
||||
author_homepage: "",
|
||||
audio_inputs: 1,
|
||||
audio_side_chain_inputs: 0,
|
||||
audio_outputs: 1,
|
||||
has_midi_input: false,
|
||||
has_midi_output: false,
|
||||
description: "",
|
||||
controls: [
|
||||
new UiControl().applyProperties({
|
||||
symbol: "mainIn",
|
||||
name: "Main In",
|
||||
index: 0,
|
||||
is_input: true,
|
||||
min_value: -60.0,
|
||||
max_value: 20.0,
|
||||
default_value: 0.0,
|
||||
is_bypass: false,
|
||||
units: Units.db,
|
||||
controlType: ControlType.Dial,
|
||||
is_program_controller: false,
|
||||
custom_units: "",
|
||||
connection_optional: false,
|
||||
scale_points: [
|
||||
new ScalePoint().deserialize({ value: -60, label: "-INF" }),
|
||||
],
|
||||
}),
|
||||
new UiControl().applyProperties({
|
||||
symbol: "mainOut",
|
||||
name: "Main Out",
|
||||
index: 1,
|
||||
is_input: true,
|
||||
min_value: -60.0,
|
||||
max_value: 20.0,
|
||||
default_value: 0.0,
|
||||
is_bypass: false,
|
||||
units: Units.db,
|
||||
controlType: ControlType.Dial,
|
||||
is_program_controller: false,
|
||||
custom_units: "",
|
||||
connection_optional: false,
|
||||
scale_points: [
|
||||
new ScalePoint().deserialize({ value: -60, label: "-INF" }),
|
||||
],
|
||||
|
||||
}),
|
||||
new UiControl().applyProperties({
|
||||
symbol: "auxIn",
|
||||
name: "Aux In",
|
||||
index: 2,
|
||||
is_input: true,
|
||||
min_value: -60.0,
|
||||
max_value: 20.0,
|
||||
default_value: 0.0,
|
||||
is_bypass: false,
|
||||
units: Units.db,
|
||||
controlType: ControlType.Dial,
|
||||
is_program_controller: false,
|
||||
custom_units: "",
|
||||
connection_optional: false,
|
||||
scale_points: [
|
||||
new ScalePoint().deserialize({ value: -60, label: "-INF" }),
|
||||
],
|
||||
}),
|
||||
new UiControl().applyProperties({
|
||||
symbol: "auxOut",
|
||||
name: "Aux Out",
|
||||
index: 3,
|
||||
is_input: true,
|
||||
min_value: -60.0,
|
||||
max_value: 20.0,
|
||||
default_value: 0.0,
|
||||
is_bypass: false,
|
||||
units: Units.db,
|
||||
controlType: ControlType.Dial,
|
||||
is_program_controller: false,
|
||||
custom_units: "",
|
||||
connection_optional: false,
|
||||
scale_points: [
|
||||
new ScalePoint().deserialize({ value: -60, label: "-INF" }),
|
||||
],
|
||||
|
||||
}),
|
||||
new UiControl().applyProperties({
|
||||
symbol: "mainInVu",
|
||||
name: "In",
|
||||
index: 4,
|
||||
is_input: false,
|
||||
min_value: -60.0,
|
||||
max_value: 20.0,
|
||||
default_value: 0.0,
|
||||
controlType: ControlType.Vu,
|
||||
is_bypass: false,
|
||||
is_program_controller: false,
|
||||
units: Units.db,
|
||||
custom_units: "",
|
||||
connection_optional: false,
|
||||
}),
|
||||
new UiControl().applyProperties({
|
||||
symbol: "mainInVu",
|
||||
name: "In",
|
||||
index: 5,
|
||||
is_input: false,
|
||||
min_value: -60.0,
|
||||
max_value: 20.0,
|
||||
default_value: 0.0,
|
||||
controlType: ControlType.Vu,
|
||||
is_bypass: false,
|
||||
is_program_controller: false,
|
||||
units: Units.db,
|
||||
custom_units: "",
|
||||
connection_optional: false,
|
||||
}),
|
||||
new UiControl().applyProperties({
|
||||
symbol: "mainOutVu",
|
||||
name: "Out",
|
||||
index: 6,
|
||||
is_input: false,
|
||||
min_value: -60.0,
|
||||
max_value: 20.0,
|
||||
default_value: 0.0,
|
||||
controlType: ControlType.Vu,
|
||||
is_bypass: false,
|
||||
is_program_controller: false,
|
||||
units: Units.db,
|
||||
custom_units: "",
|
||||
connection_optional: false,
|
||||
}),
|
||||
new UiControl().applyProperties({
|
||||
symbol: "auxOutVu",
|
||||
name: "Out",
|
||||
index: 7,
|
||||
is_input: false,
|
||||
min_value: -60.0,
|
||||
max_value: 20.0,
|
||||
default_value: 0.0,
|
||||
controlType: ControlType.Vu,
|
||||
is_bypass: false,
|
||||
is_program_controller: false,
|
||||
units: Units.db,
|
||||
custom_units: "",
|
||||
connection_optional: false,
|
||||
}),
|
||||
],
|
||||
port_groups: [],
|
||||
fileProperties: [],
|
||||
frequencyPlots: [],
|
||||
is_vst3: false,
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -364,6 +364,7 @@ export class Pedalboard implements Deserializable<Pedalboard> {
|
||||
|
||||
static readonly START_CONTROL = -2; // synthetic PedalboardItem for input volume.
|
||||
static readonly END_CONTROL = -3; // synthetic PedalboardItem for output volume.
|
||||
static readonly CHANNEL_MIXER_INSTANCE_ID = -4; // Synthetic instance ID for the channel routing mixer.
|
||||
static readonly START_PEDALBOARD_ITEM_URI = "uri://two-play/pipedal/pedalboard#Start";
|
||||
static readonly END_PEDALBOARD_ITEM_URI = "uri://two-play/pipedal/pedalboard#End";
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ import AudioFileMetadata from './AudioFileMetadata';
|
||||
import { pathFileName } from './FileUtils';
|
||||
import { AlsaSequencerConfiguration, AlsaSequencerPortSelection } from './AlsaSequencer';
|
||||
import { getDefaultModGuiPreference } from './ModGuiHost';
|
||||
import ChannelMixerSettings from './ChannelMixerSettings';
|
||||
|
||||
export enum State {
|
||||
Loading,
|
||||
@@ -529,6 +530,13 @@ export class PiPedalModel //implements PiPedalModel
|
||||
jackServerSettings: ObservableProperty<JackServerSettings>
|
||||
= new ObservableProperty<JackServerSettings>(new JackServerSettings());
|
||||
|
||||
channelMixerControlValues: ObservableProperty<ControlValue[]> = new ObservableProperty<ControlValue[]>([]);
|
||||
channelMixerSettings: ObservableProperty<ChannelMixerSettings> = new ObservableProperty<ChannelMixerSettings>(new ChannelMixerSettings());
|
||||
|
||||
setChannelMixerSettings(settings: ChannelMixerSettings) {
|
||||
this.channelMixerSettings.set(settings);
|
||||
this.channelMixerControlValues.set(settings.controls);
|
||||
}
|
||||
wifiConfigSettings: ObservableProperty<WifiConfigSettings> = new ObservableProperty<WifiConfigSettings>(new WifiConfigSettings());
|
||||
wifiDirectConfigSettings: ObservableProperty<WifiDirectConfigSettings> = new ObservableProperty<WifiDirectConfigSettings>(new WifiDirectConfigSettings());
|
||||
governorSettings: ObservableProperty<GovernorSettings> = new ObservableProperty<GovernorSettings>(new GovernorSettings());
|
||||
@@ -1643,35 +1651,52 @@ export class PiPedalModel //implements PiPedalModel
|
||||
|
||||
private lastControlMessageWasSentbyMe = false;
|
||||
|
||||
private _setChannelMixerControlValue(key: string, value: number, notifyServer: boolean) : void {
|
||||
let channelMixerSettings = this.channelMixerSettings.get();
|
||||
let changed = channelMixerSettings.setControlValue(key, value);
|
||||
if (changed)
|
||||
{
|
||||
this.channelMixerControlValues.set(this.channelMixerSettings.get().controls);
|
||||
if (notifyServer) {
|
||||
this.lastControlMessageWasSentbyMe = true;
|
||||
this._setServerControl("setControl", Pedalboard.CHANNEL_MIXER_INSTANCE_ID, key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
private _setPedalboardControlValue(instanceId: number, key: string, value: number, notifyServer: boolean): void {
|
||||
let pedalboard = this.pedalboard.get();
|
||||
if (pedalboard === undefined) throw new PiPedalStateError("Pedalboard not ready.");
|
||||
let newPedalboard = pedalboard.clone();
|
||||
|
||||
if (instanceId === Pedalboard.START_CONTROL && key === "volume_db") {
|
||||
this._setInputVolume(value, notifyServer);
|
||||
if (instanceId == Pedalboard.CHANNEL_MIXER_INSTANCE_ID) {
|
||||
this._setChannelMixerControlValue(key,value,notifyServer);
|
||||
return;
|
||||
} else if (instanceId === Pedalboard.END_CONTROL) {
|
||||
this._setOutputVolume(value, notifyServer);
|
||||
return;
|
||||
}
|
||||
let item = newPedalboard.getItem(instanceId);
|
||||
let changed = item.setControlValue(key, value);
|
||||
} else {
|
||||
let changed: boolean;
|
||||
let newPedalboard = pedalboard.clone();
|
||||
|
||||
if (changed) {
|
||||
if (notifyServer) {
|
||||
this.lastControlMessageWasSentbyMe = true;
|
||||
this._setServerControl("setControl", instanceId, key, value);
|
||||
if (instanceId === Pedalboard.START_CONTROL && key === "volume_db") {
|
||||
this._setInputVolume(value, notifyServer);
|
||||
return;
|
||||
} else if (instanceId === Pedalboard.END_CONTROL) {
|
||||
this._setOutputVolume(value, notifyServer);
|
||||
return;
|
||||
}
|
||||
this.setModelPedalboard(newPedalboard);
|
||||
for (let i = 0; i < this._controlValueChangeItems.length; ++i) {
|
||||
let item = this._controlValueChangeItems[i];
|
||||
if (instanceId === item.instanceId) {
|
||||
item.onValueChanged(key, value);
|
||||
let item = newPedalboard.getItem(instanceId);
|
||||
changed = item.setControlValue(key, value);
|
||||
if (changed) {
|
||||
if (notifyServer) {
|
||||
this.lastControlMessageWasSentbyMe = true;
|
||||
this._setServerControl("setControl", instanceId, key, value);
|
||||
}
|
||||
this.setModelPedalboard(newPedalboard);
|
||||
for (let i = 0; i < this._controlValueChangeItems.length; ++i) {
|
||||
let item = this._controlValueChangeItems[i];
|
||||
if (instanceId === item.instanceId) {
|
||||
item.onValueChanged(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
private _setVst3PedalboardControlValue(instanceId: number, key: string, value: number, state: string, notifyServer: boolean): void {
|
||||
let pedalboard = this.pedalboard.get();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2022 Robin Davies
|
||||
// Copyright (c) 2026 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
|
||||
|
||||
@@ -20,11 +20,13 @@
|
||||
|
||||
import React, { SyntheticEvent, Component } from 'react';
|
||||
import Switch from "@mui/material/Switch";
|
||||
import ChannelMixerSettings from './ChannelMixerSettings';
|
||||
import ChannelMixerSettingsDialog from './ChannelMixerSettingsDialog';
|
||||
import OkCancelDialog from './OkCancelDialog';
|
||||
import RadioSelectDialog from './RadioSelectDialog';
|
||||
import IconButtonEx from './IconButtonEx';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import {isDarkMode} from './DarkMode';
|
||||
import { isDarkMode } from './DarkMode';
|
||||
import { PiPedalModel, PiPedalModelFactory, State } from './PiPedalModel';
|
||||
import { ColorTheme } from './DarkMode';
|
||||
import ButtonBase from "@mui/material/ButtonBase";
|
||||
@@ -75,7 +77,8 @@ interface SettingsDialogState {
|
||||
showStatusMonitor: boolean;
|
||||
showStatusMonitorDialog: boolean;
|
||||
jackConfiguration: JackConfiguration;
|
||||
jackSettings: JackChannelSelection;
|
||||
jackSettings: JackChannelSelection | null;
|
||||
channelMixerSettings: ChannelMixerSettings | null;
|
||||
jackServerSettings: JackServerSettings;
|
||||
alsaSequencerConfiguration: AlsaSequencerConfiguration;
|
||||
keepScreenOn: boolean;
|
||||
@@ -98,6 +101,7 @@ interface SettingsDialogState {
|
||||
showMidiSelectDialog: boolean;
|
||||
showThemeSelectDialog: boolean;
|
||||
showJackServerSettingsDialog: boolean;
|
||||
showChannelMixerSettingsDialog: boolean;
|
||||
shuttingDown: boolean;
|
||||
restarting: boolean;
|
||||
isAndroidHosted: boolean;
|
||||
@@ -190,6 +194,7 @@ const SettingsDialog = withStyles(
|
||||
showStatusMonitorDialog: false,
|
||||
|
||||
jackServerSettings: this.model.jackServerSettings.get(),
|
||||
channelMixerSettings: this.model.channelMixerSettings.get(),
|
||||
jackConfiguration: this.model.jackConfiguration.get(),
|
||||
jackStatus: undefined,
|
||||
jackSettings: this.model.jackSettings.get(),
|
||||
@@ -210,6 +215,7 @@ const SettingsDialog = withStyles(
|
||||
showMidiSelectDialog: false,
|
||||
showThemeSelectDialog: false,
|
||||
showJackServerSettingsDialog: false,
|
||||
showChannelMixerSettingsDialog: false,
|
||||
shuttingDown: false,
|
||||
restarting: false,
|
||||
showShutdownOkDialog: false,
|
||||
@@ -222,6 +228,7 @@ const SettingsDialog = withStyles(
|
||||
this.handleAlsaSequencerConfigurationChanged = this.handleAlsaSequencerConfigurationChanged.bind(this);
|
||||
this.handleJackSettingsChanged = this.handleJackSettingsChanged.bind(this);
|
||||
this.handleJackServerSettingsChanged = this.handleJackServerSettingsChanged.bind(this);
|
||||
this.handleChannelMixerSettingsChanged = this.handleChannelMixerSettingsChanged.bind(this);
|
||||
this.handleWifiConfigSettingsChanged = this.handleWifiConfigSettingsChanged.bind(this);
|
||||
this.handleWifiDirectConfigSettingsChanged = this.handleWifiDirectConfigSettingsChanged.bind(this);
|
||||
this.handleGovernorSettingsChanged = this.handleGovernorSettingsChanged.bind(this);
|
||||
@@ -323,6 +330,12 @@ const SettingsDialog = withStyles(
|
||||
continueDisabled: !this.model.jackServerSettings.get().valid
|
||||
});
|
||||
}
|
||||
handleChannelMixerSettingsChanged(): void {
|
||||
this.setState({
|
||||
channelMixerSettings: this.model.channelMixerSettings.get(),
|
||||
continueDisabled: !this.model.channelMixerSettings.get().configured
|
||||
});
|
||||
}
|
||||
|
||||
handleJackConfigurationChanged(): void {
|
||||
this.setState({
|
||||
@@ -365,6 +378,7 @@ const SettingsDialog = withStyles(
|
||||
this.model.jackConfiguration.addOnChangedHandler(this.handleJackConfigurationChanged);
|
||||
this.model.alsaSequencerConfiguration.addOnChangedHandler(this.handleAlsaSequencerConfigurationChanged);
|
||||
this.model.jackServerSettings.addOnChangedHandler(this.handleJackServerSettingsChanged);
|
||||
this.model.channelMixerSettings.addOnChangedHandler(this.handleChannelMixerSettingsChanged);
|
||||
this.model.wifiConfigSettings.addOnChangedHandler(this.handleWifiConfigSettingsChanged);
|
||||
this.model.wifiDirectConfigSettings.addOnChangedHandler(this.handleWifiDirectConfigSettingsChanged);
|
||||
this.model.governorSettings.addOnChangedHandler(this.handleGovernorSettingsChanged);
|
||||
@@ -387,6 +401,7 @@ const SettingsDialog = withStyles(
|
||||
this.handleJackSettingsChanged();
|
||||
this.handleShowStatusMonitorChanged();
|
||||
this.handleJackServerSettingsChanged();
|
||||
this.handleChannelMixerSettingsChanged();
|
||||
this.handleWifiConfigSettingsChanged();
|
||||
this.handleWifiDirectConfigSettingsChanged();
|
||||
this.setState({ hasWifiDevice: this.model.hasWifiDevice.get() });
|
||||
@@ -405,6 +420,7 @@ const SettingsDialog = withStyles(
|
||||
this.model.alsaSequencerConfiguration.removeOnChangedHandler(this.handleAlsaSequencerConfigurationChanged);
|
||||
this.model.jackSettings.removeOnChangedHandler(this.handleJackSettingsChanged);
|
||||
this.model.jackServerSettings.removeOnChangedHandler(this.handleJackServerSettingsChanged);
|
||||
this.model.channelMixerSettings.removeOnChangedHandler(this.handleChannelMixerSettingsChanged);
|
||||
this.model.wifiConfigSettings.removeOnChangedHandler(this.handleWifiConfigSettingsChanged);
|
||||
this.model.wifiDirectConfigSettings.removeOnChangedHandler(this.handleWifiDirectConfigSettingsChanged);
|
||||
this.model.governorSettings.removeOnChangedHandler(this.handleGovernorSettingsChanged);
|
||||
@@ -503,6 +519,7 @@ const SettingsDialog = withStyles(
|
||||
}
|
||||
|
||||
handleSelectChannelsDialogResult(channels: string[] | null): void {
|
||||
if (this.state.jackSettings === null) { return; }
|
||||
if (channels) {
|
||||
let newSelection = this.state.jackSettings.clone();
|
||||
if (this.state.showInputSelectDialog) {
|
||||
@@ -595,10 +612,14 @@ const SettingsDialog = withStyles(
|
||||
const classes = withStyles.getClasses(this.props);
|
||||
|
||||
let isConfigValid = this.state.jackConfiguration.isValid;
|
||||
let selectedChannels: string[] = this.state.showInputSelectDialog ? this.state.jackSettings.inputAudioPorts : this.state.jackSettings.outputAudioPorts;
|
||||
let selectedChannels: string[] =
|
||||
this.state.jackSettings === null ? [] :
|
||||
this.state.showInputSelectDialog ? this.state.jackSettings.inputAudioPorts : this.state.jackSettings.outputAudioPorts;
|
||||
let disableShutdown = this.state.shuttingDown || this.state.restarting;
|
||||
|
||||
let canKeepScreenOn = this.model.canKeepScreenOn;
|
||||
let hasAudioConfig = isConfigValid && this.state.jackConfiguration.outputAudioPorts.length >= 1
|
||||
&& this.state.jackConfiguration.inputAudioPorts.length >= 1
|
||||
|
||||
return (
|
||||
<DialogEx tag="settings" fullScreen open={this.props.open}
|
||||
@@ -663,8 +684,8 @@ const SettingsDialog = withStyles(
|
||||
{(!isConfigValid) ?
|
||||
(
|
||||
<div className={classes.cpuStatusColor} style={{ paddingLeft: 48, position: "relative", top: -12 }}>
|
||||
<Typography display="block" variant="caption" color="textSecondary">Status:
|
||||
<span style={{ color: isDarkMode() ? "#F88" : "#F00" }}>Not configured.</span></Typography>
|
||||
<Typography display="block" variant="caption" color="textSecondary">Status:
|
||||
<span style={{ color: isDarkMode() ? "#F88" : "#F00" }}>Not configured.</span></Typography>
|
||||
{(!this.props.onboarding) && (
|
||||
<Typography display="block" variant="caption" color="inherit">Governor: </Typography>
|
||||
)}
|
||||
@@ -693,23 +714,51 @@ const SettingsDialog = withStyles(
|
||||
</Typography>
|
||||
<ButtonBase className={classes.setting} onClick={() => this.handleJackServerSettings()}
|
||||
>
|
||||
<SelectHoverBackground selected={false} showHover={true} />
|
||||
<SelectHoverBackground selected={false} showHover={true} />
|
||||
<div style={{ width: "100%" }}>
|
||||
<Typography display="block" variant="body2" noWrap>Audio device</Typography>
|
||||
<Typography display="block" variant="caption" noWrap color="textSecondary">{this.state.jackServerSettings.getSummaryText()}</Typography>
|
||||
</div>
|
||||
</ButtonBase>
|
||||
<JackServerSettingsDialog
|
||||
open={this.state.showJackServerSettingsDialog}
|
||||
jackServerSettings={this.state.jackServerSettings}
|
||||
onClose={() => this.setState({ showJackServerSettingsDialog: false })}
|
||||
onApply={(jackServerSettings) => {
|
||||
{this.state.showChannelMixerSettingsDialog && (
|
||||
<ChannelMixerSettingsDialog
|
||||
open={this.state.showChannelMixerSettingsDialog}
|
||||
onClose={() => this.setState({ showChannelMixerSettingsDialog: false })}
|
||||
/>
|
||||
)}
|
||||
{this.state.showJackServerSettingsDialog && (
|
||||
|
||||
<JackServerSettingsDialog
|
||||
open={this.state.showJackServerSettingsDialog}
|
||||
jackServerSettings={this.state.jackServerSettings}
|
||||
onClose={() => this.setState({ showJackServerSettingsDialog: false })}
|
||||
onApply={(jackServerSettings) => {
|
||||
this.setState({
|
||||
jackServerSettings: jackServerSettings
|
||||
});
|
||||
this.model.setJackServerSettings(jackServerSettings);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<ButtonBase className={classes.setting}
|
||||
onClick={() => {
|
||||
this.setState({
|
||||
jackServerSettings: jackServerSettings
|
||||
showChannelMixerSettingsDialog: true
|
||||
});
|
||||
this.model.setJackServerSettings(jackServerSettings);
|
||||
}}
|
||||
/>
|
||||
disabled={!hasAudioConfig}
|
||||
style={{ opacity: (!hasAudioConfig) ? 0.6 : 1.0 }}
|
||||
>
|
||||
<SelectHoverBackground selected={false} showHover={true} />
|
||||
<div style={{ width: "100%" }}>
|
||||
<Typography display="block" variant="body2" noWrap>Channel Routing</Typography>
|
||||
<Typography display="block" variant="caption" color="textSecondary" noWrap>{
|
||||
this.state.jackSettings == null ? "" :
|
||||
this.state.jackSettings.getAudioInputDisplayValue(this.state.jackConfiguration)}</Typography>
|
||||
</div>
|
||||
</ButtonBase>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -721,7 +770,9 @@ const SettingsDialog = withStyles(
|
||||
<SelectHoverBackground selected={false} showHover={true} />
|
||||
<div style={{ width: "100%" }}>
|
||||
<Typography display="block" variant="body2" noWrap>Input channels</Typography>
|
||||
<Typography display="block" variant="caption" color="textSecondary" noWrap>{this.state.jackSettings.getAudioInputDisplayValue(this.state.jackConfiguration)}</Typography>
|
||||
<Typography display="block" variant="caption" color="textSecondary" noWrap>{
|
||||
this.state.jackSettings == null ? "" :
|
||||
this.state.jackSettings.getAudioInputDisplayValue(this.state.jackConfiguration)}</Typography>
|
||||
</div>
|
||||
</ButtonBase>
|
||||
<ButtonBase className={classes.setting} onClick={() => this.handleOutputSelection()}
|
||||
@@ -731,7 +782,9 @@ const SettingsDialog = withStyles(
|
||||
<SelectHoverBackground selected={false} showHover={true} />
|
||||
<div style={{ width: "100%" }}>
|
||||
<Typography display="block" variant="body2" noWrap>Output channels</Typography>
|
||||
<Typography display="block" variant="caption" color="textSecondary" noWrap>{this.state.jackSettings.getAudioOutputDisplayValue(this.state.jackConfiguration)}</Typography>
|
||||
<Typography display="block" variant="caption" color="textSecondary" noWrap>{
|
||||
this.state.jackSettings == null ? "" :
|
||||
this.state.jackSettings.getAudioOutputDisplayValue(this.state.jackConfiguration)}</Typography>
|
||||
</div>
|
||||
</ButtonBase>
|
||||
<Divider />
|
||||
|
||||
Reference in New Issue
Block a user