Psychopy音频的使用
本文主要解决以下问题:
- 指定音频引擎与设备;
- 播放音频文件
本文所使用的环境:
Python3.10
numpy==2.2.6
psychopy==2025.1.1
psychtoolbox==3.0.19.14
一、音频配置
Psychopy文档链接为Sound - for audio playback — PsychoPy v2025.1.1。
1. 指定音频引擎
Psychopy支持多种音频引擎,包括:
但由于种种原因,现在仅对ptb支持较好。因此建议使用ptb作为音频引擎。
配置方式如下:
from psychopy import prefs
prefs.hardware["audioLib"] = ["ptb"] # 强制使用 ptb 后端
当然,默认的引擎就是ptb,上面的代码也可以忽略不写。
需要指出的是,对于音频的配置,最好在导入sound组件前进行。
原因:因为一旦导入sound模块,并创建了sound对象,后端和设备可能就已经确定了。如果在导入sound模块之后更改prefs,可能不会影响已经创建的音频流。
特别地,sound.init()方法或许可以解决这个问题。
2. 指定音频设备
2.1 获取所有音频设备
指定音频设备的前提是我们知道哪些音频设备可以使用,下面的程序实现了这个效果:
import psychtoolbox.audio
devices = psychtoolbox.audio.get_devices()
for dev in devices:
print(dev)
运行结果如下图所示:
每一个都是一个字典,结构如下所示:
{'DefaultSampleRate': 48000.0,
'DeviceIndex': 5.0,
'DeviceName': 'DELL S3423DWC (NVIDIA High Definition Audio)',
'HighInputLatency': 0.0,
'HighOutputLatency': 0.01,
'HostAudioAPIId': 13.0,
'HostAudioAPIName': 'Windows WASAPI',
'LowInputLatency': 0.0,
'LowOutputLatency': 0.003,
'NrInputChannels': 0.0,
'NrOutputChannels': 2.0},
我这里准备使用的音频设备就是DELL S3423DWC (NVIDIA High Definition Audio),即DeviceName的值。
2.2 指定音频设备
如下所示,与指定音频引擎相类似:
prefs.hardware["audioDevice"] = 'DELL S3423DWC (NVIDIA High Definition Audio)'
注意导入prefs。
2.3 总结
推荐把上述内容写成函数:
def init_audio(device_name: str):
prefs.hardware["audioLib"] = ["ptb"]
devices = psychtoolbox.audio.get_devices()
exists = any(dev['DeviceName'] == device_name for dev in devices)
if not exists:
raise ValueError(f"音频设备 {device_name} 不存在")
prefs.hardware["audioDevice"] = device_name # 设置音频设备
二、播放音频文件
1. 读取音频文件
下面代码用于读取音频文件:
import os
from psychopy import sound
wav_path = os.path.join(base_path, "sound_click_1000Hz.wav")
tone = sound.Sound(wav_path, stereo=True, hamming=False)
需要注意的是,psychopy对部分主流音频文件格式没有支持,例如mp3等都无法使用。推荐的音频文件格式是.wav。
对部分属性说明如下:
- volume:音量,浮点数取值0~1.0,默认为1.0。
- stereo:是否使用立体声播放,True表示自动开启立体声,False表示单声道,默认值为True。
- loops:循环播放次数,0表示只播放1次,-1表示一直循环,默认值为0。
- hamming:用于控制是否对声音应用汉明窗,默认为True。
这里对汉明窗做特别说明:加窗是信号分析与处理中的常见操作,这里不再赘述,只简单概述结论。
汉明窗对信号有平滑的作用,对于需要精确控制声音波形的场景、已预处理的声音或其他特殊情况,可以选择关闭汉明窗,否则最好默认开启。
文档中对这一部分的解释是
boolean (default True) to indicate if the sound should be apodized (i.e., the onset and offset smoothly ramped up from down to zero).
其中apodized表示变迹。
此外还有一点需要说明:
The function apodize uses a Hanning window, but arguments named ‘hamming’ are preserved so that existing code is not broken by the change from Hamming to Hanning internally. Not applied to sounds from files.
可见使用的其实是汉宁窗,而不是汉明。
2. 播放音频
音频的播放非常简单,如下所示:
tone.play()
3. 音频播放状态
这个Sound有两个属性,可以反应音频是否在播放中或已经播放完毕
- isPlaying
- isFinished
下面的代码就起到了等待音频播放完毕的效果:
# 等待音频播放完毕
while tone.isFinished == False:
pass
4. 获取音频时长
有时候我们希望音频播放完毕后等待一段时间,这里提供了一种写法:
duration = tone.getDuration()
tone.play()
core.wait(1.5*duration)
播放后等待1.5个duration,也即播放后等待0.5个duration。