# python; audio; detecting silence in audio signal

I have real-time audio application written in python, and I have question about detecting silence. What's the most efficient way to distinguish silence from non-silence, in an input audio signal? By non-silence I mean any sound, even noise. I need to know when silence-period is in the process, as accurately as possible. I don't necessarily ask for some code-algorithm; theoretical tips and explanations would be also useful. I know that silence have low level of oscillations (signal is flat); in numpy.fft.fft output, silence has highest amplitude in [1] bin, and so on, there are several significant things when silence is there. But using any of these conditions in code, cannot give me absolutely accurate result to detect silence and completely split it from sound. Any advanced techniques for this task?

i'm not gonna deal with python nor numpy nor any other parochial computational platform.

1. first, since "silence" is a perceptual property, you need to apply a weighting filter, such as A-weighting to boost the frequency components of the audio that our ears are more sensitive to and attenuate the portions we're less sensitive to. there are other weighting curves besides A-weighting. i like Robert Wannamaker's E-weighting the best.

2. then square the filtered signal (to get power or energy).

3. then low-pass filter the squared and filtered signal (to get an envelope).

4. then compare this envelope to a threshold that will correspond to the threshold of hearing.

5. lastly, you may need to put in a little hysteresis in the threshold comparison so that the silence/no-silence result does not jitter as your envelope crosses the threshold.

this is similar to the "gate" function in a compressor/limiter/gate process that sometimes finds expression in audio applications. the "gate" was to make sure that something that's perceived as silence (or sufficiently low-level that it should be perceived as silence) becomes zero during the "silent" periods of time.

Python librosa library has a functionality you can use:

librosa.effects.split(y=buffer, frame_length=8000, top_db=40)


Split an audio signal into non-silent intervals.
Given sampling rate of 8000 it will split the audio by detecting audio lower than 40db for period of 1 sec

Or, you can trim the audio "silent parts" using:

librosa.effects.trim(y=buffer, frame_length=8000, top_db=40)


I needed something similar, to split large WAV files into chunks, on silences longer than a certain length. So I wrote this. Feel free to use it.

It memory maps the input file using scipy.io.wavfile.read(..., mmap=True) (since my WAV files are larger than my available RAM), and then uses generators to process it. That means, you could easily adapt it for an incoming realtime audio stream.

The algorithm is not as elegant as suggested by @robert-bristow-johnson, but it should be faster, and work for arbitrarily large files. It splits the input file into overlapping windows of equal length, calculates the energy of each window, and determines whether each window's energy is above or below a certain threshold.

I also included a progress bar using tqdm, which you should remove if you are processing an input stream of indefinite length.

Your not going to find an "absolutely correct" method for your problem.

I like Robert's method. One thing that I would add to his approach is to adjust the level of the input audio to a predetermined value (say 65 dB SPL) to establish a fixed level prior to the remaining processing steps he has provided. This will remove possible variation in audio level and help set the threshold mentioned in step 4. You'll also likely need to do several listening tests to aid in setting a reasonable threshold.