/// <summary> /// Fast convolution via FFT for arrays of samples (maximally in-place). /// This version is best suited for block processing when memory needs to be reused. /// Input arrays must have size equal to the size of FFT. /// FFT size MUST be set properly in constructor! /// </summary> /// <param name="input">Real parts of the 1st signal (zero-padded)</param> /// <param name="kernel">Real parts of the 2nd signal (zero-padded)</param> /// <param name="output">Real parts of resulting convolution (zero-padded)</param> public void Convolve(float[] input, float[] kernel, float[] output) { Array.Clear(_real1, 0, _fftSize); Array.Clear(_real2, 0, _fftSize); Array.Clear(_imag1, 0, _fftSize); Array.Clear(_imag2, 0, _fftSize); input.FastCopyTo(_real1, input.Length); kernel.FastCopyTo(_real2, kernel.Length); // 1) do FFT of both signals _fft.Direct(_real1, _real1, _imag1); _fft.Direct(_real2, _real2, _imag2); // 2) do complex multiplication of spectra and normalize for (var i = 0; i <= _fftSize / 2; i++) { var re = _real1[i] * _real2[i] - _imag1[i] * _imag2[i]; var im = _real1[i] * _imag2[i] + _imag1[i] * _real2[i]; _real1[i] = 2 * re / _fftSize; _imag1[i] = 2 * im / _fftSize; } // 3) do inverse FFT of resulting spectrum _fft.Inverse(_real1, _imag1, output); }
/// <summary> /// Estimate noise power spectrum /// </summary> /// <param name="noise"></param> private void EstimateNoise(DiscreteSignal noise) { var numFrames = 0; var noiseAcc = new float[_fftSize / 2 + 1]; for (var pos = 0; pos + _fftSize < noise.Length; pos += _hopSize, numFrames++) { noise.Samples.FastCopyTo(_re, _fftSize, pos); Array.Clear(_im, 0, _fftSize); _fft.Direct(_re, _re, _im); for (var j = 0; j <= _fftSize / 2; j++) { noiseAcc[j] += _re[j] * _re[j] + _im[j] * _im[j]; } } // (including smoothing) for (var j = 1; j < _fftSize / 2; j++) { _noiseEstimate[j] = (noiseAcc[j - 1] + noiseAcc[j] + noiseAcc[j + 1]) / (3 * numFrames); } _noiseEstimate[0] /= numFrames; _noiseEstimate[_fftSize / 2] /= numFrames; }
/// <summary> /// Changes <paramref name="kernel"/> coefficients online. /// </summary> public void ChangeKernel(float[] kernel) { if (kernel.Length != _kernel.Length) { return; } Array.Clear(_kernelSpectrumRe, 0, _fftSize); kernel.FastCopyTo(_kernel, kernel.Length); kernel.FastCopyTo(_kernelSpectrumRe, kernel.Length); _fft.Direct(_kernelSpectrumRe, _kernelSpectrumRe, _kernelSpectrumIm); }
public void FftArray() { var input = new float[FrameSize]; var re = new float[FrameSize]; var im = new float[FrameSize]; for (var i = 0; i < N - FrameSize; i += HopSize) { _samples.FastCopyTo(input, FrameSize, i); _fft.Direct(input, re, im); } }
/// <summary> /// Process one frame (block) /// </summary> public void ProcessFrame() { var M = _kernel.Length; var halfSize = _fftSize / 2; Array.Clear(_blockRe, HopSize, M - 1); _fft.Direct(_blockRe, _blockRe, _blockIm); for (var j = 0; j <= halfSize; j++) { _convRe[j] = (_blockRe[j] * _kernelSpectrumRe[j] - _blockIm[j] * _kernelSpectrumIm[j]) / _fftSize; _convIm[j] = (_blockRe[j] * _kernelSpectrumIm[j] + _blockIm[j] * _kernelSpectrumRe[j]) / _fftSize; } _fft.Inverse(_convRe, _convIm, _convRe); for (var j = 0; j < M - 1; j++) { _convRe[j] += _lastSaved[j]; } _convRe.FastCopyTo(_lastSaved, M - 1, HopSize); _outputBufferOffset = 0; _bufferOffset = 0; }
/// <summary> /// Process one frame (FFT block) /// </summary> public virtual void ProcessFrame() { // analysis ========================================================= _dl.FastCopyTo(_re, _fftSize); _re.ApplyWindow(_window); _fft.Direct(_re, _re, _im); // processing ======================================================= ProcessSpectrum(_re, _im, _filteredRe, _filteredIm); // synthesis ======================================================== _fft.Inverse(_filteredRe, _filteredIm, _filteredRe); _filteredRe.ApplyWindow(_window); for (var j = 0; j < _overlapSize; j++) { _filteredRe[j] += _lastSaved[j]; } _filteredRe.FastCopyTo(_lastSaved, _overlapSize, _hopSize); for (var i = 0; i < _filteredRe.Length; i++) // Wet/Dry mix { _filteredRe[i] *= Wet * _gain; _filteredRe[i] += _dl[i] * Dry; } _dl.FastCopyTo(_dl, _overlapSize, _hopSize); _inOffset = _overlapSize; _outOffset = 0; }
private void UpdateSpectrumAndCepstrum() { var fftSize = int.Parse(fftSizeTextBox.Text); var cepstrumSize = int.Parse(cepstrumSizeTextBox.Text); _hopSize = int.Parse(hopSizeTextBox.Text); if (fftSize != _fftSize) { _fftSize = fftSize; _fft = new RealFft(fftSize); _cepstralTransform = new CepstralTransform(cepstrumSize, _fftSize); } if (cepstrumSize != _cepstrumSize) { _cepstrumSize = cepstrumSize; _cepstralTransform = new CepstralTransform(_cepstrumSize, _fftSize); } var pos = _hopSize * _specNo; var block = _signal[pos, pos + _fftSize]; //block.ApplyWindow(WindowTypes.Hamming); var cepstrum = new float[_fftSize]; _cepstralTransform.RealCepstrum(block.Samples, cepstrum); // ************************************************************************ // just visualize spectrum estimated from cepstral coefficients: // ************************************************************************ var real = new float[_fftSize]; var imag = new float[_fftSize]; for (var i = 0; i < 32; i++) { real[i] = cepstrum[i]; } _fft.Direct(real, real, imag); var spectrum = _fft.PowerSpectrum(block, normalize: false).Samples; var avg = spectrum.Average(s => LevelScale.ToDecibel(s)); var spectrumEstimate = real.Take(_fftSize / 2 + 1) .Select(s => (float)LevelScale.FromDecibel(s * 40 - avg)) .ToArray(); spectrumPanel.Line = spectrum; spectrumPanel.Markline = spectrumEstimate; spectrumPanel.ToDecibel(); var pitch = Pitch.FromCepstrum(block); cepstrumPanel.Line = cepstrum; cepstrumPanel.Mark = (int)(_signal.SamplingRate / pitch); }
/// <summary> /// Phase Vocoder algorithm /// </summary> /// <param name="signal"></param> /// <param name="method"></param> /// <returns></returns> public DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { var input = signal.Samples; var output = new float[(int)(input.Length * _stretch) + _fftSize]; var posSynthesis = 0; for (var posAnalysis = 0; posAnalysis + _fftSize < input.Length; posAnalysis += _hopAnalysis) { input.FastCopyTo(_re, _fftSize, posAnalysis); Array.Clear(_im, 0, _fftSize); _re.ApplyWindow(_window); _fft.Direct(_re, _re, _im); for (var j = 1; j <= _fftSize / 2; j++) { var mag = Math.Sqrt(_re[j] * _re[j] + _im[j] * _im[j]); var phase = Math.Atan2(_im[j], _re[j]); var delta = phase - _prevPhase[j]; var deltaUnwrapped = delta - _hopAnalysis * _omega[j]; var deltaWrapped = MathUtils.Mod(deltaUnwrapped + Math.PI, 2 * Math.PI) - Math.PI; var freq = _omega[j] + deltaWrapped / _hopAnalysis; _phaseTotal[j] += _hopSynthesis * freq; _prevPhase[j] = phase; _re[j] = (float)(mag * Math.Cos(_phaseTotal[j])); _im[j] = (float)(mag * Math.Sin(_phaseTotal[j])); } _fft.Inverse(_re, _im, _re); for (var j = 0; j < _re.Length; j++) { output[posSynthesis + j] += _re[j] * _window[j]; } for (var j = 0; j < _hopSynthesis; j++) { output[posSynthesis + j] *= _gain; } posSynthesis += _hopSynthesis; } for (; posSynthesis < output.Length; posSynthesis++) { output[posSynthesis] *= _gain; } return(new DiscreteSignal(signal.SamplingRate, output)); }
/// <summary> /// Process one frame (block) /// </summary> public void ProcessFrame() { Array.Clear(_im1, 0, _fftSize); Array.Clear(_im2, 0, _fftSize); _dl1.FastCopyTo(_re1, _fftSize); _dl2.FastCopyTo(_re2, _fftSize); _re1.ApplyWindow(_window); _re2.ApplyWindow(_window); _fft.Direct(_re1, _re1, _im1); _fft.Direct(_re2, _re2, _im2); for (var j = 1; j <= _fftSize / 2; j++) { var mag1 = Math.Sqrt(_re1[j] * _re1[j] + _im1[j] * _im1[j]); var phase2 = Math.Atan2(_im2[j], _re2[j]); _filteredRe[j] = (float)(mag1 * Math.Cos(phase2)); _filteredIm[j] = (float)(mag1 * Math.Sin(phase2)); } _fft.Inverse(_filteredRe, _filteredIm, _filteredRe); _filteredRe.ApplyWindow(_window); for (var j = 0; j < _overlapSize; j++) { _filteredRe[j] += _lastSaved[j]; } _filteredRe.FastCopyTo(_lastSaved, _overlapSize, _hopSize); for (var i = 0; i < _filteredRe.Length; i++) // Wet / Dry mix { _filteredRe[i] *= Wet / _fftSize; _filteredRe[i] += _dl2[i] * Dry; } _dl1.FastCopyTo(_dl1, _overlapSize, _hopSize); _dl2.FastCopyTo(_dl2, _overlapSize, _hopSize); _inOffset = _overlapSize; _outOffset = 0; }
public void FFT(bool forward) { if (forward) { fft.Direct(data, re, im); } else { fft.Inverse(re, im, data); } }
public void TestRealFft() { float[] array = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; // Enumerable.Range(0, 16); float[] re = new float[9]; float[] im = new float[9]; var realFft = new RealFft(16); realFft.Direct(array, re, im); Assert.That(re, Is.EqualTo(new float[] { 120, -8, -8, -8, -8, -8, -8, -8, -8 }).Within(1e-5)); Assert.That(im, Is.EqualTo(new float[] { 0, 40.21872f, 19.31371f, 11.97285f, 8, 5.34543f, 3.31371f, 1.591299f, 0 }).Within(1e-5)); }
public void TestInverseRealFft() { float[] array = { 1, 5, 3, 7, 2, 3, 0, 7 }; float[] output = new float[array.Length]; float[] re = new float[5]; float[] im = new float[5]; var realFft = new RealFft(8); realFft.Direct(array, re, im); realFft.Inverse(re, im, output); Assert.That(output, Is.EqualTo(array.Select(a => a * 4)).Within(1e-5)); }
/// <summary> /// Phase Vocoder algorithm /// </summary> /// <param name="signal"></param> /// <param name="method"></param> /// <returns></returns> public DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { var input = signal.Samples; var output = new float[(int)(input.Length * _stretch) + _fftSize]; var posSynthesis = 0; for (var posAnalysis = 0; posAnalysis + _fftSize < input.Length; posAnalysis += _hopAnalysis) { input.FastCopyTo(_re, _fftSize, posAnalysis); Array.Clear(_im, 0, _fftSize); _re.ApplyWindow(_window); _fft.Direct(_re, _re, _im); for (var j = 1; j <= _fftSize / 2; j++) { var mag = Math.Sqrt(_re[j] * _re[j] + _im[j] * _im[j]); var phase = 2 * Math.PI * _rand.NextDouble(); _re[j] = (float)(mag * Math.Cos(phase)); _im[j] = (float)(mag * Math.Sin(phase)); } _fft.Inverse(_re, _im, _re); for (var j = 0; j < _re.Length; j++) { output[posSynthesis + j] += _re[j] * _window[j]; } for (var j = 0; j < _hopSynthesis; j++) { output[posSynthesis + j] *= _gain; } posSynthesis += _hopSynthesis; } for (; posSynthesis < output.Length; posSynthesis++) { output[posSynthesis] *= _gain; } return(new DiscreteSignal(signal.SamplingRate, output)); }
/// <summary> /// Phase Vocoder algorithm /// </summary> /// <param name="signal"></param> /// <param name="method"></param> /// <returns></returns> public DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { var input = signal.Samples; var output = new float[(int)(input.Length * _stretch) + _fftSize]; var posSynthesis = 0; for (var posAnalysis = 0; posAnalysis + _fftSize < input.Length; posAnalysis += _hopAnalysis) { input.FastCopyTo(_re, _fftSize, posAnalysis); // analysis ================================================== _re.ApplyWindow(_window); _fft.Direct(_re, _re, _im); // processing ================================================ ProcessSpectrum(); // synthesis ================================================= _fft.Inverse(_re, _im, _re); for (var j = 0; j < _re.Length; j++) { output[posSynthesis + j] += _re[j] * _window[j]; } for (var j = 0; j < _hopSynthesis; j++) { output[posSynthesis + j] *= _gain; } posSynthesis += _hopSynthesis; } for (; posSynthesis < output.Length; posSynthesis++) { output[posSynthesis] *= _gain; } return(new DiscreteSignal(signal.SamplingRate, output)); }
public double[] Spectrum(double[] input, bool scale) { int length = input.Length; var data = Helper.ConvertToFloat(input); var re = new float[length]; var im = new float[length]; var fft = new RealFft(length); fft.Direct(data, re, im); var spectrum = Helper.ComputeSpectrum(length / 2, re, im); fft.Inverse(re, im, data); return(spectrum); }
/// <summary> /// Process one frame (block) /// </summary> public void ProcessFrame() { Array.Clear(_im, 0, _fftSize); _dl.FastCopyTo(_re, _fftSize); _re.ApplyWindow(_window); _fft.Direct(_re, _re, _im); for (var j = 1; j <= _fftSize / 2; j++) { var mag = Math.Sqrt(_re[j] * _re[j] + _im[j] * _im[j]); var phase = 2 * Math.PI * _rand.NextDouble(); _filteredRe[j] = (float)(mag * Math.Cos(phase)); _filteredIm[j] = (float)(mag * Math.Sin(phase)); } _fft.Inverse(_filteredRe, _filteredIm, _filteredRe); _filteredRe.ApplyWindow(_window); for (var j = 0; j < _overlapSize; j++) { _filteredRe[j] += _lastSaved[j]; } _filteredRe.FastCopyTo(_lastSaved, _overlapSize, _hopSize); for (var i = 0; i < _filteredRe.Length; i++) // Wet / Dry mix { _filteredRe[i] *= Wet * _gain; _filteredRe[i] += _dl[i] * Dry; } _dl.FastCopyTo(_dl, _overlapSize, _hopSize); _inOffset = _overlapSize; _outOffset = 0; }
public void process(float[] x, int length) { int i; for (i = 0; i < length; i++) { real[i] = x[i]; imag[i] = 0; } for (; i < rfft.Size; i++) { real[i] = imag[i] = 0; } rfft.Direct(real, real, imag); stretcher.stretch(real, real); stretcher.stretch(imag, imag); rfft.Inverse(real, imag, real); for (i = 0; i < length; i++) { x[i] = real[i] / rfft.Size; } }
/// <summary> /// Constructor /// </summary> /// <param name="kernel"></param> /// <param name="fftSize"></param> public OlsBlockConvolver(IEnumerable <float> kernel, int fftSize) { _kernel = kernel.ToArray(); _fftSize = MathUtils.NextPowerOfTwo(fftSize); Guard.AgainstExceedance(_kernel.Length, _fftSize, "Kernel length", "the size of FFT"); _fft = new RealFft(_fftSize); _kernelSpectrumRe = _kernel.PadZeros(_fftSize); _kernelSpectrumIm = new float[_fftSize]; _convRe = new float[_fftSize]; _convIm = new float[_fftSize]; _blockRe = new float[_fftSize]; _blockIm = new float[_fftSize]; _lastSaved = new float[_kernel.Length - 1]; _fft.Direct(_kernelSpectrumRe, _kernelSpectrumRe, _kernelSpectrumIm); Reset(); }
/// <summary> /// Constructor /// </summary> /// <param name="kernel"></param> /// <param name="fftSize"></param> public OlsBlockConvolver(IEnumerable <float> kernel, int fftSize) { _fftSize = MathUtils.NextPowerOfTwo(fftSize); if (kernel.Count() > _fftSize) { throw new ArgumentException("Kernel length must not exceed the size of FFT!"); } _fft = new RealFft(_fftSize); _kernel = kernel.ToArray(); _kernelSpectrumRe = _kernel.PadZeros(_fftSize); _kernelSpectrumIm = new float[_fftSize]; _convRe = new float[_fftSize]; _convIm = new float[_fftSize]; _blockRe = new float[_fftSize]; _blockIm = new float[_fftSize]; _lastSaved = new float[_kernel.Length - 1]; _fft.Direct(_kernelSpectrumRe, _kernelSpectrumRe, _kernelSpectrumIm); Reset(); }
/// <summary> /// Process one frame (block) /// </summary> public void ProcessFrame() { Array.Clear(_im, 0, _fftSize); _dl.FastCopyTo(_re, _fftSize); _re.ApplyWindow(_window); _fft.Direct(_re, _re, _im); var nextPhase = (float)(2 * Math.PI * _hopSize / _fftSize); for (var j = 1; j <= _fftSize / 2; j++) { _mag[j] = (float)Math.Sqrt(_re[j] * _re[j] + _im[j] * _im[j]); _phase[j] = (float)Math.Atan2(_im[j], _re[j]); var delta = _phase[j] - _prevPhase[j]; _prevPhase[j] = _phase[j]; delta -= j * nextPhase; var deltaWrapped = MathUtils.Mod(delta + Math.PI, 2 * Math.PI) - Math.PI; _phase[j] = _freqResolution * (j + (float)deltaWrapped / nextPhase); } Array.Clear(_re, 0, _fftSize); Array.Clear(_im, 0, _fftSize); // "stretch" spectrum: var stretchPos = 0; for (var j = 0; j <= _fftSize / 2 && stretchPos <= _fftSize / 2; j++) { _re[stretchPos] += _mag[j]; _im[stretchPos] = _phase[j] * _shift; stretchPos = (int)(j * _shift); } for (var j = 1; j <= _fftSize / 2; j++) { var mag = _re[j]; var freqIndex = (_im[j] - j * _freqResolution) / _freqResolution; _phaseTotal[j] += nextPhase * (freqIndex + j); _filteredRe[j] = (float)(mag * Math.Cos(_phaseTotal[j])); _filteredIm[j] = (float)(mag * Math.Sin(_phaseTotal[j])); } _fft.Inverse(_filteredRe, _filteredIm, _filteredRe); _filteredRe.ApplyWindow(_window); for (var j = 0; j < _overlapSize; j++) { _filteredRe[j] += _lastSaved[j]; } _filteredRe.FastCopyTo(_lastSaved, _overlapSize, _hopSize); for (var i = 0; i < _filteredRe.Length; i++) // Wet / Dry mix { _filteredRe[i] *= Wet * _gain; _filteredRe[i] += _dl[i] * Dry; } _dl.FastCopyTo(_dl, _overlapSize, _hopSize); _inOffset = _overlapSize; _outOffset = 0; }
/// <summary> /// Phase locking procedure /// </summary> /// <param name="signal"></param> /// <returns></returns> public DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { var input = signal.Samples; var output = new float[(int)(input.Length * _stretch) + _fftSize]; var peakCount = 0; var posSynthesis = 0; for (var posAnalysis = 0; posAnalysis + _fftSize < input.Length; posAnalysis += _hopAnalysis) { input.FastCopyTo(_re, _fftSize, posAnalysis); Array.Clear(_im, 0, _fftSize); _re.ApplyWindow(_window); _fft.Direct(_re, _re, _im); for (var j = 0; j < _mag.Length; j++) { _mag[j] = Math.Sqrt(_re[j] * _re[j] + _im[j] * _im[j]); _phase[j] = Math.Atan2(_im[j], _re[j]); } // spectral peaks in magnitude spectrum peakCount = 0; for (var j = 2; j < _mag.Length - 3; j++) { if (_mag[j] <= _mag[j - 1] || _mag[j] <= _mag[j - 2] || _mag[j] <= _mag[j + 1] || _mag[j] <= _mag[j + 2]) { continue; // if not a peak } _peaks[peakCount++] = j; } _peaks[peakCount++] = _mag.Length - 1; // assign phases at peaks to all neighboring frequency bins var leftPos = 1; for (var j = 0; j < peakCount - 1; j++) { var peakPos = _peaks[j]; var peakPhase = _phase[peakPos]; _delta[peakPos] = peakPhase - _prevPhase[peakPos]; var deltaUnwrapped = _delta[peakPos] - _hopAnalysis * _omega[peakPos]; var deltaWrapped = MathUtils.Mod(deltaUnwrapped + Math.PI, 2 * Math.PI) - Math.PI; var freq = _omega[peakPos] + deltaWrapped / _hopAnalysis; _phaseTotal[peakPos] = _phaseTotal[peakPos] + _hopSynthesis * freq; var rightPos = (_peaks[j] + _peaks[j + 1]) / 2; for (var k = leftPos; k < rightPos; k++) { _phaseTotal[k] = _phaseTotal[peakPos] + _phase[k] - _phase[peakPos]; _prevPhase[k] = _phase[k]; _re[k] = (float)(_mag[k] * Math.Cos(_phaseTotal[k])); _im[k] = (float)(_mag[k] * Math.Sin(_phaseTotal[k])); } leftPos = rightPos; } _fft.Inverse(_re, _im, _re); for (var j = 0; j < _re.Length; j++) { output[posSynthesis + j] += _re[j] * _window[j]; } for (var j = 0; j < _hopSynthesis; j++) { output[posSynthesis + j] *= _gain; } posSynthesis += _hopSynthesis; } for (; posSynthesis < output.Length; posSynthesis++) { output[posSynthesis] *= _gain; } return(new DiscreteSignal(signal.SamplingRate, output)); }