Пример #1
0
        /// <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);
        }
Пример #2
0
        /// <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;
        }
Пример #3
0
        /// <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);
        }
Пример #4
0
        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);
            }
        }
Пример #5
0
        /// <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;
        }
Пример #6
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;
        }
Пример #7
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);
        }
Пример #8
0
        /// <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));
        }
Пример #9
0
        /// <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;
        }
Пример #10
0
 public void FFT(bool forward)
 {
     if (forward)
     {
         fft.Direct(data, re, im);
     }
     else
     {
         fft.Inverse(re, im, data);
     }
 }
Пример #11
0
        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));
        }
Пример #12
0
        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));
        }
Пример #13
0
        /// <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));
        }
Пример #14
0
        /// <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));
        }
Пример #15
0
        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);
        }
Пример #16
0
        /// <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;
        }
Пример #17
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;
            }
        }
Пример #18
0
        /// <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();
        }
Пример #19
0
        /// <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();
        }
Пример #20
0
        /// <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;
        }
Пример #21
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));
        }