/// <summary> /// Method returns FIR bandpass (close to trapezoidal) filterbank based on given frequencies. /// </summary> /// <param name="fftSize">Assumed size of FFT</param> /// <param name="samplingRate">Assumed sampling rate of a signal</param> /// <param name="frequencies">Array of frequency tuples (left, center, right) for each filter</param> /// <param name="vtln">VTLN frequency warper</param> /// <param name="mapper">Frequency scale mapper (e.g. herz-to-mel)</param> /// <returns>Array of rectangular filters</returns> public static float[][] Trapezoidal(int fftSize, int samplingRate, Tuple <double, double, double>[] frequencies, VtlnWarper vtln = null, Func <double, double> mapper = null) { var filterBank = Rectangular(fftSize, samplingRate, frequencies, vtln, mapper); for (var i = 0; i < filterBank.Length; i++) { var filterTf = new TransferFunction(DesignFilter.Fir(fftSize / 4 + 1, filterBank[i])); filterBank[i] = filterTf.FrequencyResponse(fftSize).Magnitude.ToFloats(); // normalize gain to 1.0 var maxAmp = 0.0f; for (var j = 0; j < filterBank[i].Length; j++) { if (filterBank[i][j] > maxAmp) { maxAmp = filterBank[i][j]; } } for (var j = 0; j < filterBank[i].Length; j++) { filterBank[i][j] /= maxAmp; } } return(filterBank); }
/// <summary> /// Method returns universal triangular filterbank weights based on given frequencies. /// </summary> /// <param name="fftSize">Assumed size of FFT</param> /// <param name="samplingRate">Assumed sampling rate of a signal</param> /// <param name="frequencies">Array of frequency tuples (left, center, right) for each filter</param> /// <param name="vtln">VTLN frequency warper</param> /// <param name="mapper">Frequency scale mapper (e.g. herz-to-mel) used here only for proper weighting</param> /// <returns>Array of triangular filters</returns> public static float[][] Triangular(int fftSize, int samplingRate, Tuple <double, double, double>[] frequencies, VtlnWarper vtln = null, Func <double, double> mapper = null) { if (mapper == null) { mapper = x => x; } Func <double, double> warp = vtln == null ? mapper : x => mapper(vtln.Warp(x)); var herzResolution = (double)samplingRate / fftSize; var herzFrequencies = Enumerable.Range(0, fftSize / 2 + 1) .Select(f => f * herzResolution) .ToArray(); var filterCount = frequencies.Length; var filterBank = new float[filterCount][]; for (var i = 0; i < filterCount; i++) { filterBank[i] = new float[fftSize / 2 + 1]; var left = warp(frequencies[i].Item1); var center = warp(frequencies[i].Item2); var right = warp(frequencies[i].Item3); var j = 0; for (; mapper(herzFrequencies[j]) <= left; j++) { ; } for (; mapper(herzFrequencies[j]) <= center; j++) { filterBank[i][j] = (float)((mapper(herzFrequencies[j]) - left) / (center - left)); } for (; j < herzFrequencies.Length && mapper(herzFrequencies[j]) < right; j++) { filterBank[i][j] = (float)((right - mapper(herzFrequencies[j])) / (right - center)); } } return(filterBank); }
/// <summary> /// Method creates overlapping triangular mel filters (as suggested by Malcolm Slaney). /// </summary> /// <param name="erbFilterCount">Number of mel filters</param> /// <param name="fftSize">Assumed size of FFT</param> /// <param name="samplingRate">Assumed sampling rate</param> /// <param name="lowFreq">Lower bound of the frequency range</param> /// <param name="highFreq">Upper bound of the frequency range</param> /// <param name="normalizeGain">True if gain should be normalized; false if all filters should have same height 1.0</param> /// <param name="vtln">VTLN frequency warper</param> /// <returns>Array of mel filters</returns> public static float[][] MelBankSlaney( int filterCount, int fftSize, int samplingRate, double lowFreq = 0, double highFreq = 0, bool normalizeGain = true, VtlnWarper vtln = null) { if (lowFreq < 0) { lowFreq = 0; } if (highFreq <= lowFreq) { highFreq = samplingRate / 2.0; } var frequencies = UniformBands(Scale.HerzToMelSlaney, Scale.MelToHerzSlaney, filterCount, samplingRate, lowFreq, highFreq, true); var filterBank = Triangular(fftSize, samplingRate, frequencies, vtln); if (normalizeGain) { Normalize(filterCount, frequencies, filterBank); } return(filterBank); }