Source code for VuVoPy.features.utils.hnr

import numpy as np
from VuVoPy.data.containers.prepocessing import Preprocessed as pp
from VuVoPy.data.containers.sample import VoiceSample as vs
from VuVoPy.data.containers.segmentation import Segmented as sg

[docs] def hnr(folder_path, winlen=512, winover=256 , wintype='hann', f0_min=75, f0_max=500): """ Compute Harmonics-to-Noise Ratio (HNR) using an autocorrelation-based method. This function processes a WAV file, divides it into overlapping frames, and estimates the harmonic-to-noise ratio (HNR) for each frame using pitch period information derived from the autocorrelation method. Args: folder_path (str): Path to the audio file (WAV format). winlen (int): Frame length in samples. winover (int): Overlap between consecutive frames in samples. wintype (str): Type of window function to apply (e.g., 'hann', 'hamming'). f0_min (float): Minimum fundamental frequency in Hz. f0_max (float): Maximum fundamental frequency in Hz. Returns: float: Mean HNR value across all frames. """ # Load and preprocess the audio file segment = sg.from_voice_sample(pp.from_voice_sample(vs.from_wav(folder_path)), winlen, wintype, winover) signal = segment.get_norm_segment().T # Transpose to get shape (num_frames, num_samples) fs = segment.get_sampling_rate() # Get sampling rate hnr_values = [] num_frames = signal.shape[0] for i in range(num_frames): frame = signal[i, :] frame = frame - np.mean(frame) # Remove DC offset frame = frame / (np.max(np.abs(frame)) + 1e-10) # Normalize to prevent floating-point errors if np.max(np.abs(frame)) < 1e-6: continue # Skip silent frames autocorr = np.correlate(frame, frame, mode='full') autocorr = autocorr[len(autocorr) // 2:] # Keep only positive lags autocorr /= np.max(autocorr) # Normalize to 1 # Find fundamental period (within F0 range) min_period = int(fs / f0_max) max_period = min(int(fs / f0_min), len(autocorr) - 1) if max_period <= min_period: continue # Skip unreliable frames # Find the actual F0 peak (ignore zero lag) peak_idx = np.argmax(autocorr[min_period:max_period]) + min_period r_max = autocorr[peak_idx] # Peak value at estimated fundamental period # Ensure r_max is in valid range r_max = np.clip(r_max, 1e-4, 0.999) # Avoid log(0) issues # Compute HNR hnr = 10 * np.log10(r_max / (1 - r_max)) hnr_values.append(hnr) return np.mean(hnr_values) if hnr_values else float('nan')