From 874d85991672fb5b69e45074318a176a8b2ff4a0 Mon Sep 17 00:00:00 2001 From: Extremesecrecy <10959169+extremesecrecy@users.noreply.github.com> Date: Thu, 24 Jul 2025 19:27:39 -0700 Subject: [PATCH] Email Changes - Backwards Compatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce legacy alsaDevice_ field in JackServerSettings to preserve compatibility with older configs. Include this field in serialization to allow old config files to load. Add migration logic in Storage::GetJackServerSettings to copy the legacy device value to new separate input/output fields and clear the legacy value after migration. Update frontend validation so that device selections are only validated when the user presses OK. Improve buffer selection logic to prioritize 32×4, then other x4, x3, or x2 options, falling back to 64×3 if none match. Use standard MUI styling for device selectors. Labels stay above the field and no longer animate. Dropdown labels now use background padding to prevent underline overlap. Add a compactWidth flag to adjust layout on narrow screens. Stack device selectors vertically in compact mode while keeping the refresh button on the right. Refreshing ALSA info sets unknown device IDs to a blank value. Add real-time status updates with a polling timer. Separate Apply and OK handlers so settings can be tested without closing the dialog. Add an Apply button alongside Cancel and OK. Update the UI summary logic to show “Input → Output” when devices differ and “Not selected” when config is incomplete. Filter out internal devices like HDMI and bcm2835 from the device list. Retain the legacy alsaDevice_ field in the JSON for backward compatibility, with helper accessors and documentation for legacy constructor behavior. Update latency text calculation to show when sample rate and buffer size are defined, even if the config is invalid. Removed libsdl2-dev from dependency checking and dependencies. --- docs/BuildPrerequisites.md | 2 +- scripts/check_deps.sh | 2 +- src/JackServerSettings.hpp | 9 +- src/PiLatencyMain.cpp | 2 +- src/PiPedalAlsa.cpp | 17 +- src/Storage.cpp | 16 +- vite/src/pipedal/JackServerSettings.tsx | 19 +- vite/src/pipedal/JackServerSettingsDialog.tsx | 416 +++++++++++------- 8 files changed, 300 insertions(+), 183 deletions(-) diff --git a/docs/BuildPrerequisites.md b/docs/BuildPrerequisites.md index cbc120e..6885730 100644 --- a/docs/BuildPrerequisites.md +++ b/docs/BuildPrerequisites.md @@ -27,7 +27,7 @@ Run the following commands to install dependent libraries required by the PiPeda authbind libavahi-client-dev libnm-dev libicu-dev \ libsdbus-c++-dev libzip-dev google-perftools \ libgoogle-perftools-dev \ - libpipewire-0.3-dev libwebsocketpp-dev libsdl2-dev + libpipewire-0.3-dev libwebsocketpp-dev After installing these packages, run the dependency checker to verify that everything required is present: diff --git a/scripts/check_deps.sh b/scripts/check_deps.sh index 85f249f..48a171e 100644 --- a/scripts/check_deps.sh +++ b/scripts/check_deps.sh @@ -8,7 +8,7 @@ packages=(\ authbind libavahi-client-dev libnm-dev libicu-dev \ libsdbus-c++-dev libzip-dev google-perftools \ libgoogle-perftools-dev libpipewire-0.3-dev \ - libwebsocketpp-dev libsdl2-dev + libwebsocketpp-dev ) missing=() diff --git a/src/JackServerSettings.hpp b/src/JackServerSettings.hpp index 285af30..a495f44 100644 --- a/src/JackServerSettings.hpp +++ b/src/JackServerSettings.hpp @@ -80,10 +80,17 @@ namespace pipedal bool IsValid() const { return valid_; } - // JackServerSettings(uint64_t sampleRate, uint32_t bufferSize, uint32_t numberOfBuffers) + // Legacy constructor used by tests prior to the addition of + // separate input and output ALSA devices. + // JackServerSettings(const std::string &device, + // uint64_t sampleRate, + // uint32_t bufferSize, + // uint32_t numberOfBuffers) // { // this->valid_ = true; // this->rebootRequired_ = true; + // this->alsaInputDevice_ = device; + // this->alsaOutputDevice_ = device; // this->sampleRate_ = sampleRate; // this->bufferSize_ = bufferSize; // this->numberOfBuffers_ = numberOfBuffers; diff --git a/src/PiLatencyMain.cpp b/src/PiLatencyMain.cpp index 71ed03f..e1616ac 100644 --- a/src/PiLatencyMain.cpp +++ b/src/PiLatencyMain.cpp @@ -95,7 +95,7 @@ void PrintHelp() pp << Indent(0) << "Examples\n\n"; pp << Indent(2) << "pipedal_latency_test --list\n\n"; - pp << Indent(2) << "pipedal_latency_test hw:M2\n"; + pp << Indent(2) << "pipedal_latency_test hw:M2 hw:M2\n"; pp << Indent(2) << "pipedal_latency_test hw:M2 hw:Device2\n\n"; } diff --git a/src/PiPedalAlsa.cpp b/src/PiPedalAlsa.cpp index 89eac9d..50d1a16 100644 --- a/src/PiPedalAlsa.cpp +++ b/src/PiPedalAlsa.cpp @@ -22,6 +22,7 @@ #include "alsa/asoundlib.h" #include "Lv2Log.hpp" #include +#include using namespace pipedal; @@ -191,12 +192,24 @@ std::vector PiPedalAlsaDevices::GetAlsaDevices() } snd_config_update_free_global(); + auto isFiltered = [](const AlsaDeviceInfo &d) { + std::string name = d.name_ + " " + d.longName_; + std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c){return std::tolower(c);}); + return name.find("hdmi") != std::string::npos || name.find("bcm2835") != std::string::npos; + }; + + std::vector filtered; + for (auto &d : result) + { + if (!isFiltered(d)) filtered.push_back(d); + } + Lv2Log::debug("GetAlsaDevices --"); - for (auto &device : result) + for (auto &device : filtered) { Lv2Log::debug(SS(" " << device.name_ << " " << device.longName_ << " " << device.cardId_)); } - return result; + return filtered; } static void AddMidiCardDevicesToList(snd_ctl_t *ctl, int card, int device, AlsaMidiDeviceInfo::Direction direction, std::vector *result) diff --git a/src/Storage.cpp b/src/Storage.cpp index f8bb149..7276a26 100644 --- a/src/Storage.cpp +++ b/src/Storage.cpp @@ -1573,14 +1573,6 @@ std::map Storage::GetFavorites() const { json_reader reader(f); reader.read(&result); - if (!result.GetLegacyAlsaDevice().empty() && - result.GetAlsaInputDevice().empty() && - result.GetAlsaOutputDevice().empty()) - { - result.SetAlsaInputDevice(result.GetLegacyAlsaDevice()); - result.SetAlsaOutputDevice(result.GetLegacyAlsaDevice()); - result.SetLegacyAlsaDevice(""); - } } return result; } @@ -1606,6 +1598,14 @@ pipedal::JackServerSettings Storage::GetJackServerSettings() { json_reader reader(f); reader.read(&result); + if (!result.GetLegacyAlsaDevice().empty() && + result.GetAlsaInputDevice().empty() && + result.GetAlsaOutputDevice().empty()) + { + result.SetAlsaInputDevice(result.GetLegacyAlsaDevice()); + result.SetAlsaOutputDevice(result.GetLegacyAlsaDevice()); + result.SetLegacyAlsaDevice(""); + } } #if JACK_HOST result.Initialize(); diff --git a/vite/src/pipedal/JackServerSettings.tsx b/vite/src/pipedal/JackServerSettings.tsx index 1263ad8..dd83675 100644 --- a/vite/src/pipedal/JackServerSettings.tsx +++ b/vite/src/pipedal/JackServerSettings.tsx @@ -56,13 +56,22 @@ export default class JackServerSettings { numberOfBuffers = 3; getSummaryText() { - if (this.valid) { - let inDev = this.alsaInputDevice.startsWith("hw:") ? this.alsaInputDevice .substring(3) : this.alsaInputDevice; - let outDev = this.alsaOutputDevice.startsWith("hw:") ? this.alsaOutputDevice.substring(3) : this.alsaOutputDevice; - return "In: "+inDev+" Out: "+outDev+" — Rate "+this.sampleRate+", "+this.bufferSize+"×"+this.numberOfBuffers; + if (!this.valid || !this.alsaInputDevice || !this.alsaOutputDevice) { + return "Not selected"; + } + + let inDev = this.alsaInputDevice.startsWith("hw:") + ? this.alsaInputDevice.substring(3) + : this.alsaInputDevice; + let outDev = this.alsaOutputDevice.startsWith("hw:") + ? this.alsaOutputDevice.substring(3) + : this.alsaOutputDevice; + + if (inDev === outDev) { + return inDev; } else { - return "Not configured"; + return inDev+" → "+outDev; } } diff --git a/vite/src/pipedal/JackServerSettingsDialog.tsx b/vite/src/pipedal/JackServerSettingsDialog.tsx index 18c19ff..ee8093c 100644 --- a/vite/src/pipedal/JackServerSettingsDialog.tsx +++ b/vite/src/pipedal/JackServerSettingsDialog.tsx @@ -22,6 +22,7 @@ import { Theme } from '@mui/material/styles'; +import React from 'react'; import WithStyles from './WithStyles'; import {createStyles} from './WithStyles'; @@ -44,10 +45,18 @@ import IconButtonEx from './IconButtonEx'; import RefreshIcon from '@mui/icons-material/Refresh'; import Checkbox from '@mui/material/Checkbox'; import FormControlLabel from '@mui/material/FormControlLabel'; +import JackHostStatus from './JackHostStatus'; import AlsaDeviceInfo from './AlsaDeviceInfo'; import ResizeResponsiveComponent from './ResizeResponsiveComponent'; +function filterDevices(devices: AlsaDeviceInfo[]): AlsaDeviceInfo[] { + return devices.filter(d => { + const name = (d.name + ' ' + d.longName).toLowerCase(); + return !(name.includes('hdmi') || name.includes('bcm2835')); + }); +} + function sortDevices(devices: AlsaDeviceInfo[]): AlsaDeviceInfo[] { function category(d: AlsaDeviceInfo): number { if (d.supportsCapture && d.supportsPlayback) return 0; @@ -73,14 +82,18 @@ interface BufferSetting { numberOfBuffers: number; }; -const INVALID_DEVICE_ID = "_invalid_"; +// empty string used when no valid device is selected - the default. +const INVALID_DEVICE_ID = ""; interface JackServerSettingsDialogState { latencyText: string; jackServerSettings: JackServerSettings; alsaDevices?: AlsaDeviceInfo[]; okEnabled: boolean; - useSameDevice: boolean; fullScreen: boolean; + compactWidth: boolean; + jackStatus?: JackHostStatus; + showDeviceWarning: boolean; + dontShowWarningAgain: boolean; } const styles = (theme: Theme) => @@ -92,6 +105,15 @@ const styles = (theme: Theme) => selectEmpty: { marginTop: theme.spacing(2), }, + inputLabel: { + backgroundColor: theme.palette.background.paper, + paddingLeft: 4, + paddingRight: 4 + }, + cpuStatusColor: { + color: theme.palette.text.secondary, + opacity: 0.7 + } }); export interface JackServerSettingsDialogProps extends WithStyles { open: boolean; @@ -101,13 +123,15 @@ export interface JackServerSettingsDialogProps extends WithStyles } function getLatencyText(settings?: JackServerSettings ): string { - if (!settings) - { + if (!settings) { return "\u00A0"; } - if (!settings.valid) return "\u00A0"; - let ms = settings.bufferSize * settings.numberOfBuffers / settings.sampleRate * 1000; + if (!settings.sampleRate || !settings.bufferSize || !settings.numberOfBuffers) { + return "\u00A0"; + } + + let ms = (settings.bufferSize * settings.numberOfBuffers) / settings.sampleRate * 1000; return ms.toFixed(1) + "ms"; } @@ -213,33 +237,57 @@ function getBestBuffers( return {bufferSize: bufferSize, numberOfBuffers:numberOfBuffers}; } } - // otherwise select a sensible starting value. + // otherwise select a sensible starting value. - // favored default: 64x3. - if (64*3 >= minSize && 64*3 <= maxSize) - { - return { bufferSize: 64, numberOfBuffers: 3}; + // Build candidate sizes with 32 first if present. + let validBufferSizes = getValidBufferSizesMultiple(inDevice, outDevice); + validBufferSizes.sort((a,b)=>a-b); + if (validBufferSizes.indexOf(32) !== -1) { + validBufferSizes = [32, ...validBufferSizes.filter(v => v !== 32)]; } - // if that isn't possible then minBufferSize/2 x 4. - bufferSize = minSize/2; + + function tryPick(count: number): BufferSetting | undefined { + for (let bs of validBufferSizes) { + const counts = getValidBufferCountsMultiple(bs, inDevice, outDevice); + if (counts.indexOf(count) !== -1) { + if (bs * count >= minSize && bs * count <= maxSize) { + return { bufferSize: bs, numberOfBuffers: count }; + } + } + } + return undefined; + } + + let result = tryPick(4); + if (!result) result = tryPick(3); + if (!result) result = tryPick(2); + if (result) return result; + + // Fallback to previous behaviour. + if (64*3 >= minSize && 64*3 <= maxSize) { + return { bufferSize: 64, numberOfBuffers: 3 }; + } + bufferSize = minSize/2; numberOfBuffers = 4; - // otherwise, minBufferSize/2 x 2. - if (bufferSize*numberOfBuffers > maxSize) - { + if (bufferSize * numberOfBuffers > maxSize) { numberOfBuffers = 2; } - return { bufferSize: bufferSize, numberOfBuffers: numberOfBuffers}; + return { bufferSize: bufferSize, numberOfBuffers: numberOfBuffers }; }; function isOkEnabled(jackServerSettings: JackServerSettings, alsaDevices?: AlsaDeviceInfo[]) { - if (!jackServerSettings.valid) return false; if (!alsaDevices) return false; - const inDevice = alsaDevices.find(d => d.id === jackServerSettings.alsaInputDevice && d.supportsCapture); + if (!jackServerSettings.alsaInputDevice || !jackServerSettings.alsaOutputDevice) return false; + + const inDevice = alsaDevices.find(d => d.id === jackServerSettings.alsaInputDevice && d.supportsCapture); const outDevice = alsaDevices.find(d => d.id === jackServerSettings.alsaOutputDevice && d.supportsPlayback); if (!inDevice || !outDevice) return false; + const sampleRates = intersectArrays(inDevice.sampleRates, outDevice.sampleRates); + if (sampleRates.indexOf(jackServerSettings.sampleRate) === -1) return false; + const deviceBufferSize = jackServerSettings.bufferSize * jackServerSettings.numberOfBuffers; const minSize = Math.max(inDevice.minBufferSize, outDevice.minBufferSize); const maxSize = Math.min(inDevice.maxBufferSize, outDevice.maxBufferSize); @@ -248,9 +296,6 @@ function isOkEnabled(jackServerSettings: JackServerSettings, alsaDevices?: AlsaD const validBufferCounts = getValidBufferCountsMultiple(jackServerSettings.bufferSize, inDevice, outDevice); if (validBufferCounts.indexOf(jackServerSettings.numberOfBuffers) === -1) return false; - const sampleRates = intersectArrays(inDevice.sampleRates, outDevice.sampleRates); - if (sampleRates.indexOf(jackServerSettings.sampleRate) === -1) return false; - return true; } @@ -265,18 +310,24 @@ const JackServerSettingsDialog = withStyles( this.model = PiPedalModelFactory.getInstance(); - const sameDevice = props.jackServerSettings.alsaInputDevice === props.jackServerSettings.alsaOutputDevice; - this.state = { - latencyText: getLatencyText(props.jackServerSettings), - jackServerSettings: props.jackServerSettings.clone(), // invalid, but not nullish - alsaDevices: undefined, - okEnabled: false, - useSameDevice: sameDevice, - fullScreen: this.getFullScreen() - }; + this.suppressDeviceWarning = localStorage.getItem("suppressSeparateDeviceWarning") === "1"; + this.state = { + latencyText: getLatencyText(props.jackServerSettings), + jackServerSettings: props.jackServerSettings.clone(), // invalid, but not nullish + alsaDevices: undefined, + okEnabled: false, + fullScreen: this.getFullScreen(), + compactWidth: document.documentElement.clientWidth < 600, + jackStatus: undefined, + showDeviceWarning: false, + dontShowWarningAgain: false + }; } mounted: boolean = false; + statusTimer?: number = undefined; + suppressDeviceWarning: boolean; + getFullScreen() { return document.documentElement.clientWidth < 420 || document.documentElement.clientHeight < 700; @@ -285,8 +336,9 @@ const JackServerSettingsDialog = withStyles( onWindowSizeChanged(width: number, height: number): void { super.onWindowSizeChanged(width, height); const fullScreen = this.getFullScreen(); - if (fullScreen !== this.state.fullScreen) { - this.setState({ fullScreen }); + const compactWidth = width < 600; + if (fullScreen !== this.state.fullScreen || compactWidth !== this.state.compactWidth) { + this.setState({ fullScreen, compactWidth }); } } @@ -295,15 +347,16 @@ const JackServerSettingsDialog = withStyles( .then((devices) => { if (this.mounted) { if (this.props.open) { - let settings = this.applyAlsaDevices(this.state.jackServerSettings, devices); + let f = filterDevices(devices); + let settings = this.applyAlsaDevices(this.state.jackServerSettings, f); this.setState({ - alsaDevices: devices, + alsaDevices: f, jackServerSettings: settings, latencyText: getLatencyText(settings), - okEnabled: isOkEnabled(settings,devices) + okEnabled: isOkEnabled(settings, f) }); } else { - this.setState({ alsaDevices: devices }); + this.setState({ alsaDevices: filterDevices(devices) }); } } }) @@ -312,7 +365,28 @@ const JackServerSettingsDialog = withStyles( }); } - applyAlsaDevices(jackServerSettings: JackServerSettings, alsaDevices?: AlsaDeviceInfo[], useSameDevice?: boolean) { + tickStatus() { + this.model.getJackStatus() + .then(status => { + if (this.mounted) { + this.setState({ jackStatus: status }); + } + }) + .catch(() => { }); + } + startStatusTimer() { + if (this.statusTimer) return; + this.tickStatus(); + this.statusTimer = window.setInterval(() => this.tickStatus(), 1000); + } + stopStatusTimer() { + if (this.statusTimer) { + clearInterval(this.statusTimer); + this.statusTimer = undefined; + } + } + + applyAlsaDevices(jackServerSettings: JackServerSettings, alsaDevices?: AlsaDeviceInfo[]) { let result = jackServerSettings.clone(); if (!alsaDevices || alsaDevices.length === 0) { result.valid = false; @@ -321,47 +395,33 @@ const JackServerSettingsDialog = withStyles( return result; } - let inDevice = alsaDevices.find(d => d.id === result.alsaInputDevice && d.supportsCapture); - let outDevice = alsaDevices.find(d => d.id === result.alsaOutputDevice && d.supportsPlayback); + let devices = filterDevices(alsaDevices); + let inDevice = devices.find(d => d.id === result.alsaInputDevice && d.supportsCapture); + let outDevice = devices.find(d => d.id === result.alsaOutputDevice && d.supportsPlayback); - const sameDevice = useSameDevice !== undefined ? useSameDevice : this.state.useSameDevice; - if (sameDevice) { - if (!inDevice || !inDevice.supportsPlayback) { - const both = alsaDevices.find(d => d.supportsCapture && d.supportsPlayback); - if (both) { - result.alsaInputDevice = both.id; - result.alsaOutputDevice = both.id; - inDevice = both; - outDevice = both; - } else { - result.valid = false; - result.alsaInputDevice = INVALID_DEVICE_ID; - result.alsaOutputDevice = INVALID_DEVICE_ID; - return result; - } - } else { - result.alsaOutputDevice = result.alsaInputDevice; - outDevice = inDevice; - } - } else { - if (!inDevice) { - result.valid = false; - result.alsaInputDevice = INVALID_DEVICE_ID; - } - if (!outDevice) { - result.valid = false; - result.alsaOutputDevice = INVALID_DEVICE_ID; - } - if (!inDevice || !outDevice) { - return result; + if (!inDevice || !outDevice) { + const capture = devices.filter(d => d.supportsCapture); + const playback = devices.filter(d => d.supportsPlayback); + if (capture.length === 1 && playback.length === 1) { + inDevice = capture[0]; + outDevice = playback[0]; + result.alsaInputDevice = inDevice.id; + result.alsaOutputDevice = outDevice.id; + } else if (capture.length === 1 && capture[0].supportsPlayback && playback.length === 0) { + inDevice = capture[0]; + outDevice = capture[0]; + result.alsaInputDevice = inDevice.id; + result.alsaOutputDevice = outDevice.id; } } if (!inDevice || !outDevice) { result.valid = false; + if (!inDevice) result.alsaInputDevice = INVALID_DEVICE_ID; + if (!outDevice) result.alsaOutputDevice = INVALID_DEVICE_ID; return result; } - + let sampleRates = intersectArrays(inDevice.sampleRates, outDevice.sampleRates); if (sampleRates.length !== 0 && sampleRates.indexOf(result.sampleRate) === -1) { let bestSr = sampleRates[0]; @@ -376,8 +436,6 @@ const JackServerSettingsDialog = withStyles( let bestBuffers = getBestBuffers(inDevice, outDevice, result.bufferSize, result.numberOfBuffers); result.bufferSize = bestBuffers.bufferSize; result.numberOfBuffers = bestBuffers.numberOfBuffers; - - result.valid = !!result.alsaInputDevice && !!result.alsaOutputDevice; return result; } @@ -386,27 +444,31 @@ const JackServerSettingsDialog = withStyles( this.mounted = true; if (this.props.open) { this.requestAlsaInfo(); + this.startStatusTimer(); } } componentDidUpdate(oldProps: JackServerSettingsDialogProps) { if ((this.props.open && !oldProps.open) && this.mounted) { let settings = this.applyAlsaDevices(this.props.jackServerSettings.clone(), this.state.alsaDevices); - this.setState({ + this.setState({ jackServerSettings: settings, latencyText: getLatencyText(settings), - okEnabled: isOkEnabled(settings,this.state.alsaDevices), - useSameDevice: settings.alsaInputDevice === settings.alsaOutputDevice + okEnabled: isOkEnabled(settings,this.state.alsaDevices) }); if (!this.state.alsaDevices) { this.requestAlsaInfo(); } + this.startStatusTimer(); + } else if (!this.props.open && oldProps.open) { + this.stopStatusTimer(); } } componentWillUnmount() { - super.componentWillUnmount(); + super.componentWillUnmount(); this.mounted = false; + this.stopStatusTimer(); } getSelectedDevice(deviceId: string): AlsaDeviceInfo | undefined { @@ -427,6 +489,7 @@ const JackServerSettingsDialog = withStyles( if (!inDev && !outDev) return; let settings = this.state.jackServerSettings.clone(); settings.sampleRate = rate; + settings.valid = false; this.setState({ jackServerSettings: settings, @@ -442,7 +505,8 @@ const JackServerSettingsDialog = withStyles( if (!inDev && !outDev) return; let settings = this.state.jackServerSettings.clone(); settings.bufferSize = size; - let bestBufferSetting = getBestBuffers(inDev,outDev,settings.bufferSize,settings.numberOfBuffers,true); + settings.valid = false; + let bestBufferSetting = getBestBuffers(inDev,outDev,settings.bufferSize,settings.numberOfBuffers,true); settings.bufferSize = bestBufferSetting.bufferSize; settings.numberOfBuffers = bestBufferSetting.numberOfBuffers; @@ -460,6 +524,7 @@ const JackServerSettingsDialog = withStyles( if (!inDev && !outDev) return; let settings = this.state.jackServerSettings.clone(); settings.numberOfBuffers = bufferCount; + settings.valid = false; this.setState({ jackServerSettings: settings, @@ -471,16 +536,46 @@ const JackServerSettingsDialog = withStyles( handleApply() { if (this.state.okEnabled) { - this.props.onApply(this.state.jackServerSettings.clone()); + let s = this.state.jackServerSettings.clone(); + s.valid = false; + this.model.setJackServerSettings(s); } }; + handleOk() { + if (!this.state.okEnabled) return; + if (this.state.jackServerSettings.alsaInputDevice !== this.state.jackServerSettings.alsaOutputDevice + && !this.suppressDeviceWarning) { + this.setState({ showDeviceWarning: true }); + return; + } + let s = this.state.jackServerSettings.clone(); + s.valid = true; + this.props.onApply(s); + }; + + handleWarningProceed() { + if (this.state.dontShowWarningAgain) { + localStorage.setItem("suppressSeparateDeviceWarning", "1"); + this.suppressDeviceWarning = true; + } + this.setState({ showDeviceWarning: false, dontShowWarningAgain: false }, () => { + let s = this.state.jackServerSettings.clone(); + s.valid = true; + this.props.onApply(s); + }); + } + handleWarningCancel() { + this.setState({ showDeviceWarning: false, dontShowWarningAgain: false }); + } + + handleWarningCheck(e: any, checked: boolean) { + this.setState({ dontShowWarningAgain: checked }); + } handleInputDeviceChanged(e: any) { const d = e.target.value as string; let s = this.state.jackServerSettings.clone(); s.alsaInputDevice = d; - if (this.state.useSameDevice) { - s.alsaOutputDevice = d; - } + s.valid = false; let settings = this.applyAlsaDevices(s, this.state.alsaDevices); this.setState({ jackServerSettings: settings, @@ -493,9 +588,7 @@ const JackServerSettingsDialog = withStyles( const d = e.target.value as string; let s = this.state.jackServerSettings.clone(); s.alsaOutputDevice = d; - if (this.state.useSameDevice) { - s.alsaInputDevice = d; - } + s.valid = false; let settings = this.applyAlsaDevices(s, this.state.alsaDevices); this.setState({ jackServerSettings: settings, @@ -503,28 +596,8 @@ const JackServerSettingsDialog = withStyles( okEnabled: isOkEnabled(settings, this.state.alsaDevices) }); } - handleUseSameDeviceChanged(e: any, checked: boolean) { - let s = this.state.jackServerSettings.clone(); - const useSame = checked; - if (!useSame) { - // clear selections when switching to separate devices - s.alsaInputDevice = INVALID_DEVICE_ID; - s.alsaOutputDevice = INVALID_DEVICE_ID; - s.valid = false; - } - - let settings = this.applyAlsaDevices(s, this.state.alsaDevices, useSame); - this.setState({ - useSameDevice: useSame, - jackServerSettings: settings, - latencyText: getLatencyText(settings), - okEnabled: isOkEnabled(settings, this.state.alsaDevices) - }, () => { - this.requestAlsaInfo(); - }); - } - render() { + render() { const classes = withStyles.getClasses(this.props); const { onClose, open } = this.props; @@ -537,14 +610,9 @@ const JackServerSettingsDialog = withStyles( let selectedInputDevice = this.getSelectedDevice(this.state.jackServerSettings.alsaInputDevice); let selectedOutputDevice = this.getSelectedDevice(this.state.jackServerSettings.alsaOutputDevice); - if (this.state.useSameDevice) { - selectedOutputDevice = selectedInputDevice; - } - const devicesSelected = this.state.useSameDevice ? - !!selectedInputDevice : - (selectedInputDevice && selectedOutputDevice); - + const devicesSelected = (selectedInputDevice && selectedOutputDevice); + let bufferSizes: number[] = devicesSelected ? getValidBufferSizesMultiple(selectedInputDevice, selectedOutputDevice) : []; let bufferCounts = devicesSelected ? @@ -553,67 +621,61 @@ const JackServerSettingsDialog = withStyles( let bufferCountDisabled = !devicesSelected; let sampleRates = devicesSelected && selectedInputDevice && selectedOutputDevice ? intersectArrays(selectedInputDevice.sampleRates, selectedOutputDevice.sampleRates) - : (this.state.useSameDevice && selectedInputDevice ? selectedInputDevice.sampleRates : []); + : []; let sampleRateOptions = sampleRates.length === 0 && this.state.jackServerSettings.sampleRate ? [this.state.jackServerSettings.sampleRate] : sampleRates; return ( + <> { - this.handleApply(); + this.handleOk(); }} - fullScreen={this.state.fullScreen} + fullScreen={this.state.fullScreen} > -
+
+
+
{/* Audio Input Device */} - - {this.state.useSameDevice ? "Device" : "Input Device"} - this.handleInputDeviceChanged(e)} + disabled={!this.state.alsaDevices || this.state.alsaDevices.length === 0} + style={{ width: 220 }} + > + {sortedDevices.filter(d => d.supportsCapture).map(d => ( + {d.name} )) || Loading...} - - + + {/* Audio Output Device */} - - Output Device - this.handleOutputDeviceChanged(e)} + disabled={!this.state.alsaDevices || this.state.alsaDevices.length === 0} + style={{ width: 220 }} + > {sortedDevices.filter(d => d.supportsPlayback).map(d => ( {d.name} )) || Loading...} - - this.requestAlsaInfo()} aria-label="refresh-audio-devices"> - - - this.handleUseSameDeviceChanged(e, c)} - /> - } - label="Use same device for input/output" - style={{ marginLeft: 16 }} - /> + +
+ this.requestAlsaInfo()} aria-label="refresh-audio-devices"> + + +
- - Sample rate + + Sample rate
- - Buffer size + + Buffer size - - Buffers + + Buffers