32

I'm tasked with building a .NET client app to detect silence in a WAV files.

Is this possible with the built-in Windows APIs? Or alternately, any good libraries out there to help with this?

Judah Gabriel Himango
  • 58,906
  • 38
  • 158
  • 212

8 Answers8

14

Audio analysis is a difficult thing requiring a lot of complex math (think Fourier Transforms). The question you have to ask is "what is silence". If the audio that you are trying to edit is captured from an analog source, the chances are that there isn't any silence... they will only be areas of soft noise (line hum, ambient background noise, etc).

All that said, an algorithm that should work would be to determine a minimum volume (amplitude) threshold and duration (say, <10dbA for more than 2 seconds) and then simply do a volume analysis of the waveform looking for areas that meet this criteria (with perhaps some filters for millisecond spikes). I've never written this in C#, but this CodeProject article looks interesting; it describes C# code to draw a waveform... that is the same kind of code which could be used to do other amplitude analysis.

gerard
  • 174
  • 1
  • 4
  • 17
Simon Gillbee
  • 3,932
  • 4
  • 35
  • 48
10

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

This has all the code necessary to strip silence, and mix wave files.

Enjoy.

Cristian Lupascu
  • 39,078
  • 16
  • 100
  • 137
FlySwat
  • 172,459
  • 74
  • 246
  • 311
10

If you want to efficiently calculate the average power over a sliding window: square each sample, then add it to a running total. Subtract the squared value from N samples previous. Then move to the next step. This is the simplest form of a CIC Filter. Parseval's Theorem tells us that this power calculation is applicable to both time and frequency domains.

Also you may want to add Hysteresis to the system to avoid switching on&off rapidly when power level is dancing about the threshold level.

Marek Grzenkowicz
  • 17,024
  • 9
  • 81
  • 111
Mark Borgerding
  • 8,117
  • 4
  • 30
  • 51
10

I'm using NAudio, and I wanted to detect the silence in audio files so I can either report or truncate.

After a lot of research, I came up with this basic implementation. So, I wrote an extension method for the AudioFileReader class which returns the silence duration at the start/end of the file, or starting from a specific position.

Here:

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);
    }
}

This will accept almost any audio file format not just WAV.

Usage:

using (AudioFileReader reader = new AudioFileReader(filePath))
{
    TimeSpan duration = reader.GetSilenceDuration(AudioFileReaderExt.SilenceLocation.Start);
    Console.WriteLine(duration.TotalMilliseconds);
}

References:

  • what is `amplitude` in db formula? I'm working on detect and remove silence from recorded audio for about 1.5 month, but I'm not succeeded yet...these are my questions about this subject in stack, [Question 1](https://stackoverflow.com/questions/55429213/how-to-improve-the-code-to-remove-silent-from-a-recorded-wave-file) And [Question 2](https://stackoverflow.com/questions/55566814/how-to-convert-audio-byte-to-samples) I'll be grateful if you help me to solve this problem, I'm a student and it's my homework project and I'm not professional in this subject – j.doe Apr 10 '19 at 09:28
  • When I run this code on an mp4 video, the values are between -96ish to 0. 0 is the max volume – Josh Graham Jan 03 '21 at 23:31
6

Here a nice variant to detect threshold alternatings:

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;
    }


}

Main usage:

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;
            }
        }
Ibai
  • 755
  • 7
  • 9
1

Use Sox. It can remove leading and trailing silences, but you'll have to call it as an exe from your app.

Manu
  • 28,753
  • 28
  • 75
  • 83
1

I don't think you'll find any built-in APIs for detection of silence. But you can always use good ol' math/discreete signal processing to find out loudness. Here's a small example: http://msdn.microsoft.com/en-us/magazine/cc163341.aspx

chitza
  • 1,434
  • 2
  • 11
  • 20
-2

See code below from 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();
}
Aleks
  • 492
  • 5
  • 5
  • 7
    The code above requires a third party library (Alvas Audio) which is not exactly cheap. – Saverio Terracciano Feb 28 '14 at 18:52
  • 1
    Looks like advertisement of the non-open-source `Alvas Audio` library. – V. Panchenko Mar 24 '18 at 20:41
  • 2
    I'm not sure why commercial solutions are downvoted as answers. Is there a SO policy for that? There could be a lot of qualities about this software that other's cannot achieve that the OP or future visitor may need. You should criticize the answer for it's relevance to the question, not your ideology. – Kind Contributor Nov 07 '18 at 23:48
  • 3
    I'm sure if the post included a heads up about using a third party non-free library it wouldn't have been downvoted, instead of casually sneaking in an ad like that. – Francois Zard Dec 18 '18 at 11:50