2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Diagnostics;
8 namespace Visualization
10 public class SpectrumBase : INotifyPropertyChanged
12 private const int ScaleFactorLinear = 9;
13 protected const int ScaleFactorSqr = 2;
14 protected const double MinDbValue = -90;
15 protected const double MaxDbValue = 0;
16 protected const double DbScale = (MaxDbValue - MinDbValue);
19 protected float[] iFftBuffer;
21 private bool _isXLogScale;
22 private int _maxFftIndex;
23 private int _maximumFrequency = 20000;
24 private int _maximumFrequencyIndex;
25 private int _minimumFrequency = 20; //Default spectrum from 20Hz to 20kHz
26 private int _minimumFrequencyIndex;
27 private ScalingStrategy _scalingStrategy;
28 private int[] _spectrumIndexMax;
29 private int[] _spectrumLogScaleIndexMax;
30 private ISpectrumProvider _spectrumProvider;
32 protected int SpectrumResolution;
33 private bool _useAverage;
35 public int MaximumFrequency
37 get { return _maximumFrequency; }
40 if (value <= MinimumFrequency)
42 throw new ArgumentOutOfRangeException("value",
43 "Value must not be less or equal the MinimumFrequency.");
45 _maximumFrequency = value;
46 UpdateFrequencyMapping();
48 RaisePropertyChanged("MaximumFrequency");
52 public int MinimumFrequency
54 get { return _minimumFrequency; }
58 throw new ArgumentOutOfRangeException("value");
59 _minimumFrequency = value;
60 UpdateFrequencyMapping();
62 RaisePropertyChanged("MinimumFrequency");
66 [BrowsableAttribute(false)]
67 public ISpectrumProvider SpectrumProvider
69 get { return _spectrumProvider; }
73 throw new ArgumentNullException("value");
74 _spectrumProvider = value;
76 RaisePropertyChanged("SpectrumProvider");
80 public bool IsXLogScale
82 get { return _isXLogScale; }
86 UpdateFrequencyMapping();
87 RaisePropertyChanged("IsXLogScale");
91 public ScalingStrategy ScalingStrategy
93 get { return _scalingStrategy; }
96 _scalingStrategy = value;
97 RaisePropertyChanged("ScalingStrategy");
101 public bool UseAverage
103 get { return _useAverage; }
107 RaisePropertyChanged("UseAverage");
111 [BrowsableAttribute(false)]
112 public FftSize FftSize
114 get { return (FftSize) _fftSize; }
117 if ((int) Math.Log((int) value, 2) % 1 != 0)
118 throw new ArgumentOutOfRangeException("value");
120 _fftSize = (int) value;
121 _maxFftIndex = _fftSize / 2 - 1;
122 iFftBuffer = new float[_fftSize];
124 RaisePropertyChanged("FFTSize");
128 public event PropertyChangedEventHandler PropertyChanged;
130 protected virtual void UpdateFrequencyMapping()
132 _maximumFrequencyIndex = Math.Min(_spectrumProvider.GetFftBandIndex(MaximumFrequency) + 1, _maxFftIndex);
133 _minimumFrequencyIndex = Math.Min(_spectrumProvider.GetFftBandIndex(MinimumFrequency), _maxFftIndex);
135 int actualResolution = SpectrumResolution;
137 int indexCount = _maximumFrequencyIndex - _minimumFrequencyIndex;
138 double linearIndexBucketSize = Math.Round(indexCount / (double) actualResolution, 3);
140 _spectrumIndexMax = _spectrumIndexMax.CheckBuffer(actualResolution, true);
141 _spectrumLogScaleIndexMax = _spectrumLogScaleIndexMax.CheckBuffer(actualResolution, true);
143 double maxLog = Math.Log(actualResolution, actualResolution);
144 for (int i = 1; i < actualResolution; i++)
147 (int) ((maxLog - Math.Log((actualResolution + 1) - i, (actualResolution + 1))) * indexCount) +
148 _minimumFrequencyIndex;
150 _spectrumIndexMax[i - 1] = _minimumFrequencyIndex + (int) (i * linearIndexBucketSize);
151 _spectrumLogScaleIndexMax[i - 1] = logIndex;
154 if (actualResolution > 0)
156 _spectrumIndexMax[_spectrumIndexMax.Length - 1] =
157 _spectrumLogScaleIndexMax[_spectrumLogScaleIndexMax.Length - 1] = _maximumFrequencyIndex;
161 protected virtual SpectrumPointData[] CalculateSpectrumPoints(double maxValue, float[] fftBuffer)
163 var dataPoints = new List<SpectrumPointData>();
165 //double value0 = 0, value = 0;
166 //double lastValue = 0;
167 //double actualMaxValue = maxValue;
168 //int spectrumPointIndex = 0;
170 int b0 = _minimumFrequencyIndex;
172 for (x = 0; x < SpectrumResolution; x++)
175 //SL: We should have to compute that only once for a given resolution.
176 // Try using the existing pre-computed array we have and refactor that whole mess.
177 int b1 = (int)Math.Pow(2, x * 10.0 / (SpectrumResolution - 1));
178 //int b1 = (IsXLogScale ? _spectrumLogScaleIndexMax[x] : _spectrumIndexMax[x]);
179 if (b1 > _maximumFrequencyIndex) b1 = _maximumFrequencyIndex;
180 if (b1 <= b0) b1 = b0 + 1;
181 for (; b0 < b1; b0++)
183 if (peak < fftBuffer[1 + b0]) peak = fftBuffer[1 + b0];
186 const int KHeightScaleFactor = 5; // SL: You can play with that scale factor to tune the height of the bars
187 y = (int)(Math.Sqrt(peak) * KHeightScaleFactor * maxValue);
188 if (y > maxValue) y = (int)maxValue;
191 dataPoints.Add(new SpectrumPointData { SpectrumPointIndex = x, Value = y });
192 //_spectrumdata.Add((byte)y);
193 //Console.Write("{0, 3} ", y);
197 for (int i = _minimumFrequencyIndex; i <= _maximumFrequencyIndex; i++)
199 switch (ScalingStrategy)
201 case ScalingStrategy.Decibel:
202 value0 = (((20 * Math.Log10(fftBuffer[i])) - MinDbValue) / DbScale) * actualMaxValue;
204 case ScalingStrategy.Linear:
205 value0 = (fftBuffer[i] * ScaleFactorLinear) * actualMaxValue;
207 case ScalingStrategy.Sqrt:
208 value0 = ((Math.Sqrt(fftBuffer[i])) * ScaleFactorSqr) * actualMaxValue;
214 value = Math.Max(0, Math.Max(value0, value));
216 while (spectrumPointIndex <= _spectrumIndexMax.Length - 1 &&
219 ? _spectrumLogScaleIndexMax[spectrumPointIndex]
220 : _spectrumIndexMax[spectrumPointIndex]))
225 if (value > maxValue)
228 if (_useAverage && spectrumPointIndex > 0)
229 value = (lastValue + value) / 2.0;
231 dataPoints.Add(new SpectrumPointData {SpectrumPointIndex = spectrumPointIndex, Value = value});
235 spectrumPointIndex++;
244 return dataPoints.ToArray();
247 protected void RaisePropertyChanged(string propertyName)
249 if (PropertyChanged != null && !String.IsNullOrEmpty(propertyName))
250 PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
253 [DebuggerDisplay("{Value}")]
254 protected struct SpectrumPointData
256 public int SpectrumPointIndex;