#!/usr/bin/env python3.11 # # A simple demonstration on how to compute signal-to-noise ratio of an audio (wav) file # Note that computing the SNR of an audio is a non-trivial task, since it is not clear how to separate a generic audio into the "signal" and "noise". # The solution demonstrated here applies a simple bandpass filter to extract the "signal", i.e., within a frequency range of interest. Frequencies outside of the band are considered noise. # # Stilianos Louca # Copyright 2024 # # LICENSE AGREEMENT # - - - - - - - - - # All rights reserved. # Use and redistributions of this code is permitted for commercial and non-commercial purposes, # under the following conditions: # # * Redistributions must retain the above copyright notice, this list of # conditions and the following disclaimer in the code itself, as well # as in documentation and/or other materials provided with the code. # * Neither the name of the original author (Stilianos Louca), nor the names # of its contributors may be used to endorse or promote products derived # from this code without specific prior written permission. # * Proper attribution must be given to the original author, including a # reference to any peer-reviewed publication through which the code was published. # # THIS CODE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS" AND ANY # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS CODE, # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # - - - - - - - - - import librosa import numpy import glob import os import numpy as np import scipy.signal import soundfile RESAMPLING_RATE = 22050 wav_filepaths = glob.glob("input/number_individual_recordings/**/*.wav", recursive=True) print("Found %d wav files"%(len(wav_filepaths))) # function for computing the signal-to-noise ratio of an audio (basically a 1D numpy array) # The SNR is the ratio of the signal power (sum of squares) over noise power # Here, "singal" is defined as the audio part that passes a specific band filter, whilel "noise" is defined as the residual (noise = audio - signal). def get_snr(audio, sampling_rate, lowest_freq, highest_freq): B, A = scipy.signal.butter(N=6, Wn=(lowest_freq,highest_freq), fs=sampling_rate, btype='bandpass', analog=False) signal_part = scipy.signal.lfilter(B, A, audio) noise_part = audio-signal_part snr = numpy.nanmean(signal_part**2)/numpy.nanmean(noise_part**2) return snr for w,filepath in enumerate(wav_filepaths): print("Loading wav '%s'.."%(filepath)) audio, sampling_rate = librosa.load(filepath, sr=RESAMPLING_RATE) if(audio.ndim>1): audio = audio[:,0] # keep only first channel, if there are multiple snr = get_snr(audio, sampling_rate, lowest_freq=100, highest_freq=1000) print(" --> SNR = %g"%(snr))