fix(graph & apply): merge user_eq into baked graph, migrate active streams on cold restart, and bind volume keys
This commit is contained in:
129
apply.sh
129
apply.sh
@@ -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."
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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"]
|
||||
|
||||
Reference in New Issue
Block a user