Separate IO

Initial separation of input and output devices. Assisted by our super friend ChatGpt.
This commit is contained in:
ExtremesecrecyOne
2025-07-22 16:20:30 -07:00
parent 884eb31619
commit d8043d2041
9 changed files with 109 additions and 63 deletions
+11 -8
View File
@@ -1223,7 +1223,9 @@ namespace pipedal
{ {
int err; int err;
alsa_device_name = jackServerSettings.GetAlsaInputDevice(); std::string inputName = jackServerSettings.GetAlsaInputDevice();
std::string outputName = jackServerSettings.GetAlsaOutputDevice();
this->numberOfBuffers = jackServerSettings.GetNumberOfBuffers(); this->numberOfBuffers = jackServerSettings.GetNumberOfBuffers();
this->bufferSize = jackServerSettings.GetBufferSize(); this->bufferSize = jackServerSettings.GetBufferSize();
@@ -1232,7 +1234,7 @@ namespace pipedal
try try
{ {
err = snd_pcm_open(&playbackHandle, alsa_device_name.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); err = snd_pcm_open(&playbackHandle, outputName.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
if (err < 0) if (err < 0)
{ {
switch (errno) switch (errno)
@@ -1267,7 +1269,7 @@ namespace pipedal
snd_pcm_nonblock(playbackHandle, 0); snd_pcm_nonblock(playbackHandle, 0);
} }
err = snd_pcm_open(&captureHandle, alsa_device_name.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); err = snd_pcm_open(&captureHandle, inputName.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
if (err < 0) if (err < 0)
{ {
@@ -1934,7 +1936,8 @@ namespace pipedal
} }
}}; }};
std::string alsaDeviceName = jackServerSettings.GetAlsaInputDevice(); std::string inDev = jackServerSettings.GetAlsaInputDevice();
std::string outDev = jackServerSettings.GetAlsaOutputDevice();
bool result = false; bool result = false;
try try
@@ -1942,7 +1945,7 @@ namespace pipedal
int err; int err;
for (int retry = 0; retry < 2; ++retry) for (int retry = 0; retry < 2; ++retry)
{ {
err = snd_pcm_open(&playbackHandle, alsaDeviceName.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); err = snd_pcm_open(&playbackHandle, outDev.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
if (err < 0) // field report of a device that is present, but won't immediately open. if (err < 0) // field report of a device that is present, but won't immediately open.
{ {
sleep(1); sleep(1);
@@ -1958,7 +1961,7 @@ namespace pipedal
for (int retry = 0; retry < 15; ++retry) for (int retry = 0; retry < 15; ++retry)
{ {
err = snd_pcm_open(&captureHandle, alsaDeviceName.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); err = snd_pcm_open(&captureHandle, inDev.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
if (err == -EBUSY) if (err == -EBUSY)
{ {
sleep(1); sleep(1);
@@ -1981,8 +1984,8 @@ namespace pipedal
snd_pcm_hw_params_any(playbackHandle, playbackHwParams); snd_pcm_hw_params_any(playbackHandle, playbackHwParams);
snd_pcm_hw_params_any(captureHandle, captureHwParams); snd_pcm_hw_params_any(captureHandle, captureHwParams);
SetPreferredAlsaFormat(alsaDeviceName, "capture", captureHandle, captureHwParams); SetPreferredAlsaFormat(inDev, "capture", captureHandle, captureHwParams);
SetPreferredAlsaFormat(alsaDeviceName, "output", playbackHandle, playbackHwParams); SetPreferredAlsaFormat(outDev, "output", playbackHandle, playbackHwParams);
unsigned int sampleRate = jackServerSettings.GetSampleRate(); unsigned int sampleRate = jackServerSettings.GetSampleRate();
err = snd_pcm_hw_params_set_rate_near(playbackHandle, playbackHwParams, &sampleRate, 0); err = snd_pcm_hw_params_set_rate_near(playbackHandle, playbackHwParams, &sampleRate, 0);
+12 -3
View File
@@ -148,7 +148,13 @@ void JackServerSettings::ReadJackDaemonConfiguration()
this->bufferSize_ = GetJackArg(argv, "-p", "--period"); this->bufferSize_ = GetJackArg(argv, "-p", "--period");
this->numberOfBuffers_ = (uint32_t)GetJackArg(argv, "-n", "--nperiods"); this->numberOfBuffers_ = (uint32_t)GetJackArg(argv, "-n", "--nperiods");
this->sampleRate_ = (uint32_t)GetJackArg(argv, "-r", "--rate"); this->sampleRate_ = (uint32_t)GetJackArg(argv, "-r", "--rate");
this->alsaDevice_ = GetJackStringArg(argv,"-d", "--device"); // read new dual device flags, fallback on old -d/--device
std::string capDev = GetJackStringArg(argv, "-C", "--capture");
std::string playDev = GetJackStringArg(argv, "-P", "--playback");
std::string dev = "";
try { dev = GetJackStringArg(argv, "-d", "--device"); } catch(...) {}
this->alsaInputDevice_ = capDev.empty() ? dev : capDev;
this->alsaOutputDevice_ = playDev.empty() ? dev : playDev;
this->valid_ = true; this->valid_ = true;
} }
catch (std::exception &) catch (std::exception &)
@@ -223,7 +229,9 @@ void JackServerSettings::WriteDaemonConfig()
// Lower to -P70 in order to allow the USB soft-irq to run at higher priority than JACK (it runs at 80). // Lower to -P70 in order to allow the USB soft-irq to run at higher priority than JACK (it runs at 80).
output << "/usr/bin/jackd " output << "/usr/bin/jackd "
<< "-R -P70 --silent" << "-R -P70 --silent"
<< " -dalsa -d" << this->alsaDevice_ << " -dalsa"
<< " -C" << this->GetAlsaInputDevice()
<< " -P" << this->GetAlsaOutputDevice()
<< " -r" << this->sampleRate_ << " -r" << this->sampleRate_
<< " -p" << this->bufferSize_ << " -p" << this->bufferSize_
<< " -n" << this->numberOfBuffers_ << " -Xseq" << " -n" << this->numberOfBuffers_ << " -Xseq"
@@ -246,7 +254,8 @@ JSON_MAP_REFERENCE(JackServerSettings, valid)
JSON_MAP_REFERENCE(JackServerSettings, isOnboarding) JSON_MAP_REFERENCE(JackServerSettings, isOnboarding)
JSON_MAP_REFERENCE(JackServerSettings, rebootRequired) JSON_MAP_REFERENCE(JackServerSettings, rebootRequired)
JSON_MAP_REFERENCE(JackServerSettings, isJackAudio) JSON_MAP_REFERENCE(JackServerSettings, isJackAudio)
JSON_MAP_REFERENCE(JackServerSettings, alsaDevice) JSON_MAP_REFERENCE(JackServerSettings, alsaInputDevice)
JSON_MAP_REFERENCE(JackServerSettings, alsaOutputDevice)
JSON_MAP_REFERENCE(JackServerSettings, sampleRate) JSON_MAP_REFERENCE(JackServerSettings, sampleRate)
JSON_MAP_REFERENCE(JackServerSettings, bufferSize) JSON_MAP_REFERENCE(JackServerSettings, bufferSize)
JSON_MAP_REFERENCE(JackServerSettings, numberOfBuffers) JSON_MAP_REFERENCE(JackServerSettings, numberOfBuffers)
+16 -9
View File
@@ -31,18 +31,24 @@ namespace pipedal
bool isOnboarding_ = true; bool isOnboarding_ = true;
bool isJackAudio_ = JACK_HOST ? true : false; bool isJackAudio_ = JACK_HOST ? true : false;
bool rebootRequired_ = false; bool rebootRequired_ = false;
std::string alsaDevice_; std::string alsaInputDevice_;
std::string alsaOutputDevice_;
uint64_t sampleRate_ = 0; uint64_t sampleRate_ = 0;
uint32_t bufferSize_ = 64; uint32_t bufferSize_ = 64;
uint32_t numberOfBuffers_ = 3; uint32_t numberOfBuffers_ = 3;
public: public:
void SetAlsaInputDevice(const std::string &d){ alsaInputDevice_ = d; }
void SetAlsaOutputDevice(const std::string &d){ alsaOutputDevice_ = d; }
JackServerSettings(); JackServerSettings();
JackServerSettings( JackServerSettings(
const std::string alsaInputDevice, const std::string alsaInputDevice,
uint64_t sampleRate, uint32_t bufferSize, uint32_t numberOfBuffers) uint64_t sampleRate,
uint32_t bufferSize,
uint32_t numberOfBuffers)
: valid_(true), : valid_(true),
alsaDevice_(alsaInputDevice), alsaInputDevice_(alsaInputDevice),
alsaOutputDevice_(alsaInputDevice), // default same
sampleRate_(sampleRate), sampleRate_(sampleRate),
bufferSize_(bufferSize), bufferSize_(bufferSize),
numberOfBuffers_(numberOfBuffers), numberOfBuffers_(numberOfBuffers),
@@ -53,16 +59,17 @@ namespace pipedal
uint64_t GetSampleRate() const { return sampleRate_; } uint64_t GetSampleRate() const { return sampleRate_; }
uint32_t GetBufferSize() const { return bufferSize_; } uint32_t GetBufferSize() const { return bufferSize_; }
uint32_t GetNumberOfBuffers() const { return numberOfBuffers_; } uint32_t GetNumberOfBuffers() const { return numberOfBuffers_; }
const std::string &GetAlsaInputDevice() const { return alsaDevice_; } const std::string &GetAlsaInputDevice() const { return alsaInputDevice_; }
const std::string &GetAlsaOutputDevice() const { return alsaOutputDevice_; }
void UseDummyAudioDevice() { void UseDummyAudioDevice() {
this->valid_ = true; this->valid_ = true;
if (sampleRate_ == 0) sampleRate_ = 48000; if (sampleRate_ == 0) sampleRate_ = 48000;
this->alsaDevice_ = "dummy:channels_2"; this->alsaInputDevice_ = "__DUMMY_AUDIO__dummy:channels_2";
this->alsaOutputDevice_ = "__DUMMY_AUDIO__dummy:channels_2";
} }
bool IsDummyAudioDevice() const { bool IsDummyAudioDevice() const {
return return this->alsaInputDevice_.starts_with("__DUMMY_AUDIO__")
this->alsaDevice_.starts_with("__DUMMY_AUDIO__") || this->alsaOutputDevice_.starts_with("__DUMMY_AUDIO__");
|| this->alsaDevice_.starts_with("dummy:");
} }
void ReadJackDaemonConfiguration(); void ReadJackDaemonConfiguration();
@@ -89,7 +96,7 @@ namespace pipedal
bool Equals(const JackServerSettings &other) bool Equals(const JackServerSettings &other)
{ {
return this->alsaDevice_ == other.alsaDevice_ && this->sampleRate_ == other.sampleRate_ && this->bufferSize_ == other.bufferSize_ && this->numberOfBuffers_ == other.numberOfBuffers_; return this->alsaInputDevice_ == other.alsaInputDevice_ && this->alsaOutputDevice_ == other.alsaOutputDevice_ && this->sampleRate_ == other.sampleRate_ && this->bufferSize_ == other.bufferSize_ && this->numberOfBuffers_ == other.numberOfBuffers_;
} }
DECLARE_JSON_MAP(JackServerSettings); DECLARE_JSON_MAP(JackServerSettings);
+8 -5
View File
@@ -25,7 +25,8 @@ export default class JackServerSettings {
this.isOnboarding = input.isOnboarding; this.isOnboarding = input.isOnboarding;
this.isJackAudio = input.isJackAudio; this.isJackAudio = input.isJackAudio;
this.rebootRequired = input.rebootRequired; this.rebootRequired = input.rebootRequired;
this.alsaDevice = input.alsaDevice?? ""; this.alsaInputDevice = input.alsaInputDevice ?? "";
this.alsaOutputDevice = input.alsaOutputDevice ?? "";
this.sampleRate = input.sampleRate; this.sampleRate = input.sampleRate;
this.bufferSize = input.bufferSize; this.bufferSize = input.bufferSize;
this.numberOfBuffers = input.numberOfBuffers; this.numberOfBuffers = input.numberOfBuffers;
@@ -48,16 +49,18 @@ export default class JackServerSettings {
isOnboarding: boolean = true; isOnboarding: boolean = true;
rebootRequired = false; rebootRequired = false;
isJackAudio = false; isJackAudio = false;
alsaDevice: string = ""; alsaInputDevice: string = "";
alsaOutputDevice: string = "";
sampleRate = 48000; sampleRate = 48000;
bufferSize = 64; bufferSize = 64;
numberOfBuffers = 3; numberOfBuffers = 3;
getSummaryText() { getSummaryText() {
if (this.valid) { if (this.valid) {
let device = this.alsaDevice; let inDev = this.alsaInputDevice.startsWith("hw:") ? this.alsaInputDevice .substring(3) : this.alsaInputDevice;
if (device.startsWith("hw:")) device = device.substring(3); let outDev = this.alsaOutputDevice.startsWith("hw:") ? this.alsaOutputDevice.substring(3) : this.alsaOutputDevice;
return device + " - Rate: " + this.sampleRate + " BufferSize: " + this.bufferSize + " Buffers: " + this.numberOfBuffers; return "In: "+inDev+" Out: "+outDev+" Rate "+this.sampleRate+", "+this.bufferSize+"×"+this.numberOfBuffers;
} else { } else {
return "Not configured"; return "Not configured";
} }
+46 -22
View File
@@ -282,7 +282,9 @@ const JackServerSettingsDialog = withStyles(
} }
if (!selectedDevice) { if (!selectedDevice) {
selectedDevice = alsaDevices[0]; selectedDevice = alsaDevices[0];
result.alsaDevice = selectedDevice.id; // if nothing selected yet, pick first for both:
if (!result.alsaInputDevice ) result.alsaInputDevice = selectedDevice.id;
if (!result.alsaOutputDevice) result.alsaOutputDevice = selectedDevice.id;
} }
if (result.sampleRate === 0) result.sampleRate = 48000; if (result.sampleRate === 0) result.sampleRate = 48000;
@@ -292,7 +294,7 @@ const JackServerSettingsDialog = withStyles(
result.bufferSize = bestBuffers.bufferSize; result.bufferSize = bestBuffers.bufferSize;
result.numberOfBuffers = bestBuffers.numberOfBuffers; result.numberOfBuffers = bestBuffers.numberOfBuffers;
result.valid = true; result.valid = !!result.alsaInputDevice && !!result.alsaOutputDevice;
return result; return result;
} }
@@ -399,7 +401,25 @@ const JackServerSettingsDialog = withStyles(
this.props.onApply(this.state.jackServerSettings.clone()); this.props.onApply(this.state.jackServerSettings.clone());
} }
}; };
handleInputDeviceChanged(e: any) {
const d = e.target.value as string;
let s = this.state.jackServerSettings.clone();
s.alsaInputDevice = d;
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;
this.setState({
jackServerSettings: s,
okEnabled: isOkEnabled(s, this.state.alsaDevices)
});
}
render() { render() {
const classes = withStyles.getClasses(this.props); const classes = withStyles.getClasses(this.props);
@@ -433,29 +453,33 @@ const JackServerSettingsDialog = withStyles(
> >
<DialogContent> <DialogContent>
<div> <div>
{/* Audio Input Device */}
<FormControl className={classes.formControl}> <FormControl className={classes.formControl}>
<InputLabel htmlFor="jsd_device">Device</InputLabel> <InputLabel htmlFor="jsd_inputDevice">Input Device</InputLabel>
<Select variant="standard" onChange={(e) => this.handleDeviceChanged(e)} <Select
value={this.state.jackServerSettings.alsaDevice} id="jsd_inputDevice"
style={{width: 220}} value={this.state.jackServerSettings.alsaInputDevice}
inputProps={{ onChange={e => this.handleInputDeviceChanged(e)}
name: "Device", style={{ width: 220 }}
id: "jsd_device",
}}
> >
{(noDevices && !waitingForDevices) && {this.state.alsaDevices!.map(d => (
( <MenuItem key={d.id} value={d.id}>{d.name}</MenuItem>
<MenuItem value={INVALID_DEVICE_ID}>No suitable devices.</MenuItem> ))}
) </Select>
} </FormControl>
{((!noDevices) && !waitingForDevices) && (
this.state.alsaDevices!.map((device) =>
(
<MenuItem key={device.id} value={device.id}>{device.name}</MenuItem>
)
) {/* Audio Output Device */}
)} <FormControl className={classes.formControl}>
<InputLabel htmlFor="jsd_outputDevice">Output Device</InputLabel>
<Select
id="jsd_outputDevice"
value={this.state.jackServerSettings.alsaOutputDevice}
onChange={e => this.handleOutputDeviceChanged(e)}
style={{ width: 220 }}
>
{this.state.alsaDevices!.map(d => (
<MenuItem key={d.id} value={d.id}>{d.name}</MenuItem>
))}
</Select> </Select>
</FormControl> </FormControl>
</div><div> </div><div>