private void GetSparseKernel() { int sampleRate = 1; if (InputArgs.ContainsKey("SAMPLE_RATE")) { if (InputArgs["SAMPLE_RATE"].GetType().Name == "Int32") { sampleRate = (int)InputArgs["SAMPLE_RATE"]; } } float threshold = 0.000054f; int numOctaves = int.Parse(Settings["OCTAVES"][0]); int binsPerOctave = int.Parse(Settings["BINS_PER_OCTAVE"][0]); double minFreq = double.Parse(Settings["MIN_FREQ"][0]); int numBins = numOctaves * binsPerOctave; double Q = 1 / (Math.Pow(2, 1 / (double)binsPerOctave) - 1); int fftLen = 1; while (fftLen < Q * sampleRate / minFreq) { fftLen *= 2; } NAudio.Dsp.Complex[] tempKernel = new NAudio.Dsp.Complex[fftLen]; List <Complex[]> sparKernel = new List <Complex[]>(); for (int k = 0; k < numBins; k++) { int N = (int)Math.Ceiling(Q * sampleRate / (minFreq * Math.Pow(2, k / (double)binsPerOctave))); for (int n = 0; n < N; n++) { Complex temp = NAudio.Dsp.FastFourierTransform.HammingWindow(n, N) / (N * (1 + (double.Parse(Settings["N_WEIGHTING"][0]) * N))) * Complex.Exp(2 * Math.PI * Complex.ImaginaryOne * n * (Q / N)) * (1000 * (1 + (double.Parse(Settings["N_WEIGHTING"][0]) * 1000))); tempKernel[n].X = (float)temp.Real; tempKernel[n].Y = (float)temp.Imaginary; } NAudio.Dsp.FastFourierTransform.FFT(true, (int)Math.Log(fftLen, 2.0), tempKernel); Complex[] compKernel = new Complex[tempKernel.Length]; for (int i = 0; i < tempKernel.Length; i++) { if (tempKernel[i].X < threshold && tempKernel[i].Y < threshold) { compKernel[i] = new Complex(0, 0); } else { compKernel[i] = new Complex(tempKernel[i].X, tempKernel[i].Y); } } sparKernel.Add(compKernel); } Matrix <Complex> kernelMat = CreateMatrix.SparseOfRowArrays(sparKernel.ToArray()); kernelMat.Multiply(1000); kernel = kernelMat.ConjugateTranspose(); }
void Analyze_chunk() { // fill data with FFT info short[] data = new short[fft_size]; data = unanalyzed_values.GetRange(0, fft_size).ToArray(); // remove the left-most (oldest) column of data spec_data.RemoveAt(0); // insert new data to the right-most (newest) position List <double> new_data = new List <double>(); // prepare the complex data which will be FFT'd NAudio.Dsp.Complex[] fft_buffer = new NAudio.Dsp.Complex[fft_size]; for (int i = 0; i < fft_size; i++) { //fft_buffer[i].X = (float)unanalyzed_values[i]; // no window fft_buffer[i].X = (float)(unanalyzed_values[i] * FastFourierTransform.HammingWindow(i, fft_size)); fft_buffer[i].Y = 0; } // perform the FFT FastFourierTransform.FFT(true, (int)Math.Log(fft_size, 2.0), fft_buffer); // fill that new data list with fft values for (int i = 0; i < spec_data[spec_data.Count - 1].Count; i++) { double val; val = (double)fft_buffer[i].X + (double)fft_buffer[i].Y; val = Math.Abs(val); new_data.Add(val); } new_data.Reverse(); spec_data.Insert(spec_data.Count, new_data); // replaces, doesn't append! // remove a certain amount of unanalyzed data unanalyzed_values.RemoveRange(0, fft_size / pixelsPerBuffer); }
public void Process() { short[] input = null; if (InputBuffer.GetType().Name == "Int16[]") { input = (short[])InputBuffer; } if (input == null) { return; } if (kernel == null) { GetSparseKernel(); } if (input.Length != kernel.RowCount) { Array.Resize(ref input, kernel.RowCount); } int fftPoints = 2; while (fftPoints * 2 <= input.Length) { fftPoints *= 2; } NAudio.Dsp.Complex[] fftFull = new NAudio.Dsp.Complex[fftPoints]; for (int i = 0; i < fftPoints; i++) { fftFull[i].X = (float)(input[i] * NAudio.Dsp.FastFourierTransform.HammingWindow(i, fftPoints)); } NAudio.Dsp.FastFourierTransform.FFT(true, (int)Math.Log(kernel.RowCount, 2.0), fftFull); Complex[] fftComp = new Complex[fftFull.Length]; for (int i = 0; i < fftComp.Length; i++) { fftComp[i] = new Complex(fftFull[i].X, fftFull[i].Y); } Matrix <Complex> fftVec = CreateVector.DenseOfArray(fftComp).ToRowMatrix(); Complex[] product = (fftVec * kernel).Row(0).AsArray(); double[] mag = new double[product.Length]; for (int i = 0; i < mag.Length; i++) { mag[i] = product[i].Magnitude; } if (Settings["OUTPUT_MODE"][0] == "dB") { double maxDB = 20 * Math.Log10(mag.Max()); for (int i = 0; i < mag.Length; i++) { mag[i] = 20 * Math.Log10(product[i].Magnitude) - maxDB; } } else if (Settings["SQUARE"][0] == "Yes") { for (int i = 0; i < mag.Length; i++) { mag[i] = Math.Pow(mag[i], 2) / 100; } } OutputBuffer = mag; Func <double, double> scale = i => double.Parse(Settings["MIN_FREQ"][0]) * Math.Pow(2, (double)i / int.Parse(Settings["BINS_PER_OCTAVE"][0])); OutputArgs.Add("SCALE", scale); }
public void Process() { short[] input = null; if (InputBuffer.GetType().Name == "Int16[]") { input = (short[])InputBuffer; } if (input == null) { return; } int sampleRate = 1; if (InputArgs.ContainsKey("SAMPLE_RATE")) { if (InputArgs["SAMPLE_RATE"].GetType().Name == "Int32") { sampleRate = (int)InputArgs["SAMPLE_RATE"]; } } int fftPoints = 2; while (fftPoints * 2 <= input.Length) // Sets fftPoints to largest multiple of 2 in BUFFERSIZE { fftPoints *= 2; } double[] output = new double[fftPoints / 2]; // FFT Process NAudio.Dsp.Complex[] fftFull = new NAudio.Dsp.Complex[fftPoints]; for (int i = 0; i < fftPoints; i++) { if (Settings["WINDOW"][0] == "Hamming") { fftFull[i].X = (float)(input[i] * NAudio.Dsp.FastFourierTransform.HammingWindow(i, fftPoints)); } else if (Settings["WINDOW"][0] == "Hann") { fftFull[i].X = (float)(input[i] * NAudio.Dsp.FastFourierTransform.HannWindow(i, fftPoints)); } else if (Settings["WINDOW"][0] == "BlackmannHarris") { fftFull[i].X = (float)(input[i] * NAudio.Dsp.FastFourierTransform.BlackmannHarrisWindow(i, fftPoints)); } else { fftFull[i].X = input[i]; } } NAudio.Dsp.FastFourierTransform.FFT(true, (int)Math.Log(fftPoints, 2.0), fftFull); for (int i = 0; i < fftPoints / 2; i++) // Since FFT output is mirrored above Nyquist limit (fftPoints / 2), these bins are summed with those in base band { double fft = Math.Abs(fftFull[i].X + fftFull[i].Y); double fftMirror = Math.Abs(fftFull[fftPoints - i - 1].X + fftFull[fftPoints - i - 1].Y); if (Settings["OUTPUT_MODE"][0] == "dB") { output[i] = 20 * Math.Log10(fft + fftMirror) - 20 * Math.Log10(input.Length); // Estimates gain of FFT bin } else { if (fft + fftMirror <= int.Parse(Settings["MAG_LIMIT"][0])) { output[i] = (fft + fftMirror) * (0.5 + (i / (fftPoints * 2))); } else { output[i] = int.Parse(Settings["MAG_LIMIT"][0]); } } if (Settings["SQUARE"][0] == "Yes") { output[i] = Math.Pow(output[i], 2) / 100; } } OutputBuffer = output; double scale = (double)fftPoints / sampleRate; OutputArgs.Add("SCALE", scale); }
public static AudioData GetFrequencyBands(float[] sampleArray) { NAudio.Dsp.Complex[] complices = new NAudio.Dsp.Complex[sampleArray.Length]; List <float>[] amplitudeLists = GetEmptyFloatListArray(); // Collates the amplitudes into complexes wtih the Hann window applied for (int fftPosition = 0; fftPosition < sampleArray.Length; fftPosition++) { complices[fftPosition].X = Convert.ToSingle(sampleArray[fftPosition] * FastFourierTransform.HannWindow(fftPosition, sampleArray.Length)); complices[fftPosition].Y = 0; } // Performs the FFT to get the frequencies out FastFourierTransform.FFT(true, (int)Math.Log(sampleArray.Length, 2.0), complices); float[] realFrequencies = new float[complices.Length]; // Gets the real numbers out of the complex numbers for (int realPosition = 0; realPosition < complices.Length; realPosition++) { realFrequencies[realPosition] = Convert.ToSingle(Math.Pow(complices[realPosition].X, 2) + Math.Pow(complices[realPosition].Y, 2)); } // Creates an array of empty numbers to collate the counts of categorisation int[] counts = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; float maxFrequency = realFrequencies.Max(); for (int lightPosition = 0; lightPosition < realFrequencies.Length; lightPosition++) { // Gets the frequency on a scale from 0 to 1 in this window float realFrequencyNormal = realFrequencies[lightPosition] / maxFrequency; int lightToIncrement; // If the max frequency in this time slice is 0, that means that all of the values are 0 and we should avoid doing any more maths if (maxFrequency == 0) { lightToIncrement = 0; } else { lightToIncrement = Convert.ToInt32(Math.Round(16 * (Math.Sqrt(realFrequencyNormal)))); } // Prevents the index from going over the maximum range if (lightToIncrement == 16) { lightToIncrement = 15; } counts[lightToIncrement]++; amplitudeLists[lightToIncrement].Add(sampleArray[lightPosition]); } float[] averageAmplitudes = new float[16]; for (int averageAmplitudePosition = 0; averageAmplitudePosition < averageAmplitudes.Length; averageAmplitudePosition++) { if (amplitudeLists[averageAmplitudePosition].Count != 0) { averageAmplitudes[averageAmplitudePosition] = Math.Abs(amplitudeLists[averageAmplitudePosition].Average()); } else { averageAmplitudes[averageAmplitudePosition] = 0; } } AudioData output = new AudioData(counts, averageAmplitudes); // These mask functions were intended to distribute values, solving the issue of the frequencies dogpiling the first band, but I found these did more harm than good, and commented them out //Vector<short> countVector = GetLightVector(counts); //for (int vectorPosition = 0; vectorPosition < counts.Length; vectorPosition++) //{ // Vector<short> currentMask = GetMaskVector(vectorPosition); // countVector = Vector.Divide(countVector, currentMask); //} //for (int vectorToArrayPosition = 0; vectorToArrayPosition < 16; vectorToArrayPosition++) //{ // counts[vectorToArrayPosition] = Convert.ToInt32(countVector[vectorToArrayPosition]); //} return(output); }