## decoded complex from ctor | ||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
## decoded complex after FFT | ||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
private static void FreqencyDataFromSignal(ComplexSignal sig, out double[] power, out double[] magnitudes, out double[] freqv) { Debug.Assert(sig.Channels == 1, "I only do mono signals."); power = Accord.Audio.Tools.GetPowerSpectrum( sig.GetChannel(0)); magnitudes = Accord.Audio.Tools.GetMagnitudeSpectrum( sig.GetChannel(0)); freqv = Accord.Audio.Tools.GetFrequencyVector( sig.Length, sig.SampleRate); }
public static FrequencyFeatures GetFrequencyFeatures(FileInfo file, int windowLen=512) { var reader = new Accord.Audio.Formats.WaveDecoder(file.FullName); Signal decoded = reader.Decode(); RaisedCosineWindow window = RaisedCosineWindow.Hamming(windowLen); // len(window) -> len(chunk) // Splits the source signal by walking each windowLen/2 samples, then creating // a windowLen sample window. Note that this will result in overlapped windows. Signal[] windowedSignals = decoded.Split(window, windowLen / 2); // You need to import Accord.Math in order to call this: ComplexSignal[] complexWindowedSigs = windowedSignals.Apply(ComplexSignal.FromSignal); // Forward to the Fourier domain complexWindowedSigs.ForwardFourierTransform(); // Loop through all windowed chunks of signal to get average values ComplexSignal complex0Signal = complexWindowedSigs[0]; var len = complex0Signal.Length / 2 + 1; // maybe because FFT info is only in n/2 + 1 bins? var meanPower = new double[len]; var meanMags = new double[len]; double[] power = {}; double[] magnitudes = {}; double[] freqv = {}; int numSignals=0; foreach (ComplexSignal sig in complexWindowedSigs) { FreqencyDataFromSignal(sig, out power, out magnitudes, out freqv); Debug.Assert(power.Length == len); Debug.Assert(magnitudes.Length == len); for (int i = 0; i < len; i++) { meanPower[i] += power[i]; meanMags[i] += magnitudes[i]; } numSignals++; } for (int i = 0; i < len; i++) { meanPower[i] /= numSignals; meanMags[i] /= numSignals; } // Sort to find the top freqs Array.Sort(meanMags, freqv); Array.Reverse(meanMags); Array.Reverse(freqv); return FrequencyFeatures.FromData(new FrequencyData(freqv, meanMags)); }
public class FrequencyFeatures { public double SpectralCentroid { get; private set; } public double Bandwidth { get; private set; } public FrequencyData Data { get; private set; } public FrequencyFeatures(double spectralCentroid, double bandwidth, FrequencyData data) { SpectralCentroid = spectralCentroid; Bandwidth = bandwidth; Data = data; } public static FrequencyFeatures FromData(FrequencyData data) { double[] meanMags = data.Magnitudes; double[] freqv = data.Frequencies; // spectral centroid - seems close to orig signal's "energy", at least for sine double spectralCentroid = meanMags.Zip(freqv, (m, f) => m*f).Sum()/ meanMags.Sum(); // bandwidth = highest - smallest freq double avgMagnitude = meanMags.Average(); // need a threshold or else we just get half the sample rate var nonzeroFreqs = Enumerable.Zip(meanMags, freqv, Tuple.Create) .Where(t => t.Item1 > avgMagnitude) .Select(t => t.Item2).ToList(); double bandwidth = nonzeroFreqs.Max() - nonzeroFreqs.Min(); return new FrequencyFeatures(spectralCentroid, bandwidth, data); } } public class FrequencyData { public double[] Frequencies { get; private set; } public double[] Magnitudes { get; private set; } public FrequencyData(double[] frequencies, double[] magnitudes) { Frequencies = frequencies; Magnitudes = magnitudes; } }
Did you do any more with this? I'm cribbing from your code to drive visual effects with amplitude. I'm most interested in what you found about how to interpret the data...
Best, R