简单了解下音频和VAD

一、音频数据

声音的本质是空气的振动,是一种模拟信号(声波 → 麦克风 → 电信号(连续变化) → 数字信号(离散采样))。

数字音频有三个关键参数:采样率、位深度以及声道数。

  • 采样率:每秒采样多少次,16kHz = 每秒16k个点
  • 位深度:每个样本用多少位表示。16位表示范围是(也就是-32768 到 32767)
  • 声道数:单声道、双声道(立体声)以及多声道。单声道表示一个声道

数字音频数组

  • 单声道是一维数组,该数组包含时间点和值两个参数,其中时间点是通过采样率决定的(如:16kHz 也就是说0-1秒中的第一个采样点为1/16000),时间点对应的值是通过采样点的振幅 * 位深度的最大值得到的。
python 复制代码
# 创建一段简单的正弦波(模拟人的声音)
import matplotlib.pyplot as plt
import numpy as np

# 生成一个 440Hz 的正弦波(标准A音)
sample_rate = 16000
duration = 0.1  # 100毫秒
t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)
frequency = 440  # 440Hz(音高A)
audio_wave = 0.5 * np.sin(2 * np.pi * frequency * t)

# 可视化
plt.figure(figsize=(10, 4))
plt.plot(t[:100], audio_wave[:100])  # 只看前100个点
plt.title("音频波形(正弦波)")
plt.xlabel("时间 (秒)")
plt.ylabel("振幅")
plt.grid(True)
plt.show()
  • 多声道至少是二维数组,第一维度表示时间,第二维度表示声道,其中的值就是该时间点不同声道的值。
python 复制代码
import numpy as np
import matplotlib.pyplot as plt
import soundfile as sf

# 参数设置
sample_rate = 16000
duration = 0.1  # 100毫秒
num_samples = int(sample_rate * duration)

# 生成时间序列
t = np.linspace(0, duration, num_samples, endpoint=False)

# 创建双声道音频
# 左声道:440Hz 正弦波
frequency_left = 440  # A音
left_channel = 0.5 * np.sin(2 * np.pi * frequency_left * t)

# 右声道:523.25Hz 正弦波(C音,比左声道高)
frequency_right = 523.25
right_channel = 0.3 * np.sin(2 * np.pi * frequency_right * t)

# 组合成双声道音频(形状:[样本数, 2])
stereo_audio = np.column_stack((left_channel, right_channel))

print("双声道音频形状:", stereo_audio.shape)  # 应该是 (1600, 2)
print(f"总样本数: {num_samples}")
print(f"左声道数据类型: {left_channel.dtype}")
print(f"右声道数据类型: {right_channel.dtype}")
print(f"立体声音频数据类型: {stereo_audio.dtype}")

# 可视化双声道音频
fig, axes = plt.subplots(3, 1, figsize=(12, 8))

# 左声道波形
axes[0].plot(t[:200], left_channel[:200], 'b-', linewidth=1.5)
axes[0].set_title(f'左声道 (左耳) - {frequency_left}Hz 正弦波', fontsize=12)
axes[0].set_xlabel('时间 (秒)')
axes[0].set_ylabel('振幅')
axes[0].grid(True, alpha=0.3)
axes[0].set_xlim([0, 0.0125])  # 只看前12.5ms

# 右声道波形
axes[1].plot(t[:200], right_channel[:200], 'r-', linewidth=1.5)
axes[1].set_title(f'右声道 (右耳) - {frequency_right}Hz 正弦波', fontsize=12)
axes[1].set_xlabel('时间 (秒)')
axes[1].set_ylabel('振幅')
axes[1].grid(True, alpha=0.3)
axes[1].set_xlim([0, 0.0125])

# 双声道对比
axes[2].plot(t[:200], left_channel[:200], 'b-', label='左声道', alpha=0.7)
axes[2].plot(t[:200], right_channel[:200], 'r-', label='右声道', alpha=0.7)
axes[2].set_title('双声道对比', fontsize=12)
axes[2].set_xlabel('时间 (秒)')
axes[2].set_ylabel('振幅')
axes[2].legend()
axes[2].grid(True, alpha=0.3)
axes[2].set_xlim([0, 0.0125])

plt.tight_layout()
plt.show()

二、VAD

VAD本质上是模式识别器:输入:480个数字(30ms音频)-> 特征提取(频谱分析)-> 分类器(机器学习模型)-> 输出:True(语音)或 False(非语音)

WebRTC VAD (最简模式) SpeechBrain VAD (深度学习范式) Silero VAD (现代化接口)
核心方法数量 1个 is_speech() 10+个 5-8个
复杂度 ⭐⭐⭐⭐⭐ ⭐⭐⭐
学习曲线 ⭐⭐⭐⭐ ⭐⭐
准确性 中等(85-92%) 高(95%+) 很高(98%+)
速度 ⭐⭐⭐⭐⭐ 初始化就直接创建检测器对象 ⭐⭐ 初始化的时候需要下载模型、加载权重、构建网络 ⭐⭐⭐⭐ 初始化需要下载并加载模型
资源占用 ⭐⭐⭐⭐⭐ <1MB ⭐⭐ 几百MB ⭐⭐⭐ 10-50MB
部署难度 ⭐⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐
长音频处理 ⭐⭐⭐⭐⭐ 最稳定 ⭐⭐⭐ ⭐⭐⭐⭐
噪声鲁棒性 ⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
多语言支持 语言无关 语言无关 多语言
可解释性 高 参数明确,方便调试 低(黑盒) 中等
社区生态 ⭐⭐⭐⭐⭐ 最成熟 ⭐⭐⭐⭐ ⭐⭐⭐

这三个VAD的使用流程分别为:

  • WebRTC VAD:初始化 → 手动预处理 → 手动分帧 → 逐帧检测 → 手动后处理
python 复制代码
import webrtcvad

# 初始化
vad = webrtcvad.Vad(mode=2)  # 一行初始化

# 检测(最基础)
frame_bytes = audio_frame.tobytes()  # 必须是16位PCM
is_speech = vad.is_speech(frame_bytes, sample_rate=16000)

# 但实际上需要更多步骤:
def full_webrtcvad_process(audio_data, sample_rate=16000):
    """完整的WebRTC VAD处理流程"""
    # 1. 初始化
    vad = webrtcvad.Vad(mode=2)
    
    # 2. 音频预处理(必要步骤)
    if audio_data.dtype != np.int16:
        audio_data = (audio_data * 32767).astype(np.int16)
    
    # 3. 分帧处理(必须)
    frame_ms = 30
    frame_len = int(sample_rate * frame_ms / 1000)
    frames = [audio_data[i:i+frame_len] 
              for i in range(0, len(audio_data), frame_len)]
    
    # 4. 逐帧检测
    results = []
    for frame in frames:
        if len(frame) == frame_len:  # WebRTC严格要求帧长度
            is_speech = vad.is_speech(frame.tobytes(), sample_rate)
            results.append(is_speech)
    
    # 5. 后处理(通常需要),平滑处理、合并相邻片段、过滤短段、增加边界扩展等
    speech_segments = smooth_results(results, frame_ms)
    
    return speech_segments
  • SpeechBrain:下载模型 → 初始化 → 一键检测(内置前后处理)
python 复制代码
from speechbrain.pretrained import VAD

# SpeechBrain有更复杂的流程
def speechbrain_vad_process(audio_path):
    """SpeechBrain VAD处理流程"""
    # 1. 初始化(加载模型,可能很重)
    # 方式一:使用预训练模型
    vad_model = VAD.from_hparams(
        source="speechbrain/vad-crdnn-libriparty",
        savedir="pretrained_models/vad-crdnn"
    )
    
    # 或者方式二:自定义模型
    # 需要定义:compute_features()、Encoder()、Decoder()等
    
    # 2. 音频加载和预处理(内置处理)
    # SpeechBrain有完整的音频处理管道
    audio = vad_model.load_audio(audio_path)
    
    # 3. 检测(单次调用处理整个音频)
    boundaries = vad_model.get_speech_segments(
        audio_file=audio_path,
        large_chunk_size=30,    # 分块大小(秒)
        small_chunk_size=10,    # 内部处理块
        overlap_small_chunk=True
    )
    
    # 4. 后处理(模型可能内置)
    # boundaries已经是处理好的时间区间
    
    return boundaries

# 更细粒度的控制
def advanced_speechbrain_vad():
    # 可以访问内部概率
    probs = vad_model.get_speech_prob_file(audio_file)
    
    # 然后自己应用阈值
    decisions = vad_model.apply_threshold(
        probs, 
        activation_th=0.5,  # 阈值
        deactivation_th=0.25
    ).float()
    
    # 再进行边界检测
    boundaries = vad_model.get_boundaries(decisions)
    
    return boundaries
  • Silero VAD:下载模型 → 初始化 → 灵活检测(部分自动化)
python 复制代码
import torch
import numpy as np

# Silero VAD的使用方式
def silero_vad_process(audio_data, sample_rate=16000):
    """Silero VAD处理流程"""
    # 1. 初始化(需要下载模型)
    model, utils = torch.hub.load(
        repo_or_dir='snakers4/silero-vad',
        model='silero_vad',
        force_reload=False
    )
    
    # 获取辅助函数
    (get_speech_timestamps, 
     save_audio, 
     read_audio, 
     VADIterator, 
     collect_chunks) = utils
    
    # 2. 音频准备(Silero有专用加载函数)
    # 如果从文件开始
    wav = read_audio(audio_path, sampling_rate=sample_rate)
    
    # 3. 检测(有多种使用方式)
    
    # 方式一:一次性获取所有时间戳(最简单)
    speech_timestamps = get_speech_timestamps(
        wav, 
        model,
        sampling_rate=sample_rate,
        threshold=0.5,           # 检测阈值
        min_speech_duration_ms=250,
        max_speech_duration_s=float('inf'),
        min_silence_duration_ms=100,
        window_size_samples=512,
        speech_pad_ms=200
    )
    
    # 方式二:流式处理(适合实时)
    vad_iterator = VADIterator(model, threshold=0.5)
    window_size_samples = 512
    
    for i in range(0, len(wav), window_size_samples):
        chunk = wav[i: i + window_size_samples]
        if len(chunk) < window_size_samples:
            break
        speech_dict = vad_iterator(chunk, return_seconds=True)
        if speech_dict:
            print(f"Speech detected: {speech_dict}")
    
    vad_iterator.reset_states()  # 重置状态
    
    # 方式三:提取语音片段
    speech_chunks = collect_chunks(speech_timestamps, wav)
    
    return speech_timestamps, speech_chunks
相关推荐
Facechat18 小时前
视频混剪-撤销/重做系统
音视频
程序员哈基耄19 小时前
小红书在线去水印工具:一键下载高清无水印图片与视频
音视频
科技小E19 小时前
EasyGBS算法算力平台重构服务业视频监控AI应用
人工智能·重构·音视频
彷徨而立20 小时前
【Windows API】音频 API 对比:wavein/waveout、DirectSound、ASIO、WASAPI
windows·音视频
小咖自动剪辑20 小时前
小咖批量剪辑助手:视频批量自动剪辑与混剪处理软件(Windows)
人工智能·实时互动·音视频·语音识别·视频编解码
努力犯错20 小时前
LTX-2 进阶 Prompt 技巧:从入门到专业视频创作
人工智能·数码相机·机器学习·计算机视觉·开源·prompt·音视频
百锦再20 小时前
AI视频生成模型从无到有:构建、实现与调试完全指南
人工智能·python·ai·小程序·aigc·音视频·notepad++
Android系统攻城狮1 天前
Android16音频之获取录制状态AudioRecord.getRecordingState:用法实例(一百七十六)
音视频·android16·音频进阶
天天进步20151 天前
KrillinAI 源码级深度拆解二:时间轴的艺术:深入 KrillinAI 的字幕对齐与音频切分算法
算法·音视频