slop glorping alsa device options for laptop only

This commit is contained in:
legop3
2026-07-07 21:41:00 -04:00
parent 3a40a65d46
commit 5d60544904
3 changed files with 280 additions and 8 deletions
+6 -2
View File
@@ -1,13 +1,17 @@
# Dedicated ALSA routing for the Debian laptop rover profile.
# Reference ALSA routing for the Debian laptop rover profile.
#
# This intentionally mirrors pi/asound.conf as closely as a normal PC can:
# one fixed hardware card, one dmix playback engine, separate softvol controls
# for TTS/horn/forwarded audio, and a raw capture alias for the rover mic.
#
# The Debian laptop installer no longer copies this file directly. It renders
# /etc/asound.conf from /etc/roverd-installer.env so laptops with HDMI as card 0
# can point these same logical mixer devices at their real speaker card.
#
# This is NOT meant to preserve normal desktop audio behavior. The laptop rover
# installer disables PipeWire/PulseAudio so roverd owns the audio hardware like
# the Raspberry Pi rover does. If the laptop's real speaker/mic card is not ALSA
# card 0, change the hw:0,0/card 0 references below to the card shown by:
# card 0, rerun the Debian laptop installer and answer the ALSA prompts using:
# aplay -l
# arecord -l
+273 -5
View File
@@ -31,6 +31,8 @@ install_debian_laptop_deps() {
ffmpeg alsa-utils v4l-utils ca-certificates flite espeak python3 curl xz-utils unzip libasound2-plugins libgcc-s1 libstdc++6 libc++1-14 libc++abi1-14
}
DEBIAN_LAPTOP_INSTALLER_CONFIG="/etc/roverd-installer.env"
disable_debian_laptop_desktop_audio_stack() {
# This profile is for a dedicated rover laptop. PipeWire/PulseAudio are good
# desktop defaults, but they can grab the hardware device and make the rover's
@@ -60,14 +62,280 @@ disable_debian_laptop_desktop_audio_stack() {
pkill -x pulseaudio >/dev/null 2>&1 || true
}
install_debian_laptop_audio_support() {
if [[ ! -f pi/asound.debian-laptop.conf ]]; then
log "WARNING: pi/asound.debian-laptop.conf missing; skipping Debian laptop ALSA config install"
derive_debian_laptop_alsa_card_from_device() {
local device="$1"
# The common ALSA hardware device shape is hw:CARD,DEVICE. Pulling the card
# number from that string gives the installer a useful default while still
# allowing the prompt to handle named cards or uncommon PCM strings.
if [[ "$device" =~ ^hw:([0-9]+),[0-9]+$ ]]; then
printf '%s\n' "${BASH_REMATCH[1]}"
return
fi
install -m 0644 pi/asound.debian-laptop.conf /etc/asound.conf
log "Installed dedicated Debian laptop ALSA config to /etc/asound.conf"
printf '0\n'
}
read_debian_laptop_installer_value() {
local prompt="$1"
local default_value="$2"
local value=""
# Prompting through /dev/tty keeps this usable even when the installer is
# launched through sudo with stdin redirected. The caller already checks for
# an interactive terminal before reaching this function, so failure here is
# genuinely unexpected and should stop the install instead of guessing.
read -r -p "${prompt} [${default_value}]: " value </dev/tty
if [[ -z "$value" ]]; then
value="$default_value"
fi
printf '%s\n' "$value"
}
validate_debian_laptop_alsa_config() {
# The PCM fields are written inside quoted ALSA strings, so keep them to the
# device spellings ALSA normally uses for hardware/plugin PCMs. Rejecting
# whitespace and shell/config punctuation prevents a bad installer config
# from generating an asound.conf that changes structure instead of values.
if [[ ! "$ROVERD_ALSA_PLAYBACK_DEVICE" =~ ^[A-Za-z0-9_.,:+/-]+$ ]]; then
echo "Invalid ROVERD_ALSA_PLAYBACK_DEVICE: $ROVERD_ALSA_PLAYBACK_DEVICE" >&2
exit 1
fi
if [[ ! "$ROVERD_ALSA_CAPTURE_DEVICE" =~ ^[A-Za-z0-9_.,:+/-]+$ ]]; then
echo "Invalid ROVERD_ALSA_CAPTURE_DEVICE: $ROVERD_ALSA_CAPTURE_DEVICE" >&2
exit 1
fi
# Softvol controls and ctl.!default need the playback card, because
# TTSMaster, HornMaster, and ForwardMaster are all playback mixer controls.
# Keep this numeric to match the prompt and avoid needing quoted ALSA card
# ids in the generated config.
if [[ ! "$ROVERD_ALSA_PLAYBACK_CARD" =~ ^[0-9]+$ ]]; then
echo "Invalid ROVERD_ALSA_PLAYBACK_CARD: $ROVERD_ALSA_PLAYBACK_CARD" >&2
exit 1
fi
}
load_debian_laptop_installer_config_file() {
local config_path="$1"
local line key val
# Read only the small allowlist this installer owns. Avoid sourcing the file
# because it lives in /etc and is meant to be installer data, not shell code.
while IFS= read -r line || [[ -n "$line" ]]; do
[[ "$line" =~ ^[[:space:]]*$ ]] && continue
[[ "$line" =~ ^[[:space:]]*# ]] && continue
if [[ "$line" =~ ^[[:space:]]*([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then
key="${BASH_REMATCH[1]}"
val="${BASH_REMATCH[2]}"
else
continue
fi
val="${val#${val%%[![:space:]]*}}"
val="${val%${val##*[![:space:]]}}"
if [[ "$val" =~ ^\".*\"$ ]]; then
val="${val:1:${#val}-2}"
elif [[ "$val" =~ ^\'.*\'$ ]]; then
val="${val:1:${#val}-2}"
fi
case "$key" in
ROVERD_ALSA_PLAYBACK_DEVICE|ROVERD_ALSA_PLAYBACK_CARD|ROVERD_ALSA_CAPTURE_DEVICE)
printf -v "$key" '%s' "$val"
export "$key"
;;
esac
done < "$config_path"
}
write_debian_laptop_installer_config_file() {
local config_path="$1"
local tmp_path
tmp_path="$(mktemp)"
# This file is intentionally plain KEY=VALUE shell-style data so future
# installs can reuse the same laptop-specific card choices without asking
# again. It is still parsed by an allowlist reader instead of sourced.
cat > "$tmp_path" <<EOF
# Created by install_roverd.sh for the Debian laptop rover profile.
# These values choose the physical ALSA hardware behind the rover's logical
# mixer devices: tts, horn, forward, default playback, and rovermic capture.
ROVERD_ALSA_PLAYBACK_DEVICE="${ROVERD_ALSA_PLAYBACK_DEVICE}"
ROVERD_ALSA_PLAYBACK_CARD="${ROVERD_ALSA_PLAYBACK_CARD}"
ROVERD_ALSA_CAPTURE_DEVICE="${ROVERD_ALSA_CAPTURE_DEVICE}"
EOF
install -o root -g root -m 0644 "$tmp_path" "$config_path"
rm -f "$tmp_path"
}
load_or_create_debian_laptop_alsa_config() {
if [[ -f "$DEBIAN_LAPTOP_INSTALLER_CONFIG" ]]; then
load_debian_laptop_installer_config_file "$DEBIAN_LAPTOP_INSTALLER_CONFIG"
log "Using Debian laptop ALSA installer config from $DEBIAN_LAPTOP_INSTALLER_CONFIG"
elif [[ -n "${ROVERD_ALSA_PLAYBACK_DEVICE:-}" && -n "${ROVERD_ALSA_PLAYBACK_CARD:-}" && -n "${ROVERD_ALSA_CAPTURE_DEVICE:-}" ]]; then
# This keeps unattended installs possible without adding a pile of CLI
# flags. The generated /etc file still becomes the durable source for
# future installs on the same laptop.
validate_debian_laptop_alsa_config
write_debian_laptop_installer_config_file "$DEBIAN_LAPTOP_INSTALLER_CONFIG"
log "Wrote Debian laptop ALSA installer config to $DEBIAN_LAPTOP_INSTALLER_CONFIG from environment"
else
if ! { true </dev/tty >/dev/tty; } 2>/dev/null; then
echo "Missing $DEBIAN_LAPTOP_INSTALLER_CONFIG and no interactive terminal is available for ALSA setup." >&2
echo "Run sudo ./pi/install_roverd.sh --debian-laptop once from a terminal, then reuse the generated config for future installs." >&2
exit 1
fi
log "No $DEBIAN_LAPTOP_INSTALLER_CONFIG found; creating Debian laptop ALSA installer config"
if command -v aplay >/dev/null 2>&1; then
echo "Playback devices from aplay -l:" >/dev/tty
aplay -l >/dev/tty 2>/dev/tty || true
fi
if command -v arecord >/dev/null 2>&1; then
echo "Capture devices from arecord -l:" >/dev/tty
arecord -l >/dev/tty 2>/dev/tty || true
fi
ROVERD_ALSA_PLAYBACK_DEVICE="$(read_debian_laptop_installer_value "ALSA playback device for rover speaker output" "${ROVERD_ALSA_PLAYBACK_DEVICE:-hw:0,0}")"
ROVERD_ALSA_PLAYBACK_CARD="$(read_debian_laptop_installer_value "ALSA playback card number for mixer controls" "${ROVERD_ALSA_PLAYBACK_CARD:-$(derive_debian_laptop_alsa_card_from_device "$ROVERD_ALSA_PLAYBACK_DEVICE")}")"
ROVERD_ALSA_CAPTURE_DEVICE="$(read_debian_laptop_installer_value "ALSA capture device for rover microphone input" "${ROVERD_ALSA_CAPTURE_DEVICE:-$ROVERD_ALSA_PLAYBACK_DEVICE}")"
validate_debian_laptop_alsa_config
write_debian_laptop_installer_config_file "$DEBIAN_LAPTOP_INSTALLER_CONFIG"
log "Wrote Debian laptop ALSA installer config to $DEBIAN_LAPTOP_INSTALLER_CONFIG"
fi
ROVERD_ALSA_PLAYBACK_DEVICE="${ROVERD_ALSA_PLAYBACK_DEVICE:-hw:0,0}"
ROVERD_ALSA_PLAYBACK_CARD="${ROVERD_ALSA_PLAYBACK_CARD:-$(derive_debian_laptop_alsa_card_from_device "$ROVERD_ALSA_PLAYBACK_DEVICE")}"
ROVERD_ALSA_CAPTURE_DEVICE="${ROVERD_ALSA_CAPTURE_DEVICE:-$ROVERD_ALSA_PLAYBACK_DEVICE}"
validate_debian_laptop_alsa_config
}
render_debian_laptop_asound_config() {
local tmp_path
tmp_path="$(mktemp)"
# The rover-facing ALSA names stay stable even when the laptop's physical
# sound card changes. dmixer owns the one real playback PCM, while tts,
# horn, and forward each wrap that mixer with a separate softvol control.
cat > "$tmp_path" <<EOF
# Dedicated ALSA routing for the Debian laptop rover profile.
#
# Generated by install_roverd.sh from $DEBIAN_LAPTOP_INSTALLER_CONFIG.
# Change the physical devices there, then rerun the Debian laptop installer.
#
# Logical playback devices:
# tts - default text-to-speech output with TTSMaster softvol
# horn - horn synth output with HornMaster softvol
# forward - browser-forwarded audio with ForwardMaster softvol
# default - TTS playback plus rovermic capture
#
# Physical routing selected for this laptop:
# playback PCM: ${ROVERD_ALSA_PLAYBACK_DEVICE}
# playback card: ${ROVERD_ALSA_PLAYBACK_CARD}
# capture PCM: ${ROVERD_ALSA_CAPTURE_DEVICE}
# Mix multiple playback clients in software with a fixed low-cost format.
pcm.dmixer {
type dmix
ipc_key 1024
ipc_perm 0666
slave {
pcm "${ROVERD_ALSA_PLAYBACK_DEVICE}"
format S16_LE
rate 16000
channels 1
period_time 0
period_size 1024
buffer_size 4096
}
}
# TTS volume control. TTS uses the default playback route, so this control lets
# generated speech move independently from horns and forwarded browser audio.
pcm.tts_softvol {
type softvol
slave.pcm "dmixer"
control {
name "TTSMaster"
card ${ROVERD_ALSA_PLAYBACK_CARD}
}
min_dB -60.0
max_dB 12.0
}
# Horn volume control. The horn synth opens the logical "horn" device, which
# keeps horn loudness adjustable without changing the shared hardware PCM.
pcm.horn_softvol {
type softvol
slave.pcm "dmixer"
control {
name "HornMaster"
card ${ROVERD_ALSA_PLAYBACK_CARD}
}
min_dB -60.0
max_dB 12.0
}
# Forwarded audio volume control. The browser-audio listener opens "forward",
# so remote audio can be mixed with local rover sounds without bypassing dmix.
pcm.forward_softvol {
type softvol
slave.pcm "dmixer"
control {
name "ForwardMaster"
card ${ROVERD_ALSA_PLAYBACK_CARD}
}
min_dB -60.0
max_dB 12.0
}
# Per-source playback PCMs.
pcm.tts {
type plug
slave.pcm "tts_softvol"
}
pcm.horn {
type plug
slave.pcm "horn_softvol"
}
pcm.forward {
type plug
slave.pcm "forward_softvol"
}
# Capture alias used by laptop rover config defaults. Capture is deliberately
# separate from playback because laptop speakers and microphones often appear
# on different ALSA cards.
pcm.rovermic {
type plug
slave.pcm "${ROVERD_ALSA_CAPTURE_DEVICE}"
}
# Defaults: TTS direct playback + raw capture on the selected laptop devices.
pcm.!default {
type asym
playback.pcm "tts"
capture.pcm "rovermic"
}
ctl.!default {
type hw
card ${ROVERD_ALSA_PLAYBACK_CARD}
}
EOF
install -o root -g root -m 0644 "$tmp_path" /etc/asound.conf
rm -f "$tmp_path"
log "Installed dedicated Debian laptop ALSA config to /etc/asound.conf using playback ${ROVERD_ALSA_PLAYBACK_DEVICE}"
}
install_debian_laptop_audio_support() {
load_or_create_debian_laptop_alsa_config
render_debian_laptop_asound_config
install -D -o root -g root -m 0755 pi/bin/chromegtts-daemon-laptop.py /usr/local/bin/chromegtts-daemon
log "Installed laptop chromegtts daemon"