//----------------------------------------------------------------------------- // SmoothingWithRecovery() carries out the spectral smoothing and spectral // recovery on the Cepstrum domain. //----------------------------------------------------------------------------- void SmoothingWithRecovery(double f0, int fs, ForwardRealFFT forwardRealFFT, InverseRealFFT inverseRealFFT, double[] spectralEnvelope) { var smoothingLifter = new double[FFTSize]; var compensationLifter = new double[FFTSize]; smoothingLifter[0] = 1; compensationLifter[0] = (1.0 - 2.0 * Q1) + 2.0 * Q1; for (int i = 1, limit = forwardRealFFT.FFTSize / 2; i <= limit; i++) { var quefrency = (double)i / fs; smoothingLifter[i] = Math.Sin(Math.PI * f0 * quefrency) / (Math.PI * f0 * quefrency); compensationLifter[i] = (1.0 - 2.0 * Q1) + 2.0 * Q1 * Math.Cos(2.0 * Math.PI * quefrency * f0); } for (int i = 0, limit = FFTSize / 2; i <= limit; i++) { forwardRealFFT.Waveform[i] = Math.Log(forwardRealFFT.Waveform[i]); } for (int i = 1, limit = FFTSize / 2; i < limit; i++) { forwardRealFFT.Waveform[FFTSize - i] = forwardRealFFT.Waveform[i]; } FFT.Execute(forwardRealFFT.ForwardFFT); for (int i = 0, limit = FFTSize / 2; i <= limit; i++) { inverseRealFFT.Spectrum[i] = new Complex(forwardRealFFT.Spectrum[i].Real * smoothingLifter[i] * compensationLifter[i] / FFTSize, 0.0); } FFT.Execute(inverseRealFFT.InverseFFT); for (int i = 0, limit = FFTSize / 2; i <= limit; i++) { spectralEnvelope[i] = Math.Exp(inverseRealFFT.Waveform[i]); } }
public void Synthesize(double[] f0, int f0Length, double[][] spectrogram, double[][] aperiodicity, int fftSize, double framePeriod, int fs, double[] y) { var minimumPhase = MinimumPhaseAnalysis.Create(fftSize); var inverseRealFFT = InverseRealFFT.Create(fftSize); var forwardRealFFT = ForwardRealFFT.Create(fftSize); var pulseLocations = new double[y.Length]; var pulseLocationsIndex = new int[y.Length]; var pulseLocationsTimeShift = new double[y.Length]; var interpolatedVUV = new double[y.Length]; var numberOfPulses = GetTimeBase(f0, f0Length, fs, framePeriod / 1000.0, y.Length, fs / fftSize + 1.0, pulseLocations, pulseLocationsIndex, pulseLocationsTimeShift, interpolatedVUV); var dcRemover = GetDCRemover(fftSize); framePeriod /= 1000.0; var impulseResponse = new double[fftSize]; for (var i = 0; i < numberOfPulses; i++) { var noiseSize = pulseLocationsIndex[Math.Min(numberOfPulses - 1, i + 1)] - pulseLocationsIndex[i]; GetOneFrameSegment(interpolatedVUV[pulseLocationsIndex[i]], noiseSize, spectrogram, fftSize, aperiodicity, f0Length, framePeriod, pulseLocations[i], pulseLocationsTimeShift[i], fs, forwardRealFFT, inverseRealFFT, minimumPhase, dcRemover, impulseResponse); for (var j = 0; j < fftSize; j++) { var index = j + pulseLocationsIndex[i] - fftSize / 2 + 1; if (index < 0 || index > y.Length - 1) { continue; } y[index] += impulseResponse[j]; } } }
public void Estimate(double[] x, int fs, double[] temporalPositions, double[] f0, int f0Length, int fftSize, double[][] aperiodicity) { aperiodicity.ForEach((a) => a.Fill(1.0 - SafeGuardMinimum)); var fftSizeD4C = (int)Math.Pow(2.0, 1.0 + (int)MathUtil.Log2(4.0 * fs / FloorF0D4C + 1.0)); var forwardRealFFT = ForwardRealFFT.Create(fftSizeD4C); var numberOfAperiodicities = (int)(Math.Min(UpperLimit, fs / 2.0 - FrequencyInterval) / FrequencyInterval); // Since the window function is common in D4CGeneralBody(), // it is designed here to speed up. var windowLength = (int)(FrequencyInterval * fftSizeD4C / fs) * 2 + 1; var window = new double[windowLength]; Common.NuttallWindow(windowLength, window); // D4C Love Train (Aperiodicity of 0 Hz is given by the different algorithm) var aperiodicity0 = new double[f0Length]; D4CLoveTrain(x, fs, f0, f0Length, temporalPositions, aperiodicity0); var coarseAperiodicity = new double[numberOfAperiodicities + 2]; coarseAperiodicity[0] = -60.0; coarseAperiodicity[numberOfAperiodicities + 1] = -SafeGuardMinimum; var coarseFrequencyAxis = Enumerable.Range(0, numberOfAperiodicities + 2).Select((i) => i * FrequencyInterval).ToArray(); coarseFrequencyAxis[numberOfAperiodicities + 1] = fs / 2.0; var frequencyAxis = Enumerable.Range(0, fftSize / 2 + 1).Select((i) => (double)i * fs / fftSize).ToArray(); for (var i = 0; i < f0Length; i++) { if (f0[i] == 0.0 || aperiodicity0[i] <= Threshold) { continue; } D4CGeneralBody(x, fs, Math.Max(FloorF0D4C, f0[i]), fftSizeD4C, temporalPositions[i], numberOfAperiodicities, window, forwardRealFFT, coarseAperiodicity.SubSequence(1)); // Linear interpolation to convert the coarse aperiodicity into its // spectral representation. GetAperiodicity(coarseFrequencyAxis, coarseAperiodicity, numberOfAperiodicities, frequencyAxis, fftSize, aperiodicity[i]); } }
void GetNoiseSpectrum(int noiseSize, int fftSize, ForwardRealFFT forwardRealFFT) { var waveform = forwardRealFFT.Waveform; for (var i = 0; i < noiseSize; i++) { waveform[i] = Rand.Next(); } var average = noiseSize > 0 ? waveform.Take(noiseSize).Average() : 1.0; for (var i = 0; i < noiseSize; i++) { waveform[i] -= average; } Array.Clear(waveform, noiseSize, fftSize - noiseSize); FFT.Execute(forwardRealFFT.ForwardFFT); }
//----------------------------------------------------------------------------- // GetPowerSpectrum() calculates the power_spectrum with DC correction. // DC stands for Direct Current. In this case, the component from 0 to F0 Hz // is corrected. //----------------------------------------------------------------------------- void GetPowerSpectrum(int fs, double f0, ForwardRealFFT forwardRealFFT) { var halfWindowLength = MatlabFunctions.MatlabRound(1.5 * fs / f0); // FFT Array.Clear(forwardRealFFT.Waveform, halfWindowLength * 2 + 1, FFTSize - halfWindowLength * 2 - 1); FFT.Execute(forwardRealFFT.ForwardFFT); var powerSpectrum = forwardRealFFT.Waveform; var spectrum = forwardRealFFT.Spectrum; for (int i = 0, limit = FFTSize / 2; i <= limit; i++) { powerSpectrum[i] = spectrum[i].Real * spectrum[i].Real + spectrum[i].Imaginary * spectrum[i].Imaginary; } Common.DCCorrection(powerSpectrum, f0, fs, FFTSize, powerSpectrum); }
public SynthesisRealTime(int sampleRate, double framePeriod, int fftSize, int bufferSize, int ringBufferCapacity) { SampleRate = sampleRate; FramePeriod = framePeriod * 0.001; AudioBufferSize = bufferSize; AudioBuffer = new double[bufferSize * 2 + fftSize]; Buffer = new RingBuffer(ringBufferCapacity); FFTSize = fftSize; ImpulseResponse = new double[fftSize]; DCRemover = GetDCRemover(fftSize / 2); RefreshSynthesizer(); MinimumPhase = MinimumPhaseAnalysis.Create(fftSize); InverseRealFFT = InverseRealFFT.Create(fftSize); ForwardRealFFT = ForwardRealFFT.Create(fftSize); }
public void Estimate(double[] x, int fs, double[] temporalPositions, double[] f0, double[][] spectrogram) { var f0Floor = GetF0FloorForCheapTrick(fs, FFTSize); var spectralEnvelope = new double[FFTSize]; var forwardRealFFT = ForwardRealFFT.Create(FFTSize); var inverseRealFFT = InverseRealFFT.Create(FFTSize); for (var i = 0; i < f0.Length; i++) { var currentF0 = f0[i] <= f0Floor ? DefaultF0 : f0[i]; CheapTrickGeneralBody(x, fs, currentF0, temporalPositions[i], forwardRealFFT, inverseRealFFT, spectralEnvelope); for (int j = 0, limit = FFTSize / 2; j <= limit; j++) { spectrogram[i][j] = spectralEnvelope[j]; } } }
//----------------------------------------------------------------------------- // D4CLoveTrain() determines the aperiodicity with VUV detection. // If a frame was determined as the unvoiced section, aperiodicity is set to // very high value as the safeguard. // If it was voiced section, the aperiodicity of 0 Hz is set to -60 dB. //----------------------------------------------------------------------------- void D4CLoveTrain(double[] x, int fs, double[] f0, int f0Length, double[] temporalPositions, double[] aperiodicity0) { var lowestF0 = 40.0; var fftSize = (int)Math.Pow(2.0, 1.0 + (int)MathUtil.Log2(3.0 * fs / lowestF0 + 1)); var forwardRealFFT = ForwardRealFFT.Create(fftSize); // Cumulative powers at 100, 4000, 7900 Hz are used for VUV identification. var boundary0 = (int)Math.Ceiling(100.0 * fftSize / fs); var boundary1 = (int)Math.Ceiling(4000.0 * fftSize / fs); var boundary2 = (int)Math.Ceiling(7900.0 * fftSize / fs); for (var i = 0; i < f0Length; i++) { if (f0[i] == 0.0) { aperiodicity0[i] = 0.0; continue; } aperiodicity0[i] = D4CLoveTrainSub(x, fs, Math.Max(f0[i], lowestF0), temporalPositions[i], f0Length, fftSize, boundary0, boundary1, boundary2, forwardRealFFT); } }
static void FastFFTFilt(double[] x, double[] h, int fftSize, ForwardRealFFT forwardRealFFT, InverseRealFFT inverseRealFFT, double[] y) { for (var i = 0; i < x.Length; i++) { forwardRealFFT.Waveform[i] = x[i] / fftSize; } if (x.Length < fftSize) { Array.Clear(forwardRealFFT.Waveform, x.Length, fftSize - x.Length); } FFT.Execute(forwardRealFFT.ForwardFFT); var xSpectrum = new Complex[fftSize]; Array.Copy(forwardRealFFT.Spectrum, xSpectrum, fftSize / 2 + 1); for (var i = 0; i < h.Length; i++) { forwardRealFFT.Waveform[i] = h[i] / fftSize; } if (h.Length < fftSize) { Array.Clear(forwardRealFFT.Waveform, h.Length, fftSize - h.Length); } FFT.Execute(forwardRealFFT.ForwardFFT); for (var i = fftSize / 2 + 1; i > -1; i--) { inverseRealFFT.Spectrum[i] = new Complex( xSpectrum[i].Real * forwardRealFFT.Spectrum[i].Real - xSpectrum[i].Imaginary * forwardRealFFT.Spectrum[i].Imaginary, xSpectrum[i].Real * forwardRealFFT.Spectrum[i].Imaginary + xSpectrum[i].Imaginary * forwardRealFFT.Spectrum[i].Real ); } FFT.Execute(inverseRealFFT.InverseFFT); inverseRealFFT.Waveform.BlockCopy(0, y, 0, fftSize); }
//----------------------------------------------------------------------------- // D4CGeneralBody() calculates a spectral envelope at a temporal // position. This function is only used in D4C(). // Caution: // forward_fft is allocated in advance to speed up the processing. //----------------------------------------------------------------------------- void D4CGeneralBody(double[] x, int fs, double currentF0, int fftSize, double currentPosition, int numberOfAperiodicities, double[] window, ForwardRealFFT forwardRealFFT, SubSequence <double> coarseAperiodicity) { var staticCentroid = new double[fftSize / 2 + 1]; var smoothedPowerSpectrum = new double[fftSize / 2 + 1]; var staticGroupDelay = new double[fftSize / 2 + 1]; GetStaticCentroid(x, fs, currentF0, fftSize, currentPosition, forwardRealFFT, staticCentroid); GetSmoothedPowerSpectrum(x, fs, currentF0, fftSize, currentPosition, forwardRealFFT, smoothedPowerSpectrum); GetStaticGroupDelay(staticCentroid, smoothedPowerSpectrum, fs, currentF0, fftSize, staticGroupDelay); GetCoarseAperiodicity(staticGroupDelay, fs, fftSize, numberOfAperiodicities, window, forwardRealFFT, coarseAperiodicity); // Revision of the result based on the F0 for (var i = 0; i < numberOfAperiodicities; i++) { coarseAperiodicity[i] = Math.Min(0.0, coarseAperiodicity[i] + (currentF0 - 100) / 50.0); } }
//----------------------------------------------------------------------------- // GetSmoothedPowerSpectrum() calculates the smoothed power spectrum. // The parameters used for smoothing are optimized in davance. //----------------------------------------------------------------------------- void GetSmoothedPowerSpectrum(double[] x, int fs, double currentF0, int fftSize, double currentPosition, ForwardRealFFT forwardRealFFT, double[] smoothedPowerSpectrum) { Array.Clear(forwardRealFFT.Waveform, 0, fftSize); GetWindowedWaveform(x, fs, currentF0, currentPosition, WindowType.Hanning, 4.0, forwardRealFFT.Waveform); FFT.Execute(forwardRealFFT.ForwardFFT); var spectrum = forwardRealFFT.Spectrum; for (int i = 0, limit = fftSize / 2; i <= limit; i++) { smoothedPowerSpectrum[i] = spectrum[i].Real * spectrum[i].Real + spectrum[i].Imaginary * spectrum[i].Imaginary; } Common.DCCorrection(smoothedPowerSpectrum, currentF0, fs, fftSize, smoothedPowerSpectrum); Common.LinearSmoothing(smoothedPowerSpectrum, currentF0, fs, fftSize, smoothedPowerSpectrum); }
//----------------------------------------------------------------------------- // CheapTrickGeneralBody() calculates a spectral envelope at a temporal // position. This function is only used in CheapTrick(). // Caution: // forward_fft is allocated in advance to speed up the processing. //----------------------------------------------------------------------------- void CheapTrickGeneralBody(double[] x, int fs, double currentF0, double currentPosition, ForwardRealFFT forwardRealFFT, InverseRealFFT inverseRealFFT, double[] spectralEnvelope) { // F0-adaptive windowing GetWindowedWaveform(x, fs, currentF0, currentPosition, forwardRealFFT); // Calculate power spectrum with DC correction // Note: The calculated power spectrum is stored in an array for waveform. // In this imprementation, power spectrum is transformed by FFT (NOT IFFT). // However, the same result is obtained. // This is tricky but important for simple implementation. GetPowerSpectrum(fs, currentF0, forwardRealFFT); // Smoothing of the power (linear axis) // forward_real_fft.waveform is the power spectrum. Common.LinearSmoothing(forwardRealFFT.Waveform, currentF0 * 2.0 / 3.0, fs, FFTSize, forwardRealFFT.Waveform); // Add infinitesimal noise // This is a safeguard to avoid including zero in the spectrum. AddInfinitesimalNoise(forwardRealFFT.Waveform, FFTSize, forwardRealFFT.Waveform); // Smoothing (log axis) and spectral recovery on the cepstrum domain. SmoothingWithRecovery(currentF0, fs, forwardRealFFT, inverseRealFFT, spectralEnvelope); }
//----------------------------------------------------------------------------- // GetStaticCentroid() calculates the temporally static energy centroid. // Basic idea was proposed by H. Kawahara. //----------------------------------------------------------------------------- void GetStaticCentroid(double[] x, int fs, double currentF0, int fftSize, double currentPosition, ForwardRealFFT forwardRealFFT, double[] staticCentroid) { var centroid1 = new double[fftSize / 2 + 1]; var centroid2 = new double[fftSize / 2 + 1]; GetCentroid(x, fs, currentF0, fftSize, currentPosition - 0.25 / currentF0, forwardRealFFT, centroid1); GetCentroid(x, fs, currentF0, fftSize, currentPosition + 0.25 / currentF0, forwardRealFFT, centroid2); for (int i = 0, limit = fftSize / 2; i <= limit; i++) { staticCentroid[i] = centroid1[i] + centroid2[i]; } Common.DCCorrection(staticCentroid, currentF0, fs, fftSize, staticCentroid); }
//----------------------------------------------------------------------------- // GetWindowedWaveform() windows the waveform by F0-adaptive window //----------------------------------------------------------------------------- void GetWindowedWaveform(double[] x, int fs, double currentF0, double currentPosition, ForwardRealFFT forwardRealFFT) { var halfWindowLength = MatlabFunctions.MatlabRound(1.5 * fs / currentF0); var baseIndex = new int[halfWindowLength * 2 + 1]; var safeIndex = new int[halfWindowLength * 2 + 1]; var window = new double[halfWindowLength * 2 + 1]; SetParametersForGetWindowedWaveform(halfWindowLength, x.Length, currentPosition, fs, currentF0, baseIndex, safeIndex, window); // F0-adaptive windowing var waveForm = forwardRealFFT.Waveform; for (int i = 0, limit = halfWindowLength * 2; i <= limit; i++) { waveForm[i] = x[safeIndex[i]] * window[i] + Rand.Next() * SafeGuardMinimum; } var tmpWeight1 = waveForm.Take(halfWindowLength * 2 + 1).Sum(); var tmpWeight2 = window.Sum(); var weightingCoefficient = tmpWeight1 / tmpWeight2; for (int i = 0, limit = halfWindowLength * 2; i <= limit; i++) { waveForm[i] -= window[i] * weightingCoefficient; } }
void GetCentroid(double[] x, int fs, double currentF0, int fftSize, double currentPosition, ForwardRealFFT forwardRealFFT, double[] centroid) { Array.Clear(forwardRealFFT.Waveform, 0, fftSize); GetWindowedWaveform(x, fs, currentF0, currentPosition, WindowType.Blackman, 4.0, forwardRealFFT.Waveform); var windowRange = MatlabFunctions.MatlabRound(2.0 * fs / currentF0) * 2; var power = Math.Sqrt(forwardRealFFT.Waveform.Take(windowRange).Sum((w) => w * w)); for (var i = 0; i <= windowRange; i++) { forwardRealFFT.Waveform[i] /= power; } FFT.Execute(forwardRealFFT.ForwardFFT); var tmpSpectrum = new Complex[fftSize / 2 + 1]; Array.Copy(forwardRealFFT.Spectrum, tmpSpectrum, tmpSpectrum.Length); for (var i = 0; i < fftSize; i++) { forwardRealFFT.Waveform[i] *= i + 1.0; } FFT.Execute(forwardRealFFT.ForwardFFT); var spectrum = forwardRealFFT.Spectrum; for (int i = 0, limit = fftSize / 2; i <= limit; i++) { centroid[i] = spectrum[i].Real * tmpSpectrum[i].Real + spectrum[i].Imaginary * tmpSpectrum[i].Imaginary; } }
//----------------------------------------------------------------------------- // GetAperiodicResponse() calculates an aperiodic response. //----------------------------------------------------------------------------- void GetAperiodicResponse(int noiseSize, int fftSize, double[] spectrum, double[] aperiodicRatio, double currentVUV, ForwardRealFFT forwardRealFFT, InverseRealFFT inverseRealFFT, MinimumPhaseAnalysis minimumPhase, double[] aperiodicResponse) { GetNoiseSpectrum(noiseSize, fftSize, forwardRealFFT); var logSpectrum = minimumPhase.LogSpectrum; if (currentVUV != 0.0) { for (int i = 0, limit = minimumPhase.FFTSize / 2; i <= limit; i++) { logSpectrum[i] = Math.Log(spectrum[i] * aperiodicRatio[i]) / 2.0; } } else { for (int i = 0, limit = minimumPhase.FFTSize / 2; i <= limit; i++) { logSpectrum[i] = Math.Log(spectrum[i]) / 2.0; } } Common.GetMinimumPhaseSpectrum(minimumPhase); var minimumPhaseSpectrum = minimumPhase.MinimumPhaseSpectrum; Array.Copy(minimumPhaseSpectrum, 0, inverseRealFFT.Spectrum, 0, fftSize / 2 + 1); for (int i = 0, limit = fftSize / 2; i <= limit; i++) { var real = minimumPhaseSpectrum[i].Real * forwardRealFFT.Spectrum[i].Real - minimumPhaseSpectrum[i].Imaginary * forwardRealFFT.Spectrum[i].Imaginary; var imaginary = minimumPhaseSpectrum[i].Real * forwardRealFFT.Spectrum[i].Imaginary + minimumPhaseSpectrum[i].Imaginary * forwardRealFFT.Spectrum[i].Real; inverseRealFFT.Spectrum[i] = new Complex(real, imaginary); } FFT.Execute(inverseRealFFT.InverseFFT); MatlabFunctions.FFTShift(inverseRealFFT.Waveform.SubSequence(0, fftSize), aperiodicResponse); }
double D4CLoveTrainSub(double[] x, int fs, double currentF0, double currentPosition, int f0Length, int fftSize, int boundary0, int boundary1, int boundary2, ForwardRealFFT forwardRealFFT) { var powerSpectrum = new double[fftSize]; var windowLength = MatlabFunctions.MatlabRound(1.5 * fs / currentF0) * 2 + 1; GetWindowedWaveform(x, fs, currentF0, currentPosition, WindowType.Blackman, 3.0, forwardRealFFT.Waveform); Array.Clear(forwardRealFFT.Waveform, windowLength, fftSize - windowLength); FFT.Execute(forwardRealFFT.ForwardFFT); var spectrum = forwardRealFFT.Spectrum; for (int i = boundary0 + 1, limit = fftSize / 2 + 1; i < limit; i++) { powerSpectrum[i] = spectrum[i].Real * spectrum[i].Real + spectrum[i].Imaginary * spectrum[i].Imaginary; } for (var i = boundary0; i <= boundary2; i++) { powerSpectrum[i] += powerSpectrum[i - 1]; } return(powerSpectrum[boundary1] / powerSpectrum[boundary2]); }
//----------------------------------------------------------------------------- // GetOneFrameSegment() calculates a periodic and aperiodic response at a time. //----------------------------------------------------------------------------- void GetOneFrameSegment(double currentVUV, int noiseSize, double[][] spectrogram, int fftSize, double[][] aperiodicity, int f0Length, double framePeriod, double currentTime, double fractionalTimeShift, int fs, ForwardRealFFT forwardRealFFT, InverseRealFFT inverseRealFFT, MinimumPhaseAnalysis minimumPhase, double[] dcRemover, double[] response) { var aperiodicResponse = new double[fftSize]; var periodicResponse = new double[fftSize]; var spectralEnvelope = new double[fftSize]; var aperiodicRatio = new double[fftSize]; GetSpectralEnvelope(currentTime, framePeriod, f0Length, spectrogram, fftSize, spectralEnvelope); GetAperiodicRatio(currentTime, framePeriod, f0Length, aperiodicity, fftSize, aperiodicRatio); // Synthesis of the periodic response GetPeriodicResponse(fftSize, spectralEnvelope, aperiodicRatio, currentVUV, inverseRealFFT, minimumPhase, dcRemover, fractionalTimeShift, fs, periodicResponse); // Synthesis of the aperiodic response GetAperiodicResponse(noiseSize, fftSize, spectralEnvelope, aperiodicRatio, currentVUV, forwardRealFFT, inverseRealFFT, minimumPhase, aperiodicResponse); var sqrtNoiseSize = Math.Sqrt(noiseSize); for (var i = 0; i < fftSize; i++) { response[i] = (periodicResponse[i] * sqrtNoiseSize + aperiodicResponse[i]) / fftSize; } }
//----------------------------------------------------------------------------- // GetCoarseAperiodicity() calculates the aperiodicity in multiples of 3 kHz. // The upper limit is given based on the sampling frequency. //----------------------------------------------------------------------------- private void GetCoarseAperiodicity(double[] staticGroupDelay, int fs, int fftSize, int numberOfAperiodicities, double[] window, ForwardRealFFT forwardRealFFT, SubSequence <double> coarseAperiodicity) { var boundary = MatlabFunctions.MatlabRound(fftSize * 8.0 / window.Length); var halfWindowLength = window.Length / 2; Array.Clear(forwardRealFFT.Waveform, 0, fftSize); var powerSpectrum = new double[fftSize / 2 + 1]; for (var i = 0; i < numberOfAperiodicities; i++) { var center = (int)(FrequencyInterval * (i + 1) * fftSize / fs); for (int j = 0, limit = halfWindowLength * 2; j <= limit; j++) { forwardRealFFT.Waveform[j] = staticGroupDelay[center - halfWindowLength + j] * window[j]; } FFT.Execute(forwardRealFFT.ForwardFFT); var spectrum = forwardRealFFT.Spectrum; for (int j = 0, limit = fftSize / 2; j <= limit; j++) { powerSpectrum[j] = spectrum[j].Real * spectrum[j].Real + spectrum[j].Imaginary * spectrum[j].Imaginary; } Array.Sort(powerSpectrum); for (int j = 1, limit = fftSize / 2; j <= limit; j++) { powerSpectrum[j] += powerSpectrum[j - 1]; } coarseAperiodicity[i] = 10.0 * Math.Log10(powerSpectrum[fftSize / 2 - boundary - 1] / powerSpectrum[fftSize / 2]); } }