PyAudio本身不提供声音触发逻辑,需自行计算音频能量(如RMS值)并设阈值判断是否“有声音”,否则会一直录音或永不启动;关键在于每100ms取帧计算音量,低于阈值(如0.01)即视为静音。
为什么直接用 pyaudio 录音常卡在“没声音就不录”上
因为 pyaudio 本身不提供“声音触发”逻辑,它只是读音频流的管道。你得自己计算每段音频的能量(比如 RMS 值),再设阈值判断是否算“有声音”。很多人直接写个 while 循环 stream.read() 却忘了做能量检测,结果要么一直录(哪怕静音),要么永远不启动录音。
关键点在于:静音检测不是靠听,是靠算——每 100ms 左右取一帧(如 1024 个采样点),转成 numpy 数组后算 np.sqrt(np.mean(np.square(data))),结果低于 0.01 基本就是环境底噪水平(具体阈值需实测)。
- 麦克风设备索引错会导致
OSError: No Default Input Device,先用pyaudio.PyAudio().get_device_count()和.get_device_info_by_index(i)列出来确认 - 采样率选
16000足够识别语音,比44100省 CPU,也降低误触概率 - 别用
time.sleep(0.1)控制循环节奏——它不准,改用stream.get_read_available()配合非阻塞读更稳
怎样用 sounddevice + numpy 实现低延迟触发
sounddevice 比 pyaudio 更容易做实时流回调,且默认支持 float32 数据,省去类型转换麻烦。核心是注册一个 stream.start(callback=...),每次有新音频进来就进回调函数,在里面做 RMS 计算和状态切换。
示例片段(仅关键逻辑):
import sounddevice as sd import numpy as npis_recording = False audio_buffer = [] threshold = 0.015 samplerate = 16000
def audio_callback(indata, frames, time, status): global is_recording, audio_buffer if status: print(status)
只取左声道(单麦常见),转为绝对值均方根
volume_norm = np.linalg.norm(indata[:, 0]) / len(indata) if volume_norm > threshold and not is_recording: print("?️ 声音触发,开始录音") is_recording = True audio_buffer = [indata.copy()] elif is_recording: audio_buffer.append(indata.copy()) # 录够 5 秒自动停(防无限录) if len(audio_buffer) * frames / samplerate > 5.0: save_recording() is_recording = Falsedef save_recording():
full = np.concatenate(audio_buffer, axis=0)
import wavio
wavio.write("recording.wav", full, samplerate, sampwidth=2)
- 回调里别做文件 IO(如
wavio.write),会拖慢流处理,应只存内存,另起线程保存 -
indata是 float32 类型,范围 [-1.0, 1.0],直接算范数比转 int16 再算更准 - Windows 上若报
PortAudioError: Error opening Stream,大概率是设备被微信/QQ 占了,关掉再试
如何避免“录到一半突然停”或“反复启停”
真实环境里声音是断续的(比如人说话有停顿),如果一掉到阈值以下就立刻停,会把一句完整的话切成三段。得加“释放延时”和“防抖”逻辑。
Python 3.14.3
微软官方的 Python 扩展,是 VS Code 安装量最高的扩展(209M+)。集成 IntelliSense(通过 Pylance)、调试(通过 Python Debugger)、代码检查、格式化、重构和单元测试等功能。支持 Jupyter Notebook、虚拟环境管理和多 Python 版本切换。
- 引入
silence_counter:音量低于阈值时计数,连续 30 帧(约 200ms)才认为真静音 - 启动时加“预录缓冲”:从第一次触发往前补 300ms 音频(需 ring buffer 实现),否则总丢开头字
- 避免高频触发:停录后强制锁定 1.5 秒,期间忽略所有声音,防止键盘敲击、咳嗽等干扰
- Linux 下若用 PulseAudio,记得在
/etc/pulse/default.pa加load-module module-loopback latency_msec=1降低延迟(可选)
Mac 上权限被拒、设备列不出来怎么办
macOS 10.15+ 强制要求显式授权麦克风,Python 脚本首次运行会静默失败,不会弹窗。必须手动打开:「系统设置 → 隐私与安全性 → 麦克风」,勾选你的终端(如 Terminal 或 iTerm)或 Python IDE(如 PyCharm)。
另外,Mac 的默认输入设备名常含空格或括号,比如 "MacBook Pro Microphone (Built-in)",用 sd.query_devices() 查到后,要原样传给 sd.InputStream(device=...),不能截断或替换空格。
- 如果
sd.query_devices()返回空列表,重启coreaudiod:执行sudo killall coreaudiod - VS Code 终端跑脚本没权限?换用系统的 Terminal.app 启动,或在 VS Code 设置里启用
terminal.integrated.env.osx注入权限上下文 - 录出来的 WAV 播放时有爆音?检查是否用了
sampwidth=3(24bit),macOS 对非 16bit 支持不稳定,统一用sampwidth=2
实际部署时最常被忽略的是环境差异:同一段代码在办公室能用,在家里可能因空调底噪高而狂录,这时阈值就得从 0.015 动态调到 0.03;还有人把脚本扔进 crontab,却忘了 GUI 权限上下文丢失——这种问题不会报错,只会静默失效。
就爱读
