使用 C# 检测 WAV 文件中的音频静音

Posted

技术标签:

【中文标题】使用 C# 检测 WAV 文件中的音频静音【英文标题】:Detecting audio silence in WAV files using C# 【发布时间】:2010-09-06 08:51:48 【问题描述】:

我的任务是构建一个 .NET 客户端应用程序来检测 WAV 文件中的静音。

这可以通过内置的 Windows API 实现吗?或者,有什么好的图书馆可以帮助解决这个问题吗?

【问题讨论】:

【参考方案1】:

音频分析是一件困难的事情,需要大量复杂的数学运算(想想傅立叶变换)。你要问的问题是“什么是沉默”。如果您尝试编辑的音频是从模拟源捕获的,则可能没有任何静音......它们只会是软噪声区域(线路嗡嗡声、环境背景噪声等)。

总而言之,一种应该起作用的算法是确定最小音量(幅度)阈值和持续时间(例如,CodeProject article 看起来很有趣;它描述了用于绘制波形的 C# 代码......这与可用于进行其他幅度分析的代码相同。

【讨论】:

【参考方案2】:

如果您想有效地计算滑动窗口的平均功率:对每个样本进行平方,然后将其添加到运行总计中。从之前的 N 个样本中减去平方值。然后进行下一步。这是CIC 过滤器的最简单形式。 Parseval's Theorem 告诉我们,这种功率计算适用于时域和频域。

此外,您可能希望将Hysteresis 添加到系统中,以避免在功率水平接近阈值水平时快速打开和关闭。

【讨论】:

【参考方案3】:

http://www.codeproject.com/Articles/19590/WAVE-File-Processor-in-C

这包含去除静音和混合波形文件所需的所有代码。

享受吧。

【讨论】:

【参考方案4】:

我正在使用NAudio,我想检测音频文件中的静音,以便报告或截断。

经过大量研究,我想出了这个基本的实现。因此,我为 AudioFileReader 类编写了一个扩展方法,它返回文件开头/结尾处或从特定位置开始的静音持续时间。

这里:

static class AudioFileReaderExt

    public enum SilenceLocation  Start, End 

    private static bool IsSilence(float amplitude, sbyte threshold)
    
        double dB = 20 * Math.Log10(Math.Abs(amplitude));
        return dB < threshold;
    
    public static TimeSpan GetSilenceDuration(this AudioFileReader reader,
                                              SilenceLocation location,
                                              sbyte silenceThreshold = -40)
    
        int counter = 0;
        bool volumeFound = false;
        bool eof = false;
        long oldPosition = reader.Position;

        var buffer = new float[reader.WaveFormat.SampleRate * 4];
        while (!volumeFound && !eof)
        
            int samplesRead = reader.Read(buffer, 0, buffer.Length);
            if (samplesRead == 0)
                eof = true;

            for (int n = 0; n < samplesRead; n++)
            
                if (IsSilence(buffer[n], silenceThreshold))
                
                    counter++;
                
                else
                
                    if (location == SilenceLocation.Start)
                    
                        volumeFound = true;
                        break;
                    
                    else if (location == SilenceLocation.End)
                    
                        counter = 0;
                    
                
            
        

        // reset position
        reader.Position = oldPosition;

        double silenceSamples = (double)counter / reader.WaveFormat.Channels;
        double silenceDuration = (silenceSamples / reader.WaveFormat.SampleRate) * 1000;
        return TimeSpan.FromMilliseconds(silenceDuration);
    

这将接受几乎任何音频文件格式不仅仅是 WAV

用法:

using (AudioFileReader reader = new AudioFileReader(filePath))

    TimeSpan duration = reader.GetSilenceDuration(AudioFileReaderExt.SilenceLocation.Start);
    Console.WriteLine(duration.TotalMilliseconds);

参考资料:

How audio dB levels are calculated. Floating-point samples range。 More about amplitude。

【讨论】:

db 公式中的amplitude 是什么?我正在努力从录制的音频中检测和删除静音大约 1.5 个月,但我还没有成功......这些是我在堆栈中关于这个主题的问题,Question 1 和 Question 2 我将不胜感激你帮我解决这个问题,我是一名学生,这是我的家庭作业,我不是这个学科的专业人士 当我在 mp4 视频上运行此代码时,值在 -96ish 到 0 之间。0 是最大音量【参考方案5】:

这是检测阈值交替的一个很好的变体:

static class AudioFileReaderExt



    private static bool IsSilence(float amplitude, sbyte threshold)
    
        double dB = 20 * Math.Log10(Math.Abs(amplitude));
        return dB < threshold;
    

    private static bool IsBeep(float amplitude, sbyte threshold)
    
        double dB = 20 * Math.Log10(Math.Abs(amplitude));
        return dB > threshold;
    

    public static double GetBeepDuration(this AudioFileReader reader,
                                              double StartPosition, sbyte silenceThreshold = -40)
    
        int counter = 0;
        bool eof = false;
        int initial = (int)(StartPosition * reader.WaveFormat.Channels * reader.WaveFormat.SampleRate / 1000);
        if (initial > reader.Length) return -1;
        reader.Position = initial;
        var buffer = new float[reader.WaveFormat.SampleRate * 4];
        while (!eof)
        
            int samplesRead = reader.Read(buffer, 0, buffer.Length);
            if (samplesRead == 0)
                eof = true;

            for (int n = initial; n < samplesRead; n++)
            
                if (IsBeep(buffer[n], silenceThreshold))
                
                    counter++;
                
                else
                
                    eof=true; break;
                
            
        


        double silenceSamples = (double)counter / reader.WaveFormat.Channels;
        double silenceDuration = (silenceSamples / reader.WaveFormat.SampleRate) * 1000;

        return TimeSpan.FromMilliseconds(silenceDuration).TotalMilliseconds;
    

    public static double GetSilenceDuration(this AudioFileReader reader,
                                              double StartPosition, sbyte silenceThreshold = -40)
    
        int counter = 0;
        bool eof = false;
        int initial = (int)(StartPosition * reader.WaveFormat.Channels * reader.WaveFormat.SampleRate / 1000);
        if (initial > reader.Length) return -1;
        reader.Position = initial;
        var buffer = new float[reader.WaveFormat.SampleRate * 4];
        while (!eof)
        
            int samplesRead = reader.Read(buffer, 0, buffer.Length);
            if (samplesRead == 0)                    
                eof=true;

            for (int n = initial; n < samplesRead; n++)
            
                if (IsSilence(buffer[n], silenceThreshold))
                
                    counter++;
                
                else
                
                    eof=true; break;
                
            
        


        double silenceSamples = (double)counter / reader.WaveFormat.Channels;
        double silenceDuration = (silenceSamples / reader.WaveFormat.SampleRate) * 1000;

        return TimeSpan.FromMilliseconds(silenceDuration).TotalMilliseconds;
    



主要用途:

using (AudioFileReader reader = new AudioFileReader("test.wav"))
        
            double duratioff = 1;
            double duration = 1;
            double position = 1;
            while (duratioff >-1 && duration >-1)
            
                duration = reader.GetBeepDuration(position);
                Console.WriteLine(duration);
                position = position + duration;
                duratioff = reader.GetSilenceDuration(position);
                Console.WriteLine(-duratioff);
                position = position + duratioff;
            
        

【讨论】:

【参考方案6】:

我认为您不会找到任何用于检测静音的内置 API。但是您始终可以使用良好的数学/离散信号处理来找出响度。 这是一个小例子:http://msdn.microsoft.com/en-us/magazine/cc163341.aspx

【讨论】:

此链接已损坏...:(【参考方案7】:

使用Sox。它可以删除前导和尾随的静音,但您必须将其作为应用程序中的 exe 调用。

【讨论】:

调用外部进程有时看起来很淘气。但更重要的是使用最好的工具。【参考方案8】:

查看下面来自Detecting audio silence in WAV files using C#的代码

private static void SkipSilent(string fileName, short silentLevel)

    WaveReader wr = new WaveReader(File.OpenRead(fileName));
    IntPtr format = wr.ReadFormat();
    WaveWriter ww = new WaveWriter(File.Create(fileName + ".wav"), 
        AudioCompressionManager.FormatBytes(format));
    int i = 0;
    while (true)
    
        byte[] data = wr.ReadData(i, 1);
        if (data.Length == 0)
        
            break;
        
        if (!AudioCompressionManager.CheckSilent(format, data, silentLevel))
        
            ww.WriteData(data);
        
    
    ww.Close();
    wr.Close();

【讨论】:

上面的代码需要第三方库(Alvas Audio),这并不便宜。 貌似非开源Alvas Audio库的广告。 我不确定为什么商业解决方案被低估为答案。有没有这样的政策?该软件可能有很多其他人无法实现的品质,而 OP 或未来的访问者可能需要这些品质。你应该批评答案与问题相关,而不是你的意识形态。 我敢肯定,如果帖子中包含关于使用第三方非免费库的提醒,它就不会被否决,而不是随便偷偷插入这样的广告。

以上是关于使用 C# 检测 WAV 文件中的音频静音的主要内容,如果未能解决你的问题,请参考以下文章

使用ffmpeg在特定时间向wav文件添加静音[重复]

使用静音检测分割音频文件

在 Python 中检测并录制音频为 PCM 格式

如何在 PCM 音频中使用 IMediaSample 检测静音

从 numpy 生成的 wav 文件听不见(完全静音)

在 ffmpeg 中添加静音音频