【Agent】在基于WSL2的Linux的ALSA输出音频
Linux使用ALSA作为音频驱动。作为Agent的音频输出,我计划在WSL(Ubuntu 22.04.5 LTS)上播放音频。
起因
WSL默认没有安装alsa工具包,需要通过如下命令安装:
sudo apt install alsa-utils
安装成功后,尝试执行如下命令打印音频设备:
bluebonnet27@bluebonnet27:~$ aplay -l
aplay: device_list:274: no soundcards found...
怎么会没有音频设备呢?
为WSL2添加音频驱动
WSL2使用的是微软定制的Linux内核,为了保持轻量级,默认不包含许多硬件驱动,包括音频驱动。
网上使用socket共享的方式适用于旧版的WSL,新版的WSLg使用了更简单的方式映射音频设备。
执行wsl --version
查询wsl版本:
WSL 版本: 2.6.1.0
内核版本: 6.6.87.2-1
WSLg 版本: 1.0.66
MSRDC 版本: 1.2.6353
Direct3D 版本: 1.611.1-81528511
DXCore 版本: 10.0.26100.1-240331-1435.ge-release
Windows: 10.0.26100.6584
执行以下命令,以配置PulseAudio使用WSLg的音频服务:
# 创建或编辑PulseAudio配置文件
mkdir -p ~/.config/pulse
echo "default-server = unix:/mnt/wslg/PulseServer" > ~/.config/pulse/client.conf
此时/mnt/wslg/
下会多出一个链接:
total 56
srwxrwxrwx 1 bluebonnet27 bluebonnet27 0 Sep 27 18:47 PulseAudioRDPSink
srwxrwxrwx 1 bluebonnet27 bluebonnet27 0 Sep 27 18:47 PulseAudioRDPSource
srwxrwxrwx 1 bluebonnet27 bluebonnet27 0 Sep 27 18:47 PulseServer
drwxr-xr-x 19 root root 4096 Sep 27 18:46 distro
drwxr-xr-x 65 root root 4096 Sep 13 2024 doc
-rw------- 1 bluebonnet27 bluebonnet27 1371 Sep 27 18:47 pulseaudio.log
drwxr-xr-x 3 root root 60 Sep 27 18:46 run
drwxrwxrwx 3 bluebonnet27 bluebonnet27 100 Sep 27 18:47 runtime-dir
-r--r--r-- 1 root root 893 Sep 27 18:47 stderr.log
-rw-r--r-- 1 root root 329 Sep 13 2024 versions.txt
-rw-rw-rw- 1 bluebonnet27 bluebonnet27 32091 Sep 27 19:45 weston.log
-rw-rw-rw- 1 bluebonnet27 bluebonnet27 1679 Sep 27 18:47 wlog.log
接下来,默认的ALSA 直接访问硬件设备(通过 /dev/snd/ 设备节点),而 WSLg 中默认不创建这些节点(因为音频是通过 PulseAudio 桥接到 Windows,而非直接暴露硬件),所以会显示 “无设备”。因此,需要添加全局配置:
sudo tee /etc/asound.conf <<EOF
pcm.!default {type pulse
}
ctl.!default {type pulse
}
pcm.pulse {type pulse
}
ctl.pulse {type pulse
}
EOF
最后,需要安装一个语音相关的库:
sudo apt install libasound2-plugins
重启WSL,此时使用aplay -L
就能打印设备了:
nullDiscard all samples (playback) or generate zero samples (capture)
samplerateRate Converter Plugin Using Samplerate Library
speexrateRate Converter Plugin Using Speex Resampler
jackJACK Audio Connection Kit
ossOpen Sound System
pulsePulseAudio Sound Server
upmixPlugin for channel upmix (4,6,8)
vdownmixPlugin for channel downmix (stereo) with a simple spacialization
default
ubuntu有默认的测试音频,可以播放试试:
aplay /usr/share/sounds/alsa/Front_Center.wav
或者使用alsamixer
打开声音面板:
因为是pulseaudio
转的音频设备,因此只有默认这一种设备。
使用pyaudio播放
安装pyaudio之前,需要安装如下依赖:
sudo apt install python3-pyaudio portaudio19-dev libpulse-dev
然后安装即可:
pip install pyaudio
如下python代码可以打印当前pyaudio能读取到的音频设备,我们只是确认有即可,由于之前的原因,这里应该只有一个设备。
import pyaudiop = pyaudio.PyAudio()
for i in range(p.get_device_count()):dev = p.get_device_info_by_index(i)print(f"设备 {i}: {dev}")
p.terminate()
打印信息如下(略去ALSA的打印):
设备 0: {'index': 0, 'structVersion': 2, 'name': 'pulse', 'hostApi': 0, 'maxInputChannels': 32, 'maxOutputChannels': 32, 'defaultLowInputLatency': 0.008684807256235827, 'defaultLowOutputLatency': 0.008684807256235827, 'defaultHighInputLatency': 0.034807256235827665, 'defaultHighOutputLatency': 0.034807256235827665, 'defaultSampleRate': 44100.0}
设备 1: {'index': 1, 'structVersion': 2, 'name': 'default', 'hostApi': 0, 'maxInputChannels': 32, 'maxOutputChannels': 32, 'defaultLowInputLatency': 0.008684807256235827, 'defaultLowOutputLatency': 0.008684807256235827, 'defaultHighInputLatency': 0.034807256235827665, 'defaultHighOutputLatency': 0.034807256235827665, 'defaultSampleRate': 44100.0}
接下来就可以编写代码,使用pyaudio播放音频了。我这里给一个示例代码:
import pyaudio
import wave
import sysdef list_audio_devices():"""列出所有可用的音频输出设备"""p = pyaudio.PyAudio()print("可用音频输出设备:")for i in range(p.get_device_count()):device_info = p.get_device_info_by_index(i)if device_info.get('max_output_channels', 0) > 0:default = "(默认设备)" if device_info['default_output'] else ""print(f" 设备 {i}: {device_info['name']} {default}")p.terminate()def play_wav(file_path, device_index=None):"""播放WAV文件:param file_path: WAV文件路径:param device_index: 输出设备索引,None表示使用默认设备"""# 打开WAV文件try:wf = wave.open(file_path, 'rb')except FileNotFoundError:print(f"错误:找不到文件 {file_path}")return# 初始化PyAudiop = pyaudio.PyAudio()# 打开音频流stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),channels=wf.getnchannels(),rate=wf.getframerate(),output=True,output_device_index=device_index # 指定输出设备,None则使用默认)# 读取并播放音频数据print(f"开始播放 {file_path}...")chunk = 1024data = wf.readframes(chunk)while data:stream.write(data)data = wf.readframes(chunk)# 清理资源print("播放结束")stream.stop_stream()stream.close()wf.close()p.terminate()if __name__ == "__main__":# 默认播放的WAV文件路径wav_file = "/usr/share/sounds/alsa/Front_Center.wav"# 列出可用设备list_audio_devices()# 询问用户是否使用默认设备use_default = input("\n是否使用默认输出设备?(y/n): ").strip().lower()if use_default == 'n':try:device_idx = int(input("请输入要使用的设备索引:").strip())play_wav(wav_file, device_idx)except ValueError:print("无效的设备索引,使用默认设备播放")play_wav(wav_file)else:play_wav(wav_file)