test(audio): add root uac sanity helper
This commit is contained in:
parent
9e00e7ea89
commit
74e664a28a
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -1642,7 +1642,7 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lesavka_client"
|
name = "lesavka_client"
|
||||||
version = "0.13.7"
|
version = "0.13.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-stream",
|
"async-stream",
|
||||||
@ -1676,7 +1676,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lesavka_common"
|
name = "lesavka_common"
|
||||||
version = "0.13.7"
|
version = "0.13.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
@ -1688,7 +1688,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lesavka_server"
|
name = "lesavka_server"
|
||||||
version = "0.13.7"
|
version = "0.13.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
|
|||||||
@ -4,7 +4,7 @@ path = "src/main.rs"
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "lesavka_client"
|
name = "lesavka_client"
|
||||||
version = "0.13.7"
|
version = "0.13.8"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lesavka_common"
|
name = "lesavka_common"
|
||||||
version = "0.13.7"
|
version = "0.13.8"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
|
|||||||
@ -408,6 +408,7 @@ sudo install -Dm755 "$SRC_DIR/target/release/lesavka-server" /usr/local/bin/lesa
|
|||||||
sudo install -Dm755 "$SRC_DIR/target/release/lesavka-uvc" /usr/local/bin/lesavka-uvc
|
sudo install -Dm755 "$SRC_DIR/target/release/lesavka-uvc" /usr/local/bin/lesavka-uvc
|
||||||
sudo install -Dm755 "$SRC_DIR/scripts/daemon/lesavka-core.sh" /usr/local/bin/lesavka-core.sh
|
sudo install -Dm755 "$SRC_DIR/scripts/daemon/lesavka-core.sh" /usr/local/bin/lesavka-core.sh
|
||||||
sudo install -Dm755 "$SRC_DIR/scripts/daemon/lesavka-uvc.sh" /usr/local/bin/lesavka-uvc.sh
|
sudo install -Dm755 "$SRC_DIR/scripts/daemon/lesavka-uvc.sh" /usr/local/bin/lesavka-uvc.sh
|
||||||
|
sudo install -Dm755 "$SRC_DIR/scripts/manual/run_uac_output_sanity.sh" /usr/local/bin/lesavka-uac-sanity
|
||||||
|
|
||||||
echo "==> 5b. Runtime environment defaults"
|
echo "==> 5b. Runtime environment defaults"
|
||||||
sudo install -d -m 0755 /etc/lesavka
|
sudo install -d -m 0755 /etc/lesavka
|
||||||
|
|||||||
139
scripts/manual/run_uac_output_sanity.sh
Normal file
139
scripts/manual/run_uac_output_sanity.sh
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# scripts/manual/run_uac_output_sanity.sh - play a short tone into the Theia UAC sink
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [[ ${EUID:-$(id -u)} -ne 0 ]]; then
|
||||||
|
echo "❌ run this as root so it uses the same UAC leg as lesavka-server." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
SERVER_ENV=${LESAVKA_SERVER_ENV:-/etc/lesavka/server.env}
|
||||||
|
if [[ -r $SERVER_ENV ]]; then
|
||||||
|
set -a
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$SERVER_ENV"
|
||||||
|
set +a
|
||||||
|
fi
|
||||||
|
|
||||||
|
DURATION_SECONDS=${LESAVKA_UAC_SANITY_SECONDS:-4}
|
||||||
|
TONE_FREQ=${LESAVKA_UAC_SANITY_FREQ:-880}
|
||||||
|
TONE_VOLUME=${LESAVKA_UAC_SANITY_VOLUME:-0.45}
|
||||||
|
BUFFER_TIME_US=${LESAVKA_UAC_BUFFER_TIME_US:-20000}
|
||||||
|
LATENCY_TIME_US=${LESAVKA_UAC_LATENCY_TIME_US:-5000}
|
||||||
|
REQUESTED_DEV=${LESAVKA_UAC_SANITY_DEV:-${LESAVKA_UAC_DEV:-hw:UAC2Gadget,0}}
|
||||||
|
|
||||||
|
declare -A SEEN_CANDIDATES=()
|
||||||
|
CANDIDATES=()
|
||||||
|
|
||||||
|
append_candidate() {
|
||||||
|
local candidate="${1:-}"
|
||||||
|
candidate=${candidate//[$'\r\n']/}
|
||||||
|
candidate=${candidate#"${candidate%%[![:space:]]*}"}
|
||||||
|
candidate=${candidate%"${candidate##*[![:space:]]}"}
|
||||||
|
[[ -n $candidate ]] || return 0
|
||||||
|
if [[ -z ${SEEN_CANDIDATES[$candidate]+x} ]]; then
|
||||||
|
CANDIDATES+=("$candidate")
|
||||||
|
SEEN_CANDIDATES["$candidate"]=1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
append_candidate_family() {
|
||||||
|
local candidate="${1:-}"
|
||||||
|
[[ -n ${candidate//[[:space:]]/} ]] || return 0
|
||||||
|
append_candidate "$candidate"
|
||||||
|
if [[ $candidate == hw:* ]]; then
|
||||||
|
append_candidate "plughw:${candidate#hw:}"
|
||||||
|
elif [[ $candidate == plughw:* ]]; then
|
||||||
|
append_candidate "hw:${candidate#plughw:}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
discover_uac_numeric_candidates() {
|
||||||
|
python3 - <<'PY'
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
try:
|
||||||
|
output = subprocess.check_output(["aplay", "-l"], text=True, stderr=subprocess.DEVNULL)
|
||||||
|
except Exception:
|
||||||
|
raise SystemExit(0)
|
||||||
|
|
||||||
|
seen = set()
|
||||||
|
for line in output.splitlines():
|
||||||
|
match = re.search(r"^card\s+(\d+):.*device\s+(\d+):", line)
|
||||||
|
if not match:
|
||||||
|
continue
|
||||||
|
lowered = line.lower()
|
||||||
|
if not any(token in lowered for token in ("uac", "lesavka", "composite", "usb audio")):
|
||||||
|
continue
|
||||||
|
candidate = f"hw:{match.group(1)},{match.group(2)}"
|
||||||
|
if candidate not in seen:
|
||||||
|
print(candidate)
|
||||||
|
seen.add(candidate)
|
||||||
|
PY
|
||||||
|
}
|
||||||
|
|
||||||
|
build_candidates() {
|
||||||
|
while IFS= read -r candidate; do
|
||||||
|
append_candidate_family "$candidate"
|
||||||
|
done < <(discover_uac_numeric_candidates)
|
||||||
|
|
||||||
|
append_candidate_family "$REQUESTED_DEV"
|
||||||
|
|
||||||
|
for alias in \
|
||||||
|
"hw:UAC2Gadget,0" \
|
||||||
|
"hw:UAC2_Gadget,0" \
|
||||||
|
"hw:Composite,0" \
|
||||||
|
"hw:Lesavka,0"
|
||||||
|
do
|
||||||
|
append_candidate_family "$alias"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
run_tone() {
|
||||||
|
local candidate="$1"
|
||||||
|
echo "🎧 trying UAC playback device: $candidate" >&2
|
||||||
|
set +e
|
||||||
|
timeout --signal=INT "${DURATION_SECONDS}s" \
|
||||||
|
gst-launch-1.0 -q \
|
||||||
|
audiotestsrc wave=sine freq="$TONE_FREQ" volume="$TONE_VOLUME" is-live=true \
|
||||||
|
! audio/x-raw,format=S16LE,channels=2,rate=48000 \
|
||||||
|
! audioconvert \
|
||||||
|
! audioresample \
|
||||||
|
! alsasink \
|
||||||
|
device="$candidate" \
|
||||||
|
sync=false \
|
||||||
|
async=false \
|
||||||
|
provide-clock=false \
|
||||||
|
enable-last-sample=false \
|
||||||
|
buffer-time="$BUFFER_TIME_US" \
|
||||||
|
latency-time="$LATENCY_TIME_US"
|
||||||
|
local rc=$?
|
||||||
|
set -e
|
||||||
|
if [[ $rc -eq 0 || $rc -eq 124 ]]; then
|
||||||
|
echo "✅ UAC tone completed on $candidate" >&2
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo "⚠️ tone failed on $candidate (rc=$rc)" >&2
|
||||||
|
return "$rc"
|
||||||
|
}
|
||||||
|
|
||||||
|
build_candidates
|
||||||
|
if [[ ${#CANDIDATES[@]} -eq 0 ]]; then
|
||||||
|
echo "❌ no UAC playback candidates found. Check 'aplay -l' and /etc/lesavka/server.env." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "🎵 running Theia UAC sanity tone for ${DURATION_SECONDS}s at ${TONE_FREQ} Hz" >&2
|
||||||
|
printf ' candidates: %s\n' "${CANDIDATES[*]}" >&2
|
||||||
|
for candidate in "${CANDIDATES[@]}"; do
|
||||||
|
if run_tone "$candidate"; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "❌ none of the UAC playback candidates accepted the sanity tone." >&2
|
||||||
|
echo " requested device: $REQUESTED_DEV" >&2
|
||||||
|
echo " current aplay -l:" >&2
|
||||||
|
aplay -l >&2 || true
|
||||||
|
exit 1
|
||||||
@ -10,7 +10,7 @@ bench = false
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "lesavka_server"
|
name = "lesavka_server"
|
||||||
version = "0.13.7"
|
version = "0.13.8"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
autobins = false
|
autobins = false
|
||||||
|
|
||||||
|
|||||||
@ -31,3 +31,15 @@ fn server_install_pins_hdmi_camera_and_display_defaults() {
|
|||||||
assert!(SERVER_INSTALL.contains("${LESAVKA_HDMI_HEIGHT:-1080}"));
|
assert!(SERVER_INSTALL.contains("${LESAVKA_HDMI_HEIGHT:-1080}"));
|
||||||
assert!(SERVER_INSTALL.contains("${LESAVKA_HDMI_SINK:-fbdevsink}"));
|
assert!(SERVER_INSTALL.contains("${LESAVKA_HDMI_SINK:-fbdevsink}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn server_install_keeps_uac_sanity_helper_available() {
|
||||||
|
assert!(
|
||||||
|
SERVER_INSTALL.contains("run_uac_output_sanity.sh"),
|
||||||
|
"install script should ship the root UAC sanity helper"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
SERVER_INSTALL.contains("/usr/local/bin/lesavka-uac-sanity"),
|
||||||
|
"install script should install the UAC sanity helper to a stable path"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user