joeylib2/scripts/pulseRecordLib.sh
2026-09-26 21:02:41 -05:00

53 lines
2 KiB
Bash

# pulseRecordLib.sh - shared PulseAudio capture helpers. SOURCE, do not run.
#
# Used by verify-amiga-audio.sh and verify-dos-audio.sh, the two gates that
# grade recorded sound because Paula and the Sound Blaster cannot be tapped
# register-by-register under fs-uae and DOSBox.
#
# The recording scheme in both: create a null sink for THIS run, point only
# the emulator at it with PULSE_SINK, and record that sink's monitor with
# parec. The host's default sink is never touched, so the gates are safe to
# run on a desktop that is playing something else, and two gates can never
# collide because the sink name carries the pid.
# pulseEnsureServer -- locate the audio server, or fail with a real reason.
#
# A sanitised environment (cron, CI, `env -i`, a systemd unit without the
# user session) has no XDG_RUNTIME_DIR, and pactl then reports "no server"
# even though one is running -- measured: `env -i make verify-audio` skipped
# both recording gates for exactly this. The socket lives at a predictable
# path, so derive it before giving up.
pulseEnsureServer() {
if pactl info >/dev/null 2>&1; then
return 0
fi
if [ -z "${XDG_RUNTIME_DIR:-}" ] && [ -S "/run/user/$(id -u)/pulse/native" ]; then
export XDG_RUNTIME_DIR="/run/user/$(id -u)"
if pactl info >/dev/null 2>&1; then
return 0
fi
fi
if [ -z "${PULSE_SERVER:-}" ] && [ -S "/run/user/$(id -u)/pulse/native" ]; then
export PULSE_SERVER="unix:/run/user/$(id -u)/pulse/native"
if pactl info >/dev/null 2>&1; then
return 0
fi
fi
return 1
}
# pulseCreateSink <name> -- echo the module id of a new null sink.
pulseCreateSink() {
pactl load-module module-null-sink \
sink_name="$1" \
sink_properties=device.description="$1" 2>/dev/null
}
# pulseUnloadSink <moduleId> -- always call this; a leaked null sink on the
# user's audio server is the one lasting side effect these gates can have.
pulseUnloadSink() {
[ -n "${1:-}" ] && pactl unload-module "$1" 2>/dev/null
return 0
}