feat(audio): add skip_sink_switch option for JACK/DAW routing (#263)
When enabled, skips pactl set-default-sink on stream creation. Preserves external audio routing (JACK, qjackctl, Reaper). Mutually exclusive with DAC passthrough. cc
This commit is contained in:
8 files changed
+112
-4
No files matched your search
@@ -128,6 +128,10 @@ pub struct BackendConfig {
|
||||
|
||||
/// When true, force PipeWire clock.force-quantum for bit-perfect playback
|
||||
pub pw_force_bitperfect: bool,
|
||||
|
||||
/// When true, skip `pactl set-default-sink` on stream creation.
|
||||
/// Preserves external routing (JACK, qjackctl, Reaper).
|
||||
pub skip_sink_switch: bool,
|
||||
}
|
||||
|
||||
/// Result type for backend operations
|
||||
|
||||
@@ -308,7 +308,10 @@ impl AudioBackend for PipeWireBackend {
|
||||
// Temporarily set default sink to target (if specified)
|
||||
// We DON'T restore it - let the user's system keep the selected device as default
|
||||
// This is actually the expected behavior: when you select a device, it becomes the default
|
||||
if let Some(sink_name) = &target_sink {
|
||||
// When skip_sink_switch is true, skip this entirely to preserve external routing (JACK/qjackctl)
|
||||
if config.skip_sink_switch {
|
||||
log::info!("[PipeWire Backend] Skipping set-default-sink (skip_sink_switch enabled)");
|
||||
} else if let Some(sink_name) = &target_sink {
|
||||
log::info!("[PipeWire Backend] Setting default sink to: {}", sink_name);
|
||||
|
||||
let set_result = Command::new("pactl")
|
||||
|
||||
@@ -50,6 +50,9 @@ pub struct AudioSettings {
|
||||
/// When true, force PipeWire clock.force-quantum alongside clock.force-rate for bit-perfect.
|
||||
/// Reset both to 0 on stop. PipeWire-only, requires dac_passthrough.
|
||||
pub pw_force_bitperfect: bool,
|
||||
/// When true, skip `pactl set-default-sink` on stream creation.
|
||||
/// Preserves external routing (JACK, qjackctl, Reaper).
|
||||
pub skip_sink_switch: bool,
|
||||
}
|
||||
|
||||
impl Default for AudioSettings {
|
||||
@@ -72,6 +75,7 @@ impl Default for AudioSettings {
|
||||
normalization_target_lufs: -14.0, // Spotify/YouTube standard
|
||||
gapless_enabled: false, // Off by default — user opts in
|
||||
pw_force_bitperfect: false, // Off by default — experimental PipeWire feature
|
||||
skip_sink_switch: false, // Off by default — only for JACK/DAW routing setups
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -160,6 +164,10 @@ impl AudioSettingsStore {
|
||||
"ALTER TABLE audio_settings ADD COLUMN pw_force_bitperfect INTEGER DEFAULT 0",
|
||||
[],
|
||||
);
|
||||
let _ = conn.execute(
|
||||
"ALTER TABLE audio_settings ADD COLUMN skip_sink_switch INTEGER DEFAULT 0",
|
||||
[],
|
||||
);
|
||||
|
||||
Ok(Self { conn })
|
||||
}
|
||||
@@ -178,7 +186,7 @@ impl AudioSettingsStore {
|
||||
pub fn get_settings(&self) -> Result<AudioSettings, String> {
|
||||
self.conn
|
||||
.query_row(
|
||||
"SELECT output_device, exclusive_mode, dac_passthrough, preferred_sample_rate, backend_type, alsa_plugin, alsa_hardware_volume, stream_first_track, stream_buffer_seconds, streaming_only, limit_quality_to_device, device_max_sample_rate, normalization_enabled, normalization_target_lufs, gapless_enabled, device_sample_rate_limits, pw_force_bitperfect FROM audio_settings WHERE id = 1",
|
||||
"SELECT output_device, exclusive_mode, dac_passthrough, preferred_sample_rate, backend_type, alsa_plugin, alsa_hardware_volume, stream_first_track, stream_buffer_seconds, streaming_only, limit_quality_to_device, device_max_sample_rate, normalization_enabled, normalization_target_lufs, gapless_enabled, device_sample_rate_limits, pw_force_bitperfect, skip_sink_switch FROM audio_settings WHERE id = 1",
|
||||
[],
|
||||
|row| {
|
||||
// Parse backend_type from JSON string
|
||||
@@ -215,6 +223,7 @@ impl AudioSettingsStore {
|
||||
normalization_target_lufs: row.get::<_, Option<f64>>(13)?.unwrap_or(-14.0) as f32,
|
||||
gapless_enabled: row.get::<_, Option<i64>>(14)?.unwrap_or(0) != 0,
|
||||
pw_force_bitperfect: row.get::<_, Option<i64>>(16)?.unwrap_or(0) != 0,
|
||||
skip_sink_switch: row.get::<_, Option<i64>>(17)?.unwrap_or(0) != 0,
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
@@ -508,6 +508,7 @@ fn try_init_stream_with_backend(
|
||||
exclusive_mode: audio_settings.exclusive_mode,
|
||||
alsa_plugin: audio_settings.alsa_plugin,
|
||||
pw_force_bitperfect: audio_settings.pw_force_bitperfect,
|
||||
skip_sink_switch: audio_settings.skip_sink_switch,
|
||||
};
|
||||
|
||||
// For ALSA backend with hw: devices, try direct ALSA first (Linux only)
|
||||
|
||||
Reference in new issue
Block a user