Same Device Support

Updated the input and output device change handlers so new settings are validated through applyAlsaDevices, ensuring latency and OK status are refreshed
Added a new “useSameDevice” flag in the Jack server settings dialog state to track whether input and output devices are locked together

Initialized the flag based on whether the current configuration uses the same input and output device

Updated device handlers to mirror the input device into the output device when the toggle is enabled

Inserted a checkbox labeled “Use same device for input/output” into the dialog UI and hid the output selector when this option is active
This commit is contained in:
Extremesecrecy
2025-07-23 19:08:50 -07:00
parent 6ac8968583
commit c8a80e5aaa
+69 -25
View File
@@ -42,6 +42,8 @@ import Typography from '@mui/material/Typography';
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel'; import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
import IconButtonEx from './IconButtonEx'; import IconButtonEx from './IconButtonEx';
import RefreshIcon from '@mui/icons-material/Refresh'; import RefreshIcon from '@mui/icons-material/Refresh';
import Checkbox from '@mui/material/Checkbox';
import FormControlLabel from '@mui/material/FormControlLabel';
import AlsaDeviceInfo from './AlsaDeviceInfo'; import AlsaDeviceInfo from './AlsaDeviceInfo';
@@ -60,6 +62,7 @@ interface JackServerSettingsDialogState {
jackServerSettings: JackServerSettings; jackServerSettings: JackServerSettings;
alsaDevices?: AlsaDeviceInfo[]; alsaDevices?: AlsaDeviceInfo[];
okEnabled: boolean; okEnabled: boolean;
useSameDevice: boolean;
} }
const styles = (theme: Theme) => const styles = (theme: Theme) =>
@@ -249,11 +252,13 @@ const JackServerSettingsDialog = withStyles(
this.model = PiPedalModelFactory.getInstance(); this.model = PiPedalModelFactory.getInstance();
const sameDevice = props.jackServerSettings.alsaInputDevice === props.jackServerSettings.alsaOutputDevice;
this.state = { this.state = {
latencyText: getLatencyText(props.jackServerSettings), latencyText: getLatencyText(props.jackServerSettings),
jackServerSettings: props.jackServerSettings.clone(), // invalid, but not nullish jackServerSettings: props.jackServerSettings.clone(), // invalid, but not nullish
alsaDevices: undefined, alsaDevices: undefined,
okEnabled: false okEnabled: false,
useSameDevice: sameDevice
}; };
} }
mounted: boolean = false; mounted: boolean = false;
@@ -268,7 +273,8 @@ const JackServerSettingsDialog = withStyles(
alsaDevices: devices, alsaDevices: devices,
jackServerSettings: settings, jackServerSettings: settings,
latencyText: getLatencyText(settings), latencyText: getLatencyText(settings),
okEnabled: isOkEnabled(settings,devices) okEnabled: isOkEnabled(settings,devices),
useSameDevice: settings.alsaInputDevice === settings.alsaOutputDevice
}); });
} else { } else {
this.setState({ alsaDevices: devices }); this.setState({ alsaDevices: devices });
@@ -304,6 +310,11 @@ const JackServerSettingsDialog = withStyles(
} }
} }
if (this.state.useSameDevice) {
result.alsaOutputDevice = result.alsaInputDevice;
outDevice = inDevice;
}
if (!inDevice || !outDevice) { if (!inDevice || !outDevice) {
result.valid = false; result.valid = false;
return result; return result;
@@ -341,7 +352,8 @@ const JackServerSettingsDialog = withStyles(
this.setState({ this.setState({
jackServerSettings: settings, jackServerSettings: settings,
latencyText: getLatencyText(settings), latencyText: getLatencyText(settings),
okEnabled: isOkEnabled(settings,this.state.alsaDevices) okEnabled: isOkEnabled(settings,this.state.alsaDevices),
useSameDevice: settings.alsaInputDevice === settings.alsaOutputDevice
}); });
if (!this.state.alsaDevices) { if (!this.state.alsaDevices) {
this.requestAlsaInfo(); this.requestAlsaInfo();
@@ -418,25 +430,43 @@ const JackServerSettingsDialog = withStyles(
this.props.onApply(this.state.jackServerSettings.clone()); this.props.onApply(this.state.jackServerSettings.clone());
} }
}; };
handleInputDeviceChanged(e: any) { handleInputDeviceChanged(e: any) {
const d = e.target.value as string; const d = e.target.value as string;
let s = this.state.jackServerSettings.clone(); let s = this.state.jackServerSettings.clone();
s.alsaInputDevice = d; s.alsaInputDevice = d;
this.setState({ if (this.state.useSameDevice) {
jackServerSettings: s, s.alsaOutputDevice = d;
okEnabled: isOkEnabled(s, this.state.alsaDevices) }
}); this.setState({
} jackServerSettings: s,
okEnabled: isOkEnabled(s, this.state.alsaDevices)
handleOutputDeviceChanged(e: any) { });
const d = e.target.value as string; }
let s = this.state.jackServerSettings.clone();
s.alsaOutputDevice = d; handleOutputDeviceChanged(e: any) {
this.setState({ const d = e.target.value as string;
jackServerSettings: s, let s = this.state.jackServerSettings.clone();
okEnabled: isOkEnabled(s, this.state.alsaDevices) s.alsaOutputDevice = d;
}); if (this.state.useSameDevice) {
} s.alsaInputDevice = d;
}
this.setState({
jackServerSettings: s,
okEnabled: isOkEnabled(s, this.state.alsaDevices)
});
}
handleUseSameDeviceChanged(e: any, checked: boolean) {
let s = this.state.jackServerSettings.clone();
const useSame = checked;
if (useSame) {
s.alsaOutputDevice = s.alsaInputDevice;
}
this.setState({
useSameDevice: useSame,
jackServerSettings: s,
okEnabled: isOkEnabled(s, this.state.alsaDevices)
});
}
render() { render() {
const classes = withStyles.getClasses(this.props); const classes = withStyles.getClasses(this.props);
@@ -448,7 +478,10 @@ const JackServerSettingsDialog = withStyles(
let selectedInputDevice = this.getSelectedDevice(this.state.jackServerSettings.alsaInputDevice); let selectedInputDevice = this.getSelectedDevice(this.state.jackServerSettings.alsaInputDevice);
let selectedOutputDevice = this.getSelectedDevice(this.state.jackServerSettings.alsaOutputDevice); let selectedOutputDevice = this.getSelectedDevice(this.state.jackServerSettings.alsaOutputDevice);
if (this.state.useSameDevice) {
selectedOutputDevice = selectedInputDevice;
}
let bufferSizes: number[] = getValidBufferSizesMultiple(selectedInputDevice, selectedOutputDevice); let bufferSizes: number[] = getValidBufferSizesMultiple(selectedInputDevice, selectedOutputDevice);
let bufferCounts = getValidBufferCountsMultiple(this.state.jackServerSettings.bufferSize, selectedInputDevice, selectedOutputDevice); let bufferCounts = getValidBufferCountsMultiple(this.state.jackServerSettings.bufferSize, selectedInputDevice, selectedOutputDevice);
let bufferSizeDisabled = !(selectedInputDevice || selectedOutputDevice); let bufferSizeDisabled = !(selectedInputDevice || selectedOutputDevice);
@@ -483,13 +516,14 @@ const JackServerSettingsDialog = withStyles(
</FormControl> </FormControl>
{/* Audio Output Device */} {/* Audio Output Device */}
<FormControl className={classes.formControl}> <FormControl className={classes.formControl}
style={{ display: this.state.useSameDevice ? 'none' : undefined }}>
<InputLabel htmlFor="jsd_outputDevice">Output Device</InputLabel> <InputLabel htmlFor="jsd_outputDevice">Output Device</InputLabel>
<Select <Select
id="jsd_outputDevice" id="jsd_outputDevice"
value={this.state.jackServerSettings.alsaOutputDevice} value={this.state.jackServerSettings.alsaOutputDevice}
onChange={e => this.handleOutputDeviceChanged(e)} onChange={e => this.handleOutputDeviceChanged(e)}
disabled={!this.state.alsaDevices || this.state.alsaDevices.length === 0} disabled={this.state.useSameDevice || !this.state.alsaDevices || this.state.alsaDevices.length === 0}
style={{ width: 220 }} style={{ width: 220 }}
> >
{this.state.alsaDevices?.filter(d => d.supportsPlayback).map(d => ( {this.state.alsaDevices?.filter(d => d.supportsPlayback).map(d => (
@@ -500,6 +534,16 @@ const JackServerSettingsDialog = withStyles(
<IconButtonEx tooltip="Refresh devices" onClick={() => this.requestAlsaInfo()} aria-label="refresh-audio-devices"> <IconButtonEx tooltip="Refresh devices" onClick={() => this.requestAlsaInfo()} aria-label="refresh-audio-devices">
<RefreshIcon /> <RefreshIcon />
</IconButtonEx> </IconButtonEx>
<FormControlLabel
control={
<Checkbox
checked={this.state.useSameDevice}
onChange={(e, c) => this.handleUseSameDeviceChanged(e, c)}
/>
}
label="Use same device for input/output"
style={{ marginLeft: 16 }}
/>
</div><div> </div><div>
<FormControl className={classes.formControl}> <FormControl className={classes.formControl}>
<InputLabel htmlFor="jsd_sampleRate">Sample rate</InputLabel> <InputLabel htmlFor="jsd_sampleRate">Sample rate</InputLabel>