/// <summary> /// Inverse STFT /// </summary> /// <param name="stft"></param> /// <returns></returns> public float[] Inverse(List <Tuple <float[], float[]> > stft) { var spectraCount = stft.Count; var samples = new float[spectraCount * _hopSize + _windowSize]; var re = new float[_windowSize]; var im = new float[_windowSize]; var pos = 0; for (var i = 0; i < spectraCount; i++) { stft[i].Item1.FastCopyTo(re, _windowSize); stft[i].Item2.FastCopyTo(im, _windowSize); _fft.Inverse(re, im); // windowing and reconstruction for (var j = 0; j < re.Length; j++) { samples[pos + j] += re[j] * _windowSamples[j] * _norm; } pos += _hopSize; } return(samples); }
/// <summary> /// Compute complex analytic signal, single precision /// </summary> /// <param name="samples">Array of samples</param> /// <param name="norm">Normalize by fft size</param> /// <returns>Complex analytic signal</returns> public Tuple <float[], float[]> AnalyticSignal(float[] samples, bool norm = true) { var sre = new DiscreteSignal(1, samples, allocateNew: true); var sim = new DiscreteSignal(1, samples.Length); var re = sre.Samples; var im = sim.Samples; _fft32.Direct(re, im); for (var i = 1; i < re.Length / 2; i++) { re[i] *= 2; im[i] *= 2; } for (var i = re.Length / 2 + 1; i < re.Length; i++) { re[i] = 0.0f; im[i] = 0.0f; } _fft32.Inverse(re, im); if (norm) { sre.Attenuate(re.Length); sim.Attenuate(im.Length); } return(new Tuple <float[], float[]>(re, im)); }
/// <summary> /// Does Fast Hilbert Transform. /// </summary> /// <param name="input">Input data</param> /// <param name="output">Output data</param> public void Direct(float[] input, float[] output) { // just here, for code brevity, use alias _im for output (i.e. it's not internal _im) var _im = output; Array.Clear(_re, 0, _re.Length); Array.Clear(_im, 0, _im.Length); input.FastCopyTo(_re, input.Length); _fft.Direct(_re, _im); for (var i = 1; i < _re.Length / 2; i++) { _re[i] *= 2; _im[i] *= 2; } for (var i = _re.Length / 2 + 1; i < _re.Length; i++) { _re[i] = 0.0f; _im[i] = 0.0f; } _fft.Inverse(_re, _im); }
/// <summary> /// Method for computing real cepstrum from array of samples /// </summary> /// <param name="samples"></param> /// <param name="cepstrum"></param> /// <param name="power"></param> /// <returns></returns> public void Direct(float[] samples, float[] cepstrum, bool power = false) { samples.FastCopyTo(_realSpectrum, _realSpectrum.Length); Array.Clear(_imagSpectrum, 0, _imagSpectrum.Length); // complex fft _fft.Direct(_realSpectrum, _imagSpectrum); // logarithm of power spectrum for (var i = 0; i < _realSpectrum.Length; i++) { var ps = _realSpectrum[i] * _realSpectrum[i] + _imagSpectrum[i] * _imagSpectrum[i]; _realSpectrum[i] = (float)Math.Log10(ps + float.Epsilon); _imagSpectrum[i] = 0.0f; } // complex ifft _fft.Inverse(_realSpectrum, _imagSpectrum); // take truncated part if (power) { for (var i = 0; i < Size; i++) { cepstrum[i] = (_realSpectrum[i] * _realSpectrum[i] + _imagSpectrum[i] * _imagSpectrum[i]) / _realSpectrum.Length; } } else { _realSpectrum.FastCopyTo(cepstrum, Size); } }
/// <summary> /// Method for computing real cepstrum from array of samples /// </summary> /// <param name="samples"></param> /// <param name="cepstrum"></param> /// <param name="power"></param> /// <returns></returns> public void Direct(float[] samples, float[] cepstrum, bool power = false) { // complex fft _fft.PowerSpectrum(samples, _realSpectrum, false); // logarithm of power spectrum for (var i = 0; i < _realSpectrum.Length; i++) { _realSpectrum[i] = (float)Math.Log10(_realSpectrum[i] + float.Epsilon); _imagSpectrum[i] = 0.0f; } // complex ifft _fft.Inverse(_realSpectrum, _imagSpectrum); // take truncated part if (power) { for (var i = 0; i < _cepstrumSize; i++) { cepstrum[i] = (_realSpectrum[i] * _realSpectrum[i] + _imagSpectrum[i] * _imagSpectrum[i]) / _realSpectrum.Length; } } else { _realSpectrum.FastCopyTo(cepstrum, _cepstrumSize); } }
/// <summary> /// Inverse STFT /// </summary> /// <param name="stft"></param> /// <returns></returns> public float[] Inverse(List <Tuple <float[], float[]> > stft) { var spectraCount = stft.Count; var output = new float[spectraCount * _hopSize + _windowSize]; var windowSum = new float[output.Length]; var re = new float[_windowSize]; var im = new float[_windowSize]; var pos = 0; for (var i = 0; i < spectraCount; i++) { stft[i].Item1.FastCopyTo(re, _windowSize); stft[i].Item2.FastCopyTo(im, _windowSize); _fft.Inverse(re, im); // windowing and reconstruction for (var j = 0; j < re.Length; j++) { output[pos + j] += re[j] * _windowSamples[j]; windowSum[pos + j] += _windowSquared[j]; } pos += _hopSize; } for (var j = 0; j < output.Length; j++) { if (windowSum[j] < 1e-10) { continue; } output[j] /= (windowSum[j] * _fftSize); } return(output); }
/// <summary> /// Inverse DCT-II via FFT /// </summary> /// <param name="input"></param> /// <param name="output"></param> public void Inverse(float[] input, float[] output) { // multiply by exp(j * pi * n / 2N): int N = _fft.Size; for (int i = 0; i < N; i++) { _temp[i] = (float)(input[i] * Math.Cos(0.5 * Math.PI * i / N)); output[i] = (float)(input[i] * Math.Sin(0.5 * Math.PI * i / N)); } _temp[0] *= 0.5f; output[0] *= 0.5f; _fft.Inverse(_temp, output); for (int m = 0; m < _temp.Length / 2; m++) { output[2 * m] = 2 * _temp[m]; output[2 * m + 1] = 2 * _temp[N - 1 - m]; } }
/// <summary> /// Evaluates complex cepstrum as: /// <code> /// Real{IFFT(log(abs(FFT(x)) + unwrapped_phase))} /// </code> /// </summary> /// <param name="input">Input data</param> /// <param name="cepstrum">Complex cepstrum</param> /// <param name="normalize">Normalize cepstrum by FFT size</param> /// <returns>Circular delay (number of samples) added to <paramref name="input"/></returns> public double ComplexCepstrum(float[] input, float[] cepstrum, bool normalize = true) { Array.Clear(_re, 0, _re.Length); Array.Clear(_im, 0, _im.Length); input.FastCopyTo(_re, input.Length); // complex fft _fft.Direct(_re, _im); // complex logarithm of magnitude spectrum // the most difficult part is phase unwrapping which is slightly different from MathUtils.Unwrap var offset = 0.0; _unwrapped[0] = 0.0; var prevPhase = Math.Atan2(_im[0], _re[0]); for (var n = 1; n < _unwrapped.Length; n++) { var phase = Math.Atan2(_im[n], _re[n]); var delta = phase - prevPhase; if (delta > Math.PI) { offset -= 2 * Math.PI; } else if (delta < -Math.PI) { offset += 2 * Math.PI; } _unwrapped[n] = phase + offset; prevPhase = phase; } var mid = _re.Length / 2; var delay = Math.Round(_unwrapped[mid] / Math.PI); for (var i = 0; i < _re.Length; i++) { _unwrapped[i] -= Math.PI * delay * i / mid; var mag = Math.Sqrt(_re[i] * _re[i] + _im[i] * _im[i]); _re[i] = (float)Math.Log(mag + float.Epsilon, _logBase); _im[i] = (float)_unwrapped[i]; } // complex ifft _fft.Inverse(_re, _im); // take truncated part _re.FastCopyTo(cepstrum, Size); if (normalize) { for (var i = 0; i < cepstrum.Length; i++) { cepstrum[i] /= _fft.Size; } } return(delay); }