diff --git a/src/AlsaDriverTest.cpp b/src/AlsaDriverTest.cpp index 29b1f2c..2508570 100644 --- a/src/AlsaDriverTest.cpp +++ b/src/AlsaDriverTest.cpp @@ -64,7 +64,7 @@ public: AlsaFormatEncodeDecodeTest(this); - JackServerSettings serverSettings("hw:M2",48000,32,3); + JackServerSettings serverSettings("hw:M2","hw:M2",48000,32,3); JackConfiguration jackConfiguration; if (useJack) diff --git a/src/JackServerSettings.hpp b/src/JackServerSettings.hpp index 5fd9aad..7983bc8 100644 --- a/src/JackServerSettings.hpp +++ b/src/JackServerSettings.hpp @@ -41,14 +41,15 @@ namespace pipedal void SetAlsaInputDevice(const std::string &d){ alsaInputDevice_ = d; } void SetAlsaOutputDevice(const std::string &d){ alsaOutputDevice_ = d; } JackServerSettings(); - JackServerSettings( - const std::string alsaInputDevice, + JackServerSettings( + const std::string &alsaInputDevice, + const std::string &alsaOutputDevice, uint64_t sampleRate, uint32_t bufferSize, uint32_t numberOfBuffers) : valid_(true), alsaInputDevice_(alsaInputDevice), - alsaOutputDevice_(alsaInputDevice), // default same + alsaOutputDevice_(alsaOutputDevice), sampleRate_(sampleRate), bufferSize_(bufferSize), numberOfBuffers_(numberOfBuffers), diff --git a/src/PiLatencyMain.cpp b/src/PiLatencyMain.cpp index c7e7147..71ed03f 100644 --- a/src/PiLatencyMain.cpp +++ b/src/PiLatencyMain.cpp @@ -57,9 +57,9 @@ void PrintHelp() pp << "Copyright (c) 2022 Robin Davies\n"; pp << "\n"; pp << Indent(0) << "Syntax\n\n"; - pp << Indent(2) << "pipedal_latency_test [] \n\n"; - pp << "where is the name of an ALSA device. Typically this should be the name of a hardware " - "device (a device name starting with 'hw:').\n\n"; + pp << Indent(2) << "pipedal_latency_test [] []\n\n"; + pp << "where is the name of an ALSA capture device and is the name of a playback device. " + "If is omitted, the input device will be used for both capture and playback. Typically the device names start with 'hw:'.\n\n"; pp << Indent(0) << "Options\n\n"; pp << Indent(15); @@ -95,7 +95,8 @@ 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\n"; + pp << Indent(2) << "pipedal_latency_test hw:M2\n"; + pp << Indent(2) << "pipedal_latency_test hw:M2 hw:Device2\n\n"; } void ListDevices() @@ -138,7 +139,8 @@ public: private: AudioDriver *audioDriver = nullptr; - const std::string &deviceId; + const std::string &inputDeviceId; + const std::string &outputDeviceId; ChannelsT inputChannels; ChannelsT outputChannels; uint32_t sampleRate; @@ -147,11 +149,13 @@ private: public: AlsaTester( - const std::string &deviceId, + const std::string &inputDeviceId, + const std::string &outputDeviceId, const ChannelsT &inputChannels, const ChannelsT &outputChannels, uint32_t sampleRate, int bufferSize, int buffers) - : deviceId(deviceId), + : inputDeviceId(inputDeviceId), + outputDeviceId(outputDeviceId), sampleRate(sampleRate), inputChannels(inputChannels), outputChannels(outputChannels), @@ -186,7 +190,7 @@ public: TestResult result; try { - JackServerSettings serverSettings(deviceId, sampleRate, bufferSize, buffers); + JackServerSettings serverSettings(inputDeviceId, outputDeviceId, sampleRate, bufferSize, buffers); JackConfiguration jackConfiguration; jackConfiguration.AlsaInitialize(serverSettings); @@ -403,12 +407,13 @@ public: }; TestResult RunLatencyTest( - const std::string deviceId, + const std::string inputDeviceId, + const std::string outputDeviceId, const ChannelsT &inputChannels, const ChannelsT &outputChannels, uint32_t sampleRate, int bufferSize, int buffers) { - AlsaTester tester(deviceId, inputChannels, outputChannels, sampleRate, bufferSize, buffers); + AlsaTester tester(inputDeviceId, outputDeviceId, inputChannels, outputChannels, sampleRate, bufferSize, buffers); return tester.Test(); } @@ -428,13 +433,14 @@ static std::string overheadDisplay(float value) } void RunLatencyTest( - const std::string &deviceId, + const std::string &inputDeviceId, + const std::string &outputDeviceId, const ChannelsT &inputChannels, const ChannelsT &outputChannels, uint32_t sampleRate) { PrettyPrinter pp; - pp << "Device: " << deviceId << " Rate: " << sampleRate << "\n\n"; + pp << "Input: " << inputDeviceId << " Output: " << outputDeviceId << " Rate: " << sampleRate << "\n\n"; const int SIZE_COLUMN_WIDTH = 8; const int BUFFERS_COLUMN_WIDTH = 20; @@ -461,7 +467,7 @@ void RunLatencyTest( for (auto bufferCount : bufferCounts) { - auto result = RunLatencyTest(deviceId, inputChannels,outputChannels, sampleRate, bufferSize, bufferCount); + auto result = RunLatencyTest(inputDeviceId, outputDeviceId, inputChannels,outputChannels, sampleRate, bufferSize, bufferCount); pp.Column(column); column += BUFFERS_COLUMN_WIDTH; @@ -556,11 +562,14 @@ std: { ListDevices(); } - else if (parser.Arguments().size() == 1) + else if (parser.Arguments().size() >= 1 && parser.Arguments().size() <= 2) { inputChannels = ParseChannels(strInputChannels); - outputChannels = ParseChannels(strInputChannels); - RunLatencyTest(parser.Arguments()[0], inputChannels, outputChannels, sampleRate); + outputChannels = ParseChannels(strOutputChannels); + + std::string inDev = parser.Arguments()[0]; + std::string outDev = parser.Arguments().size() == 2 ? parser.Arguments()[1] : inDev; + RunLatencyTest(inDev, outDev, inputChannels, outputChannels, sampleRate); } else { diff --git a/src/PiPedalAlsa.cpp b/src/PiPedalAlsa.cpp index 59a2561..89eac9d 100644 --- a/src/PiPedalAlsa.cpp +++ b/src/PiPedalAlsa.cpp @@ -119,21 +119,10 @@ std::vector PiPedalAlsaDevices::GetAlsaDevices() int dir; err = snd_pcm_hw_params_get_rate_min(params, &minRate, &dir); - if (err != 0) + if (err == 0) { - Lv2Log::warning(SS("Failed to get minimum sample rate for device '" << info.name_ << "'. Using default 48000.")); - info.sampleRates_.push_back(48000); - err = 0; // continue using fallback rate - } - else - { - int err2 = snd_pcm_hw_params_get_rate_max(params, &maxRate, &dir); - if (err2 != 0) - { - Lv2Log::warning(SS("Failed to get maximum sample rate for device '" << info.name_ << "'. Using default 48000.")); - info.sampleRates_.push_back(48000); - } - else + int err2 = snd_pcm_hw_params_get_rate_max(params, &maxRate, &dir); + if (err2 == 0) { for (size_t i = 0; i < sizeof(RATES) / sizeof(RATES[0]); ++i) { @@ -142,14 +131,18 @@ std::vector PiPedalAlsaDevices::GetAlsaDevices() { info.sampleRates_.push_back(rate); } - } - if (info.sampleRates_.empty()) - { - Lv2Log::warning(SS("No supported sample rates for device '" << info.name_ << "'. Using default 48000.")); - info.sampleRates_.push_back(48000); - } + } + } + else + { + Lv2Log::warning(SS("Failed to get maximum sample rate for device '" << info.name_ << "'.")); } } + else + { + Lv2Log::warning(SS("Failed to get minimum sample rate for device '" << info.name_ << "'.")); + err = 0; // continue using fallback rate for other parameters + } err = snd_pcm_hw_params_get_buffer_size_min(params, &minBufferSize); if (err == 0) diff --git a/vite/src/pipedal/JackServerSettingsDialog.tsx b/vite/src/pipedal/JackServerSettingsDialog.tsx index b1c66c3..36a6373 100644 --- a/vite/src/pipedal/JackServerSettingsDialog.tsx +++ b/vite/src/pipedal/JackServerSettingsDialog.tsx @@ -47,6 +47,22 @@ import FormControlLabel from '@mui/material/FormControlLabel'; import AlsaDeviceInfo from './AlsaDeviceInfo'; +function sortDevices(devices: AlsaDeviceInfo[]): AlsaDeviceInfo[] { + function category(d: AlsaDeviceInfo): number { + if (d.supportsCapture && d.supportsPlayback) return 0; + if (d.longName.toLowerCase().includes("usb")) return 1; + return 2; + } + let copy = [...devices]; + copy.sort((a,b) => { + let ca = category(a); + let cb = category(b); + if (ca !== cb) return ca - cb; + return a.name.localeCompare(b.name); + }); + return copy; +} + const MIN_BUFFER_SIZE = 16; const MAX_BUFFER_SIZE = 2048; @@ -263,7 +279,7 @@ const JackServerSettingsDialog = withStyles( .then((devices) => { if (this.mounted) { if (this.props.open) { - let settings = this.applyAlsaDevices(this.props.jackServerSettings, devices); + let settings = this.applyAlsaDevices(this.state.jackServerSettings, devices); this.setState({ alsaDevices: devices, jackServerSettings: settings, @@ -292,21 +308,9 @@ const JackServerSettingsDialog = withStyles( let inDevice = alsaDevices.find(d => d.id === result.alsaInputDevice && d.supportsCapture); let outDevice = alsaDevices.find(d => d.id === result.alsaOutputDevice && d.supportsPlayback); - if (!inDevice) { - inDevice = alsaDevices.find(d => d.supportsCapture); - if (inDevice) { - result.alsaInputDevice = inDevice.id; - } - } - if (!outDevice) { - outDevice = alsaDevices.find(d => d.supportsPlayback); - if (outDevice) { - result.alsaOutputDevice = outDevice.id; - } - } - const sameDevice = useSameDevice !== undefined ? useSameDevice : this.state.useSameDevice; - if (sameDevice) { + 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) { @@ -323,6 +327,18 @@ const JackServerSettingsDialog = withStyles( } 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; } } @@ -330,18 +346,17 @@ const JackServerSettingsDialog = withStyles( result.valid = false; return result; } - - if (result.sampleRate === 0) result.sampleRate = 48000; - let sampleRates = intersectArrays(inDevice.sampleRates, outDevice.sampleRates); - if (sampleRates.length === 0) sampleRates = inDevice.sampleRates; - if (sampleRates.length === 0) sampleRates = [result.sampleRate || 48000]; - let bestSr = sampleRates[0]; - let bestErr = 1e36; - for (let sr of sampleRates) { - let err = (sr - result.sampleRate) * (sr - result.sampleRate); - if (err < bestErr) { bestErr = err; bestSr = sr; } + + let sampleRates = intersectArrays(inDevice.sampleRates, outDevice.sampleRates); + if (sampleRates.length !== 0 && sampleRates.indexOf(result.sampleRate) === -1) { + let bestSr = sampleRates[0]; + let bestErr = 1e36; + for (let sr of sampleRates) { + let err = (sr - result.sampleRate) * (sr - result.sampleRate); + if (err < bestErr) { bestErr = err; bestSr = sr; } + } + result.sampleRate = bestSr; } - result.sampleRate = bestSr; let bestBuffers = getBestBuffers(inDevice, outDevice, result.bufferSize, result.numberOfBuffers); result.bufferSize = bestBuffers.bufferSize; @@ -507,7 +522,9 @@ const JackServerSettingsDialog = withStyles( onClose(); }; - let selectedInputDevice = this.getSelectedDevice(this.state.jackServerSettings.alsaInputDevice); + const sortedDevices = sortDevices(this.state.alsaDevices ?? []); + + let selectedInputDevice = this.getSelectedDevice(this.state.jackServerSettings.alsaInputDevice); let selectedOutputDevice = this.getSelectedDevice(this.state.jackServerSettings.alsaOutputDevice); if (this.state.useSameDevice) { selectedOutputDevice = selectedInputDevice; @@ -520,7 +537,7 @@ const JackServerSettingsDialog = withStyles( let sampleRates = selectedInputDevice && selectedOutputDevice ? intersectArrays(selectedInputDevice.sampleRates, selectedOutputDevice.sampleRates) : (selectedInputDevice ? selectedInputDevice.sampleRates : (selectedOutputDevice ? selectedOutputDevice.sampleRates : [])); - let sampleRateOptions = sampleRates.length === 0 ? [this.state.jackServerSettings.sampleRate || 48000] : sampleRates; + let sampleRateOptions = sampleRates.length === 0 && this.state.jackServerSettings.sampleRate ? [this.state.jackServerSettings.sampleRate] : sampleRates; return ( - {this.state.alsaDevices?.filter(d => d.supportsCapture && (!this.state.useSameDevice || d.supportsPlayback)).map(d => ( + {sortedDevices.filter(d => d.supportsCapture && (!this.state.useSameDevice || d.supportsPlayback)).map(d => ( {d.name} )) || Loading...} @@ -558,7 +575,7 @@ const JackServerSettingsDialog = withStyles( disabled={this.state.useSameDevice || !this.state.alsaDevices || this.state.alsaDevices.length === 0} style={{ width: 220 }} > - {this.state.alsaDevices?.filter(d => d.supportsPlayback).map(d => ( + {sortedDevices.filter(d => d.supportsPlayback).map(d => ( {d.name} )) || Loading...}