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

基于webrtcvad的类Siri语音录制:WAV文件无声音问题排查

问题原因

你的代码核心问题在于音频数据类型不匹配:

  • sounddevice.RawInputStream输出的indata是numpy.ndarray对象,但webrtcvad.is_speech需要传入原始字节流(bytes);同时你将numpy数组直接存入self.frames,后续用b''.join(self.frames)拼接时无法得到正确的音频字节数据,最终导致保存的WAV文件无声。
修复方案

需要将RawInputStream输出的numpy数组转换为bytes后再传入VAD处理,同时确保self.frames存储的是字节数据。修改后的完整代码如下:

import numpy as np
import sounddevice as sd
from scipy.io.wavfile import write
import webrtcvad
import threading

class VoiceActivityDetector:
    def __init__(self, frame_duration_ms=30, padding_duration_ms=300, vad_aggressiveness=3):
        self.vad = webrtcvad.Vad(vad_aggressiveness)
        self.frame_duration_ms = frame_duration_ms
        self.padding_duration_ms = padding_duration_ms
        self.original_num_padding_frames = padding_duration_ms // frame_duration_ms
        self.num_padding_frames = self.original_num_padding_frames
        self.frames = []
        self.triggered = False
        self.has_spoken = False

    def process_frame(self, frame_bytes):
        # frame_bytes 是bytes类型,符合webrtcvad要求
        is_speech = self.vad.is_speech(frame_bytes, 16000)
        if not self.triggered:
            self.frames.append(frame_bytes)
            if is_speech:
                self.triggered = True
                self.has_spoken = True
                # 保留触发前的最后N帧,确保语音开头完整
                self.frames = self.frames[-self.original_num_padding_frames:]
                print("speech detected")
        else:
            self.frames.append(frame_bytes)
            if not is_speech and self.has_spoken:
                self.num_padding_frames -= 1
                print(f'Silence countdown: {self.num_padding_frames}')
                if self.num_padding_frames == 0:
                    print('silence detected...')
                    return False  # 停止录制
            elif is_speech:
                self.num_padding_frames = self.original_num_padding_frames
        return True

    def record(self, fs=16000):
        self.stop_recording = threading.Event()

        def callback(indata, frames, time, status):
            if status:
                print(status)
            # 将numpy数组转换为bytes,这是关键修复点
            frame_bytes = indata.tobytes()
            if not self.process_frame(frame_bytes):
                self.stop_recording.set()

        blocksize = int(fs * self.frame_duration_ms / 1000)
        with sd.RawInputStream(
            samplerate=fs,
            blocksize=blocksize,
            channels=1,
            dtype='int16',
            callback=callback
        ):
            while not self.stop_recording.is_set():
                sd.sleep(100)
        # 将拼接后的字节转换为numpy int16数组
        recording = np.frombuffer(b''.join(self.frames), dtype=np.int16)
        return recording, fs

def get_user_response():
    vad = VoiceActivityDetector()
    print("Please start speaking, when finished, pause and the recording will end...")
    recording, fs = vad.record()

    print('Saving the recording...')
    output_filename = "temporary.wav"
    write(output_filename, fs, recording)
    print(f"Recording saved as {output_filename}")

if __name__ == "__main__":
    get_user_response()
关键修改点
  • 在callback函数中添加frame_bytes = indata.tobytes(),将numpy数组转换为符合VAD要求的字节流
  • 确保process_frame接收和存储的都是bytes类型数据,后续拼接和转换为numpy数组的逻辑才能正常工作

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 21:45:28