This commit is contained in:
Robin E.R. Davies
2026-01-27 23:45:13 -05:00
parent 96cb9967aa
commit 6c441ecfee
28 changed files with 480 additions and 262 deletions
+124 -7
View File
@@ -1,23 +1,61 @@
import { Pedalboard, ControlValue } from "./Pedalboard.tsx";
import JackServerSettings from "./JackServerSettings.tsx";
import JackConfiguration from "./Jack.tsx";
function isActiveChannel(channels: number[]): boolean {
if (channels.length < 2) return false;
return channels[1] >= 0 || channels[0] >= 0;
}
function chName(ch: number): string {
if (ch === -1) {
return "None";
}
return "Ch " + (ch + 1);
}
function channelPairName(channels: number[], maxChannels: number): string {
if (channels.length !== 2) {
return "Invalid";
}
if (channels[0] === -1 && channels[1] === -1) {
return "None";
}
if (maxChannels === 2) {
if (channels[0] === 0 && channels[1] === 1) {
return "Stereo";
}
if (channels[0] === 0 && (channels[0] === channels[1] || channels[1] === -1)) {
return "Left";
}
if (channels[1] === 1 && (channels[0] === channels[1])) {
return "Right";
}
if (channels[0] === 1 && channels[1] === 0) {
return "Right,Left";
}
}
return chName(channels[0]) + "," + chName(channels[1]);
}
export default class ChannelRouterSettings {
configured: boolean = false;
channelRouterPresetId: number = -1;
mainInputChannels: number[] = [1,1];
mainOutputChannels: number[] = [0,1];
mainInputChannels: number[] = [1, 1];
mainOutputChannels: number[] = [0, 1];
mainInserts: Pedalboard = new Pedalboard();
auxInputChannels: number[] = [0,0];
auxOutputChannels: number[] = [-1,-1];
auxInputChannels: number[] = [0, 0];
auxOutputChannels: number[] = [-1, -1];
auxInserts: Pedalboard = new Pedalboard();
sendInputChannels: number[] = [-1,-1];
sendOutputChannels: number[] = [-1,-1];
sendInputChannels: number[] = [-1, -1];
sendOutputChannels: number[] = [-1, -1];
controlValues: ControlValue[] = [];
// Inserts...
@@ -55,10 +93,89 @@ export default class ChannelRouterSettings {
return true;
}
}
let newValue = new ControlValue(symbol,value);
let newValue = new ControlValue(symbol, value);
this.controlValues.push(newValue);
this.controlValues = this.controlValues.slice(); // trigger observers.
return true;
}
getDescription(jackConfiguration: JackConfiguration): string {
if (!this.configured) {
return "Not configured";
}
if (!jackConfiguration.isValid)
{
return "Not configured"
}
if (!this.isValid(jackConfiguration)) {
return "Invalid configuration";
}
let nInputs = jackConfiguration.inputAudioPorts.length;
let nOutputs = jackConfiguration.outputAudioPorts.length;
let description = channelPairName(this.mainInputChannels, nInputs) + " -> " +
channelPairName(this.mainOutputChannels, nOutputs);
if (isActiveChannel(this.auxInputChannels) && isActiveChannel(this.auxOutputChannels))
{
description += " " + "+ re-amp: " + channelPairName(this.auxInputChannels, nInputs) + " -> " +
channelPairName(this.auxOutputChannels, nOutputs);
}
if (isActiveChannel(this.sendInputChannels) && isActiveChannel(this.sendOutputChannels)) {
description += " " + "+ send: " +channelPairName(this.sendOutputChannels, nOutputs)
+ " -> " + channelPairName(this.sendInputChannels, nInputs);
}
return description;
}
canEdit(jackConfiguration: JackConfiguration): boolean {
return jackConfiguration.isValid;
}
isValid(jackConfiguration: JackConfiguration): boolean {
if (!this.configured) {
return false;
}
let maxInputChannels = jackConfiguration.inputAudioPorts.length;
let maxOutputChannels = jackConfiguration.outputAudioPorts.length;
let hasInput = false;
let hasOutput = false;
for (let ch of this.mainInputChannels) {
if (ch >= 0) {
hasInput = true;
if (ch >= maxInputChannels) {
return false;
}
}
}
if (!hasInput) {
return false;
}
for (let ch of this.mainOutputChannels) {
if (ch >= 0) {
hasOutput = true;
if (ch >= maxOutputChannels) {
return false;
}
}
}
if (!hasOutput) {
return false;
}
for (let ch of this.auxInputChannels) {
if (ch >= maxInputChannels) {
return false;
}
}
for (let ch of this.auxOutputChannels) {
if (ch >= maxOutputChannels) {
return false;
}
}
for (let ch of this.sendInputChannels) {
if (ch >= maxInputChannels) {
return false;
}
}
return true;
}
}
@@ -42,6 +42,11 @@ import DialogEx from './DialogEx';
import { PiPedalModelFactory } from './PiPedalModel';
let debugInputChannels: number | null = 2;
let debugOutputChannels: number | null = 4;
export interface ChannelRouterSettingsDialogProps {
open: boolean;
onClose: () => void;
@@ -309,6 +314,12 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
let ChannelSelect = (routeType: RouteType, channelIndex: number, input: boolean, icon: boolean = true) => {
let channelCount = input ? config.inputAudioPorts.length : config.outputAudioPorts.length;
if (input && debugInputChannels != null) {
channelCount = debugInputChannels;
}
if (!input && debugOutputChannels != null) {
channelCount = debugOutputChannels;
}
let value: number;
@@ -336,7 +347,7 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
}
onChange={(event) => {
let newValue = event.target.value as number;
let newSettings = Object.assign(new ChannelRouterSettings(), settings);
let newSettings = new ChannelRouterSettings().deserialize(settings);
switch (routeType) {
case RouteType.Main:
if (input) {
@@ -385,7 +396,11 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
}
for (let preset of channelRouterPresets) {
if (preset.id === presetId) {
model.setChannelRouterSettings(preset.settings);
let newPreset = Object.assign(new ChannelRouterSettings(), preset.settings);
// mark the presets as inserts.
newPreset.mainInserts.nextInstanceId = Pedalboard.MAIN_INSERT_INSTANCE_BASE;
newPreset.auxInserts.nextInstanceId = Pedalboard.AUX_INSERT_INSTANCE_BASE;
model.setChannelRouterSettings(newPreset);
return;
}
}
@@ -759,7 +774,7 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
{ChannelSelect(RouteType.Send, 0, true, false)}
</td>
<td style={cellLeft}>
{ChannelSelect(RouteType.Send, 1, false, false)}
{ChannelSelect(RouteType.Send, 1, true, false)}
</td>
<td></td>
</tr>
+4 -4
View File
@@ -381,10 +381,10 @@ 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 MAIN_INSERT_START_CONTROL_ID = Pedalboard.MAIN_INSERT_INSTANCE_BASE + MAX_INSTANCE_ID -2;
static readonly MAIN_INSERT_END_CONTROL_ID = Pedalboard.MAIN_INSERT_INSTANCE_BASE + MAX_INSTANCE_ID -3;
static readonly AUX_INSERT_START_CONTROL_ID = Pedalboard.AUX_INSERT_INSTANCE_BASE + MAX_INSTANCE_ID -2;
static readonly AUX_INSERT_END_CONTROL_ID = Pedalboard.AUX_INSERT_INSTANCE_BASE + MAX_INSTANCE_ID -3;
static readonly MAIN_INSERT_START_CONTROL_ID = Pedalboard.MAIN_INSERT_INSTANCE_BASE + -2 + MAX_INSTANCE_ID;
static readonly MAIN_INSERT_END_CONTROL_ID = Pedalboard.MAIN_INSERT_INSTANCE_BASE + -3 + MAX_INSTANCE_ID;
static readonly AUX_INSERT_START_CONTROL_ID = Pedalboard.AUX_INSERT_INSTANCE_BASE + -2 + MAX_INSTANCE_ID
static readonly AUX_INSERT_END_CONTROL_ID = Pedalboard.AUX_INSERT_INSTANCE_BASE + -3 + MAX_INSTANCE_ID;
static readonly START_PEDALBOARD_ITEM_URI = "uri://two-play/pipedal/pedalboard#Start";
+7 -6
View File
@@ -431,9 +431,9 @@ interface MonitorPortOutputBody {
}
interface ChannelSelectionChangedBody {
interface ChannelRouterSettingsChangedBody {
clientId: number;
jackChannelSelection: JackChannelSelection;
channelRouterSettings: ChannelRouterSettings;
}
interface RenamePresetBody {
clientId: number;
@@ -730,10 +730,11 @@ export class PiPedalModel //implements PiPedalModel
} else if (message === "onShowStatusMonitorChanged") {
let value = body as boolean;
this.showStatusMonitor.set(value);
} else if (message === "onChannelSelectionChanged") {
let channelSelectionBody = body as ChannelSelectionChangedBody;
let channelSelection = new JackChannelSelection().deserialize(channelSelectionBody.jackChannelSelection);
this.jackSettings.set(channelSelection);
} else if (message === "onChannelRouterSettingsChanged") {
let channelRouterSettingChangedBody = body as ChannelRouterSettingsChangedBody;
let channelRouterSettings = new ChannelRouterSettings().deserialize(
channelRouterSettingChangedBody.channelRouterSettings);
this.channelRouterSettings.set(channelRouterSettings);
} else if (message === "onSnapshotModified") {
let { snapshotIndex, modified } = (body as { snapshotIndex: number, modified: boolean });
let snapshots = this.pedalboard.get().snapshots;
+38 -52
View File
@@ -519,29 +519,10 @@ 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) {
newSelection.inputAudioPorts = channels;
} else {
newSelection.outputAudioPorts = channels;
}
this.setState({
jackSettings: newSelection,
showInputSelectDialog: false,
showOutputSelectDialog: false
});
this.model.setJackSettings(newSelection);
} else {
this.setState({
showInputSelectDialog: false,
showOutputSelectDialog: false
});
}
this.setState({
showInputSelectDialog: false,
showOutputSelectDialog: false
});
}
midiSummary(): string {
@@ -618,8 +599,10 @@ const SettingsDialog = withStyles(
let disableShutdown = this.state.shuttingDown || this.state.restarting;
let canKeepScreenOn = this.model.canKeepScreenOn;
let hasAudioConfig = isConfigValid && this.state.jackConfiguration.outputAudioPorts.length >= 1
let hasAudioConfig = isConfigValid
&& this.state.jackConfiguration.inputAudioPorts.length >= 1
&& this.state.jackConfiguration.outputAudioPorts.length >= 1;
return (
<DialogEx tag="settings" fullScreen open={this.props.open}
@@ -753,40 +736,43 @@ const SettingsDialog = withStyles(
<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>
<Typography display="block" variant="caption"
color={
this.state.channelRouterSettings?.isValid(this.state.jackConfiguration) ?? false
? "textSecondary": "error"} noWrap>{
this.state.channelRouterSettings?.getDescription(this.state.jackConfiguration)??""}</Typography>
</div>
</ButtonBase>
{/* Old Input and Output selection
<ButtonBase className={classes.setting} onClick={() => this.handleInputSelection()}
disabled={!isConfigValid || this.state.jackConfiguration.outputAudioPorts.length <= 1}
style={{ opacity: !isConfigValid ? 0.6 : 1.0 }}
<ButtonBase className={classes.setting} onClick={() => this.handleInputSelection()}
disabled={!isConfigValid || this.state.jackConfiguration.outputAudioPorts.length <= 1}
style={{ opacity: !isConfigValid ? 0.6 : 1.0 }}
>
<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 == null ? "" :
this.state.jackSettings.getAudioInputDisplayValue(this.state.jackConfiguration)}</Typography>
</div>
</ButtonBase>
<ButtonBase className={classes.setting} onClick={() => this.handleOutputSelection()}
disabled={!isConfigValid || this.state.jackConfiguration.outputAudioPorts.length <= 1}
style={{ opacity: !isConfigValid ? 0.6 : 1.0 }}
>
<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 == null ? "" :
this.state.jackSettings.getAudioOutputDisplayValue(this.state.jackConfiguration)}</Typography>
</div>
</ButtonBase>
>
<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 == null ? "" :
this.state.jackSettings.getAudioInputDisplayValue(this.state.jackConfiguration)}</Typography>
</div>
</ButtonBase>
<ButtonBase className={classes.setting} onClick={() => this.handleOutputSelection()}
disabled={!isConfigValid || this.state.jackConfiguration.outputAudioPorts.length <= 1}
style={{ opacity: !isConfigValid ? 0.6 : 1.0 }}
>
<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 == null ? "" :
this.state.jackSettings.getAudioOutputDisplayValue(this.state.jackConfiguration)}</Typography>
</div>
</ButtonBase>
*/}
<Divider />
<div >
<Typography className={classes.sectionHead} display="block" variant="caption" color="secondary">MIDI</Typography>