Beispiel #1
0
        void ProcessCommon(AudioReader reader, ref float[] impulse)
        {
            int targetLen = QMath.Base2Ceil((int)reader.Length) << 1;

            Complex[] commonSpectrum = new Complex[targetLen];
            FFTCache  cache          = new FFTCache(targetLen);

            for (int ch = 0; ch < reader.ChannelCount; ++ch)
            {
                float[] channel = new float[targetLen];
                WaveformUtils.ExtractChannel(impulse, channel, ch, reader.ChannelCount);

                Complex[] spectrum = Measurements.FFT(channel, cache);
                for (int band = 0; band < spectrum.Length; ++band)
                {
                    commonSpectrum[band] += spectrum[band];
                }
            }

            float mul = 1f / reader.ChannelCount;

            for (int band = 0; band < commonSpectrum.Length; ++band)
            {
                commonSpectrum[band] = (commonSpectrum[band] * mul).Invert();
            }

            Array.Resize(ref impulse, impulse.Length << 1);
            for (int ch = 0; ch < reader.ChannelCount; ++ch)
            {
                Convolver filter = GetFilter(commonSpectrum, 1, reader.SampleRate);
                filter.Process(impulse, ch, reader.ChannelCount);
            }
        }
Beispiel #2
0
 /// <summary>
 /// Gets a zero-delay convolution filter with minimally sacrificed phase that results in this EQ when applied.
 /// </summary>
 /// <param name="eq">Source <see cref="Equalizer"/></param>
 /// <param name="sampleRate">Sample rate of the target system the convolution filter could be used on</param>
 /// <param name="length">Length of the convolution filter in samples, must be a power of 2</param>
 /// <param name="gain">Signal voltage multiplier</param>
 /// <param name="initial">Custom initial spectrum to apply the EQ on - phases will be corrected, this is not convolved,
 /// and has to be twice the size of <paramref name="length"/></param>
 public static float[] GetConvolution(this Equalizer eq, int sampleRate, int length = 1024, float gain = 1,
                                      Complex[] initial = null)
 {
     length <<= 1;
     Complex[] filter = new Complex[length];
     if (initial == null)
     {
         for (int i = 0; i < length; ++i)
         {
             filter[i].Real = gain; // FFT of DiracDelta(x)
         }
     }
     else
     {
         for (int i = 0; i < length; ++i)
         {
             filter[i].Real = initial[i].Magnitude * gain;
         }
     }
     eq.Apply(filter, sampleRate);
     using (FFTCache cache = new FFTCache(length)) {
         Measurements.MinimumPhaseSpectrum(filter, cache);
         filter.InPlaceIFFT(cache);
     }
     return(Measurements.GetRealPartHalf(filter));
 }
Beispiel #3
0
        void ProcessPerChannel(AudioReader reader, ref float[] impulse)
        {
            int targetLen = QMath.Base2Ceil((int)reader.Length) << 1;

            Convolver[] filters = new Convolver[reader.ChannelCount];
            FFTCache    cache   = new FFTCache(targetLen);

            for (int ch = 0; ch < reader.ChannelCount; ++ch)
            {
                float[] channel = new float[targetLen];
                WaveformUtils.ExtractChannel(impulse, channel, ch, reader.ChannelCount);

                Complex[] spectrum = Measurements.FFT(channel, cache);
                for (int band = 0; band < spectrum.Length; ++band)
                {
                    spectrum[band] = spectrum[band].Invert();
                }
                filters[ch] = GetFilter(spectrum, WaveformUtils.GetRMS(channel), reader.SampleRate);
            }

            Array.Resize(ref impulse, impulse.Length << 1);
            for (int ch = 0; ch < reader.ChannelCount; ++ch)
            {
                filters[ch].Process(impulse, ch, reader.ChannelCount);
            }
        }
Beispiel #4
0
 /// <summary>
 /// Performs the convolution of two real signals. The FFT of the result is returned.
 /// </summary>
 /// <remarks>Requires <paramref name="excitation"/> and <paramref name="impulse"/>
 /// to match in a length of a power of 2.</remarks>
 public static Complex[] ConvolveFourier(float[] excitation, float[] impulse)
 {
     using FFTCache cache = new FFTCache(excitation.Length);
     Complex[] excitationFFT = excitation.FFT(cache);
     excitationFFT.Convolve(impulse.FFT(cache));
     return(excitationFFT);
 }
Beispiel #5
0
        /// <summary>
        /// Performs the convolution of two real signals. The real result is returned.
        /// </summary>
        /// <remarks>Requires <paramref name="excitation"/> and <paramref name="impulse"/>
        /// to match in a length of a power of 2.</remarks>
        public static float[] Convolve(float[] excitation, float[] impulse, FFTCache cache = null)
        {
            Complex[] result = cache != null?
                               ConvolveFourier(excitation, impulse, cache) :
                                   ConvolveFourier(excitation, impulse);

            result.InPlaceIFFT();
            return(Measurements.GetRealPart(result));
        }
Beispiel #6
0
        /// <summary>
        /// Constructs an optimized convolution.
        /// </summary>
        public FastConvolver(float[] impulse, int delay = 0)
        {
            int fftSize = 2 << QMath.Log2Ceil(impulse.Length); // Zero padding for the falloff to have space

            cache  = new FFTCache(fftSize);
            filter = new Complex[fftSize];
            for (int sample = 0; sample < impulse.Length; ++sample)
            {
                filter[sample].Real = impulse[sample];
            }
            filter.InPlaceFFT(cache);
            present    = new Complex[fftSize];
            future     = new float[fftSize + delay];
            this.delay = delay;
        }
Beispiel #7
0
        /// <summary>
        /// Constructs an optimized convolution.
        /// </summary>
        public DualConvolver(float[] impulse1, float[] impulse2, int delay1 = 0, int delay2 = 0)
        {
            int impulseLength = Math.Max(impulse1.Length, impulse2.Length);
            int fftSize       = 2 << QMath.Log2Ceil(impulseLength); // Zero padding for the falloff to have space

            cache  = new FFTCache(fftSize);
            filter = new Complex[fftSize];
            for (int sample = 0; sample < impulse1.Length; ++sample)
            {
                filter[sample].Real = impulse1[sample];
            }
            for (int sample = 0; sample < impulse2.Length; ++sample)
            {
                filter[sample].Imaginary = impulse2[sample];
            }
            filter.InPlaceFFT(cache);
            present     = new Complex[fftSize];
            future      = new Complex[fftSize + Math.Max(delay1, delay2)];
            this.delay1 = delay1;
            this.delay2 = delay2;
        }
Beispiel #8
0
        private void LoadFiles(object sender, RoutedEventArgs e)
        {
            AudioReader responseReader = Import("Response.wav");

            if (responseReader == null)
            {
                return;
            }
            AudioReader impulseReader = Import("Impulse.wav");

            if (impulseReader == null)
            {
                return;
            }

            float[] response = responseReader.Read(),
            impulse = impulseReader.Read();
            if (responseReader.SampleRate != impulseReader.SampleRate)
            {
                Error("The sample rate of the two clips don't match.");
                return;
            }
            int responseChannels = responseReader.ChannelCount,
                impulseChannels  = impulseReader.ChannelCount;

            if (impulseChannels != 1 && impulseChannels != responseChannels)
            {
                Error("The channel count of the two clips don't match. A single-channel impulse is also acceptable.");
                return;
            }

            int fftSize = Math.Max(
                QMath.Base2Ceil((int)responseReader.Length),
                QMath.Base2Ceil((int)impulseReader.Length)
                );

            if (padding.IsChecked.Value)
            {
                Array.Resize(ref response, fftSize + response.Length);
                Array.Copy(response, 0, response, fftSize, response.Length - fftSize);
                Array.Clear(response, 0, fftSize);

                fftSize = Math.Max(fftSize, QMath.Base2Ceil(response.Length));
            }

            Complex[] impulseFFT = new Complex[fftSize],
            responseFFT = new Complex[fftSize];
            FFTCache cache = new FFTCache(fftSize);

            float[] responseChannel = new float[response.Length / responseChannels];
            for (int channel = 0; channel < responseChannels; ++channel)
            {
                if (channel < impulseChannels)   // After the channel count check this runs once or for each channel
                {
                    float[] impulseChannel = impulse;
                    if (impulseChannels != 1)
                    {
                        impulseChannel = new float[impulseReader.Length];
                        WaveformUtils.ExtractChannel(impulse, impulseChannel, channel, impulseChannels);
                        Array.Clear(impulseFFT, 0, fftSize);
                    }
                    for (int sample = 0; sample < impulseChannel.Length; ++sample)
                    {
                        impulseFFT[sample].Real = impulseChannel[sample];
                    }
                    Measurements.InPlaceFFT(impulseFFT, cache);
                }

                if (responseChannels == 1)
                {
                    responseChannel = response;
                }
                else
                {
                    WaveformUtils.ExtractChannel(response, responseChannel, channel, responseChannels);
                }
                if (channel != 1)
                {
                    Array.Clear(responseFFT, 0, fftSize);
                }
                for (int sample = 0; sample < responseChannel.Length; ++sample)
                {
                    responseFFT[sample].Real = responseChannel[sample];
                }
                Measurements.InPlaceFFT(responseFFT, cache);

                for (int sample = 0; sample < fftSize; ++sample)
                {
                    responseFFT[sample].Divide(impulseFFT[sample]);
                }
                Measurements.InPlaceIFFT(responseFFT, cache);
                for (int i = 0, channels = responseChannels; i < responseChannel.Length; ++i)
                {
                    response[channels * i + channel] = responseFFT[i].Real;
                }
            }

            exporter.FileName = "Deconvolved.wav";
            if (exporter.ShowDialog().Value)
            {
                BinaryWriter handler = new BinaryWriter(File.OpenWrite(exporter.FileName));
                using RIFFWaveWriter writer = new RIFFWaveWriter(handler, responseChannels, responseReader.Length,
                                                                 responseReader.SampleRate, responseReader.Bits);
                writer.Write(response);
            }
        }
Beispiel #9
0
 /// <summary>
 /// Performs the convolution of two real signals. The FFT of the result is returned.
 /// </summary>
 /// <remarks>Requires <paramref name="excitation"/> and <paramref name="impulse"/>
 /// to match in a length of a power of 2.</remarks>
 public static Complex[] ConvolveFourier(float[] excitation, float[] impulse, FFTCache cache)
 {
     Complex[] excitationFFT = excitation.FFT(cache);
     excitationFFT.Convolve(impulse.FFT(cache));
     return(excitationFFT);
 }