#!/usr/bin/env bash # 一键应用 Kaisa pro-audio 基线(见 docs/linux-hdmi/OPERATION_PipeWire_Kaisa_ProAudio.md)。 # deb 不作为当前交付(见 ProAudio §4.4 存档);默认仍会 purge 已装的 kaisa-hdmi-pipewire-fix,避免历史安装与脚本路线冲突。 # 默认:purge kaisa-hdmi-pipewire-fix、reinstall alsa-ucm-conf、禁用 60-kaisa-ucm.lua、 # 禁用系统级 50-kaisa*.conf、安装用户 50-kaisa、重启 PipeWire 栈、pactl + IEC958、跑 verify-kaisa-pro-audio.sh。 # # 用法(仓库根,登录用户图形/会话): # ./scripts/apply-kaisa-pro-audio.sh # ./scripts/apply-kaisa-pro-audio.sh --keep-deb # ./scripts/apply-kaisa-pro-audio.sh --verify-only # ./scripts/apply-kaisa-pro-audio.sh --restore-only /path/to/baseline-stash/... # ./scripts/apply-kaisa-pro-audio.sh --restore /path/to/baseline-stash/... # ./scripts/apply-kaisa-pro-audio.sh --restore-stash-substring 20260406 # ./scripts/apply-kaisa-pro-audio.sh --no-verify # # 若只想卸载历史 deb(purge + reinstall alsa-ucm-conf)、不跑整套 apply: # ./scripts/remove-kaisa-hdmi-deb.sh # # 需要:sudo、systemctl --user、pactl、amixer;建议 pulseaudio-utils。 set -euo pipefail REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" VERIFY_SH="${REPO_ROOT}/scripts/verify-kaisa-pro-audio.sh" RESTORE_SH="${REPO_ROOT}/scripts/restore-ubuntu-audio-baseline.sh" CONF_SRC="${REPO_ROOT}/docs/linux-hdmi/wireplumber/50-kaisa-sof-rt5682-hdmi.conf" KEEP_DEB=0 VERIFY_ONLY=0 NO_VERIFY=0 RESTORE_DIR="" RESTORE_ONLY=0 RESTORE_SUBSTR="" resolve_stash_substring() { local sub="$1" local base="${REPO_ROOT}/audio_topology/baseline-stash" if [[ ! -d "$base" ]]; then echo "apply-kaisa-pro-audio: 无目录 $base(请先 capture-ubuntu-audio-baseline.sh)" >&2 exit 1 fi local matches=() shopt -s nullglob for d in "$base"/*"${sub}"*/; do [[ -d "$d" ]] || continue matches+=("$d") done shopt -u nullglob if ((${#matches[@]} == 0)); then echo "apply-kaisa-pro-audio: 未找到基线目录名含「${sub}」于 $base" >&2 echo "现有目录:" >&2 ls -la "$base" 2>/dev/null | head -20 >&2 || true exit 1 fi local latest latest="$(ls -td "${matches[@]}" | head -1)" realpath "$latest" } usage() { sed -n '2,18p' "$0" | sed 's/^# \{0,1\}//' exit "${1:-0}" } while [[ $# -gt 0 ]]; do case "$1" in --keep-deb) KEEP_DEB=1 ;; --verify-only) VERIFY_ONLY=1 ;; --no-verify) NO_VERIFY=1 ;; --restore-only) RESTORE_ONLY=1 if [[ -n "${2:-}" && "${2:0:1}" != - ]]; then RESTORE_DIR="$2" shift fi ;; --restore) RESTORE_DIR="${2:?--restore 需要目录参数}" shift ;; --restore-stash-substring) RESTORE_SUBSTR="${2:?--restore-stash-substring 需要子串,如 20260406}" shift ;; -h|--help) usage 0 ;; *) echo "未知参数: $1" >&2 usage 1 ;; esac shift done if [[ "$VERIFY_ONLY" -eq 1 ]]; then exec "$VERIFY_SH" fi if [[ -n "$RESTORE_SUBSTR" ]]; then RESTORE_DIR="$(resolve_stash_substring "$RESTORE_SUBSTR")" echo ">>> 解析基线目录: $RESTORE_DIR" fi if [[ "$RESTORE_ONLY" -eq 1 && -z "$RESTORE_DIR" ]]; then echo "apply-kaisa-pro-audio: --restore-only 需要目录参数,或与 --restore-stash-substring 同用。" >&2 exit 1 fi if [[ $EUID -eq 0 && -z "${SUDO_USER:-}" ]]; then echo "apply-kaisa-pro-audio: 请勿以 root 直接运行(无法正确操作 user pipewire)。请用登录用户执行。" >&2 exit 1 fi if [[ ! -f "$CONF_SRC" ]]; then echo "apply-kaisa-pro-audio: 缺少仓库 conf: $CONF_SRC" >&2 exit 1 fi if ! command -v sudo &>/dev/null; then echo "apply-kaisa-pro-audio: 需要 sudo。" >&2 exit 1 fi # pactl 前缀:root+sudo 时代理会话用户 PACTL_PREFIX=() if [[ $EUID -eq 0 && -n "${SUDO_USER:-}" ]] && id -u "$SUDO_USER" &>/dev/null; then _VU="$(id -u "$SUDO_USER")" if [[ -d "/run/user/${_VU}" ]]; then _VH="$(getent passwd "$SUDO_USER" | cut -d: -f6)" _VH="${_VH:-/home/$SUDO_USER}" PACTL_PREFIX=(sudo -u "$SUDO_USER" env XDG_RUNTIME_DIR="/run/user/${_VU}" HOME="$_VH") fi fi run_pactl() { "${PACTL_PREFIX[@]}" pactl "$@"; } restart_audio_stack() { if ((${#PACTL_PREFIX[@]})); then sudo -u "${SUDO_USER}" env XDG_RUNTIME_DIR="/run/user/$(id -u "$SUDO_USER")" \ systemctl --user restart wireplumber pipewire pipewire-pulse else systemctl --user restart wireplumber pipewire pipewire-pulse fi } if [[ -n "$RESTORE_DIR" ]]; then echo ">>> 执行基线恢复: $RESTORE_SH $RESTORE_DIR" "$RESTORE_SH" "$RESTORE_DIR" if [[ "$RESTORE_ONLY" -eq 1 ]]; then echo ">>> --restore-only:已恢复,跳过 pro-audio 应用。" exit 0 fi fi if [[ "$KEEP_DEB" -eq 0 ]]; then if dpkg -s kaisa-hdmi-pipewire-fix &>/dev/null; then echo ">>> apt purge -y kaisa-hdmi-pipewire-fix" sudo apt purge -y kaisa-hdmi-pipewire-fix else echo "(未安装 kaisa-hdmi-pipewire-fix,跳过 purge)" fi echo ">>> apt install --reinstall -y alsa-ucm-conf" sudo apt install --reinstall -y alsa-ucm-conf else echo "(--keep-deb:跳过 purge / reinstall alsa-ucm-conf)" fi LUA="/usr/share/wireplumber/main.lua.d/60-kaisa-ucm.lua" if [[ -f "$LUA" ]]; then echo ">>> sudo mv $LUA -> ${LUA}.disabled" sudo mv -n -- "$LUA" "${LUA}.disabled" else echo "(无激活的 $LUA,跳过)" fi if [[ -d /etc/wireplumber/wireplumber.conf.d ]]; then echo ">>> 禁用系统级 50-kaisa*.conf(避免与用户级重复)" sudo bash -c ' shopt -s nullglob for f in /etc/wireplumber/wireplumber.conf.d/50-kaisa*.conf; do [[ -f "$f" ]] || continue t="${f}.disabled" [[ -e "$t" ]] && t="${f}.disabled.$(date +%s)" echo "mv $f -> $t" mv -n -- "$f" "$t" done ' fi USER_CONF_DIR="${HOME}/.config/wireplumber/wireplumber.conf.d" mkdir -p "$USER_CONF_DIR" echo ">>> cp $CONF_SRC -> $USER_CONF_DIR/50-kaisa-sof-rt5682-hdmi.conf" cp -f -- "$CONF_SRC" "$USER_CONF_DIR/50-kaisa-sof-rt5682-hdmi.conf" echo ">>> strip-kaisa-default-profile-state(避免 WirePlumber 从 default-profile 恢复 stereo-fallback)" "$REPO_ROOT/scripts/strip-kaisa-default-profile-state.sh" || true if systemctl --user is-system-running &>/dev/null; then echo ">>> systemctl --user restart wireplumber pipewire pipewire-pulse" restart_audio_stack sleep 2 else echo "apply-kaisa-pro-audio: 警告:user systemd 会话不可用,请登录桌面后手动: systemctl --user restart wireplumber pipewire pipewire-pulse" >&2 fi if ! command -v pactl &>/dev/null; then echo "apply-kaisa-pro-audio: 未找到 pactl,无法 set-card-profile。请安装 pulseaudio-utils 后重试。" >&2 exit 1 fi CARD="$(run_pactl list cards short 2>/dev/null | awk '/cml_rt5682/ {print $2; exit}')" || true if [[ -z "$CARD" ]]; then echo "apply-kaisa-pro-audio: 无法从 pactl list cards short 解析 cml_rt5682 卡名(PCI 是否与 conf 中 pci-0000_00_1f.3 一致?)" >&2 exit 1 fi echo ">>> pactl set-card-profile $CARD pro-audio" run_pactl set-card-profile "$CARD" pro-audio SINK="$(run_pactl list short sinks 2>/dev/null | awk '/cml_rt5682/ && /\.pro-output-2/ {print $2; exit}')" || true if [[ -z "$SINK" ]]; then echo "apply-kaisa-pro-audio: 未找到 …pro-output-2 sink;请核对 profile 是否已为 pro-audio。" >&2 exit 1 fi echo ">>> pactl set-default-sink $SINK" run_pactl set-default-sink "$SINK" echo ">>> amixer IEC958 0/1/2 on" amixer -c0 sset 'IEC958',0 on amixer -c0 sset 'IEC958',1 on amixer -c0 sset 'IEC958',2 on if [[ "$NO_VERIFY" -eq 1 ]]; then echo ">>> --no-verify:跳过门禁(不推荐)。" exit 0 fi echo ">>> $VERIFY_SH" if ! "$VERIFY_SH"; then echo "" >&2 echo "apply-kaisa-pro-audio: 门禁未通过。可查看上文,或运行: $REPO_ROOT/scripts/verify-kaisa-audio-environment.sh --output /tmp/kaisa-verify.txt" >&2 exit 1 fi echo "apply-kaisa-pro-audio: 完成。"