Calculating silence in a WAV file I made this script to calculate the amount of silence in seconds in an audio file. Is this a good method to do this, and if yes, can it be improved? It takes about 4 seconds for 20 audio files of 100-200 seconds of audio each, which seems pretty long to me.

The audio file is split in windows of win_size seconds. Every window that is below sil_threshold is considered 'silent'. By multiplying the number of silent windows by the window size, the number of silent seconds is obtained.

import numpy as np
from scipy.io import wavfile

def calc_silence(audio, sil_threshold=0.03, win_size=0.25, ret="sec"):
    """Calculates total silence length in this audio file.

    Keywords:
        audio:          location of the audiofile OR a (samplerate, audiodata) tuple
        sil_threshold:  percentage of the maximum window-averaged amplitude
                        below which audio is considered silent
        win_size:       length of window in sec (audio is cut into windows)
        ret:            the return type; can be one of the following:
                        - "sec": return the number of silent seconds
                        - "frac": return the frac of silence (between 0 and 1)
                        - "amp": return a list of amplitude values (one for each window)
                        - "issil": return an np-array of 0s and 1s (1=silent) for each window
                        - "chunk": return a list of (start, end, is_silent) tuples,
                            with start and end in seconds and is_silent is a boolean
    """

    if isinstance(audio, tuple):
        samplerate, data = audio
    else:
        # read the sample rate and data from the wave file
        samplerate, data = wavfile.read(audio)

    win_frames = int(samplerate * win_size) # number of samples in a window
    win_amps = [] # windows in which to measure amplitude
    for win_start in np.arange(0, len(data), win_frames):
        # Find the end of the window
        win_end = min(win_start + win_frames, len(data))
        # Add the mean amplitude for this frame to the list of window amplitudes
        win_amps.append(np.nanmean(np.abs(data[win_start:win_end])))

    # Calculate the minimum threshold for a window to be silent
    threshold = sil_threshold * max(win_amps)

    # Find the windows that are silent
    sils, = np.where(win_amps &lt;= threshold)

    # The silence length is the number of silent windows times the window length
    sil = len(sils) * win_size

    if ret == "sec":
        return sil
    elif ret == "frac":
        return len(sils) / len(win_amps)
    elif ret == "amp":
        return win_amps
    elif ret == "issil":
        return (win_amps &lt;= threshold).astype(int)
    elif ret == "chunk":
        chunks = []
        t0, t1 = 0, 0
        is_sil = win_amps[0] &lt;= threshold
        for wi, amp in enumerate(win_amps):
            winsil = amp &lt;= threshold
            if winsil == is_sil:
                t1 = (wi + 1) * win_size
            else:
                chunks.append((t0*win_size, t1*win_size, is_sil))
                t0 = (wi + 1) * win_size
                is_sil = not is_sil
        chunks.append((t0*win_size, len(win_amps)*win_size, is_sil))
        return chunks
    else:
        raise ValueError("Unknown return format: {}".format(ret))
Your implementation is simple, which isn't bad, but it suffers from an accuracy issue. What if there is a single above-threshold sample at the middle of your window, preventing it from being detected as silent, and a similar subsequent window with one above-threshold sample in the middle? That constitutes one window's worth of silent samples that have not been detected. Instead, you should adopt a moving aggregate - absolute max, or maybe absolute median. Moving max requires that you maintain a queue and a sorted list. When you get a new sample, do a sorted insert of the absolute value of the sample to the list, a push to the queue, a pop to the queue, and drop the popped value from the list. The item at the end of the list will always be the max, and the item in the middle of the list will always be the median. As soon as the threshold is detected, silence has started and should not be considered to end until the threshold is exceeded.