10月07, 2025

万能音响系统搭建

将音响连接到NAS上,使全屋所有设备共用一个音响。

Windows音频播放

方案1: Scream

在电脑上安装Scream虚拟声卡,捕获电脑的音频并通过网络发送到NAS上运行的ScreamReader(Windows,其他系统详见链接)播放。

安装Scream

release页面下载最新版本的Scream,导入如下注册表, 并将系统时间修改为2022年.

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CI\Policy]
"UpgradedSystem"=dword:00000001

之后右键 Install-x64.bat, 以管理员身份运行安装。

设置单播

Scream默认使用多播方式, 会向局域网内所有设备广播音频数据. 可以通过如下注册表修改为单播模式.

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Scream\Options]
"UnicastIPv4"="192.168.1.100"
"UnicastPort"=dword:00000faa

其中, UnicastIPv4 指定了发送的IP地址, UnicastPort 指定了端口号( 0xfaa 即为十进制的 4010 端口).

运行ScreamReader

在防火墙中入站规则中新建一条 4010 端口UDP的允许入站规则,然后在下载的Scream文件夹中的 clients\Windows\ScreamReader 文件夹下, 双击运行运行ScreamReader即可(也可以在任务计划程序中设置开机自启)。

问题

据github上用户反馈延迟较小,但我自己使用时实际测试延迟在300ms左右,未解决。

方案2: Voicemeeter (Windows - Windows)

在电脑和NAS上都下载并安装Voicemeeter,设置如下:

NAS端Voicemeeter设置

NAS端Voicemeeter的作用主要有两个:

  1. 接收音频并播放
  2. 发送麦克风音频供其他设备使用

首先将Voicemeeter界面中的Stereo Input 1设置为麦克风,并点亮Bus B,再将Stereo Input 2的Bus A点亮,最后设置HARDWARE OUT中A1, A2任意一个为你的音响设备。

点击右上角的VBAN按钮,弹出的窗口中Incoming Streams是NAS将要接收的音频,配置好Stream Name(一定要和发送端保持一致)、电脑的IP地址后,选择Destination为In #2(即Stereo Input 2)。 下面的Outgoing Streams是NAS将要发送的音频,选择一条将Source置为Bus B,并配置好电脑IP。最后不要忘了将左侧的On以及左上角的VBAN is ON按钮点亮。

音频的路由大致如下:

  • 电脑发送的Stream→Stereo Input2→Bus A→音响
  • 麦克风→Bus B→将Stream发送给电脑

Voicemeeter NAS端设置

Voicemeeter会独占音响,导致其他来源的音频无法播放,需要在Windows声音设置→声音控制面板→右键音响→高级标签页中取消勾选"允许应用程序独占控制该设备"。

电脑端Voicemeeter设置

电脑端类似,将Stereo Input 2(也可以是1,与VBAN中Destination保持一致)的Bus B点亮,将VIRTUAL INPUT的Bus A点亮,再在VBAN窗口中做类似设置。最后将系统的默认音频输出改为Voicemeeter Input,输入改为Voicemeeter Out B1。

Voicemeeter 电脑端设置

问题

Voicemeeter方案基本感觉不到延迟,但有个小问题。由于我是通过RDP连接NAS的,当RDP会话关闭时,NAS的扬声器设备会刷新,导致Voicemeeter无法自动识别刷新后的音响设备(即使在RDP中选择了音频在远程计算机上播放,设备名一样),解决方法就是弃用RDP,使用KVM。

AirPlay音频播放

使用Shairport Sync作为AirPlay播放器。AirPlay需要mDNS广播,由于我的NAS是Winserver系统,而windows下的docker无法设置host网络模式,所以我选择了在hyper-v中搭建ubuntu server虚拟机,在虚拟机中安装Shairport Sync并通过pulseaudio将音频发送到NAS上播放(没有尝试bridge模式以及mDNS反射等方案)。

Shairport Sync安装

首先安装必要的库(把后面所有需要的都塞这里了)

sudo apt install --no-install-recommends build-essential git autoconf automake libtool \
    libpopt-dev libconfig-dev libasound2-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev \
    libplist-dev libsodium-dev libavutil-dev libavcodec-dev libavformat-dev uuid-dev libgcrypt-dev xxd libpulse-dev \
    pulseaudio pulseaudio-utils gstreamer1.0-gl gstreamer1.0-plugins-bad gstreamer1.0-plugins-base \
    gstreamer1.0-plugins-good gstreamer1.0-x bluez pulseaudio-module-bluetooth libmosquitto-dev

编译安装NQPTP:

git clone https://github.com/mikebrady/nqptp.git
cd nqptp
autoreconf -fi
./configure --with-systemd-startup
make
sudo make install

sudo systemctl enable nqptp
sudo systemctl start nqptp

编译安装Shairport Sync

git clone https://github.com/mikebrady/shairport-sync.git
cd shairport-sync
autoreconf -fi
./configure --sysconfdir=/etc --with-alsa \
  --with-soxr --with-avahi --with-ssl=openssl --with-systemd --with-airplay-2 --with-pa --with-stdout --with-pipe --with-metadata --with-mqtt-client
make
sudo make install

sudo systemctl enable shairport-sync
sudo systemctl start shairport-sync

Pulseaudio设置

一般情况下安装好Shairport Sync就可以了,但我需要使用pulseaudio作为Shairport Sync的音频后端,需要一些额外的设置,简单来说就是要把pulseaudio运行在系统层级(试过把shairport-sync运行在用户层级,但失败)。

首先将shairport-sync用户加入到pulse-access用户组:

sudo usermod -a -G pulse-access shairport-sync

然后复制服务文件并修改:

sudo cp /usr/lib/systemd/user/pulseaudio.service /etc/systemd/system/pulseaudio.service
sudo cp /usr/lib/systemd/user/pulseaudio.socket /etc/systemd/system/pulseaudio.socket

sudo vim /etc/systemd/system/pulseaudio.service
# 注释 ConditionUser=!root
# 在ExecStart参数中添加--system

sudo vim /etc/systemd/system/pulseaudio.socket
# 注释 ConditionUser=!root

然后配置rtp发送音频:

sudo vim /etc/pulse/default.pa
sudo vim /etc/pulse/system.pa
# 在这两个文件中添加如下三行:
load-module module-null-sink sink_name=rtp format=s16be channels=2 rate=44100 sink_properties="device.description='RTP'"
load-module module-rtp-send source=rtp.monitor destination_ip=192.168.1.100 port=4714
set-default-sink rtp

最后启动服务:

sudo systemctl enable pulseaudio.service
sudo systemctl start pulseaudio.service

然后在NAS中打开VLC,点击媒体→打开网络串流,URL输入 rtp://0.0.0.0:4714 (与上述端口一致),在更多选项中将缓存设置为较小数值(影响延迟),点击播放并挂在后台即可(linux可直接使用pulseaudio的module-rtp-recv)。

废弃方案

在windows上也使用pulseaudio接收音频。下载PulseAudio on Windows,在 default.pa 中修改 module-waveout 一项,添加 record=0:

load-module module-waveout sink_name=output source_name=input record=0

并在最后添加一行:

load-module module-native-protocol-tcp listen=0.0.0.0 auth-anonymous=1

然后运行:

.\bin\pulseaudio.exe --use-pid-file=false -D

然后在虚拟机的 /etc/pulse/default.pa/etc/pulse/system.pa 中添加两行:

load-module module-tunnel-sink server=192.168.1.100 sink_name=remote
set-default-sink remote

该方案废弃的原因是使用tcp传输音频,延迟过大(300ms左右),而且PulseAudio on Windows不支持module-rtp-recv,所以废弃。

在HomeAssistant中控制AirPlay

修改shairport-sync的配置文件:

sudo vim /etc/shairport-sync.conf

在mqtt项中配置如下:

mqtt =
{
    enabled = "yes";
    hostname = "<host_of_your_mqtt_broker>";
    port = 1883;
    topic = "your/mqtt/topic";
    publish_parsed = "yes";
    publish_cover = "yes";
    enable_remote = "yes";
    username = "username";
    passwort = "password";
}

并在metadata项中将enabled, include_cover_art, cover_art_cache_directory, pipe_name, pipe_timeout取消注释。

HomeAssistant中,首先安装mqtt,然后在HACS中搜索hass-shairport-sync并安装。

在configuration.yaml中添加:

media_player:
  - platform: shairport_sync
    name: Zireael-Audio
    topic: audio/shairport

重启HomeAssistant后就可以将AirPlay音响组件添加到首页,实现调整音量、切歌、暂停等功能。

蓝牙音频播放

通常来讲安装好 pulseaudio-module-bluetooth 之后,手机连接蓝牙后就可以直接播放音频,但我是winserver

NAS端设置

首先需要解决蓝牙问题,我选用的方案是购买一个USB蓝牙适配器并通过USB/IP传入虚拟机。将蓝牙适配器插入NAS,在NAS上安装usbipd-win,然后运行:

usbipd list
# 找到蓝牙适配器的BUSID,作为下一条命令的参数
usbipd bind -b x-x

虚拟机端设置

在虚拟机中:

# 加载vhci_hcd
sudo modprobe vhci_hcd
# 填写NAS的ip以及蓝牙适配器的BUSID
sudo usbip attach -r 192.168.1.100 -b x-x

上述两步可以设置自动完成:

sudo vim /etc/modules-load.d/vhci_hcd.conf

添加一行:

vhci_hcd

设置USB/IP自动连接:

sudo vim /etc/systemd/system/usbip-attach.service
[Unit]
Description=USB/IP Device Auto-Attach
After=network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes
# 替换成你的 IP 地址和 Bus ID
ExecStart=/usr/bin/usbip attach -r <server_ip> -b <bus_id>
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target

然后启用服务:

sudo systemctl daemon-reload
sudo systemctl enable usbip-attach.service

蓝牙连接

sudo bluetoothctl
discoverable on
scan on
# 手机蓝牙设置中点击配对后输入两次yes,然后trust手机蓝牙的MAC地址,后续可以自动连接
trust XX:XX:XX:XX:XX:XX

问题

需要保持至少一个活跃的用户会话,试过如下两种方法都没有用:

sudo usermod -a -G pulse-access $USER
sudo loginctl enable-linger $USER

只好在tty中手动登录一下。

废弃方案

将shairport-sync和pulseaudio都运行在用户层级,因为没声音,懒得修了就废弃了

将shairport-sync运行在用户层级

为shairport-sync用户创建家目录

mkdir /home/shairport-sync
chown shairport-sync:shairport-sync /home/shairport-sync

启用 shairport-sync 用户的 linger 功能,然后重启:

sudo loginctl enable-linger shairport-sync
sudo reboot

创建服务文件并更改权限:

vim /home/shairport-sync/.config/systemd/user/shairport-sync.service
chown shairport-sync:shairport-sync /home/shairport-sync/.config/systemd/user/shairport-sync.service

文件内容:

[Unit]
Description=Shairport Sync - AirPlay Audio Receiver
After=sound.target
Wants=network-online.target
After=network.target network-online.target

[Service]
ExecStart=/usr/local/bin/shairport-sync --log-to-syslog
Environment="XDG_RUNTIME_DIR=/run/user/996"

[Install]
WantedBy=default.target

启动服务

sudo -u shairport-sync XDG_RUNTIME_DIR=/run/user/$(id -u shairport-sync) systemctl --user daemon-reload
sudo -u shairport-sync XDG_RUNTIME_DIR=/run/user/$(id -u shairport-sync) systemctl --user enable shairport-sync
sudo -u shairport-sync XDG_RUNTIME_DIR=/run/user/$(id -u shairport-sync) systemctl --user start shairport-sync

配置pulseaudio发送音频

先把配置文件复制到用户目录下:

cp /etc/pulse/default.pa /home/shairport-sync/.config/pulse/
chown shairport-sync:shairport-sync /home/shairport-sync/.config/pulse/default.pa

然后修改 /home/shairport-sync/.config/pulse/default.pa, 添加如下三行:

load-module module-null-sink sink_name=rtp format=s16be channels=2 rate=44100 sink_properties="device.description='RTP'"
load-module module-rtp-send source=rtp.monitor destination_ip=192.168.1.100 port=4714
set-default-sink rtp

配置linger

以shairport-sync用户运行pulseaudio,并通过linger使服务在用户没有活动的时候保持运行。

sudo loginctl enable-linger shairport-sync

感想

如果能让我回到一年前,我一定不会选Winserver

本文链接:http://blog.zireaels.com/post/audio.html

-- EOF --

Comments