//----------------------------------------------------------------------------- // GetCoarseAperiodicity() calculates the aperiodicity in multiples of 3 kHz. // The upper limit is given based on the sampling frequency. //----------------------------------------------------------------------------- private void GetCoarseAperiodicity(double[] staticGroupDelay, int fs, int fftSize, int numberOfAperiodicities, double[] window, ForwardRealFFT forwardRealFFT, SubSequence <double> coarseAperiodicity) { var boundary = MatlabFunctions.MatlabRound(fftSize * 8.0 / window.Length); var halfWindowLength = window.Length / 2; Array.Clear(forwardRealFFT.Waveform, 0, fftSize); var powerSpectrum = new double[fftSize / 2 + 1]; for (var i = 0; i < numberOfAperiodicities; i++) { var center = (int)(FrequencyInterval * (i + 1) * fftSize / fs); for (int j = 0, limit = halfWindowLength * 2; j <= limit; j++) { forwardRealFFT.Waveform[j] = staticGroupDelay[center - halfWindowLength + j] * window[j]; } FFT.Execute(forwardRealFFT.ForwardFFT); var spectrum = forwardRealFFT.Spectrum; for (int j = 0, limit = fftSize / 2; j <= limit; j++) { powerSpectrum[j] = spectrum[j].Real * spectrum[j].Real + spectrum[j].Imaginary * spectrum[j].Imaginary; } Array.Sort(powerSpectrum); for (int j = 1, limit = fftSize / 2; j <= limit; j++) { powerSpectrum[j] += powerSpectrum[j - 1]; } coarseAperiodicity[i] = 10.0 * Math.Log10(powerSpectrum[fftSize / 2 - boundary - 1] / powerSpectrum[fftSize / 2]); } }
//----------------------------------------------------------------------------- // SmoothingWithRecovery() carries out the spectral smoothing and spectral // recovery on the Cepstrum domain. //----------------------------------------------------------------------------- void SmoothingWithRecovery(double f0, int fs, ForwardRealFFT forwardRealFFT, InverseRealFFT inverseRealFFT, double[] spectralEnvelope) { var smoothingLifter = new double[FFTSize]; var compensationLifter = new double[FFTSize]; smoothingLifter[0] = 1; compensationLifter[0] = (1.0 - 2.0 * Q1) + 2.0 * Q1; for (int i = 1, limit = forwardRealFFT.FFTSize / 2; i <= limit; i++) { var quefrency = (double)i / fs; smoothingLifter[i] = Math.Sin(Math.PI * f0 * quefrency) / (Math.PI * f0 * quefrency); compensationLifter[i] = (1.0 - 2.0 * Q1) + 2.0 * Q1 * Math.Cos(2.0 * Math.PI * quefrency * f0); } for (int i = 0, limit = FFTSize / 2; i <= limit; i++) { forwardRealFFT.Waveform[i] = Math.Log(forwardRealFFT.Waveform[i]); } for (int i = 1, limit = FFTSize / 2; i < limit; i++) { forwardRealFFT.Waveform[FFTSize - i] = forwardRealFFT.Waveform[i]; } FFT.Execute(forwardRealFFT.ForwardFFT); for (int i = 0, limit = FFTSize / 2; i <= limit; i++) { inverseRealFFT.Spectrum[i] = new Complex(forwardRealFFT.Spectrum[i].Real * smoothingLifter[i] * compensationLifter[i] / FFTSize, 0.0); } FFT.Execute(inverseRealFFT.InverseFFT); for (int i = 0, limit = FFTSize / 2; i <= limit; i++) { spectralEnvelope[i] = Math.Exp(inverseRealFFT.Waveform[i]); } }
private void ProcessFullSpectrum( SpectralFluxAnalyzer analyzer, ThreadSafeAudioClip clip, OnAudioClipProcessed callback) { var processedSamples = GetChannelsCombined(clip.Samples, clip.ChannelCount, clip.SampleCount); Debug.Log("Channels have been combined"); var iterations = processedSamples.Length / _fftSampleSize; var fft = new FFT(); fft.Initialize(_fftSampleSize); var chunk = new double[_fftSampleSize]; for (var i = 0; i < iterations; ++i) { Array.Copy(processedSamples, i * _fftSampleSize, chunk, 0, _fftSampleSize); var windowCoefficients = DSP.Window.Coefficients(DSP.Window.Type.Hamming, _fftSampleSize); var scaledSpectrumChunk = DSP.Math.Multiply(chunk, windowCoefficients); var scaleFactor = DSP.Window.ScaleFactor.Signal(windowCoefficients); var fftSpectrum = fft.Execute(scaledSpectrumChunk); var scaledFftSpectrum = DSP.ConvertComplex.ToMagnitude(fftSpectrum); scaledFftSpectrum = DSP.Math.Multiply(scaledFftSpectrum, scaleFactor); var currentSongTime = GetTimeFromIndex(i, clip.Frequency) * _fftSampleSize; analyzer.AnalyzeSpectrum(Array.ConvertAll(scaledFftSpectrum, x => (float)x), currentSongTime); } callback(analyzer); }
//----------------------------------------------------------------------------- // GetAperiodicResponse() calculates an aperiodic response. //----------------------------------------------------------------------------- void GetAperiodicResponse(int noiseSize, int fftSize, double[] spectrum, double[] aperiodicRatio, double currentVUV, ForwardRealFFT forwardRealFFT, InverseRealFFT inverseRealFFT, MinimumPhaseAnalysis minimumPhase, double[] aperiodicResponse) { GetNoiseSpectrum(noiseSize, fftSize, forwardRealFFT); var logSpectrum = minimumPhase.LogSpectrum; if (currentVUV != 0.0) { for (int i = 0, limit = minimumPhase.FFTSize / 2; i <= limit; i++) { logSpectrum[i] = Math.Log(spectrum[i] * aperiodicRatio[i]) / 2.0; } } else { for (int i = 0, limit = minimumPhase.FFTSize / 2; i <= limit; i++) { logSpectrum[i] = Math.Log(spectrum[i]) / 2.0; } } Common.GetMinimumPhaseSpectrum(minimumPhase); var minimumPhaseSpectrum = minimumPhase.MinimumPhaseSpectrum; Array.Copy(minimumPhaseSpectrum, 0, inverseRealFFT.Spectrum, 0, fftSize / 2 + 1); for (int i = 0, limit = fftSize / 2; i <= limit; i++) { var real = minimumPhaseSpectrum[i].Real * forwardRealFFT.Spectrum[i].Real - minimumPhaseSpectrum[i].Imaginary * forwardRealFFT.Spectrum[i].Imaginary; var imaginary = minimumPhaseSpectrum[i].Real * forwardRealFFT.Spectrum[i].Imaginary + minimumPhaseSpectrum[i].Imaginary * forwardRealFFT.Spectrum[i].Real; inverseRealFFT.Spectrum[i] = new Complex(real, imaginary); } FFT.Execute(inverseRealFFT.InverseFFT); MatlabFunctions.FFTShift(inverseRealFFT.Waveform.SubSequence(0, fftSize), aperiodicResponse); }
//----------------------------------------------------------------------------- // GetPeriodicResponse() calculates an aperiodic response. //----------------------------------------------------------------------------- void GetPeriodicResponse(int fftSize, double[] spectrum, double[] aperiodicRatio, double currentVUV, InverseRealFFT inverseRealFFT, MinimumPhaseAnalysis minimumPhase, double[] dcRemover, double fractionalTimeShift, int fs, double[] periodicResponse) { if (currentVUV <= 0.5 || aperiodicRatio[0] > 0.999) { Array.Clear(periodicResponse, 0, fftSize); return; } var logSpectrum = minimumPhase.LogSpectrum; for (int i = 0, limit = minimumPhase.FFTSize / 2; i <= limit; i++) { logSpectrum[i] = Math.Log(spectrum[i] * (1.0 - aperiodicRatio[i]) + SafeGuardMinimum) / 2.0; } Common.GetMinimumPhaseSpectrum(minimumPhase); Array.Copy(minimumPhase.MinimumPhaseSpectrum, 0, inverseRealFFT.Spectrum, 0, fftSize / 2 + 1); double coefficient = PI2 * fractionalTimeShift * fs / fftSize; GetSpectrumWithFractionalTimeShift(fftSize, coefficient, inverseRealFFT); FFT.Execute(inverseRealFFT.InverseFFT); MatlabFunctions.FFTShift(inverseRealFFT.Waveform.SubSequence(0, fftSize), periodicResponse); RemoveDCComponent(periodicResponse, fftSize, dcRemover, periodicResponse); }
void GetCentroid(double[] x, int fs, double currentF0, int fftSize, double currentPosition, ForwardRealFFT forwardRealFFT, double[] centroid) { Array.Clear(forwardRealFFT.Waveform, 0, fftSize); GetWindowedWaveform(x, fs, currentF0, currentPosition, WindowType.Blackman, 4.0, forwardRealFFT.Waveform); var windowRange = MatlabFunctions.MatlabRound(2.0 * fs / currentF0) * 2; var power = Math.Sqrt(forwardRealFFT.Waveform.Take(windowRange).Sum((w) => w * w)); for (var i = 0; i <= windowRange; i++) { forwardRealFFT.Waveform[i] /= power; } FFT.Execute(forwardRealFFT.ForwardFFT); var tmpSpectrum = new Complex[fftSize / 2 + 1]; Array.Copy(forwardRealFFT.Spectrum, tmpSpectrum, tmpSpectrum.Length); for (var i = 0; i < fftSize; i++) { forwardRealFFT.Waveform[i] *= i + 1.0; } FFT.Execute(forwardRealFFT.ForwardFFT); var spectrum = forwardRealFFT.Spectrum; for (int i = 0, limit = fftSize / 2; i <= limit; i++) { centroid[i] = spectrum[i].Real * tmpSpectrum[i].Real + spectrum[i].Imaginary * tmpSpectrum[i].Imaginary; } }
public void GetFullSpectrumThreaded() { // We only need to retain the samples for combined channels over the time domain float[] preProcessedSamples = new float[numTotalSamples]; int numProcessed = 0; float combinedChannelAverage = 0f; for (int i = 0; i < multiChannelSamples.Length; i++) { combinedChannelAverage += multiChannelSamples[i]; // Each time we have processed all channels samples for a point in time, we will store the average of the channels combined if ((i + 1) % numChannels == 0) { preProcessedSamples[numProcessed] = combinedChannelAverage / numChannels; numProcessed++; combinedChannelAverage = 0f; } } Debug.Log("Combine Channels done"); Debug.Log(preProcessedSamples.Length); // Once we have our audio sample data prepared, we can execute an FFT to return the spectrum data over the time domain int spectrumSampleSize = 1024; int iterations = preProcessedSamples.Length / spectrumSampleSize; FFT fft = new FFT(); fft.Initialize((UInt32)spectrumSampleSize); Debug.Log(string.Format("Processing {0} time domain samples for FFT", iterations)); sampleChunk = new double[spectrumSampleSize]; for (int i = 0; i < iterations; i++) { // Grab the current 1024 chunk of audio sample data Array.Copy(preProcessedSamples, i * spectrumSampleSize, sampleChunk, 0, spectrumSampleSize); // Apply our chosen FFT Window double[] windowCoefs = DSP.Window.Coefficients(DSP.Window.Type.Hanning, (uint)spectrumSampleSize); double[] scaledSpectrumChunk = DSP.Math.Multiply(sampleChunk, windowCoefs); double scaleFactor = DSP.Window.ScaleFactor.Signal(windowCoefs); // Perform the FFT and convert output (complex numbers) to Magnitude Complex[] fftSpectrum = fft.Execute(scaledSpectrumChunk); double[] scaledFFTSpectrum = DSPLib.DSP.ConvertComplex.ToMagnitude(fftSpectrum); scaledFFTSpectrum = DSP.Math.Multiply(scaledFFTSpectrum, scaleFactor); // These 1024 magnitude values correspond (roughly) to a single point in the audio timeline float curSongTime = GetTimeFromIndex(i) * spectrumSampleSize; // Send our magnitude data off to our Spectral Flux Analyzer to be analyzed for peaks AnalyzeSpectrum(Array.ConvertAll(scaledFFTSpectrum, x => (float)x), curSongTime); } Debug.Log("Spectrum Analysis done"); Debug.Log("Background Thread Completed"); }
private void ProcessFullSpectrum() { //Prepare audio samples float[] preProcessedSamples = new float[totalSamples]; int numProcessed = 0; float combinedChannelAverage = 0f; //Combine channels if the audio is stereo //Add to pre-processed array to be passed through the fft for (int i = 0; i < multiChannelSamples.Length; i++) { combinedChannelAverage += multiChannelSamples[i]; //Store the average of the channels combined for a single time index if ((i + 1) % this.numberOfChannels == 0) { preProcessedSamples[numProcessed] = combinedChannelAverage / numberOfChannels; numProcessed++; combinedChannelAverage = 0f; } } //Execute an FFT to return the spectrum data over the time domain int totalIterations = preProcessedSamples.Length / spectrumSampleSize; //Instantiate and initialise a new FFT FFT fft = new FFT(); fft.Initialize((uint)spectrumSampleSize); double[] sampleChunk = new double[spectrumSampleSize]; for (int i = 0; i < totalIterations; i++) { //Get the current chunk of audio sample data Array.Copy(preProcessedSamples, i * spectrumSampleSize, sampleChunk, 0, spectrumSampleSize); //Apply an FFT Window type to the input data and calculate a scale factor double[] windowCoefficients = DSP.Window.Coefficients(DSP.Window.Type.Hanning, (uint)spectrumSampleSize); double[] scaledSpectrumChunk = DSP.Math.Multiply(sampleChunk, windowCoefficients); double scaleFactor = DSP.Window.ScaleFactor.Signal(windowCoefficients); //Execute the FFT and get the scaled spectrum back Complex[] fftSpectrum = fft.Execute(scaledSpectrumChunk); //Convert the complex spectrum into a usable format of doubles double[] scaledFFTSpectrum = DSP.ConvertComplex.ToMagnitude(fftSpectrum); //Apply the window scale to the spectrum scaledFFTSpectrum = DSP.Math.Multiply(scaledFFTSpectrum, scaleFactor); //The magnitude values correspond to a single point in time float currentSongTime = GetTimeFromIndex(i) * spectrumSampleSize; //Send magnitude spectrum data to Spectral Flux Analyzer to be analyzed for peaks analyzer.AnalyzeSpectrum(Array.ConvertAll(scaledFFTSpectrum, x => (float)x), currentSongTime); } Debug.Log("Spectrum Analysis done"); }
public void calculateSpectrum() { float watch = Time.realtimeSinceStartup; float[] preProcessedSamples = LoadMonoSound(); // Once we have our audio sample data prepared, we can execute an FFT to return the spectrum data over the time domain FFTWindow = (int)Math.Pow(2, FFTWindowLog); ResizeSpectrum(preProcessedSamples.Length / FFTWindow * multisamples - (multisamples - 1), FFTWindow / 2); //float maximum = 0; FFT fft = new FFT(); fft.Initialize((uint)FFTWindow); float[] windowCoefs = DSP.Window.Coefficients(filter, (uint)FFTWindow); float scaleFactor = DSP.Window.ScaleFactor.Signal(windowCoefs); //Debug.Log(string.Format("Processing {0} time domain samples for FFT", chunks)); float[] sampleChunk = new float[FFTWindow]; for (int i = 0; i < chunks; i++) { // Grab the current 1024 chunk of audio sample data Array.Copy(preProcessedSamples, i * FFTWindow / multisamples, sampleChunk, 0, FFTWindow); // Apply our chosen FFT Window float[] scaledSpectrumChunk = DSP.Math.Multiply(sampleChunk, windowCoefs); // Perform the FFT and convert output (complex numbers) to Magnitude Complex[] fftSpectrum = fft.Execute(scaledSpectrumChunk); float[] scaledFFTSpectrum = DSP.ConvertComplex.ToMagnitude(fftSpectrum); scaledFFTSpectrum = DSP.Math.Multiply(scaledFFTSpectrum, scaleFactor); // These 1024 magnitude values correspond (roughly) to a single point in the audio timeline //float curSongTime = getTimeFromIndex(i) * FFTWindow / multisamples; //Spectrum energy correction float hzPerBin = audioClip.frequency / FFTWindow; /*for (int x = 0; x < scaledFFTSpectrum.Length; x++) * scaledFFTSpectrum[x] *= (x + 0.0f)*hzPerBin;*/ //scaledFFTSpectrum[x] = Math.Max(0.0, 20.0 + Math.Max(-20.0, Math.Log(scaledFFTSpectrum[x], 2.0))); for (int x = 0; x < bins; x++) { float rawValue = scaledFFTSpectrum[x + 1];//spectrum[0] - DC component (offset) float fons = calculateLoudness ? getFons((x + 1) * hzPerBin, rawValue) : rawValue; //maximum = Mathf.Max (maximum, fons); spectrumMap[i * bins + x] = fons; } // Send our magnitude data off to our Spectral Flux Analyzer to be analyzed for peaks //preProcessedSpectralFluxAnalyzer.analyzeSpectrum(Array.ConvertAll(scaledFFTSpectrum, x => (float)x), curSongTime); } Debug.Log("FFT execution time = " + (Time.realtimeSinceStartup - watch) + " s."); }
//----------------------------------------------------------------------------- // GetSmoothedPowerSpectrum() calculates the smoothed power spectrum. // The parameters used for smoothing are optimized in davance. //----------------------------------------------------------------------------- void GetSmoothedPowerSpectrum(double[] x, int fs, double currentF0, int fftSize, double currentPosition, ForwardRealFFT forwardRealFFT, double[] smoothedPowerSpectrum) { Array.Clear(forwardRealFFT.Waveform, 0, fftSize); GetWindowedWaveform(x, fs, currentF0, currentPosition, WindowType.Hanning, 4.0, forwardRealFFT.Waveform); FFT.Execute(forwardRealFFT.ForwardFFT); var spectrum = forwardRealFFT.Spectrum; for (int i = 0, limit = fftSize / 2; i <= limit; i++) { smoothedPowerSpectrum[i] = spectrum[i].Real * spectrum[i].Real + spectrum[i].Imaginary * spectrum[i].Imaginary; } Common.DCCorrection(smoothedPowerSpectrum, currentF0, fs, fftSize, smoothedPowerSpectrum); Common.LinearSmoothing(smoothedPowerSpectrum, currentF0, fs, fftSize, smoothedPowerSpectrum); }
private void GetSpectrumData(float[] samples) { try{ float[] preProcessedSamples = GetOutputData(samples); Debug.Log("Combine Channels done"); Debug.Log(preProcessedSamples.Length); // Once we have our audio sample data prepared, we can execute an FFT to return the spectrum data over the time domain int spectrumSampleSize = 1024; int iterations = preProcessedSamples.Length / spectrumSampleSize; FFT fft = new FFT(); fft.Initialize((UInt32)spectrumSampleSize); Debug.Log(string.Format("Processing {0} time domain samples for FFT", iterations)); double[] sampleChunk = new double[spectrumSampleSize]; for (int i = 0; i < iterations; i++) { // Grab the current 1024 chunk of audio sample data Array.Copy(preProcessedSamples, i * spectrumSampleSize, sampleChunk, 0, spectrumSampleSize); // Apply our chosen FFT Window double[] windowCoefs = DSP.Window.Coefficients(DSP.Window.Type.Hanning, (uint)spectrumSampleSize); double[] scaledSpectrumChunk = DSP.Math.Multiply(sampleChunk, windowCoefs); double scaleFactor = DSP.Window.ScaleFactor.Signal(windowCoefs); // Perform the FFT and convert output (complex numbers) to Magnitude Complex[] fftSpectrum = fft.Execute(scaledSpectrumChunk); double[] scaledFFTSpectrum = DSPLib.DSP.ConvertComplex.ToMagnitude(fftSpectrum); scaledFFTSpectrum = DSP.Math.Multiply(scaledFFTSpectrum, scaleFactor); // These 1024 magnitude values correspond (roughly) to a single point in the audio timeline float curSongTime = getTimeFromIndex(i) * spectrumSampleSize; // Send our magnitude data off to our Spectral Flux Analyzer to be analyzed for peaks preProcessedSpectralFluxAnalyzer.analyzeSpectrum(Array.ConvertAll(scaledFFTSpectrum, x => (float)x), curSongTime); Debug.Log("Spectrum Analysis done"); Debug.Log("Background Thread Completed"); } } catch (Exception e) { Debug.Log(e.ToString()); } }
void GetNoiseSpectrum(int noiseSize, int fftSize, ForwardRealFFT forwardRealFFT) { var waveform = forwardRealFFT.Waveform; for (var i = 0; i < noiseSize; i++) { waveform[i] = Rand.Next(); } var average = noiseSize > 0 ? waveform.Take(noiseSize).Average() : 1.0; for (var i = 0; i < noiseSize; i++) { waveform[i] -= average; } Array.Clear(waveform, noiseSize, fftSize - noiseSize); FFT.Execute(forwardRealFFT.ForwardFFT); }
//----------------------------------------------------------------------------- // GetPowerSpectrum() calculates the power_spectrum with DC correction. // DC stands for Direct Current. In this case, the component from 0 to F0 Hz // is corrected. //----------------------------------------------------------------------------- void GetPowerSpectrum(int fs, double f0, ForwardRealFFT forwardRealFFT) { var halfWindowLength = MatlabFunctions.MatlabRound(1.5 * fs / f0); // FFT Array.Clear(forwardRealFFT.Waveform, halfWindowLength * 2 + 1, FFTSize - halfWindowLength * 2 - 1); FFT.Execute(forwardRealFFT.ForwardFFT); var powerSpectrum = forwardRealFFT.Waveform; var spectrum = forwardRealFFT.Spectrum; for (int i = 0, limit = FFTSize / 2; i <= limit; i++) { powerSpectrum[i] = spectrum[i].Real * spectrum[i].Real + spectrum[i].Imaginary * spectrum[i].Imaginary; } Common.DCCorrection(powerSpectrum, f0, fs, FFTSize, powerSpectrum); }
private void SpectrumPrepare(int chanel, Chart PlotChart) { uint N = Convert.ToUInt32(saveDataTable.Rows.Count); double[] orignalData = new double[N]; double samplingRateHz = 1000; string windowFFT = "Hamming"; DSP.Window.Type windowToApply = (DSP.Window.Type)Enum.Parse(typeof(DSP.Window.Type), windowFFT); double[] wc = DSP.Window.Coefficients(windowToApply, N); double windowScaleFactor = DSP.Window.ScaleFactor.Signal(wc); // the sum of N and zeros must be power of 2. int pow2 = 1; while (N > Math.Pow(2, pow2)) { pow2++; } uint zeros = Convert.ToUInt32(Math.Pow(2, pow2)) - N; // Instantiate & Initialize the FFT class FFT fft = new FFT(); fft.Initialize(N, zeros); foreach (DataRow dr in saveDataTable.Rows) { // copy saveDataTable to array "orignailData" orignalData[saveDataTable.Rows.IndexOf(dr)] = Convert.ToDouble(dr[chanel]); } // remove the mean orignalData = DSP.Math.RemoveMean(orignalData); // Calculate the frequency span double[] fSpan = fft.FrequencySpan(samplingRateHz); // Convert and Plot Log Magnitude Complex[] cpxResult = fft.Execute(orignalData); double[] magResult = DSP.ConvertComplex.ToMagnitude(cpxResult); magResult = DSP.Math.Multiply(magResult, windowScaleFactor); double[] magLog = DSP.ConvertMagnitude.ToMagnitudeDBV(magResult); PlotChart.Series["Series1"].Points.Clear(); PlotChart.Series["Series1"].Points.DataBindXY(fSpan, magResult); }
//----------------------------------------------------------------------------- // GetPeriodicResponse() calculates an aperiodic response. //----------------------------------------------------------------------------- void GetPeriodicResponse(int fftSize, double[] spectrum, double[] aperiodicRatio, double currentVUV, double[] periodicResponse) { if (currentVUV <= 0.5 || aperiodicRatio[0] > 0.999) { periodicResponse.Fill(0.0, 0, fftSize); return; } var logSpectrum = MinimumPhase.LogSpectrum; for (int i = 0, limit = MinimumPhase.FFTSize / 2; i <= limit; i++) { logSpectrum[i] = Math.Log(spectrum[i] * (1.0 - aperiodicRatio[i]) + SafeGuardMinimum) / 2.0; } Common.GetMinimumPhaseSpectrum(MinimumPhase); Array.Copy(MinimumPhase.MinimumPhaseSpectrum, 0, InverseRealFFT.Spectrum, 0, fftSize / 2 + 1); FFT.Execute(InverseRealFFT.InverseFFT); MatlabFunctions.FFTShift(InverseRealFFT.Waveform.SubSequence(0, fftSize), periodicResponse); RemoveDCComponent(periodicResponse, fftSize, DCRemover, periodicResponse); }
internal static void GetMinimumPhaseSpectrum(MinimumPhaseAnalysis minimumPhase) { var fftSize = minimumPhase.FFTSize; for (var i = fftSize / 2 + 1; i < fftSize; i++) { minimumPhase.LogSpectrum[i] = minimumPhase.LogSpectrum[fftSize - i]; } FFT.Execute(minimumPhase.InverseFFT); var cepstrum = minimumPhase.Cepstrum; cepstrum[0] = new Complex(cepstrum[0].Real, cepstrum[0].Imaginary); for (int i = 1, limit = fftSize / 2; i < limit; i++) { cepstrum[i] = new Complex(cepstrum[i].Real * 2.0, cepstrum[i].Imaginary * -2.0); } cepstrum[fftSize / 2] = new Complex(cepstrum[fftSize / 2].Real, cepstrum[fftSize / 2].Imaginary * -1.0); for (var i = fftSize / 2 + 1; i < fftSize; i++) { cepstrum[i] = new Complex(); } FFT.Execute(minimumPhase.ForwardFFT); // Since x is complex number, calculation of exp(x) is as following. // Note: This FFT library does not keep the aliasing. var mininimumPhaseSpectrum = minimumPhase.MinimumPhaseSpectrum; for (int i = 0, limit = fftSize / 2; i <= limit; i++) { var tmp = Math.Exp(mininimumPhaseSpectrum[i].Real / fftSize); var real = tmp * Math.Cos(mininimumPhaseSpectrum[i].Imaginary / fftSize); var imaginary = tmp * Math.Sin(mininimumPhaseSpectrum[i].Imaginary / fftSize); mininimumPhaseSpectrum[i] = new Complex(real, imaginary); } }
public override float[] doFFT(float[] samples, WINDOW_TYPE window) { double[] dSamples = new double[samples.Length]; for (int i = 0; i < samples.Length; i++) { dSamples[i] = (double)samples[i]; } //Code from DSPLib documentation DSP.Window.Type dspWindow = getWindowType(window); double[] windowCoefs = DSP.Window.Coefficients(dspWindow, (UInt32)this.binSize); double[] windowInputData = DSP.Math.Multiply(dSamples, windowCoefs); double windowScaleFactor = DSP.Window.ScaleFactor.Signal(windowCoefs); System.Numerics.Complex[] complexSpectrum = fft.Execute(windowInputData); //Convert to magnitude and multiply by the window scale factor to get our binned array double[] fftSpectrum = DSP.Math.Multiply(DSPLib.DSP.ConvertComplex.ToMagnitude(complexSpectrum), windowScaleFactor); float[] fFFTSpectrum = new float[fftSpectrum.Length]; for (int i = 0; i < fFFTSpectrum.Length; i++) { fFFTSpectrum[i] = (float)fftSpectrum[i]; } return(fFFTSpectrum); }
double D4CLoveTrainSub(double[] x, int fs, double currentF0, double currentPosition, int f0Length, int fftSize, int boundary0, int boundary1, int boundary2, ForwardRealFFT forwardRealFFT) { var powerSpectrum = new double[fftSize]; var windowLength = MatlabFunctions.MatlabRound(1.5 * fs / currentF0) * 2 + 1; GetWindowedWaveform(x, fs, currentF0, currentPosition, WindowType.Blackman, 3.0, forwardRealFFT.Waveform); Array.Clear(forwardRealFFT.Waveform, windowLength, fftSize - windowLength); FFT.Execute(forwardRealFFT.ForwardFFT); var spectrum = forwardRealFFT.Spectrum; for (int i = boundary0 + 1, limit = fftSize / 2 + 1; i < limit; i++) { powerSpectrum[i] = spectrum[i].Real * spectrum[i].Real + spectrum[i].Imaginary * spectrum[i].Imaginary; } for (var i = boundary0; i <= boundary2; i++) { powerSpectrum[i] += powerSpectrum[i - 1]; } return(powerSpectrum[boundary1] / powerSpectrum[boundary2]); }
static void FastFFTFilt(double[] x, double[] h, int fftSize, ForwardRealFFT forwardRealFFT, InverseRealFFT inverseRealFFT, double[] y) { for (var i = 0; i < x.Length; i++) { forwardRealFFT.Waveform[i] = x[i] / fftSize; } if (x.Length < fftSize) { Array.Clear(forwardRealFFT.Waveform, x.Length, fftSize - x.Length); } FFT.Execute(forwardRealFFT.ForwardFFT); var xSpectrum = new Complex[fftSize]; Array.Copy(forwardRealFFT.Spectrum, xSpectrum, fftSize / 2 + 1); for (var i = 0; i < h.Length; i++) { forwardRealFFT.Waveform[i] = h[i] / fftSize; } if (h.Length < fftSize) { Array.Clear(forwardRealFFT.Waveform, h.Length, fftSize - h.Length); } FFT.Execute(forwardRealFFT.ForwardFFT); for (var i = fftSize / 2 + 1; i > -1; i--) { inverseRealFFT.Spectrum[i] = new Complex( xSpectrum[i].Real * forwardRealFFT.Spectrum[i].Real - xSpectrum[i].Imaginary * forwardRealFFT.Spectrum[i].Imaginary, xSpectrum[i].Real * forwardRealFFT.Spectrum[i].Imaginary + xSpectrum[i].Imaginary * forwardRealFFT.Spectrum[i].Real ); } FFT.Execute(inverseRealFFT.InverseFFT); inverseRealFFT.Waveform.BlockCopy(0, y, 0, fftSize); }
static void Main() { FFT fft = new FFT(); fft.Initialize(4096); double Fs = 1000; double[] d = new double[4096]; for (int i = 0; i < 4096; i++) { double t = i / Fs; d[i] = Math.Sin(Math.PI * 2.0 * t * 200.0) + 0.2 * Math.Sin(Math.PI * 2.0 * t * 90.0); } var c = fft.Execute(d); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new MainForm()); }
static void Main(string[] args) { const int FFTSize = 240; const int FFTComplexSize = FFTSize / 2 + 1; var fft = new FFT(FFTSize, fftw_direction.Forward); var input = new Complex[FFTSize]; var input2 = new Complex[FFTSize]; var output = new Complex[FFTSize]; const int RectWaveWidth = 40; const int RectWaveWidth2 = 30; var conv = new Convolver(Kernel); for (int i = 0; i < FFTSize; i++) { //input[i] = i / RectWaveWidth % 2 == 0 ? 1 : 0; //input[i] += i / RectWaveWidth2 % 2 == 0 ? 1 : 0; input[i] = 1000 + 50000 * Math.Sin(2 * Math.PI / FFTSize * 20.1 * i) + 40000 * Math.Sin(2 * Math.PI / FFTSize * 7 * i) + 9000 * Math.Sin(2 * Math.PI / FFTSize * 119 * i); // input[i] = 1 * Math.Sin(2 * Math.PI / FFTSize * 7 * i); // input[i] *= 2 * Math.Cos(2 * Math.PI / FFTSize * 5 * i); } var filter = new Complex[FFTSize]; for (int i = 0; i < FFTSize; i++) { filter[i] = Filter(i, FFTSize); } //fixed (double* kernel = Kernel) { // int len = conv.Convolve(input, FFTSize, input2, FFTSize); // len = conv.ConvolveFinal(input2 + len, FFTSize - len); //} //input[0] = 1; //for (int i = 1; i < FFTSize; i++) { // input[i] = 0; //} //for (int i = 0; i < FFTSize; i++) { // input[i] = i % 2 == 0 ? 1 : 0; //} fft.WriteInput(input); fft.Execute(); fft.ReadOutput(output); { var sum = 0d; for (int i = 0; i < output.Length; i++) { var c = output[i] / (i > 0 ? fft.FFTSize / 2 : FFTSize); var abs = Complex.Abs(c); sum += abs; Console.WriteLine($"{i}=abs({c.Real:N6},{c.Imaginary:N6})={abs:N6}"); } Console.WriteLine($"sum={sum:N6}"); } //output[0] = 100 * FFTSize; //for (int i = 1; i < 50; i++) { // output[i] = output[FFTSize - i] = 100 * FFTSize / 2; //} for (int i = 0; i < FFTSize; i++) { output[i] *= filter[i]; } var ifft = new FFT(FFTSize, fftw_direction.Backward); ifft.WriteInput(output); ifft.Execute(); ifft.ReadOutput(input2); for (int i = 0; i < input2.Length; i++) { input2[i] /= FFTSize; } fft.WriteInput(input2); fft.Execute(); fft.ReadOutput(output); { var sum = 0d; for (int i = 0; i < output.Length; i++) { var c = output[i] / (i > 0 ? fft.FFTSize / 2 : FFTSize); var abs = Complex.Abs(c); sum += abs; Console.WriteLine($"{i}=abs({c.Real:N6},{c.Imaginary:N6})={abs:N6}"); } Console.WriteLine($"sum={sum:N6}"); } }
void AnalyzeAudio() { if (soundFile) { //The array for the averaged sample data. (L,R,L,R,L,R are averaged into (L+R)/2, (L+R)/2, (L+R)/2) float[] preprocessedSamples = new float[numSamples]; int numberOfSamplesProcessed = 0; float combinedChannelAverage = 0f; Debug.Log("Starting sample processing..."); for (int i = 0; i < allChannelsSamples.Length; i++) { combinedChannelAverage += allChannelsSamples[i]; //for(int j = 0; j < numChannels; j++) //{ // combinedChannelAverage += allChannelsSamples[i + j]; // numberOfSamplesProcessed++; //} //preprecessedSamples[i/numChannels] = combinedChannelAverage / (float)numChannels; //combinedChannelAverage = 0; // Each time we have processed all channels samples for a point in time, we will store the average of the channels combined if ((i + 1) % numChannels == 0) { preprocessedSamples[numberOfSamplesProcessed] = combinedChannelAverage / numChannels; numberOfSamplesProcessed++; combinedChannelAverage = 0f; } } int specSampSize = 1024; int iterations = preprocessedSamples.Length / specSampSize; double[] sampleChunk = new double[specSampSize]; //LomFFT fft = new LomFFT(); FFT fft = new FFT(); fft.Initialize((System.UInt32)specSampSize); SpectralFluxAnalyzer preproAnalyzer = new SpectralFluxAnalyzer(); for (int i = 0; i < iterations; ++i) { System.Array.Copy(preprocessedSamples, i * specSampSize, sampleChunk, 0, specSampSize); double[] windowCoefs = DSP.Window.Coefficients(DSP.Window.Type.Hanning, (uint)specSampSize); double[] scaledSpectrumChunk = DSP.Math.Multiply(sampleChunk, windowCoefs); double scaleFactor = DSP.Window.ScaleFactor.Signal(windowCoefs); // Perform the FFT and convert output (complex numbers) to Magnitude System.Numerics.Complex[] fftSpectrum = fft.Execute(scaledSpectrumChunk); double[] scaledFFTSpectrum = DSP.ConvertComplex.ToMagnitude(fftSpectrum); scaledFFTSpectrum = DSP.Math.Multiply(scaledFFTSpectrum, scaleFactor); //old //fft.FFT(sampleChunk); float currTime = getTimeFromIndex(i) * specSampSize; preproAnalyzer.analyzeSpectrum(System.Array.ConvertAll(scaledFFTSpectrum, x => (float)x), currTime); //AnalyzeSpectrum(data...); } //foreach(SpectralFluxAnalyzer.SpectralFluxInfo specInfo in preproAnalyzer.spectralFluxSamples) //{ // if(specInfo.isPeak) // { // Debug.Log("Peak at: " + specInfo.time); // } //} importantMoments = null; importantMoments = new AnimationCurve(); freqCurve = null; freqCurve = new AnimationCurve(); Debug.Log("Starting graph processing..."); for (int i = 0; i < preproAnalyzer.spectralFluxSamples.Count; i++) { if (preproAnalyzer.spectralFluxSamples[i].isPeak) { importantMoments.AddKey(preproAnalyzer.spectralFluxSamples[i].time, 1); freqCurve.AddKey(preproAnalyzer.spectralFluxSamples[i].time, preproAnalyzer.spectralFluxSamples[i].spectralFlux); } } Debug.Log("Done!"); Debug.Log(numberOfSamplesProcessed); //AudioListener.GetSpectrumData(spectrums, 0, FFTWindow.BlackmanHarris); //Debug.Log(AudioSettings.outputSampleRate); } }
public void getFullSpectrumThreaded() //not used. from https://medium.com/giant-scam/algorithmic-beat-mapping-in-unity-preprocessed-audio-analysis-d41c339c135a . //I find it well visualised, but overly sensitive (doesnt seem to target lower freq, which it should). { try { // We only need to retain the samples for combined channels over the time domain float[] preProcessedSamples = new float[this.samplesTotal]; //J- check here to see if needs doubling/halving. int numProcessed = 0; float combinedChannelAverage = 0f; for (int i = 0; i < CC.Length; i++) { combinedChannelAverage += CC[i]; // Each time we have processed all channels samples for a point in time, we will store the average of the channels combined if ((i + 1) % 2 == 0) //hard coding here, soz. J- { preProcessedSamples[numProcessed] = combinedChannelAverage / 2; //here too (the 2). numProcessed++; combinedChannelAverage = 0f; } } Debug.Log("Combine Channels done"); Debug.Log(preProcessedSamples.Length); // Once we have our audio sample data prepared, we can execute an FFT to return the spectrum data over the time domain int spectrumSampleSize = 1024; int iterations = preProcessedSamples.Length / spectrumSampleSize; FFT fft = new FFT(); fft.Initialize((UInt32)spectrumSampleSize); Debug.Log(string.Format("Processing {0} time domain samples for FFT", iterations)); double[] sampleChunk = new double[spectrumSampleSize]; for (int i = 0; i < iterations; i++) { // Grab the current 1024 chunk of audio sample data Array.Copy(preProcessedSamples, i * spectrumSampleSize, sampleChunk, 0, spectrumSampleSize); // Apply our chosen FFT Window double[] windowCoefs = DSP.Window.Coefficients(DSP.Window.Type.Hanning, (uint)spectrumSampleSize); double[] scaledSpectrumChunk = DSP.Math.Multiply(sampleChunk, windowCoefs); double scaleFactor = DSP.Window.ScaleFactor.Signal(windowCoefs); // Perform the FFT and convert output (complex numbers) to Magnitude System.Numerics.Complex[] fftSpectrum = fft.Execute(scaledSpectrumChunk); double[] scaledFFTSpectrum = DSPLib.DSP.ConvertComplex.ToMagnitude(fftSpectrum); scaledFFTSpectrum = DSP.Math.Multiply(scaledFFTSpectrum, scaleFactor); // These 1024 magnitude values correspond (roughly) to a single point in the audio timeline float curSongTime = getTimeFromIndex(i) * spectrumSampleSize; // Send our magnitude data off to our Spectral Flux Analyzer to be analyzed for peaks preProcessedSpectralFluxAnalyzer.analyzeSpectrum(Array.ConvertAll(scaledFFTSpectrum, x => (float)x), curSongTime); } Debug.Log("Spectrum Analysis done"); Debug.Log("Background Thread Completed"); } catch (Exception e) { // Catch exceptions here since the background thread won't always surface the exception to the main thread Debug.Log(e.ToString()); } }//Not used currently. Copied from giant-scam. Using for reference sporadically.
void DoFFT(EEGEvent evt) { // Do an FFT on each channel List <double[]> fftOutput = new List <double[]>(); for (int i = 0; i < samples.Length; i++) { var channelSamples = samples[i]; var samplesCopy = channelSamples.ToArray(); // apply windowing function to samplesCopy DSP.Math.Multiply(samplesCopy, windowConstants); var cSpectrum = fft.Execute(samplesCopy); // complex side smoothing //cSpectrum = complexFilters[i].Smooth(cSpectrum); double[] lmSpectrum = DSP.ConvertComplex.ToMagnitude(cSpectrum); lmSpectrum = DSP.Math.Multiply(lmSpectrum, scaleFactor); fftOutput.Add(lmSpectrum); } for (int i = 0; i < fftOutput.Count; i++) { var rawFFT = fftOutput[i]; Add(new EEGEvent(evt.timestamp, EEGDataType.FFT_RAW, rawFFT, i)); // magnitude side smoothing var smoothedFFT = magSmoothers[i].Smooth(rawFFT); Add(new EEGEvent(evt.timestamp, EEGDataType.FFT_SMOOTHED, smoothedFFT, i)); } //var freqSpan = fft.FrequencySpan(sampleRate); // find abs powers for each band var absolutePowers = new Dictionary <EEGDataType, double[]>(); for (int i = 0; i < channels; i++) { var bins = fftOutput[i]; double deltaAbs = AbsBandPower(bins, 1, 4); double thetaAbs = AbsBandPower(bins, 4, 8); double alphaAbs = AbsBandPower(bins, 7.5, 13); double betaAbs = AbsBandPower(bins, 13, 30); double gammaAbs = AbsBandPower(bins, 30, 44); //Logger.Log("D={0}, T={1}, A={2}, B={3}, G={4}", deltaAbs, thetaAbs, alphaAbs, betaAbs, gammaAbs); GetBandList(absolutePowers, EEGDataType.ALPHA_ABSOLUTE)[i] = (alphaAbs); GetBandList(absolutePowers, EEGDataType.BETA_ABSOLUTE)[i] = (betaAbs); GetBandList(absolutePowers, EEGDataType.GAMMA_ABSOLUTE)[i] = (gammaAbs); GetBandList(absolutePowers, EEGDataType.DELTA_ABSOLUTE)[i] = (deltaAbs); GetBandList(absolutePowers, EEGDataType.THETA_ABSOLUTE)[i] = (thetaAbs); } // we can emit abs powers immediately Add(new EEGEvent(evt.timestamp, EEGDataType.ALPHA_ABSOLUTE, absolutePowers[EEGDataType.ALPHA_ABSOLUTE].ToArray())); Add(new EEGEvent(evt.timestamp, EEGDataType.BETA_ABSOLUTE, absolutePowers[EEGDataType.BETA_ABSOLUTE].ToArray())); Add(new EEGEvent(evt.timestamp, EEGDataType.GAMMA_ABSOLUTE, absolutePowers[EEGDataType.GAMMA_ABSOLUTE].ToArray())); Add(new EEGEvent(evt.timestamp, EEGDataType.DELTA_ABSOLUTE, absolutePowers[EEGDataType.DELTA_ABSOLUTE].ToArray())); Add(new EEGEvent(evt.timestamp, EEGDataType.THETA_ABSOLUTE, absolutePowers[EEGDataType.THETA_ABSOLUTE].ToArray())); // now calc and emit relative powers Add(new EEGEvent(evt.timestamp, EEGDataType.ALPHA_RELATIVE, RelBandPower(absolutePowers, EEGDataType.ALPHA_ABSOLUTE))); Add(new EEGEvent(evt.timestamp, EEGDataType.BETA_RELATIVE, RelBandPower(absolutePowers, EEGDataType.BETA_ABSOLUTE))); Add(new EEGEvent(evt.timestamp, EEGDataType.GAMMA_RELATIVE, RelBandPower(absolutePowers, EEGDataType.GAMMA_ABSOLUTE))); Add(new EEGEvent(evt.timestamp, EEGDataType.DELTA_RELATIVE, RelBandPower(absolutePowers, EEGDataType.DELTA_ABSOLUTE))); Add(new EEGEvent(evt.timestamp, EEGDataType.THETA_RELATIVE, RelBandPower(absolutePowers, EEGDataType.THETA_ABSOLUTE))); }
public void PerformFFTThreaded() { FFT fft = new FFT(); fft.Initialize(GetSampleCount()); double[] sampleChunk = new double[GetSampleCount()]; while (GetNextChunkToRender(out int chunkId)) { Color[][] bandColors = new Color[ColumnsPerChunk][]; for (int k = 0; k < ColumnsPerChunk; k++) { int i = (chunkId * ColumnsPerChunk) + k; // Grab the current chunk of audio sample data int curSampleSize = (int)GetSampleCount(); if (i * sampleOffset + GetSampleCount() > preProcessedSamples.Length) { // We've reached the end of the track, pad with empty data if (is3d) { waveformData.BandVolumes[i] = emptyArr; } else { waveformData.BandVolumes[i] = new float[(GetSampleCount() / 2) + 1]; if (FillEmptySpaceWithTransparency) { bandColors[k] = new Color[(GetSampleCount() / 2) + 1]; } else { bandColors[k] = Enumerable.Repeat(spectrogramHeightGradient.Evaluate(0f), ((int)GetSampleCount() / 2) + 1).ToArray(); } } continue; } Buffer.BlockCopy(preProcessedSamples, (int)(i * sampleOffset) * sizeof(double), sampleChunk, 0, curSampleSize * sizeof(double)); // Apply our chosen FFT Window double[] scaledSpectrumChunk = DSP.Math.Multiply(sampleChunk, windowCoefs); // Perform the FFT and convert output (complex numbers) to Magnitude Complex[] fftSpectrum = fft.Execute(scaledSpectrumChunk); double[] scaledFFTSpectrum = DSP.ConvertComplex.ToMagnitude(fftSpectrum); scaledFFTSpectrum = DSP.Math.Multiply(scaledFFTSpectrum, scaleFactor); if (is3d) { float[] bandVolumes = new float[_bands.Length - 1]; for (int j = 1; j < _bands.Length; j++) { bandVolumes[j - 1] = BandVol(_bands[j - 1], _bands[j], scaledFFTSpectrum, hzStep); } waveformData.BandVolumes[i] = bandVolumes; } else { int gradientFactor = 25; waveformData.BandVolumes[i] = scaledFFTSpectrum.Select(it => { if (it >= Math.Pow(Math.E, -255d / gradientFactor)) { return((float)((Math.Log(it) + (255d / gradientFactor)) * gradientFactor) / 128f); } return(0f); }).ToArray(); bandColors[k] = waveformData.BandVolumes[i].Select(it => { float lerp = Mathf.InverseLerp(0, 2, it); return(spectrogramHeightGradient.Evaluate(lerp)); }).ToArray(); } } if (!is3d) { // Render 2d texture with spectogram for entire chunk var data = waveformData.BandCData[chunkId]; try { int index = 0; if (bandColors == null) { return; } for (int y = 0; y < bandColors[0].Length; y++) { for (int x = 0; x < bandColors.Length; x++) { data[index++] = bandColors[x][y]; } } } catch (NullReferenceException) { // Cancelled some other way } catch (InvalidOperationException) { // NativeArray has been deallocated :( } } chunksComplete.Enqueue(chunkId); waveformData.ProcessedChunks++; } Debug.Log("FFT Thread Completed"); }
void process() { audioBuffer = new float[sampleCount * channels]; audioSource.Read(audioBuffer, 0, sampleCount * channels); float[] preProcessedSamples = new float[sampleCount]; int numProcessed = 0; float combinedChannelAverage = 0f; for (int i = 0; i < audioBuffer.Length; i++) { combinedChannelAverage += audioBuffer[i]; // Each time we have processed all channels samples for a point in time, we will store the average of the channels combined if ((i + 1) % channels == 0) { preProcessedSamples[numProcessed] = combinedChannelAverage / channels; numProcessed++; combinedChannelAverage = 0f; } } // Once we have our audio sample data prepared, we can execute an FFT to return the spectrum data over the time domain int spectrumSampleSize = 512; int iterations = preProcessedSamples.Length / spectrumSampleSize; GlobalVariables.entryNo = 0; GlobalVariables.times = new float[iterations]; GlobalVariables.thresholds = new float[iterations]; GlobalVariables.prunedSFValues = new float[iterations]; GlobalVariables.beats = new bool[iterations]; GlobalVariables.intensityChanged = new bool[iterations]; GlobalVariables.intensityLevel = new int[iterations]; GlobalVariables.numBeats = 0; FFT fft = new FFT(); fft.Initialize((UInt32)spectrumSampleSize); SFAnalyser = new SpectralFluxAnalyser(); double[] sampleChunk = new double[spectrumSampleSize]; for (int i = 0; i < iterations; i++) { // Grab the current 1024 chunk of audio sample data Array.Copy(preProcessedSamples, i * spectrumSampleSize, sampleChunk, 0, spectrumSampleSize); // Apply our chosen FFT Window double[] windowCoefs = DSP.Window.Coefficients(DSP.Window.Type.Hanning, (uint)spectrumSampleSize); double[] scaledSpectrumChunk = DSP.Math.Multiply(sampleChunk, windowCoefs); double scaleFactor = DSP.Window.ScaleFactor.Signal(windowCoefs); // Perform the FFT and convert output (complex numbers) to Magnitude Complex[] fftSpectrum = fft.Execute(scaledSpectrumChunk); double[] scaledFFTSpectrum = DSPLib.DSP.ConvertComplex.ToMagnitude(fftSpectrum); scaledFFTSpectrum = DSP.Math.Multiply(scaledFFTSpectrum, scaleFactor); // These 1024 magnitude values correspond (roughly) to a single point in the audio timeline float curSongTime = getTimeFromIndex(i) * spectrumSampleSize; // Send our magnitude data off to our Spectral Flux Analyzer to be analyzed for peaks SFAnalyser.analyseSpectrum(Array.ConvertAll(scaledFFTSpectrum, x => (float)x), curSongTime); } }
private void GetSpectrum(uint dataNumber) { string[] textBoxRaw; if (dataNumber == 1) { if (String.IsNullOrEmpty(textBoxData1.Text)) { return; } if (String.IsNullOrEmpty(textBoxFreq1.Text)) { MessageBox.Show("Input sampling rate"); return; } textBoxRaw = textBoxData1.Text.Split('\n'); } else { if (String.IsNullOrEmpty(textBoxData2.Text)) { return; } if (String.IsNullOrEmpty(textBoxFreq2.Text)) { MessageBox.Show("Input sampling rate"); return; } textBoxRaw = textBoxData2.Text.Split('\n'); } int len = textBoxRaw.Length; uint padLength = 0; double[] inputData = new double[len]; int i = 0; for (int j = 0; ; j++) { if (Math.Pow(2, j) > len) { padLength = Convert.ToUInt32(Math.Pow(2, j)); break; } } if (dataNumber == 1) { labelData1Count.Text = "Data #1 Count : " + len; } else { labelData2Count.Text = "Data #2 Count : " + len; } foreach (string data in textBoxRaw) { if (String.IsNullOrEmpty(data) || String.IsNullOrWhiteSpace(data)) { continue; } inputData[i] = Convert.ToDouble(data); i++; } FFT fft = new FFT(); fft.Initialize((uint)len, (uint)(padLength - len)); Complex[] cpx = fft.Execute(inputData); double[] sptr = DSP.ConvertComplex.ToMagnitude(cpx); Array.Resize(ref sptr, sptr.Length / 2); double[] freqSpan; if (dataNumber == 1) { freqSpan = fft.FrequencySpan(double.Parse(textBoxFreq1.Text)); } else { freqSpan = fft.FrequencySpan(double.Parse(textBoxFreq2.Text)); } Array.Resize(ref freqSpan, freqSpan.Length / 2); Plot plot = new Plot("FFT Spectrum Display", "FFT Bin (Freq. = x * 2)", "Magnitude (Vrms)"); plot.PlotData(freqSpan, sptr); plot.Show(); }
/// <summary> /// This class is used to process the AudioClip. /// It uses FFT to read the samples, afterwards a Spectral Flux Analysis is performed. /// This class was not modified compared to the original. /// </summary> private void GetAudioSpectrum() { try { // We only need to retain the samples for combined channels over the time domain var preProcessedSamples = new float[_numTotalSamples]; var numProcessed = 0; var combinedChannelAverage = 0f; for (var i = 0; i < _multiChannelSamples.Length; i++) { combinedChannelAverage += _multiChannelSamples[i]; // Each time we have processed all channels samples for a point in time, we will store the average of the channels combined if ((i + 1) % _numChannels == 0) { preProcessedSamples[numProcessed] = combinedChannelAverage / _numChannels; numProcessed++; combinedChannelAverage = 0f; } } Debug.Log("Combine Channels done"); Debug.Log(preProcessedSamples.Length); // Once we have our audio sample data prepared, we can execute an FFT to return the spectrum data over the time domain var spectrumSampleSize = 1024; var iterations = preProcessedSamples.Length / spectrumSampleSize; var fft = new FFT(); fft.Initialize((uint)spectrumSampleSize); Debug.Log($"Processing {iterations} time domain samples for FFT"); var sampleChunk = new double[spectrumSampleSize]; for (var i = 0; i < iterations; i++) { // Grab the current 1024 chunk of audio sample data Array.Copy(preProcessedSamples, i * spectrumSampleSize, sampleChunk, 0, spectrumSampleSize); // Apply our chosen FFT Window var windowCoefs = DSP.Window.Coefficients(DSP.Window.Type.Hanning, (uint)spectrumSampleSize); var scaledSpectrumChunk = DSP.Math.Multiply(sampleChunk, windowCoefs); var scaleFactor = DSP.Window.ScaleFactor.Signal(windowCoefs); // Perform the FFT and convert output (complex numbers) to Magnitude var fftSpectrum = fft.Execute(scaledSpectrumChunk); var scaledFFTSpectrum = DSP.ConvertComplex.ToMagnitude(fftSpectrum); scaledFFTSpectrum = DSP.Math.Multiply(scaledFFTSpectrum, scaleFactor); // These 1024 magnitude values correspond (roughly) to a single point in the audio timeline var curSongTime = GetTimeFromIndex(i) * spectrumSampleSize; // Send our magnitude data off to our Spectral Flux Analyzer to be analyzed for peaks _analyzer.analyzeSpectrum(Array.ConvertAll(scaledFFTSpectrum, x => (float)x), curSongTime); } Debug.Log("<<< Spectrum Analysis finished successfully >>>"); } catch (Exception e) { // Catch exceptions here since the background thread won't always surface the exception to the main thread Debug.Log(e.ToString()); } }
private double[] CreateFrequencyHistogramFromPcmSlices(Int16[] pcmSampleAmplitudes) { LoadFftBuffer(pcmSampleAmplitudes); return(DSP.ConvertComplex.ToMagnitude(fft.Execute(fftBuffer))); }
private void AnalyzeFullSpectrum() { try { //We only retain the samples of combined channel over the time domain float[] processedSamples = new float[totalNumberOfSamples]; int numProcessed = 0; float channelAverage = 0f; for (int i = 0; i < multiChannelSamples.Length; ++i) { channelAverage += multiChannelSamples[i]; //Store the average of the combined channels if ((i + 1) % numberOfChannels == 0) { processedSamples[numProcessed] = channelAverage / numberOfChannels; ++numProcessed; channelAverage = 0f; } } //Debug.Log("Channel combining done. Total number of samples processed: " + processedSamples.Length); //Execute Fast-Fourier Transform to return the spectrum data over time int spectrumSampleSize = 1024; int iterations = processedSamples.Length / spectrumSampleSize; FFT fft = new FFT(); fft.Initialize((System.UInt32)spectrumSampleSize); double[] sampleChunk = new double[spectrumSampleSize]; for (int i = 0; i < iterations; ++i) { //Grab the current 1024 chunk of audio data System.Array.Copy(processedSamples, i * spectrumSampleSize, sampleChunk, 0, spectrumSampleSize); //Apply FFT double[] windowCoEf = DSP.Window.Coefficients(DSP.Window.Type.Hanning, (uint)spectrumSampleSize); double[] scaledSampleChunk = DSP.Math.Multiply(sampleChunk, windowCoEf); double scaleFactor = DSP.Window.ScaleFactor.Signal(windowCoEf); //Perform FFT and convert output to magnitude Complex[] fftSpectrum = fft.Execute(scaledSampleChunk); double[] scaledFFTSpectrum = DSP.ConvertComplex.ToMagnitude(fftSpectrum); scaledFFTSpectrum = DSP.Math.Multiply(scaledFFTSpectrum, scaleFactor); //Convert the chunk values to a single point within audio timeline float currSongtime = GetSongtime(i) * spectrumSampleSize; //Analyze multitude data using SpectrumAnalyzer to detect peaks spectrumAnalyzer.AnalyzeSpectrum(System.Array.ConvertAll(scaledFFTSpectrum, x => (float)x), currSongtime); } //Debug.Log("Spectrum Analysis completed, now getting all Peak data samples."); for (float i = 0f; i <= clipLength; i += 0.0167f) { //Get the index from the audio time int index = GetIndex(i) / spectrumAnalyzer.numSamples; //Only run if index returned is within range of the spectrum analyzer sample-list if (index < spectrumAnalyzer.spectralFluxSamples.Count) { SpectralFluxInfo info = spectrumAnalyzer.spectralFluxSamples[index]; //Make sure sample is a peak if (info.isPeak) { peakInfo.Add(info); } } } //Debug.Log("Spectrum Analysis and Peak Sampling done. Thread completed."); peakInfoSize = peakInfo.Count; isDone = true; } catch (System.Exception ex) { //Error logging Debug.LogError(ex.ToString()); } }