141 lines
3.8 KiB
Bash
Executable file
141 lines
3.8 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
|
|
set -uo pipefail
|
|
|
|
readonly ECHO_ADDRESS="44:D5:CC:C8:64:39"
|
|
readonly ECHO_SINK_UUID="0000110b-0000-1000-8000-00805f9b34fb"
|
|
readonly ECHO_CARD="bluez_card.44_D5_CC_C8_64_39"
|
|
readonly ECHO_SINK="bluez_output.44_D5_CC_C8_64_39.1"
|
|
readonly KOKORO_SERVICE="kokoro.service"
|
|
readonly VOXTYPE_SERVICE="voxtype.service"
|
|
readonly STARTUP_DELAY_SECONDS=3
|
|
readonly RETRY_DELAY_SECONDS=2
|
|
readonly MAX_RETRIES=15
|
|
|
|
log() {
|
|
printf 'audio-resume: %s\n' "$*"
|
|
}
|
|
|
|
fail() {
|
|
local message="$1"
|
|
|
|
log "ERROR: ${message}"
|
|
notify-send --urgency=critical "Audio resume failed" "${message}" >/dev/null 2>&1 || true
|
|
exit 1
|
|
}
|
|
|
|
wait_for_pipewire() {
|
|
local attempt
|
|
|
|
for ((attempt = 1; attempt <= MAX_RETRIES; attempt++)); do
|
|
if pactl info >/dev/null 2>&1; then
|
|
return 0
|
|
fi
|
|
sleep "${RETRY_DELAY_SECONDS}"
|
|
done
|
|
|
|
return 1
|
|
}
|
|
|
|
echo_sink_exists() {
|
|
pactl list short sinks 2>/dev/null | awk -v sink="${ECHO_SINK}" '$2 == sink { found = 1 } END { exit !found }'
|
|
}
|
|
|
|
echo_sink_is_running() {
|
|
pactl list sinks 2>/dev/null | awk -v sink="${ECHO_SINK}" '
|
|
$1 == "Name:" { selected = ($2 == sink) }
|
|
selected && $1 == "State:" && $2 == "RUNNING" { found = 1 }
|
|
END { exit !found }
|
|
'
|
|
}
|
|
|
|
connect_echo_output() {
|
|
local attempt
|
|
|
|
bluetoothctl power on >/dev/null 2>&1 || true
|
|
|
|
for ((attempt = 1; attempt <= MAX_RETRIES; attempt++)); do
|
|
log "Connecting Echo output profile (attempt ${attempt}/${MAX_RETRIES})"
|
|
bluetoothctl connect "${ECHO_ADDRESS}" "${ECHO_SINK_UUID}" >/dev/null 2>&1 || true
|
|
|
|
if echo_sink_exists; then
|
|
pactl set-card-profile "${ECHO_CARD}" a2dp-sink >/dev/null 2>&1 || true
|
|
return 0
|
|
fi
|
|
|
|
sleep "${RETRY_DELAY_SECONDS}"
|
|
done
|
|
|
|
return 1
|
|
}
|
|
|
|
wait_for_kokoro() {
|
|
local attempt
|
|
|
|
for ((attempt = 1; attempt <= MAX_RETRIES; attempt++)); do
|
|
if test -S "${XDG_RUNTIME_DIR}/kokoro.sock"; then
|
|
return 0
|
|
fi
|
|
sleep "${RETRY_DELAY_SECONDS}"
|
|
done
|
|
|
|
return 1
|
|
}
|
|
|
|
verify_playback() {
|
|
local attempt
|
|
|
|
/usr/local/src/kokora/say "Audio resumed." || return 1
|
|
|
|
for ((attempt = 1; attempt <= MAX_RETRIES; attempt++)); do
|
|
if echo_sink_is_running; then
|
|
return 0
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
return 1
|
|
}
|
|
|
|
main() {
|
|
sleep "${STARTUP_DELAY_SECONDS}"
|
|
|
|
# Do not leave an always-open pw-cat stream attached while rebuilding the
|
|
# Bluetooth and PipeWire graph.
|
|
systemctl --user stop "${KOKORO_SERVICE}" || true
|
|
bluetoothctl disconnect "${ECHO_ADDRESS}" >/dev/null 2>&1 || true
|
|
|
|
log "Restarting PipeWire and WirePlumber with the Echo disconnected"
|
|
systemctl --user restart pipewire.service pipewire-pulse.service wireplumber.service \
|
|
|| fail "PipeWire or WirePlumber could not be restarted."
|
|
|
|
# pipewire.service wants Kokoro, so make sure it remains out of the way
|
|
# until the correct Echo output profile has been established.
|
|
systemctl --user stop "${KOKORO_SERVICE}" || true
|
|
|
|
wait_for_pipewire \
|
|
|| fail "PipeWire did not become ready after resume."
|
|
|
|
connect_echo_output \
|
|
|| fail "The Echo connected without a usable A2DP output."
|
|
|
|
# Voxtype keeps an ALSA output stream open for sound cues. Recreate it
|
|
# after PipeWire and the default output have been rebuilt.
|
|
log "Restarting Voxtype after the Echo output is ready"
|
|
systemctl --user restart "${VOXTYPE_SERVICE}" \
|
|
|| fail "Voxtype could not be restarted."
|
|
|
|
log "Starting Kokoro after the Echo output is ready"
|
|
systemctl --user start "${KOKORO_SERVICE}" \
|
|
|| fail "Kokoro could not be started."
|
|
|
|
wait_for_kokoro \
|
|
|| fail "Kokoro started without creating its socket."
|
|
|
|
verify_playback \
|
|
|| fail "The Echo sink appeared, but playback did not become active."
|
|
|
|
log "Audio recovery completed successfully"
|
|
}
|
|
|
|
main "$@"
|