diff --git a/Cargo.lock b/Cargo.lock index 714dec6..9b152cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1642,7 +1642,7 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "lesavka_client" -version = "0.13.5" +version = "0.13.6" dependencies = [ "anyhow", "async-stream", @@ -1676,7 +1676,7 @@ dependencies = [ [[package]] name = "lesavka_common" -version = "0.13.5" +version = "0.13.6" dependencies = [ "anyhow", "base64", @@ -1688,7 +1688,7 @@ dependencies = [ [[package]] name = "lesavka_server" -version = "0.13.5" +version = "0.13.6" dependencies = [ "anyhow", "base64", diff --git a/client/Cargo.toml b/client/Cargo.toml index cafaab1..336f4e8 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -4,7 +4,7 @@ path = "src/main.rs" [package] name = "lesavka_client" -version = "0.13.5" +version = "0.13.6" edition = "2024" [dependencies] diff --git a/common/Cargo.toml b/common/Cargo.toml index c54f5c9..6b45a3c 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lesavka_common" -version = "0.13.5" +version = "0.13.6" edition = "2024" build = "build.rs" diff --git a/docs/operational-env.md b/docs/operational-env.md index e36c82e..19e83a5 100644 --- a/docs/operational-env.md +++ b/docs/operational-env.md @@ -187,6 +187,7 @@ Hardware-facing assumptions belong near the code that uses them; this file is th | `LESAVKA_UAC_DEV` | server hardware/device override | | `LESAVKA_UAC_HDMI_COMPENSATION_US` | server HDMI audio sink latency override | | `LESAVKA_UAC_LATENCY_TIME_US` | server audio sink latency override | +| `LESAVKA_UAC_SESSION_CLOCK_ALIGN` | server audio sink clock-alignment override | | `LESAVKA_TEST_CAM_U32` | test/build contract variable; not runtime operator config | | `LESAVKA_TEST_CAP_CAMERA` | test/build contract variable; not runtime operator config | | `LESAVKA_TEST_CAP_MIC` | test/build contract variable; not runtime operator config | @@ -211,6 +212,7 @@ Hardware-facing assumptions belong near the code that uses them; this file is th | `LESAVKA_TEST_VIDEO_SOURCE` | test/build contract variable; not runtime operator config | | `LESAVKA_TOUCHPAD_SCALE` | input routing/clipboard override | | `LESAVKA_UAC_DEV` | server hardware/device override | +| `LESAVKA_UAC_SESSION_CLOCK_ALIGN` | disable only for A/B diagnosing UAC sink timing vs silence | | `LESAVKA_UPLINK_CAMERA_PREVIEW` | client media capture/playback override | | `LESAVKA_UPLINK_MIC_LEVEL` | client media capture/playback override | | `LESAVKA_USB_RECOVERY_` | USB recovery timing override | diff --git a/scripts/install/server.sh b/scripts/install/server.sh index 9ac68b0..5618ab0 100755 --- a/scripts/install/server.sh +++ b/scripts/install/server.sh @@ -437,6 +437,7 @@ fi printf 'LESAVKA_UAC_DEV=%s\n' "${LESAVKA_UAC_DEV:-hw:UAC2Gadget,0}" printf 'LESAVKA_ALSA_DEV=%s\n' "${LESAVKA_ALSA_DEV:-hw:UAC2Gadget,0}" printf 'LESAVKA_UAC_HDMI_COMPENSATION_US=%s\n' "${LESAVKA_UAC_HDMI_COMPENSATION_US:-0}" + printf 'LESAVKA_UAC_SESSION_CLOCK_ALIGN=%s\n' "${LESAVKA_UAC_SESSION_CLOCK_ALIGN:-1}" } | sudo tee /etc/lesavka/server.env >/dev/null echo "==> 6a. Systemd units - lesavka-core" diff --git a/server/Cargo.toml b/server/Cargo.toml index 51d417b..4c827eb 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -10,7 +10,7 @@ bench = false [package] name = "lesavka_server" -version = "0.13.5" +version = "0.13.6" edition = "2024" autobins = false diff --git a/server/src/audio/voice_input.rs b/server/src/audio/voice_input.rs index d66f1c4..b4b7a92 100644 --- a/server/src/audio/voice_input.rs +++ b/server/src/audio/voice_input.rs @@ -106,6 +106,19 @@ fn non_negative_voice_sink_timing_env(name: &str, default: i64) -> i64 { .unwrap_or(default) } +fn voice_sink_session_clock_align_enabled() -> bool { + std::env::var("LESAVKA_UAC_SESSION_CLOCK_ALIGN") + .ok() + .map(|value| { + let trimmed = value.trim(); + !(trimmed.eq_ignore_ascii_case("0") + || trimmed.eq_ignore_ascii_case("false") + || trimmed.eq_ignore_ascii_case("no") + || trimmed.eq_ignore_ascii_case("off")) + }) + .unwrap_or(true) +} + impl Voice { #[cfg(coverage)] pub async fn new(_alsa_dev: &str) -> anyhow::Result { @@ -196,10 +209,11 @@ impl Voice { let buffer_time_us = voice_sink_buffer_time_us(); let latency_time_us = voice_sink_latency_time_us(); let compensation_us = voice_sink_compensation_us(); + let clock_align_enabled = voice_sink_session_clock_align_enabled(); alsa_sink.set_property("device", alsa_dev); - alsa_sink.set_property("sync", true); - alsa_sink.set_property("async", true); + alsa_sink.set_property("sync", clock_align_enabled); + alsa_sink.set_property("async", clock_align_enabled); alsa_sink.set_property("enable-last-sample", false); alsa_sink.set_property("provide-clock", false); alsa_sink.set_property("buffer-time", buffer_time_us); @@ -214,9 +228,12 @@ impl Voice { buffer_time_us, latency_time_us, compensation_us, + clock_align_enabled, "🎤 UAC sink low-latency timing armed" ); - crate::media_timing::prepare_pipeline_clock_sync(&pipeline); + if clock_align_enabled { + crate::media_timing::prepare_pipeline_clock_sync(&pipeline); + } pipeline.add_many([ appsrc.upcast_ref(), @@ -273,7 +290,7 @@ impl Voice { Ok(Self { appsrc, _pipe: pipeline, - clock_aligned: false, + clock_aligned: !clock_align_enabled, tap: ClipTap::new("voice", Duration::from_secs(60)), }) } @@ -389,4 +406,21 @@ mod voice_sink_timing_tests { }); }); } + + #[test] + fn session_clock_alignment_defaults_on_and_accepts_disable_overrides() { + temp_env::with_var_unset("LESAVKA_UAC_SESSION_CLOCK_ALIGN", || { + assert!(super::voice_sink_session_clock_align_enabled()); + }); + + for disabled in ["0", "false", "no", "off"] { + temp_env::with_var("LESAVKA_UAC_SESSION_CLOCK_ALIGN", Some(disabled), || { + assert!(!super::voice_sink_session_clock_align_enabled()); + }); + } + + temp_env::with_var("LESAVKA_UAC_SESSION_CLOCK_ALIGN", Some("1"), || { + assert!(super::voice_sink_session_clock_align_enabled()); + }); + } }