You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

求助:实现语音触发启动、静音自动停止的WAV录音功能

实现基于语音检测的自动启停录音(NAudio)

嘿,这个需求完全可行!核心思路是实时监测音频输入的音量,当音量超过设定的阈值时启动录制,连续一段时间低于阈值就停止录制。结合你正在使用的NAudio库,我们可以通过以下步骤实现:

  • 步骤1:计算音频音量:用RMS(均方根)值来衡量音频的音量,这是音频处理中判断静音/有声的常用方法。RMS值越大,说明音量越高。
  • 步骤2:设置关键参数:定义启动录制的音量阈值,以及触发停止的静音持续时间(比如2秒静音就停止),你可以根据实际场景调整这些值。
  • 步骤3:维护录制状态:需要跟踪当前是否正在录制,以及一个计时器来统计静音持续的时间。
  • 步骤4:动态控制录制流程:在DataAvailable事件中实时计算音量,根据音量和当前状态决定是否启动/停止录制,同时动态创建/关闭WaveFileWriter

修改后的完整代码

using NAudio.Wave;
using System;
using System.Timers;

class Program
{
    // 录音相关对象
    static WaveInEvent _waveSource;
    static WaveFileWriter _waveFile;
    static string _outputFilePath = @"e:/Test/testrecord.wav";

    // 音量检测与启停参数
    static readonly double _volumeThreshold = 0.01; // 启动录制的音量阈值(0-1之间,根据实际调整)
    static readonly int _silenceTimeoutMs = 2000; // 静音超时时间(毫秒)
    static bool _isRecording = false;
    static Timer _silenceTimer;

    static void Main(string[] args)
    {
        Console.WriteLine("等待语音输入...");

        // 初始化音频输入
        _waveSource = new WaveInEvent();
        _waveSource.WaveFormat = new WaveFormat(16000, 1); // 16kHz单声道
        _waveSource.DataAvailable += WaveSource_DataAvailable;

        // 初始化静音计时器:超时后停止录制
        _silenceTimer = new Timer(_silenceTimeoutMs);
        _silenceTimer.Elapsed += SilenceTimer_Elapsed;
        _silenceTimer.AutoReset = false; // 只触发一次,需要手动重置

        _waveSource.StartRecording();
        Console.WriteLine("按任意键退出程序");
        Console.ReadKey();

        // 清理资源
        _waveSource.StopRecording();
        _waveSource.Dispose();
        _waveFile?.Dispose();
        _silenceTimer.Dispose();
    }

    static void WaveSource_DataAvailable(object sender, WaveInEventArgs e)
    {
        // 计算当前音频帧的RMS音量
        double rmsVolume = CalculateRms(e.Buffer, e.BytesRecorded);

        if (_isRecording)
        {
            // 如果正在录制:写入数据,并检查是否静音
            _waveFile.WriteData(e.Buffer, 0, e.BytesRecorded);

            if (rmsVolume < _volumeThreshold)
            {
                // 静音中,启动/重置计时器
                if (!_silenceTimer.Enabled)
                {
                    _silenceTimer.Start();
                }
            }
            else
            {
                // 检测到声音,重置静音计时器
                _silenceTimer.Stop();
            }
        }
        else
        {
            // 如果未录制:检查是否达到启动阈值
            if (rmsVolume > _volumeThreshold)
            {
                Console.WriteLine("开始录制...");
                _isRecording = true;
                // 创建WaveFileWriter开始写入
                _waveFile = new WaveFileWriter(_outputFilePath, _waveSource.WaveFormat);
                _waveFile.WriteData(e.Buffer, 0, e.BytesRecorded);
            }
        }
    }

    static void SilenceTimer_Elapsed(object sender, ElapsedEventArgs e)
    {
        // 静音超时,停止录制
        Console.WriteLine("检测到静音,停止录制");
        _isRecording = false;
        _waveFile?.Dispose();
        _waveFile = null;
    }

    /// <summary>
    /// 计算音频缓冲区的RMS音量(0-1之间)
    /// </summary>
    static double CalculateRms(byte[] buffer, int bytesRecorded)
    {
        double sum = 0;
        // 16位音频,每2字节一个样本
        for (int i = 0; i < bytesRecorded; i += 2)
        {
            short sample = BitConverter.ToInt16(buffer, i);
            // 转换为-1到1之间的浮点数
            double normalizedSample = sample / 32768.0;
            sum += normalizedSample * normalizedSample;
        }
        int sampleCount = bytesRecorded / 2;
        if (sampleCount == 0) return 0;
        // 计算均方根
        return Math.Sqrt(sum / sampleCount);
    }
}

关键代码说明

  • CalculateRms方法:把16位的音频样本转换为归一化的浮点数,计算均方根得到当前帧的音量值,范围在0-1之间。
  • 静音计时器:当检测到静音时启动计时器,一旦持续时间超过设定的_silenceTimeoutMs,就触发停止录制的逻辑。
  • 动态录制控制:只有当检测到语音时才创建WaveFileWriter,停止录制时释放资源,避免生成空文件。

注意事项

  • 你可能需要根据实际环境调整_volumeThreshold_silenceTimeoutMs参数,比如在嘈杂环境中调高阈值,避免误触发。
  • 如果需要支持多次启停录制(比如录多段语音),可以修改代码为每次录制生成不同的文件名,而不是覆盖同一个文件。

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

火山引擎 最新活动