You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyAudio读取USB Audio 2.0流每1024字节微帧后1ms延迟问题排查

问题描述

背景

我已将自带集成USB控制器的SoC开发板配置为符合USB Audio 2.0规范的单声道8位PCM麦克风,采样频率48KHz,通过单个同步IN端点实现异步音频流传输接口。我将其接入Raspberry Pi 3B+,使用PyAudio读取音频流并存储为WAV文件。
为进行测试,我通过该接口传输存储在SoC RAM中的正弦信号,信号周期为1024个采样点(占用空间1024字节)。

异常现象(bInterval=1,125微秒轮询周期)

当设备端点配置为支持125微秒轮询周期(即bInterval=1)时,录制的WAV文件在每传输1024字节后会出现空白段:
端点bInterval为125微秒时的单声道8位PCM 0.5秒录音波形
放大后可观察到空白段时长约为1毫秒:
放大后的波形图
我了解到,支持125微秒轮询周期(bInterval=1)的USB 2.0高速同步端点每微帧可传输1024字节,USB Audio设备每微帧可发送1024字节数据,而USB全速模式下bInterval为1毫秒。目前看起来USB主机控制器仅每1毫秒发送一次同步IN令牌(即轮询),而非125微秒,因此Raspberry Pi上匹配采样频率和设备bInterval配置的音频流接口需要等待后续音频数据到达。

正常现象(bInterval=4,1毫秒轮询周期)

当我重新编程SoC开发板,将其配置为bInterval=4的USB Audio 2.0设备(即支持1毫秒轮询周期)时,传输的正弦信号没有异常,1024个采样点传输完成后无空白/静音段。这符合预期,因为结合PyAudio流配置和端点配置,音频应用(PyAudio流)预期每1毫秒收到1024采样点的帧。
bInterval=4(轮询周期1毫秒)时的录音波形
放大效果:
bInterval=4时的放大录音波形

疑问

请问USB控制器无法每125微秒发送IN令牌的原因属于以下哪一种?

  • 是PyAudio本身的问题:即它不支持125微秒微帧的IN令牌请求?
  • 与操作系统上其他进程导致的延迟有关?
  • 是ALSA硬件抽象层的底层问题?
  • 是硬件问题?(尽管Raspberry Pi 3B+的端口和控制器符合USB 2.0规范)

是否有方案可实现PyAudio在125微秒轮询间隔下的无间断音频流传输?否则端点1毫秒的轮询间隔会使可达到的吞吐量降低8倍。

*注意:我并不限定使用Raspberry Pi作为USB主机,如果有在其他硬件上使用PyAudio、通过配置为125微秒间隔的USB端点实现音频输入流录制的成功案例,也请告知。

测试代码

import pyaudio
import wave

form_1 = pyaudio.paUInt8 # 8-bit resolution
chans = 1 # 1 channel
samp_rate = 48000 # 48kHz sampling rate
chunk = 4096 
record_secs = 1 # seconds to record
dev_index = 2 # device index found by p.get_device_info_by_index(ii)
wav_output_filename = 'sinad.wav' # name of .wav file

audio = pyaudio.PyAudio() # create pyaudio instantiation

# create pyaudio stream
stream = audio.open(format = form_1,rate = samp_rate,channels = chans, \
                    input_device_index = dev_index,input = True, \
                    frames_per_buffer=chunk)
print("recording")
frames = []

# loop through stream and append audio chunks to frame array
for ii in range(0,int((samp_rate/chunk)*record_secs)):
    data = stream.read(chunk)
    frames.append(data)

print("finished recording")

# stop the stream, close it, and terminate the pyaudio instantiation
stream.stop_stream()
stream.close()
audio.terminate()

# save the audio frames as .wav file
wavefile = wave.open(wav_output_filename,'wb')
wavefile.setnchannels(chans)
wavefile.setsampwidth(audio.get_sample_size(form_1))
wavefile.setframerate(samp_rate)
wavefile.writeframes(b''.join(frames))
wavefile.close()

解答

问题原因定位

首先可以排除PyAudio本身的问题,PyAudio是基于PortAudio的上层封装,本身不直接控制USB总线的令牌调度,USB令牌的发送完全由操作系统内核的USB主机控制器驱动、ALSA层的USB Audio类驱动负责。

你遇到的问题核心原因优先级从高到低排序:

  1. 树莓派3B+的USB主机控制器驱动限制:树莓派3B+搭载的博通BCM2837内置DWC2 USB控制器,默认配置为全速优先,高速同步端点的微帧调度默认未开启,官方内核默认的调度粒度为1ms,不会按125us微帧触发同步传输,属于硬件默认配置问题。
  2. ALSA配置问题:默认ALSA的USB Audio驱动默认的最大传输周期配置为1ms,会自动合并多个微帧的数据包到1ms块返回。
  3. 代码参数配置不合理:你设置的frames_per_buffer=4096对应的缓冲时长约为85ms,远大于125us的轮询间隔,也会导致驱动合并数据包。

修复方案

  1. 先修改树莓派内核启动参数:编辑/boot/cmdline.txt,在末尾追加dwc2.utmi_hibernate=0 dwc2.dma_enable=1,保存重启,开启DWC2控制器的高优先级DMA调度和微帧支持。
  2. 修改ALSA配置:编辑/etc/modprobe.d/alsa-base.conf,添加一行options snd-usb-audio nrpacks=0,保存后重新加载snd-usb-audio模块或者重启系统,关闭ALSA的自动批量打包功能,让驱动按设备实际的bInterval调度。
  3. 修改代码中的frames_per_buffer参数为128,对应约2.6ms的缓冲,和125us的轮询间隔匹配。
  4. 如果需要更高的吞吐量,也可以换用x86平台的USB 2.0以上控制器,主流x86的EHCI/XHCI控制器默认原生支持125us微帧的同步端点调度,不需要额外修改配置就可以正常工作,仅需要调整缓冲块大小就可以实现无空白传输。

内容的提问来源于stack exchange,提问作者Ali Rahman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 09:27:00