Files
collaplex-audio/脚本/安装.sh
T

89 lines
3.6 KiB
Bash
Raw Normal View History

#!/bin/bash
# Collaplex 音效 — 把链路固化装到 PipeWire(用户级)
#
# 链路: 全局音频(应用输出) -> collaplex_vsink(虚拟声卡)
# -> 响度归一化(pipe DSP) -> collaplex_hrtf_in(双耳 HRTF + 房间混响) -> 数字输出
#
# 装什么:
# $HOME/.local/bin/collaplex-loudness-norm pipe 插件入口(指向本项目 dsp/)
# $HOME/.config/pipewire/pipewire.conf.d/10-...conf 虚拟声卡 + 响度归一化
# $HOME/.config/pipewire/pipewire.conf.d/20-...conf HRTF 核心(接入 + 输出)
#
# 湿量: WET=0.3 环境变量覆盖(默认 0.3)
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
CONF_DIR="$HOME/.config/pipewire/pipewire.conf.d"
WRAPPER="$HOME/.local/bin/collaplex-loudness-norm"
WET="${WET:-0.3}"
VOL_DB="${VOL_DB:-6}"
VOL="$(/usr/bin/python3 -c "print(f'{10**(${VOL_DB}/20):.4f}')")"
HRTF_IN="collaplex_hrtf_in"
EQ_IN="collaplex_eq_in"
# ---- 1. 探测数字输出 ----
TARGET=$(pw-dump | /usr/bin/python3 -c '
import json, sys
for n in json.load(sys.stdin):
if n.get("type") != "PipeWire:Interface:Node":
continue
p = (n.get("info") or {}).get("props") or {}
if p.get("media.class") == "Audio/Sink":
nm = p.get("node.name") or ""
if "iec958" in nm or "digital" in nm.lower():
print(nm)
break
')
[ -n "$TARGET" ] || { echo "找不到数字输出设备(iec958)"; exit 1; }
echo "数字输出: $TARGET"
# ---- 2. 部署 pipe 插件入口(必须 ASCII 路径) ----
mkdir -p "$(dirname "$WRAPPER")"
sed -e "s|__DSP__|$ROOT/dsp/loudness_norm.py|" "$ROOT/bin/collaplex-loudness-norm" > "$WRAPPER"
sed -e "s|__PY__|$ROOT/.venv/bin/python|" -e "s|__DSP__|$ROOT/dsp/eq.py|" \
"$ROOT/bin/collaplex-eq" > "$HOME/.local/bin/collaplex-eq"
chmod 755 "$WRAPPER" "$HOME/.local/bin/collaplex-eq"
echo "已装入口: $WRAPPER / $HOME/.local/bin/collaplex-eq"
# ---- 3. 渲染两份配置 ----
mkdir -p "$CONF_DIR"
sed -e "s|__WRAPPER__|$WRAPPER|" -e "s|__HRTF_IN__|$HRTF_IN|" \
"$ROOT/pipewire/10-collaplex-vsink.conf" > "$CONF_DIR/10-collaplex-vsink.conf"
sed -e "s|__HRIR__|$ROOT/hrir|" -e "s|__REVERB__|$ROOT/reverb/房间混响IR-96k.wav|" \
-e "s|__EQ_IN__|$EQ_IN|" -e "s|__WET__|$WET|" -e "s|__VOL__|$VOL|" \
"$ROOT/pipewire/20-collaplex-hrtf.conf" > "$CONF_DIR/20-collaplex-hrtf.conf"
sed -e "s|__EQ_WRAPPER__|$HOME/.local/bin/collaplex-eq|" -e "s|__TARGET__|$TARGET|" \
"$ROOT/pipewire/30-collaplex-eq.conf" > "$CONF_DIR/30-collaplex-eq.conf"
echo "已装配置: 10-collaplex-vsink.conf / 20-collaplex-hrtf.conf(湿量 $WET, 总音量 +${VOL_DB}dB) / 30-collaplex-eq.conf"
# ---- 4. 重启前清掉残留 DSP(占住共享槽位会让新实例起不来) ----
for p in /proc/[0-9]*; do
c=$(tr '\0' ' ' < "$p/cmdline" 2>/dev/null) || continue
case "$c" in
"/usr/bin/python3 "*dsp/loudness_norm.py*) kill -9 "${p#/proc/}" 2>/dev/null || true;;
*dsp/eq.py*) kill -9 "${p#/proc/}" 2>/dev/null || true;;
esac
done
sleep 1
# ---- 5. 重载 ----
systemctl --user restart pipewire pipewire-pulse wireplumber
sleep 8
# ---- 6. 自检 ----
echo "=== 自检 ==="
echo -n "节点: "; pw-dump | grep -o '"node.name": "collaplex[^"]*"' | sort -u | tr '\n' ' '; echo
n=0
for p in /proc/[0-9]*; do
c=$(tr '\0' ' ' < "$p/cmdline" 2>/dev/null) || continue
case "$c" in "/usr/bin/python3 "*dsp/loudness_norm.py*) n=$((n+1));; esac
done
echo "DSP 进程数: $n (归一化, 应为 2)"
m=0
for p in /proc/[0-9]*; do
c=$(tr '\0' ' ' < "$p/cmdline" 2>/dev/null) || continue
case "$c" in */dsp/eq.py*) m=$((m+1));; esac
done
echo "EQ 进程数: $m (应为 2)"
echo -n "默认输出: "; wpctl inspect @DEFAULT_SINK@ 2>/dev/null | grep -m1 "node.name" | sed 's/.*= //'