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支持多種音頻引擎,包括:
-
SoundPTB
-
SoundDevice
-
SoundPyo
-
SoundPygame
但由于種種原因,現在僅對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。