折腾 KVM 虚拟机 GPU 直通的用户,在享受 Looking Glass 带来的超低延迟画面的同时,大概率都会被它的音频同步问题折磨过。
如果使用 QEMU 默认的 ICH9 或 AC97 模拟声卡,或者走 Spice 协议传输音频,在游戏或高负载场景下,极易出现爆音、杂音、甚至是声音逐渐比画面慢几秒的严重滞后。
要实现音画同步的极致体验,目前最完美的方案是利用 Scream 配合 IVSHMEM(虚拟机间共享内存),将音频数据直接写入内存,并在宿主机通过 PipeWire 进行极低延迟的解码和播放。这套方案不经过虚拟网络栈,延迟几乎为零,且不额外消耗 CPU。
下面是完整的配置实操指南。
准备工作
在开始之前,请确保:
- 宿主机已安装并正常运行 PipeWire(以及
pipewire-pulse兼容层)。 - 虚拟机(Windows)已经配置好 Looking Glass 并且能正常显示画面。
第一步:在宿主机创建共享内存文件
Scream 需要一块共享内存来向宿主机传递音频数据。我们需要在 /dev/shm 下创建一个名为 scream-ivshmem 的临时文件,并赋予当前用户读写权限。
为了保证宿主机每次开机自动创建该文件,建议使用 systemd-tmpfiles。
在宿主机上新建或编辑配置文件:
sudo nano /etc/tmpfiles.d/scream-ivshmem.conf
写入以下内容(注意:请将 your_username 替换为你宿主机的实际登录用户名):
# Type Path Mode User Group Age Argument
f /dev/shm/scream-ivshmem 0660 your_username kvm -
保存退出后,立即执行以下命令使配置生效并创建文件:
sudo systemd-tmpfiles --create /etc/tmpfiles.d/scream-ivshmem.conf
第二步:在虚拟机 XML 中添加共享内存设备
我们需要把刚才创建的这块内存挂载到虚拟机里。
使用 virsh edit 你的虚拟机名字 或者通过 Virt-Manager 的 XML 编辑器,在 <devices> 标签内添加以下内容:
<shmem name='scream-ivshmem'>
<model type='ivshmem-plain'/>
<size unit='M'>2</size>
</shmem>
避坑提示:Scream 只需要 2MB 的共享内存(2M 就足够容纳音频缓冲区)。如果你之前为了 Looking Glass 已经配置了一个共享内存设备(比如
looking-glass),千万不要混用。Scream 必须使用自己独立的shmem设备。
保存并启动虚拟机。
第三步:虚拟机(Windows)安装 Scream 驱动
- 启动 Windows 虚拟机,打开设备管理器。
- 你会看到一个带有黄色感叹号的未知 PCI 设备(或者是叫
PCI 标准 RAM 控制器)。这个就是我们刚才添加的 IVSHMEM 设备。 - 去 GitHub 下载 Scream 的最新发布版本。注意要下载支持 IVSHMEM 的版本:
- 访问 Scream GitHub Releases
- 下载
Scream-DXE-xxxxx.zip(或者寻找专门标注有ivshmem驱动的包,解压后会有Driver\Scream目录)。
- 在设备管理器中,右键点击那个 PCI 设备 -> 更新驱动程序 -> 浏览我的电脑以查找驱动程序。
- 定位到你解压的
Scream/driver文件夹,安装该驱动。 - 安装成功后,Windows 的声音输出设备中会出现一个名为
Scream (virtual)的设备。将其设置为默认播放设备。
第四步:宿主机配置 PipeWire 接收端
宿主机需要运行一个接收端程序,从 /dev/shm/scream-ivshmem 中读取数据并输入给 PipeWire。
1. 安装编译接收端
在 Arch Linux 上,可以直接通过 AUR 安装:
paru -S scream-git
在 Ubuntu/Debian 上,可以编译安装:
sudo apt install build-essential cmake libpulse-dev
git clone https://github.com/duncanthrax/scream.git
cd scream/Receivers/unix
mkdir build && cd build
cmake ..
make
2. 编写 Systemd 用户服务自启
为了让接收端在用户登录时自动后台运行,我们可以编写一个 Systemd 用户服务。
创建目录(如果不存在):
mkdir -p ~/.config/systemd/user/
创建服务文件:
nano ~/.config/systemd/user/scream.service
写入以下配置:
[Unit]
Description=Scream IVSHMEM Receiver
After=pipewire.service
[Service]
Type=simple
# 强制指定 PulseAudio/PipeWire 延迟环境变量,避免音频断续
Environment=PIPEWIRE_LATENCY="128/48000"
ExecStart=/usr/bin/scream -m /dev/shm/scream-ivshmem
Restart=always
RestartSec=5
[Install]
WantedBy=default.target
注意:如果你是自行编译的
scream接收端,请将ExecStart中的路径替换为你编译出来的二进制文件实际路径(例如/usr/local/bin/scream)。
保存并退出,然后启用该服务:
systemctl --user daemon-reload
systemctl --user enable --now scream.service
此时,运行 systemctl --user status scream 应该能看到服务处于活动(running)状态。
第五步:消除杂音与不同步的“终极调优”
如果你在完成上述步骤后听到沙沙的杂音、爆音,或者游戏运行一段时间后声音变慢,通常是由于采样率不匹配或系统时钟漂移导致的。请按以下步骤调优:
1. 统一采样率(最重要)
Windows 端 Scream 驱动默认输出通常是 48000 Hz(DVD 画质)。
在 Windows 的“声音控制面板”中,双击 Scream 播放设备,切换到“高级”选项卡,确保默认格式设置为 24位/16位,48000 Hz。
同时,确保你的 PipeWire 默认工作在 48000 Hz。编辑或创建 /etc/pipewire/pipewire.conf(或用户目录下的 ~/.config/pipewire/pipewire.conf),确保或添加以下配置:
default.clock.rate = 48000
default.clock.allowed-rates = [ 44100 48000 ]
修改后重启 PipeWire:systemctl --user restart pipewire。
2. 优化 QEMU 时钟配置
时钟漂移是导致“声音越来越慢”的罪魁祸首。在虚拟机的 XML 配置文件中,找到 <clock> 部分,确保包含以下针对 Windows 优化的计时器:
<clock offset='localtime'>
<timer name='rtc' tickpolicy='catchup'/>
<timer name='pit' tickpolicy='delay'/>
<timer name='hpet' present='no'/>
<timer name='hypervclock' present='yes'/>
</clock>
hypervclock 能够极大改善 Windows 客户机的计时精度,配合 rtc 的 catchup 策略,可以基本消灭由于虚拟机时钟慢于宿主机导致的音频渐进式延迟。
3. 微调 PipeWire 缓冲区大小
如果你的 CPU 负载极高时偶尔出现“咔哒”一声的爆音,可以适当增大服务文件中的 PIPEWIRE_LATENCY 缓冲区。
编辑 ~/.config/systemd/user/scream.service:
将 PIPEWIRE_LATENCY="128/48000" 改为 PIPEWIRE_LATENCY="256/48000"。
数值越大,抗波动能力越强,但延迟会微幅上升(256 采样在 48k 下仅为 5.3 毫秒,肉眼完全无法察觉)。
修改后执行:
systemctl --user daemon-reload
systemctl --user restart scream
享受你那音画完美同步、毫无延迟且纯净无杂音的 Looking Glass 游戏虚拟机吧!