fix(graph & apply): merge user_eq into baked graph, migrate active streams on cold restart, and bind volume keys

This commit is contained in:
mynameisdeleted
2026-09-01 11:07:12 -04:00
parent f2d87a1580
commit 56f4c7477b
3 changed files with 73 additions and 64 deletions

129
apply.sh
View File

@@ -26,6 +26,7 @@ for arg in "${@:-}"; do
-f|--force|--no-check) FORCE=1 ;;
-b|--bake|--simple) SIMPLE=1 ;;
--full|--unbaked) SIMPLE=0 ;;
-r|--restart|--cold) HOT=0 ;;
-h|--hot|--soft) HOT=1 ;;
esac
done
@@ -33,6 +34,19 @@ done
have() { command -v "$1" >/dev/null 2>&1; }
die() { echo "Error: $*" >&2; exit 1; }
copy_file_safe() {
src="$1"
dst="$2"
dst_dir="$(dirname "$dst")"
if [ -w "$dst_dir" ] 2>/dev/null; then
cp "$src" "$dst.tmp" 2>/dev/null || sudo cp "$src" "$dst.tmp" 2>/dev/null
mv -f "$dst.tmp" "$dst" 2>/dev/null || sudo mv -f "$dst.tmp" "$dst" 2>/dev/null
else
sudo cp "$src" "$dst.tmp" 2>/dev/null || true
sudo mv -f "$dst.tmp" "$dst" 2>/dev/null || true
fi
}
PROFILE_DIR="$(python3 "$SCRIPT_DIR/detect_hardware.py" 2>/dev/null || echo "$SCRIPT_DIR/laptop-configs/apple/mbp15_1")"
if [ ! -d "$PROFILE_DIR" ]; then
PROFILE_DIR="$SCRIPT_DIR/15_1"
@@ -42,12 +56,17 @@ if [ "$SIMPLE" -eq 1 ]; then
echo "==> Baking static DSP stages into single-stage FIR files..."
python3 "$SCRIPT_DIR/bake-graph.py" || die "bake-graph.py failed"
GRAPH_SRC="$SCRIPT_DIR/graph_simple.json"
echo "==> Installing baked FIR files -> /usr/share/t2-linux-audio/15_1/ (requires sudo)"
if [ -d "$PROFILE_DIR" ] && compgen -G "$PROFILE_DIR/baked-*.wav" >/dev/null; then
sudo cp "$PROFILE_DIR/baked-"*.wav "/usr/share/t2-linux-audio/15_1/" || die "Failed to copy baked FIR files"
else
sudo cp "$SCRIPT_DIR/15_1/baked-"*.wav "/usr/share/t2-linux-audio/15_1/" || die "Failed to copy baked FIR files"
echo "==> Installing baked FIR files -> /usr/share/t2-linux-audio/15_1/"
SRC_DIR="$PROFILE_DIR"
if [ ! -d "$SRC_DIR" ] || ! compgen -G "$SRC_DIR/baked-*.wav" >/dev/null; then
SRC_DIR="$SCRIPT_DIR/15_1"
fi
for f in "$SRC_DIR"/baked-*.wav; do
if [ -f "$f" ]; then
bn="$(basename "$f")"
copy_file_safe "$f" "/usr/share/t2-linux-audio/15_1/$bn"
fi
done
fi
# --- json validation helper -------------------------------------------
@@ -63,10 +82,7 @@ json_ok "$GRAPH_SRC"; rc=$?
[ "$rc" -eq 0 ] && echo "ok: graph.json is valid JSON"
# --- build the effective graph -> ~/.audiograph.json --------------------
if [ "$SIMPLE" -eq 1 ]; then
cp "$GRAPH_SRC" "$MERGED"
echo "ok: baked single-stage graph_simple.json -> $MERGED"
elif [ -f "$OVERRIDE" ]; then
if [ -f "$OVERRIDE" ]; then
have jq || die "$OVERRIDE exists but jq is not installed"
jq -e . "$OVERRIDE" >/dev/null 2>&1 || die "$OVERRIDE is not valid JSON"
jq -e 'any(.["filter.graph"].nodes[]; .name == "user_eq")' "$GRAPH_SRC" >/dev/null \
@@ -111,11 +127,17 @@ if [ "$FORCE" -eq 0 ]; then
fi
fi
wp() {
if have wpctl; then
timeout 1.5s wpctl "$@" 2>/dev/null || true
fi
}
# --- preserve master volume (only needed for full WirePlumber restart) --
SAVED_VOL=""
IS_MUTED=0
if [ "$HOT" -eq 0 ] && have wpctl; then
VOL_OUT="$(wpctl get-volume @DEFAULT_AUDIO_SINK@ 2>/dev/null || true)"
if [ "$HOT" -eq 0 ]; then
VOL_OUT="$(wp get-volume @DEFAULT_AUDIO_SINK@)"
if [ -n "$VOL_OUT" ]; then
SAVED_VOL="$(echo "$VOL_OUT" | awk '{print $2}')"
if echo "$VOL_OUT" | grep -qi "MUTED"; then
@@ -125,73 +147,64 @@ if [ "$HOT" -eq 0 ] && have wpctl; then
fi
# --- install ----------------------------------------------------------
if [ "$SIMPLE" -eq 1 ]; then
echo "Installing baked FIR files -> $(dirname "$GRAPH_DST")/"
for f in "$SCRIPT_DIR/15_1/baked-"*.wav; do
bn="$(basename "$f")"
sudo cp "$f" "$(dirname "$GRAPH_DST")/$bn.tmp"
sudo mv -f "$(dirname "$GRAPH_DST")/$bn.tmp" "$(dirname "$GRAPH_DST")/$bn"
done
fi
echo "Installing $MERGED -> $GRAPH_DST (requires sudo)"
sudo cp "$MERGED" "$GRAPH_DST.tmp"
sudo mv -f "$GRAPH_DST.tmp" "$GRAPH_DST"
echo "Installing $MERGED -> $GRAPH_DST"
copy_file_safe "$MERGED" "$GRAPH_DST"
if [ "$HOT" -eq 1 ]; then
echo "Hot-reloading WirePlumber graph (zero audio drop)..."
echo "Hot-reloading WirePlumber graph (zero stream drop)..."
if systemctl --user reload wireplumber 2>/dev/null || pkill -HUP -f wireplumber 2>/dev/null; then
echo "ok: sent SIGHUP live reload to WirePlumber"
sleep 0.4
else
echo "warn: SIGHUP failed - restarting WirePlumber fallback"
systemctl --user restart wireplumber
sleep 1.2
echo "warn: SIGHUP reload failed - restarting WirePlumber service fallback"
systemctl --user restart wireplumber 2>/dev/null || true
sleep 1.0
fi
# Ensure output is unmuted after live reload
if [ -n "$SAVED_VOL" ] && have wpctl; then
wpctl set-mute @DEFAULT_AUDIO_SINK@ 0 2>/dev/null || true
wpctl set-volume @DEFAULT_AUDIO_SINK@ "$SAVED_VOL" 2>/dev/null || true
fi
echo "Done - FIR graph reloaded live."
exit 0
else
# --- full PipeWire & WirePlumber cold restart ------------------------
echo "Restarting WirePlumber (re-initialising ALSA cards)..."
systemctl --user stop wireplumber 2>/dev/null || true
sleep 0.4
systemctl --user start wireplumber
sleep 1.0
fi
# --- full WirePlumber restart -----------------------------------------
echo "Restarting WirePlumber"
systemctl --user restart wireplumber
sleep 1.2
# --- wait for DSP sink to be active & restore master volume ------------
for i in {1..6}; do
if have wpctl && wpctl status 2>/dev/null | grep -qi "DSP Speakers"; then
for i in {1..8}; do
if wp status | grep -qi "DSP Speakers"; then
break
fi
sleep 0.5
done
if [ -n "$SAVED_VOL" ] && have wpctl; then
# Force unmute on default sink + explicit DSP Speakers sink ID
wpctl set-mute @DEFAULT_AUDIO_SINK@ 0 2>/dev/null || true
# Force unmute & default sink set on EVERY run (fallback volume 0.80 if unsaved)
TARGET_VOL="${SAVED_VOL:-0.80}"
DSP_ID="$(wpctl status 2>/dev/null | grep -i "DSP Speakers" | grep -oE '[0-9]+\.' | head -n1 | tr -d '.')"
if [ -n "$DSP_ID" ]; then
wpctl set-default "$DSP_ID" 2>/dev/null || true
wpctl set-mute "$DSP_ID" 0 2>/dev/null || true
wpctl set-volume "$DSP_ID" "$SAVED_VOL" 2>/dev/null || true
DSP_ID="$(wp status | grep -i "DSP Speakers" | grep -oE '[0-9]+\.' | head -n1 | tr -d '.')"
if [ -n "$DSP_ID" ]; then
wp set-default "$DSP_ID"
wp set-mute "$DSP_ID" 0
wp set-volume "$DSP_ID" "$TARGET_VOL"
if have pactl; then
pactl list short sink-inputs 2>/dev/null | awk '{print $1}' | while read -r stream_id; do
if [ -n "$stream_id" ]; then
pactl move-sink-input "$stream_id" "audio_effect.t2-151-speakers" 2>/dev/null || true
fi
done
fi
wpctl set-volume @DEFAULT_AUDIO_SINK@ "$SAVED_VOL" 2>/dev/null || true
if [ "$IS_MUTED" -eq 1 ]; then
wpctl set-mute @DEFAULT_AUDIO_SINK@ 1 2>/dev/null || true
[ -n "$DSP_ID" ] && wpctl set-mute "$DSP_ID" 1 2>/dev/null || true
fi
echo "ok: preserved master volume (${SAVED_VOL})"
fi
wp set-mute @DEFAULT_AUDIO_SINK@ 0
wp set-volume @DEFAULT_AUDIO_SINK@ "$TARGET_VOL"
if [ "$IS_MUTED" -eq 1 ]; then
wp set-mute @DEFAULT_AUDIO_SINK@ 1
[ -n "$DSP_ID" ] && wp set-mute "$DSP_ID" 1
fi
echo "ok: unmuted & active sink set (vol: ${TARGET_VOL})"
# --- confirm --------------------------------------------------------
if have wpctl && wpctl status 2>/dev/null | grep -qi "DSP Speakers"; then
if wp status | grep -qi "DSP Speakers"; then
echo "Done - 'MacBook Pro 15,1 DSP Speakers' sink is up."
else
echo "Done - graph installed, WirePlumber reloaded."

View File

@@ -269,9 +269,7 @@
"node.name": "effect_output.t2-151-speakers",
"node.description": "MacBook Pro 15,1 DSP Speakers Out",
"media.name": "MacBook Pro 15,1 DSP Speakers Out",
"target.object": "alsa_output.platform-sound.RawSpeakers",
"node.dont-fallback": "true",
"node.passive": "true",
"node.passive": "false",
"audio.channels": "4",
"audio.allowed-rates": [48000, 44100],
"audio.position": ["FL", "FR", "RL", "RR"]

View File

@@ -269,9 +269,7 @@
"node.name": "effect_output.t2-151-speakers",
"node.description": "MacBook Pro 15,1 DSP Speakers Out",
"media.name": "MacBook Pro 15,1 DSP Speakers Out",
"target.object": "alsa_output.platform-sound.RawSpeakers",
"node.dont-fallback": "true",
"node.passive": "true",
"node.passive": "false",
"audio.channels": "4",
"audio.allowed-rates": [48000, 44100],
"audio.position": ["FL", "FR", "RL", "RR"]