//----------------------------------------------------------------------------- // 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; } }
//----------------------------------------------------------------------------- // SetParametersForGetWindowedWaveform() //----------------------------------------------------------------------------- void SetParametersForGetWindowedWaveform(int halfWindowLength, int xLength, double currentPosition, int fs, double currentF0, int[] baseIndex, int[] safeIndex, double[] window) { for (var i = -halfWindowLength; i <= halfWindowLength; i++) { baseIndex[i + halfWindowLength] = i; } var origin = MatlabFunctions.MatlabRound(currentPosition * fs + 0.001); for (int i = 0, limit = halfWindowLength * 2; i <= limit; i++) { safeIndex[i] = Math.Min(xLength - 1, Math.Max(0, origin + baseIndex[i])); } var average = 0.0; for (int i = 0, limit = halfWindowLength * 2; i <= limit; i++) { var position = baseIndex[i] / 1.5 / fs; window[i] = 0.5 * Math.Cos(Math.PI * position * currentF0) + 0.5; average += window[i] * window[i]; } average = Math.Sqrt(average); for (int i = 0, limit = halfWindowLength * 2; i <= limit; i++) { window[i] /= average; } }
void GetPulseLocationsForTimeBase(double[] interpolatedF0, double[] timeAxis, int numberOfSamples, double origin) { var totalPhase = new double[numberOfSamples + Handoff]; totalPhase[0] = Handoff == 1 ? HandoffPhase : PI2 * interpolatedF0[0] / SampleRate; totalPhase[1] = totalPhase[0] + PI2 * interpolatedF0[0] / SampleRate; for (int i = 1 + Handoff, limit = numberOfSamples + Handoff; i < limit; i++) { totalPhase[i] = totalPhase[i - 1] + PI2 * interpolatedF0[i - Handoff] / SampleRate; } HandoffPhase = totalPhase[numberOfSamples - 1 + Handoff]; var wrapPhase = totalPhase.Select((t) => t % PI2).ToArray(); var wrapPhaseABS = wrapPhase.Zip(wrapPhase.Skip(1), (w, nw) => Math.Abs(nw - w)).Append(0.0).ToArray(); var pointer = Buffer.HeadIndex; var numberOfPulses = 0; for (int i = 0, limit = numberOfSamples - 1 + Handoff; i < limit; i++) { if (wrapPhaseABS[i] > Math.PI) { Buffer.PulseLocations[pointer][numberOfPulses] = timeAxis[i] - (double)Handoff / SampleRate; Buffer.PulseLocationIndex[pointer][numberOfPulses] = MatlabFunctions.MatlabRound(Buffer.PulseLocations[pointer][numberOfPulses] * SampleRate); numberOfPulses++; } } Buffer.NumberOfPulses[pointer] = numberOfPulses; if (numberOfPulses != 0) { LastLocation = Buffer.PulseLocationIndex[pointer][numberOfPulses - 1]; } }
//----------------------------------------------------------------------------- // SetParametersForGetWindowedWaveform() //----------------------------------------------------------------------------- void SetParametersForGetWindowedWaveform(int halfWindowLength, int xLength, double currentPosition, int fs, double currentF0, WindowType windowType, double windowLengthRatio, int[] baseIndex, int[] safeIndex, double[] window) { for (var i = -halfWindowLength; i <= halfWindowLength; i++) { baseIndex[i + halfWindowLength] = i; } var origin = MatlabFunctions.MatlabRound(currentPosition * fs + 0.001); for (int i = 0, limit = halfWindowLength * 2; i <= limit; i++) { safeIndex[i] = Math.Min(xLength - 1, Math.Max(0, origin + baseIndex[i])); } switch (windowType) { case WindowType.Hanning: for (int i = 0, limit = halfWindowLength * 2; i <= limit; i++) { var position = (2.0 * baseIndex[i] / windowLengthRatio) / fs; window[i] = 0.5 * Math.Cos(Math.PI * position * currentF0) + 0.5; } break; case WindowType.Blackman: for (int i = 0, limit = halfWindowLength * 2; i <= limit; i++) { var position = (2.0 * baseIndex[i] / windowLengthRatio) / fs; window[i] = 0.42 + 0.5 * Math.Cos(Math.PI * position * currentF0) + 0.08 * Math.Cos(Math.PI * position * currentF0 * 2); } break; } }
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; } }
//----------------------------------------------------------------------------- // 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]); } }
//----------------------------------------------------------------------------- // 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); }
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]); }
//----------------------------------------------------------------------------- // GetWindowedWaveform() windows the waveform by F0-adaptive window // In the variable window_type, 1: hanning, 2: blackman //----------------------------------------------------------------------------- void GetWindowedWaveform(double[] x, int fs, double currentF0, double currentPosition, WindowType windowType, double windowLengthRatio, double[] waveform) { var halfWindowLength = MatlabFunctions.MatlabRound(windowLengthRatio * fs / currentF0 / 2.0); 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, windowType, windowLengthRatio, baseIndex, safeIndex, window); for (int i = 0, limit = halfWindowLength * 2; i <= limit; i++) { waveform[i] = x[safeIndex[i]] * window[i] + Rand.Next() * SafeGuardMinimum; } var tmpWeight1 = waveform.Sum(); var tmpWeight2 = window.Sum(); var weightingCoefficient = tmpWeight1 / tmpWeight2; for (int i = 0, limit = halfWindowLength * 2; i <= limit; i++) { waveform[i] -= window[i] * weightingCoefficient; } }