Devices with only I or O Fix

Added new fields to track ALSA device capabilities, letting the application know whether a device supports capture or playback

Updated serialization to include the new flags in the JSON data exchanged with the UI

Modified the dummy driver so test devices always indicate capture and playback support

Adjusted the TypeScript model and dialog to filter device choices based on the new flags, ensuring only valid options appear in selectors
This commit is contained in:
ExtremesecrecyOne
2025-07-23 12:18:19 -07:00
parent fbd925caca
commit 6ac8968583
5 changed files with 55 additions and 30 deletions
+2
View File
@@ -517,6 +517,8 @@ namespace pipedal
result.sampleRates_.push_back(48000);
result.minBufferSize_ = 16;
result.maxBufferSize_ = 1024;
result.supportsCapture_ = true;
result.supportsPlayback_ = true;
return result;
}
+24 -16
View File
@@ -97,20 +97,16 @@ std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAlsaDevices()
info.name_ = snd_ctl_card_info_get_name(alsaInfo);
info.longName_ = snd_ctl_card_info_get_longname(alsaInfo);
snd_pcm_t *hDevice = nullptr;
snd_pcm_t *captureDevice = nullptr;
snd_pcm_t *playbackDevice = nullptr;
bool captureOk = snd_pcm_open(&captureDevice, cardId.c_str(), SND_PCM_STREAM_CAPTURE, 0) == 0;
bool playbackOk = snd_pcm_open(&playbackDevice, cardId.c_str(), SND_PCM_STREAM_PLAYBACK, 0) == 0;
info.supportsCapture_ = captureOk;
info.supportsPlayback_ = playbackOk;
// must support capture AND playback
err = snd_pcm_open(&hDevice, cardId.c_str(), SND_PCM_STREAM_CAPTURE, 0);
if (err == 0)
{
snd_pcm_close(hDevice);
}
if (err == 0)
{
err = snd_pcm_open(&hDevice, cardId.c_str(), SND_PCM_STREAM_PLAYBACK, 0);
}
if (err == 0)
if (captureOk || playbackOk)
{
snd_pcm_t *hDevice = captureOk ? captureDevice : playbackDevice;
snd_pcm_hw_params_t *params = nullptr;
err = snd_pcm_hw_params_malloc(&params);
if (err == 0)
@@ -151,15 +147,25 @@ std::vector<AlsaDeviceInfo> PiPedalAlsaDevices::GetAlsaDevices()
info.minBufferSize_ = (uint32_t)minBufferSize;
info.maxBufferSize_ = (uint32_t)maxBufferSize;
cacheDevice(info.name_, info);
result.push_back(info);
}
}
}
if (params != nullptr)
snd_pcm_hw_params_free(params);
snd_pcm_close(hDevice);
}
if (captureOk)
snd_pcm_close(captureDevice);
if (playbackOk && playbackDevice != captureDevice)
snd_pcm_close(playbackDevice);
if (err == 0)
{
cacheDevice(info.name_, info);
result.push_back(info);
}
else if (getCachedDevice(info.name_, &info))
{
result.push_back(info);
}
}
else
{
if (getCachedDevice(info.name_, &info))
@@ -480,6 +486,8 @@ JSON_MAP_REFERENCE(AlsaDeviceInfo, longName)
JSON_MAP_REFERENCE(AlsaDeviceInfo, sampleRates)
JSON_MAP_REFERENCE(AlsaDeviceInfo, minBufferSize)
JSON_MAP_REFERENCE(AlsaDeviceInfo, maxBufferSize)
JSON_MAP_REFERENCE(AlsaDeviceInfo, supportsCapture)
JSON_MAP_REFERENCE(AlsaDeviceInfo, supportsPlayback)
JSON_MAP_END()
JSON_MAP_BEGIN(AlsaMidiDeviceInfo)
+2
View File
@@ -31,6 +31,8 @@ namespace pipedal {
std::string longName_;
std::vector<uint32_t> sampleRates_;
uint32_t minBufferSize_ = 0,maxBufferSize_ = 0;
bool supportsCapture_ = false;
bool supportsPlayback_ = false;
bool isDummyDevice() const {
return id_.starts_with("dummy:");
+5 -1
View File
@@ -9,6 +9,8 @@ export default class AlsaDeviceInfo {
this.sampleRates = input.sampleRates as number[];
this.minBufferSize = input.minBufferSize;
this.maxBufferSize = input.maxBufferSize;
this.supportsCapture = input.supportsCapture ? true : false;
this.supportsPlayback = input.supportsPlayback ? true : false;
return this;
}
static deserialize_array(input: any): AlsaDeviceInfo[]
@@ -62,5 +64,7 @@ export default class AlsaDeviceInfo {
longName: string = "";
sampleRates: number[] = [];
minBufferSize: number = 0;
maxBufferSize: number = 0;
maxBufferSize: number = 0;
supportsCapture: boolean = false;
supportsPlayback: boolean = false;
};
+22 -13
View File
@@ -220,8 +220,8 @@ function isOkEnabled(jackServerSettings: JackServerSettings, alsaDevices?: AlsaD
{
if (!jackServerSettings.valid) return false;
if (!alsaDevices) return false;
const inDevice = alsaDevices.find(d => d.id === jackServerSettings.alsaInputDevice);
const outDevice = alsaDevices.find(d => d.id === jackServerSettings.alsaOutputDevice);
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 deviceBufferSize = jackServerSettings.bufferSize * jackServerSettings.numberOfBuffers;
@@ -289,15 +289,24 @@ const JackServerSettingsDialog = withStyles(
return result;
}
let inDevice = alsaDevices.find(d => d.id === result.alsaInputDevice);
let outDevice = alsaDevices.find(d => d.id === result.alsaOutputDevice);
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[0];
result.alsaInputDevice = inDevice.id;
inDevice = alsaDevices.find(d => d.supportsCapture);
if (inDevice) {
result.alsaInputDevice = inDevice.id;
}
}
if (!outDevice) {
outDevice = alsaDevices[0];
result.alsaOutputDevice = outDevice.id;
outDevice = alsaDevices.find(d => d.supportsPlayback);
if (outDevice) {
result.alsaOutputDevice = outDevice.id;
}
}
if (!inDevice || !outDevice) {
result.valid = false;
return result;
}
if (result.sampleRate === 0) result.sampleRate = 48000;
@@ -467,9 +476,9 @@ const JackServerSettingsDialog = withStyles(
disabled={!this.state.alsaDevices || this.state.alsaDevices.length === 0}
style={{ width: 220 }}
>
{this.state.alsaDevices?.map(d => (
<MenuItem key={d.id} value={d.id}>{d.name}</MenuItem>
)) || <MenuItem value="" disabled>Loading...</MenuItem>}
{this.state.alsaDevices?.filter(d => d.supportsCapture).map(d => (
<MenuItem key={d.id} value={d.id}>{d.name}</MenuItem>
)) || <MenuItem value="" disabled>Loading...</MenuItem>}
</Select>
</FormControl>
@@ -483,8 +492,8 @@ const JackServerSettingsDialog = withStyles(
disabled={!this.state.alsaDevices || this.state.alsaDevices.length === 0}
style={{ width: 220 }}
>
{this.state.alsaDevices?.map(d => (
<MenuItem key={d.id} value={d.id}>{d.name}</MenuItem>
{this.state.alsaDevices?.filter(d => d.supportsPlayback).map(d => (
<MenuItem key={d.id} value={d.id}>{d.name}</MenuItem>
)) || <MenuItem value="" disabled>Loading...</MenuItem>}
</Select>
</FormControl>