示例#1
0
        /// <summary>
        /// One dimensional Discrete Fourier Transform.
        /// </summary>
        /// 
        /// <param name="data">Data to transform.</param>
        /// <param name="direction">Transformation direction.</param>
        /// 
        public void DFT(ComplexNumber[] data, Direction direction)
        {
            int n = data.Length;
            double arg, cos, sin;
            ComplexNumber[] dst = new ComplexNumber[n];

            // for each destination element
            for (int i = 0; i < n; i++)
            {
                dst[i] = ComplexNumberConstants.Zero;

                arg = -(int)direction * 2.0 * System.Math.PI * (double)i / (double)n;

                // sum source elements
                for (int j = 0; j < n; j++)
                {
                    cos = System.Math.Cos(j * arg);
                    sin = System.Math.Sin(j * arg);

                    dst[i].Re += (data[j].Re * cos - data[j].Im * sin);
                    dst[i].Im += (data[j].Re * sin + data[j].Im * cos);
                }
            }

            // copy elements
            if (direction == Direction.Forward)
            {
                // devide also for forward transform
                for (int i = 0; i < n; i++)
                {
                    data[i].Re = dst[i].Re / n;
                    data[i].Im = dst[i].Im / n;
                }
            }
            else
            {
                for (int i = 0; i < n; i++)
                {
                    data[i].Re = dst[i].Re;
                    data[i].Im = dst[i].Im;
                }
            }
        }
示例#2
0
 /// <summary>
 /// Initializes a new instance of the <see cref="Complex"/> class.
 /// </summary>
 /// 
 /// <param name="c">Source complex number.</param>
 /// 
 public ComplexNumber( ComplexNumber c )
 {
     this.Re = c.Re;
     this.Im = c.Im;
 }
示例#3
0
        private void save(ComplexNumber[] data)
        {
            try
            {
                //byte[] fileData = new byte[data.Length];

                SaveFileDialog save = new SaveFileDialog();
                save.DefaultExt = ".txt";
                bool? saveResult = save.ShowDialog();
                if (saveResult == true)
                {
                    Stream fs = save.OpenFile();
                    StreamWriter sw = new StreamWriter(fs);
                    for (int i = 0; i < data.Length; i++)
                    {
                        sw.WriteLine(data[i].SquaredMagnitude.ToString());
                    }
                    sw.Flush();
                    sw.Close();
                }

            }
            catch (Exception e)
            {

            }
        }
示例#4
0
        /*Documentation
         * Method:  manualFrequency
         * Author:  Josh Weese
         * Purpose: Calculates the freqeuncy based on user input
         * Parameters:
         * Returns:
         * Notes:
         *          #This method is ment for accuracy testing
         *          #creates a sine wave based on the input frequency and sample count
         *          #converts to complex numbers
         *          #runs fft and calculates pitch
         * ChangeLog:   Version 1.0...5/3/2011--Documented
         */
        private void manualFrequency(double freq, int sampleCount, int sampleRate)
        {
            //create sine wave
            double increment = (double)(2 * Math.PI) * freq / sampleRate;
            double angle = 0;
            double[] samples = new double[sampleCount];

            for (int i = 0; i < samples.Length; i++)
            {
                samples[i] = ((double)Math.Sin(angle));
                angle += increment;
            }

            if ((bool)saveCheck.IsChecked)
            {
                save(samples);
            }

            //convert to complex numbers
            ComplexNumber[] complexData1 = new ComplexNumber[sampleCount];
            int y = 0;
            while (y < sampleCount)
            {
                complexData1[y] = new ComplexNumber(samples[y]);
                y++;
            }

            //run fft
            ComplexNumber[] fft1 = ft.FFT(complexData1, FourierTransform.Direction.Forward);
            if ((bool)saveCheck.IsChecked)
            {
                save(fft1);
            }

            //frequency
            fundFreq.fundamentalFreq = frequency.calcFundamentalFreq(fft1, sampleRate);
            //get pitch
            pitch = frequency.getPitch(fundFreq.fundamentalFreq, pitchList);

            //cents
            fundFreq.cents = frequency.calculateCents(fundFreq.fundamentalFreq, pitch.fundamentalFreq);
            //actual note
            fundFreq.note = pitch.note;
            //accidental
            fundFreq.accidental = pitch.accidental;
        }
示例#5
0
        /*Documentation
         * Method:  read
         * Author:  Josh Weese
         * Purpose: Reads and processes audio capture memory stream
         * Parameters:
         * Returns:
         * Notes:   #reads audio capture memory stream
         *          #converts stream into usable data
         *          #saves data to txt file if option is checked
         *          #chunks data, converts it to complex numbers, runs FFT, and determines pitch and frequency
         * ChangeLog:   Version 1.0...5/3/2011--Documented
         */
        public void read()
        {
            //make sure data reached the fft lenght thresh hold
            if (audioSink.AudioData.Length >= Constants.fftLength)
            {
                //read the entire memory stream
                audioSink.AudioData.Seek(seekPosition, SeekOrigin.Begin);
                byte[] data = new byte[audioSink.AudioData.Length];
                int chunkSize = 4096;
                int numBytesToRead = (int)audioSink.AudioData.Length;
                int numBytesRead = 0;

                while (numBytesToRead > 0)
                {
                    if (chunkSize + numBytesRead > data.Length)
                    {
                        chunkSize = data.Length - numBytesRead;
                    }
                    int n = audioSink.AudioData.Read(data, numBytesRead, chunkSize);
                    if (n == 0)
                    {
                        break;
                    }
                    numBytesRead += n;
                    numBytesToRead -= n;
                }
                //kill the buffer once read
                audioSink.AudioData.Flush();
                audioSink.AudioData.Dispose();
                //re-initialize
                audioSink.AudioData = new MemoryStream();

                if (saveFile)
                {
                    save(data);
                }
                //length of data
                int length;

                Int16[] int16Data;

                int y = 0;
                int z = 0;

                //convert raw data to useable numbers
                if (audioSink.AudioFormat.BitsPerSample == 16)
                {
                    length = data.Length / 2;
                    int16Data = new Int16[length];
                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(data);
                    }

                    for (int i = 0; i < length; i += 2)
                    {
                        if (!(i > data.Length))
                        {
                            int16Data[z] = BitConverter.ToInt16(data, i);
                        }
                        else
                        {
                            int16Data[z] = 0;
                        }

                        z++;
                    }
                }
                else
                {
                    length = data.Length;
                    int16Data = new Int16[length];
                    for (int i = 0; i < length; i++)
                    {
                        int16Data[i] = data[i];
                    }
                }

                if (saveFile)
                {
                    save(int16Data);
                }

                z = 0;

                //chunk audio data to feed to FFT and calculate freqency
                while (true)
                {
                    //chunk
                    int max;
                    if (length > Constants.fftLength)
                    {
                        max = Constants.fftLength;
                    }
                    else
                    {
                        if (IsPowerOfTwo((ulong)length))
                        {
                            max = length;
                        }
                        else
                        {
                            break;
                        }
                    }
                    //convert to complex numbers
                    ComplexNumber[] complexData1 = new ComplexNumber[max];
                    y = 0;
                    while (y < max)
                    {
                        complexData1[y] = new ComplexNumber(int16Data[z] / Constants.downsample);
                        y++;
                        z++;
                    }
                    //run fft
                    ComplexNumber[] fft1 = ft.FFT(complexData1, FourierTransform.Direction.Forward);

                    if (saveFile)
                    {
                        save(fft1);
                    }

                    //retrieve pitch
                    pitch = frequency.getPitch(fundFreq.fundamentalFreq, pitchList);
                    //frequency
                    fundFreq.fundamentalFreq = frequency.calcFundamentalFreq(fft1, audioSink.AudioFormat.SamplesPerSecond);
                    //cents
                    fundFreq.cents = frequency.calculateCents(fundFreq.fundamentalFreq, pitch.fundamentalFreq);
                    //actual note
                    fundFreq.note = pitch.note;
                    //accidental
                    fundFreq.accidental = pitch.accidental;

                    if ((length -= max) == 0)
                    {
                        break;
                    }
                }
            }
        }
示例#6
0
        /*Documentation
         * Method:  calcFundamentalFreq
         * Author:  Josh Weese and http://www.codeproject.com/KB/audio-video/FftGuitarTuner.aspx
         * Purpose: Calculates fundamental frequency
         * Parameters:  #ComplexNumber[] fftData...complex number array, the result from the fft
         *              #int samplesPerSec...sample rate
         * Returns: the fundamental frequency as a double...this is in Hz
         * Notes:  #detects peaks in fft data
         *         #determines exact bin the fundamental lies in
         *         #returns fundamental frequency
         * ChangeLog:   Version 1.0...5/3/2011--Documented
         */
        public double calcFundamentalFreq(ComplexNumber[] fftData, int samplesPerSec)
        {
            //array to store fft data
            double[] spectrogram = new double[fftData.Length];

            for (int i = 0; i < spectrogram.Length; i++)
            {
                //the squared magnitude represents the spectogram (strength of the audio signal)
                spectrogram[i] = fftData[i].SquaredMagnitude;
            }

            int usefullMinSpectrogram = Math.Max(0, (Constants.minFreq * spectrogram.Length / samplesPerSec));
            int usefullMaxSpectrogram = Math.Min(spectrogram.Length, (Constants.maxFreq * fftData.Length / samplesPerSec) + 1);

            int[] peakIndices = findPeaks(spectrogram, usefullMinSpectrogram, usefullMaxSpectrogram - usefullMinSpectrogram, Constants.peaksCount);

            if (Array.IndexOf(peakIndices, usefullMinSpectrogram) >= 0)
            {
                // lowest usefull frequency bin shows active
                // looks like is no detectable sound, return 0
                return 0;
            }

            //Quinn's interpolation to guess exact bin of the fundamental frequency
            double maximizerRatio1 = 0;
            double maximizerRatio2 = 0;
            double quinnEstimator = 0;
            double quinnEstimator1 = 0;
            double quinnEstimator2 = 0;

            maximizerRatio1 = fftData[peakIndices[0] - 1].Magnitude / fftData[peakIndices[0]].Magnitude;
            maximizerRatio2 = fftData[peakIndices[0] + 1].Magnitude / fftData[peakIndices[0]].Magnitude;
            quinnEstimator1 = maximizerRatio1 / (1 - maximizerRatio1);
            quinnEstimator2 = -maximizerRatio2 / (1 - maximizerRatio2);

            if (quinnEstimator1 > 0 && quinnEstimator2 > 0)
            {
                quinnEstimator = quinnEstimator1;
            }
            else
            {
                quinnEstimator = quinnEstimator2;
            }

            //return the fundamental frequency in hz
            return ((quinnEstimator + peakIndices[0]) / (double)fftData.Length * samplesPerSec);
        }
示例#7
0
        // Reorder data for FFT using
        private static void ReorderData(ComplexNumber[] data)
        {
            int len = data.Length;

            // check data length
            if ((len < minLength) || (len > maxLength) || (!Tools.IsPowerOf2(len)))
                throw new ArgumentException("Incorrect data length.");

            int[] rBits = GetReversedBits(Tools.Log2(len));

            for (int i = 0; i < len; i++)
            {
                int s = rBits[i];

                if (s > i)
                {
                    ComplexNumber t = data[i];
                    data[i] = data[s];
                    data[s] = t;
                }
            }
        }
示例#8
0
        // Get rotation of complex number
        private static ComplexNumber[] GetComplexRotation(int numberOfBits, Direction direction)
        {
            int directionIndex = (direction == Direction.Forward) ? 0 : 1;

            // check if the array is already calculated
            if (complexRotation[numberOfBits - 1, directionIndex] == null)
            {
                int n = 1 << (numberOfBits - 1);
                double uR = 1.0;
                double uI = 0.0;
                double angle = System.Math.PI / n * (int)direction;
                double wR = System.Math.Cos(angle);
                double wI = System.Math.Sin(angle);
                double t;
                ComplexNumber[] rotation = new ComplexNumber[n];

                for (int i = 0; i < n; i++)
                {
                    rotation[i] = new ComplexNumber(uR, uI);
                    t = uR * wI + uI * wR;
                    uR = uR * wR - uI * wI;
                    uI = t;
                }

                complexRotation[numberOfBits - 1, directionIndex] = rotation;
            }
            return complexRotation[numberOfBits - 1, directionIndex];
        }
示例#9
0
        /// <summary>
        /// Two dimensional Fast Fourier Transform.
        /// </summary>
        /// 
        /// <param name="data">Data to transform.</param>
        /// <param name="direction">Transformation direction.</param>
        /// 
        /// <remarks><para><note>The method accepts <paramref name="data"/> array of 2<sup>n</sup> size
        /// only in each dimension, where <b>n</b> may vary in the [1, 14] range. For example, 16x16 array
        /// is valid, but 15x15 is not.</note></para></remarks>
        /// 
        /// <exception cref="ArgumentException">Incorrect data length.</exception>
        /// 
        public void FFT2(ComplexNumber[,] data, Direction direction)
        {
            int k = data.GetLength(0);
            int n = data.GetLength(1);

            // check data size
            if (
                (!Tools.IsPowerOf2(k)) ||
                (!Tools.IsPowerOf2(n)) ||
                (k < minLength) || (k > maxLength) ||
                (n < minLength) || (n > maxLength)
                )
            {
                throw new ArgumentException("Incorrect data length.");
            }

            // process rows
            ComplexNumber[] row = new ComplexNumber[n];

            for (int i = 0; i < k; i++)
            {
                // copy row
                for (int j = 0; j < n; j++)
                    row[j] = data[i, j];
                // transform it
                FFT(row, direction);
                // copy back
                for (int j = 0; j < n; j++)
                    data[i, j] = row[j];
            }

            // process columns
            ComplexNumber[] col = new ComplexNumber[k];

            for (int j = 0; j < n; j++)
            {
                // copy column
                for (int i = 0; i < k; i++)
                    col[i] = data[i, j];
                // transform it
                FFT(col, direction);
                // copy back
                for (int i = 0; i < k; i++)
                    data[i, j] = col[i];
            }
        }
示例#10
0
        /// <summary>
        /// One dimensional Fast Fourier Transform.
        /// </summary>
        /// 
        /// <param name="data">Data to transform.</param>
        /// <param name="direction">Transformation direction.</param>
        /// 
        /// <remarks><para><note>The method accepts <paramref name="data"/> array of 2<sup>n</sup> size
        /// only, where <b>n</b> may vary in the [1, 14] range.</note></para></remarks>
        /// 
        /// <exception cref="ArgumentException">Incorrect data length.</exception>
        /// 
        public ComplexNumber[] FFT(ComplexNumber[] data, Direction direction)
        {
            int n = data.Length;
            int m = Tools.Log2(n);

            // reorder data first
            ReorderData(data);

            // compute FFT
            int tn = 1, tm;

            for (int k = 1; k <= m; k++)
            {
                ComplexNumber[] rotation = FourierTransform.GetComplexRotation(k, direction);

                tm = tn;
                tn <<= 1;

                for (int i = 0; i < tm; i++)
                {
                    ComplexNumber t = rotation[i];

                    for (int even = i; even < n; even += tn)
                    {
                        int odd = even + tm;
                        ComplexNumber ce = data[even];
                        ComplexNumber co = data[odd];

                        double tr = co.Re * t.Re - co.Im * t.Im;
                        double ti = co.Re * t.Im + co.Im * t.Re;

                        data[even].Re += tr;
                        data[even].Im += ti;

                        data[odd].Re = ce.Re - tr;
                        data[odd].Im = ce.Im - ti;
                    }
                }
            }

            if (direction == Direction.Forward)
            {
                for (int i = 0; i < n; i++)
                {
                    data[i].Re /= (double)n;
                    data[i].Im /= (double)n;
                }
            }
            return data;
        }
示例#11
0
        /// <summary>
        /// Two dimensional Discrete Fourier Transform.
        /// </summary>
        /// 
        /// <param name="data">Data to transform.</param>
        /// <param name="direction">Transformation direction.</param>
        /// 
        public void DFT2(ComplexNumber[,] data, Direction direction)
        {
            int n = data.GetLength(0);	// rows
            int m = data.GetLength(1);	// columns
            double arg, cos, sin;
            ComplexNumber[] dst = new ComplexNumber[System.Math.Max(n, m)];

            // process rows
            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j < m; j++)
                {
                    dst[j] = ComplexNumberConstants.Zero;

                    arg = -(int)direction * 2.0 * System.Math.PI * (double)j / (double)m;

                    // sum source elements
                    for (int k = 0; k < m; k++)
                    {
                        cos = System.Math.Cos(k * arg);
                        sin = System.Math.Sin(k * arg);

                        dst[j].Re += (data[i, k].Re * cos - data[i, k].Im * sin);
                        dst[j].Im += (data[i, k].Re * sin + data[i, k].Im * cos);
                    }
                }

                // copy elements
                if (direction == Direction.Forward)
                {
                    // devide also for forward transform
                    for (int j = 0; j < m; j++)
                    {
                        data[i, j].Re = dst[j].Re / m;
                        data[i, j].Im = dst[j].Im / m;
                    }
                }
                else
                {
                    for (int j = 0; j < m; j++)
                    {
                        data[i, j].Re = dst[j].Re;
                        data[i, j].Im = dst[j].Im;
                    }
                }
            }

            // process columns
            for (int j = 0; j < m; j++)
            {
                for (int i = 0; i < n; i++)
                {
                    dst[i] = ComplexNumberConstants.Zero;

                    arg = -(int)direction * 2.0 * System.Math.PI * (double)i / (double)n;

                    // sum source elements
                    for (int k = 0; k < n; k++)
                    {
                        cos = System.Math.Cos(k * arg);
                        sin = System.Math.Sin(k * arg);

                        dst[i].Re += (data[k, j].Re * cos - data[k, j].Im * sin);
                        dst[i].Im += (data[k, j].Re * sin + data[k, j].Im * cos);
                    }
                }

                // copy elements
                if (direction == Direction.Forward)
                {
                    // devide also for forward transform
                    for (int i = 0; i < n; i++)
                    {
                        data[i, j].Re = dst[i].Re / n;
                        data[i, j].Im = dst[i].Im / n;
                    }
                }
                else
                {
                    for (int i = 0; i < n; i++)
                    {
                        data[i, j].Re = dst[i].Re;
                        data[i, j].Im = dst[i].Im;
                    }
                }
            }
        }