/// <summary> /// Used for debugging purposes. /// </summary> public static void Print(TextWriter writer, TriangleFilter f) { for (int i = 0; i < f.leftEdge; ++i) { if (i != 0) { writer.Write(", "); } writer.Write("0"); } for (int i = 0; i < f.size; ++i) { writer.Write(", " + f.FilterData[i].ToString("0.000", CultureInfo.InvariantCulture)); } }
/// <summary> /// Creates a new MelFilterBank. /// </summary> /// <param name="minFreq">The minimum frequency in hz to be considered, i.e. /// the left edge of the first TriangleFilter.</param> /// <param name="maxFreq">The maximum frequency in hz to be considered, i.e. /// the right edge of the last TriangleFilter.</param> /// <param name="numMelBands">The number of Mel bands to be calculated, i.e. /// the number of TriangleFilters to be applied.</param> /// <param name="numBins">The number of bins that are present in the fft_buffer /// that will be passed to the MelFilterBank.Apply method. This is also /// required to properly configure the TriangleFilter instances which /// operate on array indices only. (half the window size)</param> /// <param name="sampleRate">The original sample rate the FFT buffer which will /// be passed to MelFilterBank.Apply is based on.</param> /// <param name="doNormalizeFilterArea">If set to "true", the area of the /// created TriangleFilter will be normalized, e.g. the height of the /// filter's triangle shape will be configured in a way, that the area /// of the triangle shape equals one.</param> public MelFilterBank(double minFreq, double maxFreq, int numMelBands, int numBins, int sampleRate, bool doNormalizeFilterArea) { this.minFreq = minFreq; this.maxFreq = maxFreq; this.numMelBands = numMelBands; this.numBins = numBins; this.sampleRate = sampleRate; this.doNormalizeFilterArea = doNormalizeFilterArea; // Let's do some argument checking if ((minFreq >= maxFreq) || (maxFreq == 0)) { throw new ArgumentException(String.Format("Invalid min/max frequencies for MelFilterBank: min = '{0}' max = '{1}'", minFreq, maxFreq)); } if (numMelBands == 0) { throw new ArgumentException(String.Format("Invalid number of mel bands for MelFilterBank: n = {0}", numMelBands)); } if (sampleRate == 0) { throw new ArgumentException(String.Format("Invalid sample rate for MelFilterBank: s = {0}", sampleRate)); } if (numBins == 0) { throw new ArgumentException(String.Format("Invalid number of bins for MelFilterBank: s = '{0}'", numBins)); } // 2 * numBins should be the same as window length double deltaFreq = (double)sampleRate / (2 * numBins); double melMin = MelUtils.LinToMelFreq(minFreq); double melMax = MelUtils.LinToMelFreq(maxFreq); // We divide by #band + 1 as min / max should present the beginning / end // of beginng up / ending low slope, i.e. it's not the centers of each // band that represent min/max frequency in mel bands. double deltaFreqMel = (melMax - melMin) / (numMelBands + 1); // Fill up equidistant spacing in mel-space double melLeft = melMin; for (int i = 0; i < numMelBands; i++) { double melCenter = melLeft + deltaFreqMel; double melRight = melCenter + deltaFreqMel; double leftHz = MelUtils.MelToLinFreq(melLeft); double rightHz = MelUtils.MelToLinFreq(melRight); // align to closest num_bins (round) int leftBin = (int)((leftHz / deltaFreq) + 0.5); int rightBin = (int)((rightHz / deltaFreq) + 0.5); // calculate normalized height double height = 1.0; if (doNormalizeFilterArea) { height = 2.0 / (rightBin - leftBin); } // Create the actual filter var filter = new TriangleFilter(leftBin, rightBin, height); filters.Add(filter); // next left edge is current center melLeft = melCenter; } }