StephaneLenclud@273: using System; StephaneLenclud@273: using System.Collections.Generic; StephaneLenclud@273: using System.ComponentModel; StephaneLenclud@273: using System.Diagnostics; StephaneLenclud@273: using CSCore; StephaneLenclud@273: using CSCore.DSP; StephaneLenclud@273: StephaneLenclud@273: namespace Visualization StephaneLenclud@273: { StephaneLenclud@273: public class SpectrumBase : INotifyPropertyChanged StephaneLenclud@273: { StephaneLenclud@273: private const int ScaleFactorLinear = 9; StephaneLenclud@273: protected const int ScaleFactorSqr = 2; StephaneLenclud@273: protected const double MinDbValue = -90; StephaneLenclud@273: protected const double MaxDbValue = 0; StephaneLenclud@273: protected const double DbScale = (MaxDbValue - MinDbValue); StephaneLenclud@273: StephaneLenclud@274: StephaneLenclud@274: protected float[] iFftBuffer; StephaneLenclud@274: private int _fftSize; StephaneLenclud@273: private bool _isXLogScale; StephaneLenclud@273: private int _maxFftIndex; StephaneLenclud@273: private int _maximumFrequency = 20000; StephaneLenclud@273: private int _maximumFrequencyIndex; StephaneLenclud@273: private int _minimumFrequency = 20; //Default spectrum from 20Hz to 20kHz StephaneLenclud@273: private int _minimumFrequencyIndex; StephaneLenclud@273: private ScalingStrategy _scalingStrategy; StephaneLenclud@273: private int[] _spectrumIndexMax; StephaneLenclud@273: private int[] _spectrumLogScaleIndexMax; StephaneLenclud@273: private ISpectrumProvider _spectrumProvider; StephaneLenclud@273: StephaneLenclud@273: protected int SpectrumResolution; StephaneLenclud@273: private bool _useAverage; StephaneLenclud@273: StephaneLenclud@273: public int MaximumFrequency StephaneLenclud@273: { StephaneLenclud@273: get { return _maximumFrequency; } StephaneLenclud@273: set StephaneLenclud@273: { StephaneLenclud@273: if (value <= MinimumFrequency) StephaneLenclud@273: { StephaneLenclud@273: throw new ArgumentOutOfRangeException("value", StephaneLenclud@273: "Value must not be less or equal the MinimumFrequency."); StephaneLenclud@273: } StephaneLenclud@273: _maximumFrequency = value; StephaneLenclud@273: UpdateFrequencyMapping(); StephaneLenclud@273: StephaneLenclud@273: RaisePropertyChanged("MaximumFrequency"); StephaneLenclud@273: } StephaneLenclud@273: } StephaneLenclud@273: StephaneLenclud@273: public int MinimumFrequency StephaneLenclud@273: { StephaneLenclud@273: get { return _minimumFrequency; } StephaneLenclud@273: set StephaneLenclud@273: { StephaneLenclud@273: if (value < 0) StephaneLenclud@273: throw new ArgumentOutOfRangeException("value"); StephaneLenclud@273: _minimumFrequency = value; StephaneLenclud@273: UpdateFrequencyMapping(); StephaneLenclud@273: StephaneLenclud@273: RaisePropertyChanged("MinimumFrequency"); StephaneLenclud@273: } StephaneLenclud@273: } StephaneLenclud@273: StephaneLenclud@273: [BrowsableAttribute(false)] StephaneLenclud@273: public ISpectrumProvider SpectrumProvider StephaneLenclud@273: { StephaneLenclud@273: get { return _spectrumProvider; } StephaneLenclud@273: set StephaneLenclud@273: { StephaneLenclud@273: if (value == null) StephaneLenclud@273: throw new ArgumentNullException("value"); StephaneLenclud@273: _spectrumProvider = value; StephaneLenclud@273: StephaneLenclud@273: RaisePropertyChanged("SpectrumProvider"); StephaneLenclud@273: } StephaneLenclud@273: } StephaneLenclud@273: StephaneLenclud@273: public bool IsXLogScale StephaneLenclud@273: { StephaneLenclud@273: get { return _isXLogScale; } StephaneLenclud@273: set StephaneLenclud@273: { StephaneLenclud@273: _isXLogScale = value; StephaneLenclud@273: UpdateFrequencyMapping(); StephaneLenclud@273: RaisePropertyChanged("IsXLogScale"); StephaneLenclud@273: } StephaneLenclud@273: } StephaneLenclud@273: StephaneLenclud@273: public ScalingStrategy ScalingStrategy StephaneLenclud@273: { StephaneLenclud@273: get { return _scalingStrategy; } StephaneLenclud@273: set StephaneLenclud@273: { StephaneLenclud@273: _scalingStrategy = value; StephaneLenclud@273: RaisePropertyChanged("ScalingStrategy"); StephaneLenclud@273: } StephaneLenclud@273: } StephaneLenclud@273: StephaneLenclud@273: public bool UseAverage StephaneLenclud@273: { StephaneLenclud@273: get { return _useAverage; } StephaneLenclud@273: set StephaneLenclud@273: { StephaneLenclud@273: _useAverage = value; StephaneLenclud@273: RaisePropertyChanged("UseAverage"); StephaneLenclud@273: } StephaneLenclud@273: } StephaneLenclud@273: StephaneLenclud@273: [BrowsableAttribute(false)] StephaneLenclud@273: public FftSize FftSize StephaneLenclud@273: { StephaneLenclud@273: get { return (FftSize) _fftSize; } StephaneLenclud@273: protected set StephaneLenclud@273: { StephaneLenclud@273: if ((int) Math.Log((int) value, 2) % 1 != 0) StephaneLenclud@273: throw new ArgumentOutOfRangeException("value"); StephaneLenclud@273: StephaneLenclud@273: _fftSize = (int) value; StephaneLenclud@273: _maxFftIndex = _fftSize / 2 - 1; StephaneLenclud@274: iFftBuffer = new float[_fftSize]; StephaneLenclud@273: StephaneLenclud@273: RaisePropertyChanged("FFTSize"); StephaneLenclud@273: } StephaneLenclud@273: } StephaneLenclud@273: StephaneLenclud@273: public event PropertyChangedEventHandler PropertyChanged; StephaneLenclud@273: StephaneLenclud@273: protected virtual void UpdateFrequencyMapping() StephaneLenclud@273: { StephaneLenclud@273: _maximumFrequencyIndex = Math.Min(_spectrumProvider.GetFftBandIndex(MaximumFrequency) + 1, _maxFftIndex); StephaneLenclud@273: _minimumFrequencyIndex = Math.Min(_spectrumProvider.GetFftBandIndex(MinimumFrequency), _maxFftIndex); StephaneLenclud@273: StephaneLenclud@273: int actualResolution = SpectrumResolution; StephaneLenclud@273: StephaneLenclud@273: int indexCount = _maximumFrequencyIndex - _minimumFrequencyIndex; StephaneLenclud@273: double linearIndexBucketSize = Math.Round(indexCount / (double) actualResolution, 3); StephaneLenclud@273: StephaneLenclud@273: _spectrumIndexMax = _spectrumIndexMax.CheckBuffer(actualResolution, true); StephaneLenclud@273: _spectrumLogScaleIndexMax = _spectrumLogScaleIndexMax.CheckBuffer(actualResolution, true); StephaneLenclud@273: StephaneLenclud@273: double maxLog = Math.Log(actualResolution, actualResolution); StephaneLenclud@273: for (int i = 1; i < actualResolution; i++) StephaneLenclud@273: { StephaneLenclud@273: int logIndex = StephaneLenclud@273: (int) ((maxLog - Math.Log((actualResolution + 1) - i, (actualResolution + 1))) * indexCount) + StephaneLenclud@273: _minimumFrequencyIndex; StephaneLenclud@273: StephaneLenclud@273: _spectrumIndexMax[i - 1] = _minimumFrequencyIndex + (int) (i * linearIndexBucketSize); StephaneLenclud@273: _spectrumLogScaleIndexMax[i - 1] = logIndex; StephaneLenclud@273: } StephaneLenclud@273: StephaneLenclud@273: if (actualResolution > 0) StephaneLenclud@273: { StephaneLenclud@273: _spectrumIndexMax[_spectrumIndexMax.Length - 1] = StephaneLenclud@273: _spectrumLogScaleIndexMax[_spectrumLogScaleIndexMax.Length - 1] = _maximumFrequencyIndex; StephaneLenclud@273: } StephaneLenclud@273: } StephaneLenclud@273: StephaneLenclud@273: protected virtual SpectrumPointData[] CalculateSpectrumPoints(double maxValue, float[] fftBuffer) StephaneLenclud@273: { StephaneLenclud@273: var dataPoints = new List(); StephaneLenclud@273: StephaneLenclud@273: double value0 = 0, value = 0; StephaneLenclud@273: double lastValue = 0; StephaneLenclud@273: double actualMaxValue = maxValue; StephaneLenclud@273: int spectrumPointIndex = 0; StephaneLenclud@273: StephaneLenclud@273: for (int i = _minimumFrequencyIndex; i <= _maximumFrequencyIndex; i++) StephaneLenclud@273: { StephaneLenclud@273: switch (ScalingStrategy) StephaneLenclud@273: { StephaneLenclud@273: case ScalingStrategy.Decibel: StephaneLenclud@273: value0 = (((20 * Math.Log10(fftBuffer[i])) - MinDbValue) / DbScale) * actualMaxValue; StephaneLenclud@273: break; StephaneLenclud@273: case ScalingStrategy.Linear: StephaneLenclud@273: value0 = (fftBuffer[i] * ScaleFactorLinear) * actualMaxValue; StephaneLenclud@273: break; StephaneLenclud@273: case ScalingStrategy.Sqrt: StephaneLenclud@273: value0 = ((Math.Sqrt(fftBuffer[i])) * ScaleFactorSqr) * actualMaxValue; StephaneLenclud@273: break; StephaneLenclud@273: } StephaneLenclud@273: StephaneLenclud@273: bool recalc = true; StephaneLenclud@273: StephaneLenclud@273: value = Math.Max(0, Math.Max(value0, value)); StephaneLenclud@273: StephaneLenclud@273: while (spectrumPointIndex <= _spectrumIndexMax.Length - 1 && StephaneLenclud@273: i == StephaneLenclud@273: (IsXLogScale StephaneLenclud@273: ? _spectrumLogScaleIndexMax[spectrumPointIndex] StephaneLenclud@273: : _spectrumIndexMax[spectrumPointIndex])) StephaneLenclud@273: { StephaneLenclud@273: if (!recalc) StephaneLenclud@273: value = lastValue; StephaneLenclud@273: StephaneLenclud@273: if (value > maxValue) StephaneLenclud@273: value = maxValue; StephaneLenclud@273: StephaneLenclud@273: if (_useAverage && spectrumPointIndex > 0) StephaneLenclud@273: value = (lastValue + value) / 2.0; StephaneLenclud@273: StephaneLenclud@273: dataPoints.Add(new SpectrumPointData {SpectrumPointIndex = spectrumPointIndex, Value = value}); StephaneLenclud@273: StephaneLenclud@273: lastValue = value; StephaneLenclud@273: value = 0.0; StephaneLenclud@273: spectrumPointIndex++; StephaneLenclud@273: recalc = false; StephaneLenclud@273: } StephaneLenclud@273: StephaneLenclud@273: //value = 0; StephaneLenclud@273: } StephaneLenclud@273: StephaneLenclud@273: return dataPoints.ToArray(); StephaneLenclud@273: } StephaneLenclud@273: StephaneLenclud@273: protected void RaisePropertyChanged(string propertyName) StephaneLenclud@273: { StephaneLenclud@273: if (PropertyChanged != null && !String.IsNullOrEmpty(propertyName)) StephaneLenclud@273: PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); StephaneLenclud@273: } StephaneLenclud@273: StephaneLenclud@273: [DebuggerDisplay("{Value}")] StephaneLenclud@273: protected struct SpectrumPointData StephaneLenclud@273: { StephaneLenclud@273: public int SpectrumPointIndex; StephaneLenclud@273: public double Value; StephaneLenclud@273: } StephaneLenclud@273: } StephaneLenclud@273: }