public static SpectralDecomp Decompose( IBGCStream stream, int windowCount = 400, int windowOrder = 12, int targetChannel = 0, double minFreq = 0.0, double maxFreq = double.PositiveInfinity) { //WindowSize is 2 ^ windowOrder int windowSize = 1 << windowOrder; if (stream.Channels <= targetChannel) { throw new ArgumentException( $"TargetChannel ({targetChannel}) exceeded stream channels ({stream.Channels})", nameof(targetChannel)); } float[] samples = stream.IsolateChannel(targetChannel).HardClip().Cache().Samples; int sampleOffset = (int)((samples.Length - windowSize) / (double)(windowCount - 1)); //Adjust windowSize to conform to sample size and requirements while (sampleOffset <= 0 && windowOrder > 4) { --windowOrder; windowSize = 1 << windowOrder; windowCount /= 2; sampleOffset = (int)((samples.Length - windowSize) / (double)(windowCount - 1)); } if (windowOrder == 4) { throw new ArgumentException("Clip too short to evaluate"); } //Limit Max Frquency by the size of the window maxFreq = Math.Min(maxFreq, FrequencyDomain.GetComplexSampleFrequency(windowSize, windowSize / 2)); //Limit Min Frequency by the minFreq = Math.Max(minFreq, FrequencyDomain.GetComplexSampleFrequency(windowSize, 1)); //Our output will be just the real-valued amplitudes SpectralDecomp decomp = new SpectralDecomp(minFreq, maxFreq, windowSize, windowCount); Complex64[] fftBuffer = new Complex64[windowSize]; IBGCEnvelopeStream windowStream = new BlackmanHarrisEnvelope(windowSize); for (int window = 0; window < windowCount; window++) { int specificOffset = sampleOffset * window; windowStream.Reset(); //Copy samples into buffer for (int i = 0; i < windowSize; i++) { //Set real value fftBuffer[i] = samples[specificOffset + i] * windowStream.ReadNextSample(); } Fourier.Forward(fftBuffer); decomp.Add(window, fftBuffer); } decomp.Rescale(); return(decomp); }
public static (double[] psd, double offset) Decompose( IBGCStream stream, int windowOrder = 12, int targetChannel = 0) { //WindowSize is 2 ^ windowOrder int windowSize = 1 << windowOrder; if (stream.Channels <= targetChannel) { throw new ArgumentException( $"TargetChannel ({targetChannel}) exceeded stream channels ({stream.Channels})", nameof(targetChannel)); } float[] samples = stream.IsolateChannel(targetChannel).HardClip().Cache().Samples; int sampleOffset = windowSize / 2; if (windowOrder == 4) { throw new ArgumentException("Clip too short to evaluate"); } int windowCount = 1 + (int)Math.Ceiling((samples.Length - windowSize) / (double)sampleOffset); if (windowCount < 1) { windowCount = 1; } //Our output will be just the real-valued amplitudes double[] spectralValues = new double[windowSize / 2]; Complex64[] fftBuffer = new Complex64[windowSize]; IBGCEnvelopeStream windowStream = new BlackmanHarrisEnvelope(windowSize); // 2 x due to negative frequencies // 0.5 x due to overlap double amplitudeAdjustant = 1.0 / (windowCount * windowSize); for (int window = 0; window < windowCount; window++) { int specificOffset = sampleOffset * window; windowStream.Reset(); //Copy samples into buffer for (int i = 0; i < windowSize; i++) { //Set real value if (specificOffset + i >= samples.Length) { fftBuffer[i] = Complex64.Zero; } else { fftBuffer[i] = samples[specificOffset + i] * windowStream.ReadNextSample(); } } Fourier.Forward(fftBuffer); for (int i = 0; i < fftBuffer.Length / 2; i++) { spectralValues[i] += amplitudeAdjustant * fftBuffer[i].MagnitudeSquared; } } double maxValue = double.MinValue; double minValue = double.MaxValue; for (int i = 0; i < spectralValues.Length; i++) { spectralValues[i] = 10.0 * Math.Log10(spectralValues[i]); if (!double.IsNaN(spectralValues[i]) && !double.IsNegativeInfinity(spectralValues[i])) { if (spectralValues[i] > maxValue) { maxValue = spectralValues[i]; } if (spectralValues[i] < minValue) { minValue = spectralValues[i]; } } } for (int i = 0; i < spectralValues.Length; i++) { if (double.IsNaN(spectralValues[i]) || double.IsNegativeInfinity(spectralValues[i])) { spectralValues[i] = minValue - maxValue; } else { spectralValues[i] -= maxValue; } } return(spectralValues, maxValue); }