예제 #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.
        /// </summary>
        /// <param name="real1">Real parts of the 1st signal (zero-padded)</param>
        /// <param name="imag1">Imaginary parts of the 1st signal (zero-padded)</param>
        /// <param name="real2">Real parts of the 2nd signal (zero-padded)</param>
        /// <param name="imag2">Imaginary parts of the 2nd signal (zero-padded)</param>
        /// <param name="res">Real parts of resulting convolution (zero-padded if center == 0)</param>
        /// <param name="center">
        /// Position of central sample for the case of 2*M-1 convolution
        /// (if it is set then resulting array has length of M)
        /// </param>
        public static void Convolve(float[] real1, float[] imag1, float[] real2, float[] imag2, float[] res, int center = 0)
        {
            var fftSize = real1.Length;
            var fft     = new Fft(fftSize);

            // 1) do FFT of both signals

            fft.Direct(real1, imag1);
            fft.Direct(real2, imag2);

            // 2) do complex multiplication of spectra and normalize

            for (var i = 0; i < fftSize; i++)
            {
                var re = real1[i] * real2[i] - imag1[i] * imag2[i];
                var im = real1[i] * imag2[i] + imag1[i] * real2[i];
                real1[i] = re / fftSize;
                imag1[i] = im / fftSize;
            }

            // 3) do inverse FFT of resulting spectrum

            fft.Inverse(real1, imag1);

            // 4) return output array

            if (center > 0)
            {
                real1.FastCopyTo(res, center, center - 1);
            }
            else
            {
                real1.FastCopyTo(res, fftSize);
            }
        }
예제 #2
0
        /// <summary>
        /// Estimate noise power spectrum
        /// </summary>
        /// <param name="noise"></param>
        private void EstimateNoise(DiscreteSignal noise)
        {
            var numFrames = 0;

            for (var pos = 0; pos + _fftSize < noise.Length; pos += _hopSize, numFrames++)
            {
                noise.Samples.FastCopyTo(_re, _fftSize, pos);
                _zeroblock.FastCopyTo(_im, _fftSize);

                _fft.Direct(_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>
        /// 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)
        {
            _zeroblock.FastCopyTo(_real1, _fftSize);
            _zeroblock.FastCopyTo(_real2, _fftSize);
            _zeroblock.FastCopyTo(_imag1, _fftSize);
            _zeroblock.FastCopyTo(_imag2, _fftSize);

            input.FastCopyTo(_real1, input.Length);
            kernel.FastCopyTo(_real2, kernel.Length);

            // 1) do FFT of both signals

            _fft.Direct(_real1, _imag1);
            _fft.Direct(_real2, _imag2);

            // 2) do complex multiplication of spectra and normalize

            for (var i = 0; i < _fftSize; i++)
            {
                var re = _real1[i] * _real2[i] - _imag1[i] * _imag2[i];
                var im = _real1[i] * _imag2[i] + _imag1[i] * _real2[i];
                output[i] = re / _fftSize;
                _imag1[i] = im / _fftSize;
            }

            // 3) do inverse FFT of resulting spectrum

            _fft.Inverse(output, _imag1);
        }
예제 #4
0
        /// <summary>
        /// Process one frame (block)
        /// </summary>
        public void ProcessFrame()
        {
            _zeroblock.FastCopyTo(_im1, _fftSize);
            _zeroblock.FastCopyTo(_im2, _fftSize);
            _dl1.FastCopyTo(_re1, _fftSize);
            _dl2.FastCopyTo(_re2, _fftSize);

            _re1.ApplyWindow(_window);
            _re2.ApplyWindow(_window);

            _fft.Direct(_re1, _im1);
            _fft.Direct(_re2, _im2);

            for (var j = 0; 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));
            }

            for (var j = _fftSize / 2 + 1; j < _fftSize; j++)
            {
                _filteredRe[j] = _filteredIm[j] = 0.0f;
            }

            _fft.Inverse(_filteredRe, _filteredIm);

            _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;
        }
예제 #5
0
        // Update chart of frequency domain
        private void UpdateSpectra()
        {
            var fftSize      = int.Parse(fftSizeTextBox.Text);
            var cepstrumSize = int.Parse(cepstrumSizeTextBox.Text);

            _hopSize = int.Parse(hopSizeTextBox.Text);

            if (fftSize != _fftSize)
            {
                _fftSize           = fftSize;
                _fft               = new Fft(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 = _cepstralTransform.Direct(block);

            var real = new float[_fftSize];
            var imag = new float[_fftSize];

            for (var i = 0; i < 32; i++)
            {
                real[i] = cepstrum[i];
            }

            _fft.Direct(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 / _fftSize - avg))
                                   .ToArray();

            spectrumPanel.Line     = spectrum;
            spectrumPanel.Markline = spectrumEstimate;
            spectrumPanel.ToDecibel();

            spectrumPanel.max_freq_value = spectrum.Length * _signal.SamplingRate / fftSize;
        }
예제 #6
0
        public void ComplexFftArray()
        {
            var reInput = new float[FrameSize];
            var imInput = new float[FrameSize];

            var re = new float[FrameSize];
            var im = new float[FrameSize];

            for (var i = 0; i < N - FrameSize; i += HopSize)
            {
                _samples.FastCopyTo(reInput, FrameSize, i);

                _complexFft.Direct(reInput, imInput, re, im);
            }
        }
예제 #7
0
        public double[] Spectrum(double[] input, bool scale)
        {
            int length = input.Length;

            var fft = new Fft(length);

            var re = new float[length];
            var im = new float[length];

            for (int i = 0; i < length; i++)
            {
                re[i] = (float)input[i];
                im[i] = 0f;
            }

            fft.Direct(re, im);

            var spectrum = Helper.ComputeSpectrum(length, re, im);

            fft.Inverse(re, im);

            for (int i = 0; i < length; i++)
            {
                input[i] = re[i];
            }

            return(spectrum);
        }
        /// <summary>
        /// Process one frame (block)
        /// </summary>
        public void ProcessFrame()
        {
            var M = _kernel.Length;

            _zeroblock.FastCopyTo(_blockIm, _fftSize);
            _zeroblock.FastCopyTo(_blockRe, M - 1, 0, HopSize);

            _fft.Direct(_blockRe, _blockIm);
            for (var j = 0; j < _fftSize; 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);

            for (var j = 0; j < M - 1; j++)
            {
                _convRe[j] += _lastSaved[j];
            }

            _convRe.FastCopyTo(_lastSaved, M - 1, HopSize);

            _outputBufferOffset = 0;
            _bufferOffset       = 0;
        }
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="kernel"></param>
        /// <param name="fftSize"></param>
        public OlaBlockConvolver(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 Fft(_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];
            _zeroblock        = new float[_fftSize];

            _fft.Direct(_kernelSpectrumRe, _kernelSpectrumIm);

            Reset();
        }
예제 #10
0
        public override DiscreteSignal ApplyTo(DiscreteSignal signal,
                                               FilteringMethod method = FilteringMethod.Auto)
        {
            var input  = signal.Samples;
            var output = new float[input.Length];

            var posSynthesis = 0;

            for (var posAnalysis = 0; posAnalysis + _fftSize < input.Length; posAnalysis += _hopSize)
            {
                input.FastCopyTo(_re, _fftSize, posAnalysis);
                _zeroblock.FastCopyTo(_im, _fftSize);

                _re.ApplyWindow(_window);

                _fft.Direct(_re, _im);

                for (var j = 0; j < _fftSize / 2 + 1; j++)
                {
                    var mag   = Math.Sqrt(_re[j] * _re[j] + _im[j] * _im[j]);
                    var phase = Math.Atan2(_im[j], _re[j]);

                    _re[j] = (float)mag;
                    _im[j] = 0;
                }

                for (var j = _fftSize / 2 + 1; j < _fftSize; j++)
                {
                    _re[j] = _im[j] = 0.0f;
                }

                _fft.Inverse(_re, _im);

                for (var j = 0; j < _re.Length; j++)
                {
                    output[posSynthesis + j] += _re[j] * _window[j];
                }

                for (var j = 0; j < _hopSize; j++)
                {
                    output[posSynthesis + j] *= _gain;
                    output[j] = Wet * output[j] + Dry * input[j];
                }

                posSynthesis += _hopSize;
            }

            for (; posSynthesis < output.Length; posSynthesis++)
            {
                output[posSynthesis] *= _gain;
                output[posSynthesis]  = Wet * output[posSynthesis] + Dry * input[posSynthesis];
            }

            return(new DiscreteSignal(signal.SamplingRate, output));
        }
예제 #11
0
 public void FFT(bool forward)
 {
     if (forward)
     {
         fft.Direct(re, im);
     }
     else
     {
         fft.Inverse(re, im);
     }
 }
예제 #12
0
        public void TestComplexFft()
        {
            float[] re = { 0, 1, 2, 3, 4, 5, 6, 7 };
            float[] im = new float[8];

            var fft = new Fft(8);

            fft.Direct(re, im);

            Assert.That(re, Is.EqualTo(new float[] { 28, -4, -4, -4, -4, -4, -4, -4 }).Within(1e-5));
            Assert.That(im, Is.EqualTo(new float[] { 0, 9.65685f, 4, 1.65685f, 0, -1.65685f, -4, -9.65685f }).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);
                _zeroblock.FastCopyTo(_im, _fftSize);

                _re.ApplyWindow(_window);

                _fft.Direct(_re, _im);

                for (var j = 0; j < _fftSize / 2 + 1; 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));
                }

                for (var j = _fftSize / 2 + 1; j < _fftSize; j++)
                {
                    _re[j] = _im[j] = 0.0f;
                }

                _fft.Inverse(_re, _im);

                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
        public void TestInverseFftNormalized()
        {
            float[] re = { 1, 5, 3, 7, 2, 3, 0, 7 };
            float[] im = new float[re.Length];

            var fft = new Fft(8);

            fft.Direct(re, im);
            fft.InverseNorm(re, im);

            Assert.That(re, Is.EqualTo(new[] { 1, 5, 3, 7, 2, 3, 0, 7 }).Within(1e-5));
        }
예제 #15
0
        /// <summary>
        /// Fast convolution via FFT of real-valued signals.
        /// </summary>
        /// <param name="signal1"></param>
        /// <param name="signal2"></param>
        /// <returns></returns>
        public static DiscreteSignal Convolve(DiscreteSignal signal1, DiscreteSignal signal2)
        {
            // prepare blocks in memory:

            var length = signal1.Length + signal2.Length - 1;

            var fftSize = MathUtils.NextPowerOfTwo(length);
            var fft     = new Fft(fftSize);

            var real1 = new float[fftSize];
            var imag1 = new float[fftSize];
            var real2 = new float[fftSize];
            var imag2 = new float[fftSize];

            signal1.Samples.FastCopyTo(real1, signal1.Length);
            signal2.Samples.FastCopyTo(real2, signal2.Length);

            // 1) do FFT of both signals

            fft.Direct(real1, imag1);
            fft.Direct(real2, imag2);

            // 2) do complex multiplication of spectra and normalize

            for (var i = 0; i < fftSize; i++)
            {
                var re = real1[i] * real2[i] - imag1[i] * imag2[i];
                var im = real1[i] * imag2[i] + imag1[i] * real2[i];
                real1[i] = re / fftSize;
                imag1[i] = im / fftSize;
            }

            // 3) do inverse FFT of resulting spectrum

            fft.Inverse(real1, imag1);

            // 4) return resulting meaningful part of the signal (truncate size to N + M - 1)

            return(new DiscreteSignal(signal1.SamplingRate, real1).First(length));
        }
예제 #16
0
        public void TestInverseFft()
        {
            float[] re = { 1, 5, 3, 7, 2, 3, 0, 7 };
            float[] im = new float[re.Length];

            var fft = new Fft(8);

            fft.Direct(re, im);
            fft.Inverse(re, im);

            Assert.That(re, Is.EqualTo(new[] { 8, 40, 24, 56, 16, 24, 0, 56 }).Within(1e-5));
            // re[i] * 8,  i = 0..7
        }
예제 #17
0
        /// <summary>
        /// Direct transform
        /// </summary>
        /// <param name="re"></param>
        public void Direct(float[] re)
        {
            for (var i = 0; i < _im.Length; i++)
            {
                _im[i] = 0;
            }

            _fft.Direct(re, _im);

            for (var i = 0; i < re.Length; i++)
            {
                re[i] -= _im[i];
            }
        }
예제 #18
0
        /// <summary>
        /// Process one frame (block)
        /// </summary>
        public void ProcessFrame()
        {
            _zeroblock.FastCopyTo(_im, _fftSize);
            _dl.FastCopyTo(_re, _fftSize);

            _re.ApplyWindow(_window);

            _fft.Direct(_re, _im);

            for (var j = 0; 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));
            }

            for (var j = _fftSize / 2 + 1; j < _fftSize; j++)
            {
                _filteredRe[j] = _filteredIm[j] = 0.0f;
            }

            _fft.Inverse(_filteredRe, _filteredIm);

            _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 * 2 / _fftSize;
                _filteredRe[i] += _dl[i] * Dry;
            }

            _dl.FastCopyTo(_dl, _overlapSize, _hopSize);

            _inOffset  = _overlapSize;
            _outOffset = 0;
        }
예제 #19
0
        public static void ApplyFftToSignal(string filePath)
        {
            Console.WriteLine($"Applying FFT to {filePath} file");

            using (var afr = new FileStream(filePath, FileMode.Open))
            {
                // create a signal file
                var            waveFile = new WaveFile(afr, false);
                DiscreteSignal signal   = waveFile[Channels.Left];

                float[] real = signal.First(1024).Samples;
                float[] imag = new float[1024];

                var fft = new Fft(1024);
                fft.Direct(real, imag); // in-place FFT

                Console.WriteLine($"FFT size: {fft.Size}");
            }
        }
예제 #20
0
        /// <summary>
        /// Spectral subtraction algorithm according to
        ///
        /// [1979] M. Berouti, R. Schwartz, J. Makhoul
        /// "Enhancement of Speech Corrupted by Acoustic Noise".
        ///
        /// </summary>
        /// <param name="signal"></param>
        /// <param name="noise"></param>
        /// <param name="fftSize"></param>
        /// <param name="hopSize"></param>
        /// <returns></returns>
        public static DiscreteSignal SpectralSubtract(DiscreteSignal signal,
                                                      DiscreteSignal noise,
                                                      int fftSize = 1024,
                                                      int hopSize = 410)
        {
            var input  = signal.Samples;
            var output = new float[input.Length];

            const float beta     = 0.009f;
            const float alphaMin = 2f;
            const float alphaMax = 5f;
            const float snrMin   = -5f;
            const float snrMax   = 20f;

            const float k = (alphaMin - alphaMax) / (snrMax - snrMin);
            const float b = alphaMax - k * snrMin;

            var fft           = new Fft(fftSize);
            var hannWindow    = Window.OfType(WindowTypes.Hann, fftSize);
            var windowSquared = hannWindow.Select(w => w * w).ToArray();
            var windowSum     = new float[output.Length];

            var re        = new float[fftSize];
            var im        = new float[fftSize];
            var zeroblock = new float[fftSize];


            // estimate noise power spectrum

            var noiseAcc      = new float[fftSize / 2 + 1];
            var noiseEstimate = new float[fftSize / 2 + 1];

            var numFrames = 0;
            var pos       = 0;

            for (; pos + fftSize < noise.Length; pos += hopSize, numFrames++)
            {
                noise.Samples.FastCopyTo(re, fftSize, pos);
                zeroblock.FastCopyTo(im, fftSize);

                fft.Direct(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;


            // spectral subtraction

            for (pos = 0; pos + fftSize < input.Length; pos += hopSize)
            {
                input.FastCopyTo(re, fftSize, pos);
                zeroblock.FastCopyTo(im, fftSize);

                re.ApplyWindow(hannWindow);

                fft.Direct(re, im);

                for (var j = 0; j <= fftSize / 2; j++)
                {
                    var power = re[j] * re[j] + im[j] * im[j];
                    var phase = Math.Atan2(im[j], re[j]);

                    var noisePower = noiseEstimate[j];

                    var snr   = 10 * Math.Log10(power / noisePower);
                    var alpha = Math.Max(Math.Min(k * snr + b, alphaMax), alphaMin);

                    var diff = power - alpha * noisePower;

                    var mag = Math.Sqrt(Math.Max(diff, beta * noisePower));

                    re[j] = (float)(mag * Math.Cos(phase));
                    im[j] = (float)(mag * Math.Sin(phase));
                }

                for (var j = fftSize / 2 + 1; j < fftSize; j++)
                {
                    re[j] = im[j] = 0.0f;
                }

                fft.Inverse(re, im);

                for (var j = 0; j < re.Length; j++)
                {
                    output[pos + j]    += re[j] * hannWindow[j];
                    windowSum[pos + j] += windowSquared[j];
                }
            }

            for (var j = 0; j < output.Length; j++)
            {
                if (windowSum[j] < 1e-3)
                {
                    continue;
                }
                output[j] /= (windowSum[j] * fftSize / 2);
            }

            return(new DiscreteSignal(signal.SamplingRate, output));
        }
예제 #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);
                _zeroblock.FastCopyTo(_im, _fftSize);

                _re.ApplyWindow(_window);

                _fft.Direct(_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 = 0;

                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;
                }

                for (var j = _fftSize / 2 + 1; j < _fftSize; j++)
                {
                    _re[j] = _im[j] = 0.0f;
                }

                _fft.Inverse(_re, _im);

                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));
        }
예제 #22
0
        /// <summary>
        /// Process one frame (block)
        /// </summary>
        public void ProcessFrame()
        {
            _zeroblock.FastCopyTo(_im, _fftSize);
            _dl.FastCopyTo(_re, _fftSize);

            _re.ApplyWindow(_window);

            _fft.Direct(_re, _im);

            var nextPhase = (float)(2 * Math.PI * _hopSize / _fftSize);

            for (var j = 0; j < _fftSize / 2 + 1; 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);
            }

            _zeroblock.FastCopyTo(_re, _fftSize);
            _zeroblock.FastCopyTo(_im, _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 = 0; 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]));
            }

            for (var j = _fftSize / 2 + 1; j < _fftSize; j++)
            {
                _filteredRe[j] = _filteredIm[j] = 0.0f;
            }

            _fft.Inverse(_filteredRe, _filteredIm);

            _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;
        }
예제 #23
0
        /// <summary>
        /// Phase Vocoder algorithm
        /// </summary>
        /// <param name="signal"></param>
        /// <param name="filteringOptions"></param>
        /// <returns></returns>
        public DiscreteSignal ApplyTo(DiscreteSignal signal,
                                      FilteringOptions filteringOptions = FilteringOptions.Auto)
        {
            var stretch = (float)_hopSynthesis / _hopAnalysis;

            var input  = signal.Samples;
            var output = new float[(int)(input.Length * stretch) + _fftSize];

            var fft        = new Fft(_fftSize);
            var hannWindow = Window.OfType(WindowTypes.Hann, _fftSize);

            var ratio = _fftSize / (2.0f * _hopAnalysis);
            var norm  = 4.0f / (_fftSize * ratio);

            var omega = Enumerable.Range(0, _fftSize / 2 + 1)
                        .Select(f => 2 * Math.PI * f / _fftSize)
                        .ToArray();

            var re        = new float[_fftSize];
            var im        = new float[_fftSize];
            var zeroblock = new float[_fftSize];

            var prevPhase  = new double[_fftSize / 2 + 1];
            var phaseTotal = new double[_fftSize / 2 + 1];

            var posSynthesis = 0;

            for (var posAnalysis = 0; posAnalysis + _fftSize < input.Length; posAnalysis += _hopAnalysis)
            {
                input.FastCopyTo(re, _fftSize, posAnalysis);
                zeroblock.FastCopyTo(im, _fftSize);

                re.ApplyWindow(hannWindow);

                fft.Direct(re, im);

                for (var j = 0; j < _fftSize / 2 + 1; 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]));
                }

                for (var j = _fftSize / 2 + 1; j < _fftSize; j++)
                {
                    re[j] = im[j] = 0.0f;
                }

                fft.Inverse(re, im);

                for (var j = 0; j < re.Length; j++)
                {
                    output[posSynthesis + j] += re[j] * hannWindow[j] * norm;
                }

                posSynthesis += _hopSynthesis;
            }

            return(new DiscreteSignal(signal.SamplingRate, output));
        }
예제 #24
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);
                _zeroblock.FastCopyTo(_im, _fftSize);

                _re.ApplyWindow(_window);

                _fft.Direct(_re, _im);

                for (var j = 0; j < _fftSize / 2 + 1; 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]));
                }

                for (var j = _fftSize / 2 + 1; j < _fftSize; j++)
                {
                    _re[j] = _im[j] = 0.0f;
                }

                _fft.Inverse(_re, _im);

                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));
        }
예제 #25
0
        /// <summary>
        /// Phase Vocoder algorithm
        /// </summary>
        /// <param name="signal"></param>
        /// <param name="filteringOptions"></param>
        /// <returns></returns>
        public DiscreteSignal ApplyTo(DiscreteSignal signal,
                                      FilteringOptions filteringOptions = FilteringOptions.Auto)
        {
            if (_phaseLocking)
            {
                return(PhaseLocking(signal));
            }

            var input  = signal.Samples;
            var output = new float[(int)(input.Length * _stretch) + _fftSize];

            var windowSum = new float[output.Length];

            var re        = new float[_fftSize];
            var im        = new float[_fftSize];
            var zeroblock = new float[_fftSize];

            var prevPhase  = new double[_fftSize / 2 + 1];
            var phaseTotal = new double[_fftSize / 2 + 1];

            var posSynthesis = 0;

            for (var posAnalysis = 0; posAnalysis + _fftSize < input.Length; posAnalysis += _hopAnalysis)
            {
                input.FastCopyTo(re, _fftSize, posAnalysis);
                zeroblock.FastCopyTo(im, _fftSize);

                re.ApplyWindow(_window);

                _fft.Direct(re, im);

                for (var j = 0; j < _fftSize / 2 + 1; 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]));
                }

                for (var j = _fftSize / 2 + 1; j < _fftSize; j++)
                {
                    re[j] = im[j] = 0.0f;
                }

                _fft.Inverse(re, im);

                for (var j = 0; j < re.Length; j++)
                {
                    output[posSynthesis + j]    += re[j] * _window[j];
                    windowSum[posSynthesis + j] += _windowSquared[j];
                }

                posSynthesis += _hopSynthesis;
            }

            for (var j = 0; j < output.Length; j++)
            {
                if (windowSum[j] < 1e-3)
                {
                    continue;
                }
                output[j] /= (windowSum[j] * _fftSize / 2);
            }

            return(new DiscreteSignal(signal.SamplingRate, output));
        }
예제 #26
0
        public DiscreteSignal ApplyTo(DiscreteSignal signal1,
                                      DiscreteSignal signal2,
                                      FilteringMethod method = FilteringMethod.Auto)
        {
            Guard.AgainstInequality(signal1.SamplingRate, signal2.SamplingRate, "1st signal sampling rate", "2nd signal sampling rate");

            var input1 = signal1.Samples;
            var input2 = signal2.Samples;
            var output = new float[input1.Length];

            var windowSum = new float[output.Length];

            var posMorph = 0;
            var endMorph = signal2.Length - _fftSize;

            var posSynthesis = 0;

            for (var posAnalysis = 0; posAnalysis + _fftSize < input1.Length; posAnalysis += _hopSize, posMorph += _hopSize)
            {
                input1.FastCopyTo(_re1, _fftSize, posAnalysis);
                _zeroblock.FastCopyTo(_im1, _fftSize);

                if (posMorph > endMorph)
                {
                    posMorph = 0;
                }

                input2.FastCopyTo(_re2, _fftSize, posMorph);
                _zeroblock.FastCopyTo(_im2, _fftSize);

                _re1.ApplyWindow(_window);
                _re2.ApplyWindow(_window);

                _fft.Direct(_re1, _im1);
                _fft.Direct(_re2, _im2);

                for (var j = 0; j < _fftSize / 2 + 1; j++)
                {
                    var mag1   = Math.Sqrt(_re1[j] * _re1[j] + _im1[j] * _im1[j]);
                    var phase1 = Math.Atan2(_im1[j], _re1[j]);
                    var mag2   = Math.Sqrt(_re2[j] * _re2[j] + _im2[j] * _im2[j]);
                    var phase2 = Math.Atan2(_im2[j], _re2[j]);

                    _re1[j] = (float)(mag1 * Math.Cos(phase2));
                    _im1[j] = (float)(mag1 * Math.Sin(phase2));
                }

                for (var j = _fftSize / 2 + 1; j < _fftSize; j++)
                {
                    _re1[j] = _im1[j] = 0.0f;
                }

                _fft.Inverse(_re1, _im1);

                for (var j = 0; j < _re1.Length; j++)
                {
                    output[posSynthesis + j] += _re1[j] * _window[j];
                }

                posSynthesis += _hopSize;
            }

            posMorph = 0;
            for (var j = 0; j < output.Length; j++, posMorph++)
            {
                output[j] /= _fftSize;

                if (posMorph > endMorph)
                {
                    posMorph = 0;
                }

                output[j] = Wet * output[j] + Dry * signal2[posMorph];
            }

            return(new DiscreteSignal(signal1.SamplingRate, output));
        }
예제 #27
0
        /// <summary>
        /// Method implements block convolution of signals (using either OLA or OLS algorithm)
        /// </summary>
        /// <param name="signal"></param>
        /// <param name="kernel"></param>
        /// <param name="fftSize"></param>
        /// <param name="method"></param>
        /// <returns></returns>
        public static DiscreteSignal BlockConvolve(DiscreteSignal signal,
                                                   DiscreteSignal kernel,
                                                   int fftSize,
                                                   BlockConvolution method = BlockConvolution.OverlapAdd)
        {
            var m = kernel.Length;

            if (m > fftSize)
            {
                throw new ArgumentException("Kernel length must not exceed the size of FFT!");
            }

            var fft = new Fft(fftSize);

            // pre-compute kernel's FFT:

            var kernelReal = kernel.Samples.PadZeros(fftSize);
            var kernelImag = new float[fftSize];

            fft.Direct(kernelReal, kernelImag);

            // reserve space for current signal block:

            var blockReal = new float[fftSize];
            var blockImag = new float[fftSize];
            var zeroblock = new float[fftSize];

            // reserve space for resulting spectrum at each step:

            var spectrumReal = new float[fftSize];
            var spectrumImag = new float[fftSize];

            var filtered = new DiscreteSignal(signal.SamplingRate, signal.Length);

            var hopSize = fftSize - m + 1;

            if (method == BlockConvolution.OverlapAdd)
            {
                var i = 0;
                while (i < signal.Length)
                {
                    // ============================== FFT CONVOLUTION SECTION =================================

                    // for better performance we inline FFT convolution here;
                    // alternatively, we could simply write:
                    //
                    //        var res = Convolve(signal[i, i + hopSize], kernel);
                    //
                    // ...but that would require unnecessary memory allocations
                    //    and recalculating of kernel FFT at each step.

                    zeroblock.FastCopyTo(blockReal, fftSize);
                    zeroblock.FastCopyTo(blockImag, fftSize);
                    signal.Samples.FastCopyTo(blockReal, Math.Min(hopSize, signal.Length - i), i);

                    // 1) do FFT of a signal block:

                    fft.Direct(blockReal, blockImag);

                    // 2) do complex multiplication of spectra

                    for (var j = 0; j < fftSize; j++)
                    {
                        spectrumReal[j] = (blockReal[j] * kernelReal[j] - blockImag[j] * kernelImag[j]) / fftSize;
                        spectrumImag[j] = (blockReal[j] * kernelImag[j] + blockImag[j] * kernelReal[j]) / fftSize;
                    }

                    // 3) do inverse FFT of resulting spectrum

                    fft.Inverse(spectrumReal, spectrumImag);

                    // ========================================================================================

                    for (var j = 0; j < m - 1 && i + j < filtered.Length; j++)
                    {
                        filtered[i + j] += spectrumReal[j];
                    }

                    for (var j = m - 1; j < spectrumReal.Length && i + j < filtered.Length; j++)
                    {
                        filtered[i + j] = spectrumReal[j];
                    }

                    i += hopSize;
                }

                return(filtered);
            }
            else
            {
                signal = new DiscreteSignal(signal.SamplingRate, m - 1).Concatenate(signal);

                var i = 0;
                while (i < signal.Length)
                {
                    // ============================== FFT CONVOLUTION SECTION =================================

                    signal.Samples.FastCopyTo(blockReal, Math.Min(fftSize, signal.Length - i), i);
                    zeroblock.FastCopyTo(blockImag, fftSize);

                    // 1) do FFT of a signal block:

                    fft.Direct(blockReal, blockImag);

                    // 2) do complex multiplication of spectra

                    for (var j = 0; j < fftSize; j++)
                    {
                        spectrumReal[j] = (blockReal[j] * kernelReal[j] - blockImag[j] * kernelImag[j]) / fftSize;
                        spectrumImag[j] = (blockReal[j] * kernelImag[j] + blockImag[j] * kernelReal[j]) / fftSize;
                    }

                    // 3) do inverse FFT of resulting spectrum

                    fft.Inverse(spectrumReal, spectrumImag);

                    // ========================================================================================


                    for (var j = 0; j + m - 1 < spectrumReal.Length && i + j < filtered.Length; j++)
                    {
                        filtered[i + j] = spectrumReal[j + m - 1];
                    }

                    i += hopSize;
                }

                return(filtered);
            }
        }