1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/Server/Spectrum/SpectrumBase.cs Mon Jan 02 18:43:45 2017 +0100
1.3 @@ -0,0 +1,228 @@
1.4 +using System;
1.5 +using System.Collections.Generic;
1.6 +using System.ComponentModel;
1.7 +using System.Diagnostics;
1.8 +using CSCore;
1.9 +using CSCore.DSP;
1.10 +
1.11 +namespace Visualization
1.12 +{
1.13 + public class SpectrumBase : INotifyPropertyChanged
1.14 + {
1.15 + private const int ScaleFactorLinear = 9;
1.16 + protected const int ScaleFactorSqr = 2;
1.17 + protected const double MinDbValue = -90;
1.18 + protected const double MaxDbValue = 0;
1.19 + protected const double DbScale = (MaxDbValue - MinDbValue);
1.20 +
1.21 + private int _fftSize;
1.22 + private bool _isXLogScale;
1.23 + private int _maxFftIndex;
1.24 + private int _maximumFrequency = 20000;
1.25 + private int _maximumFrequencyIndex;
1.26 + private int _minimumFrequency = 20; //Default spectrum from 20Hz to 20kHz
1.27 + private int _minimumFrequencyIndex;
1.28 + private ScalingStrategy _scalingStrategy;
1.29 + private int[] _spectrumIndexMax;
1.30 + private int[] _spectrumLogScaleIndexMax;
1.31 + private ISpectrumProvider _spectrumProvider;
1.32 +
1.33 + protected int SpectrumResolution;
1.34 + private bool _useAverage;
1.35 +
1.36 + public int MaximumFrequency
1.37 + {
1.38 + get { return _maximumFrequency; }
1.39 + set
1.40 + {
1.41 + if (value <= MinimumFrequency)
1.42 + {
1.43 + throw new ArgumentOutOfRangeException("value",
1.44 + "Value must not be less or equal the MinimumFrequency.");
1.45 + }
1.46 + _maximumFrequency = value;
1.47 + UpdateFrequencyMapping();
1.48 +
1.49 + RaisePropertyChanged("MaximumFrequency");
1.50 + }
1.51 + }
1.52 +
1.53 + public int MinimumFrequency
1.54 + {
1.55 + get { return _minimumFrequency; }
1.56 + set
1.57 + {
1.58 + if (value < 0)
1.59 + throw new ArgumentOutOfRangeException("value");
1.60 + _minimumFrequency = value;
1.61 + UpdateFrequencyMapping();
1.62 +
1.63 + RaisePropertyChanged("MinimumFrequency");
1.64 + }
1.65 + }
1.66 +
1.67 + [BrowsableAttribute(false)]
1.68 + public ISpectrumProvider SpectrumProvider
1.69 + {
1.70 + get { return _spectrumProvider; }
1.71 + set
1.72 + {
1.73 + if (value == null)
1.74 + throw new ArgumentNullException("value");
1.75 + _spectrumProvider = value;
1.76 +
1.77 + RaisePropertyChanged("SpectrumProvider");
1.78 + }
1.79 + }
1.80 +
1.81 + public bool IsXLogScale
1.82 + {
1.83 + get { return _isXLogScale; }
1.84 + set
1.85 + {
1.86 + _isXLogScale = value;
1.87 + UpdateFrequencyMapping();
1.88 + RaisePropertyChanged("IsXLogScale");
1.89 + }
1.90 + }
1.91 +
1.92 + public ScalingStrategy ScalingStrategy
1.93 + {
1.94 + get { return _scalingStrategy; }
1.95 + set
1.96 + {
1.97 + _scalingStrategy = value;
1.98 + RaisePropertyChanged("ScalingStrategy");
1.99 + }
1.100 + }
1.101 +
1.102 + public bool UseAverage
1.103 + {
1.104 + get { return _useAverage; }
1.105 + set
1.106 + {
1.107 + _useAverage = value;
1.108 + RaisePropertyChanged("UseAverage");
1.109 + }
1.110 + }
1.111 +
1.112 + [BrowsableAttribute(false)]
1.113 + public FftSize FftSize
1.114 + {
1.115 + get { return (FftSize) _fftSize; }
1.116 + protected set
1.117 + {
1.118 + if ((int) Math.Log((int) value, 2) % 1 != 0)
1.119 + throw new ArgumentOutOfRangeException("value");
1.120 +
1.121 + _fftSize = (int) value;
1.122 + _maxFftIndex = _fftSize / 2 - 1;
1.123 +
1.124 + RaisePropertyChanged("FFTSize");
1.125 + }
1.126 + }
1.127 +
1.128 + public event PropertyChangedEventHandler PropertyChanged;
1.129 +
1.130 + protected virtual void UpdateFrequencyMapping()
1.131 + {
1.132 + _maximumFrequencyIndex = Math.Min(_spectrumProvider.GetFftBandIndex(MaximumFrequency) + 1, _maxFftIndex);
1.133 + _minimumFrequencyIndex = Math.Min(_spectrumProvider.GetFftBandIndex(MinimumFrequency), _maxFftIndex);
1.134 +
1.135 + int actualResolution = SpectrumResolution;
1.136 +
1.137 + int indexCount = _maximumFrequencyIndex - _minimumFrequencyIndex;
1.138 + double linearIndexBucketSize = Math.Round(indexCount / (double) actualResolution, 3);
1.139 +
1.140 + _spectrumIndexMax = _spectrumIndexMax.CheckBuffer(actualResolution, true);
1.141 + _spectrumLogScaleIndexMax = _spectrumLogScaleIndexMax.CheckBuffer(actualResolution, true);
1.142 +
1.143 + double maxLog = Math.Log(actualResolution, actualResolution);
1.144 + for (int i = 1; i < actualResolution; i++)
1.145 + {
1.146 + int logIndex =
1.147 + (int) ((maxLog - Math.Log((actualResolution + 1) - i, (actualResolution + 1))) * indexCount) +
1.148 + _minimumFrequencyIndex;
1.149 +
1.150 + _spectrumIndexMax[i - 1] = _minimumFrequencyIndex + (int) (i * linearIndexBucketSize);
1.151 + _spectrumLogScaleIndexMax[i - 1] = logIndex;
1.152 + }
1.153 +
1.154 + if (actualResolution > 0)
1.155 + {
1.156 + _spectrumIndexMax[_spectrumIndexMax.Length - 1] =
1.157 + _spectrumLogScaleIndexMax[_spectrumLogScaleIndexMax.Length - 1] = _maximumFrequencyIndex;
1.158 + }
1.159 + }
1.160 +
1.161 + protected virtual SpectrumPointData[] CalculateSpectrumPoints(double maxValue, float[] fftBuffer)
1.162 + {
1.163 + var dataPoints = new List<SpectrumPointData>();
1.164 +
1.165 + double value0 = 0, value = 0;
1.166 + double lastValue = 0;
1.167 + double actualMaxValue = maxValue;
1.168 + int spectrumPointIndex = 0;
1.169 +
1.170 + for (int i = _minimumFrequencyIndex; i <= _maximumFrequencyIndex; i++)
1.171 + {
1.172 + switch (ScalingStrategy)
1.173 + {
1.174 + case ScalingStrategy.Decibel:
1.175 + value0 = (((20 * Math.Log10(fftBuffer[i])) - MinDbValue) / DbScale) * actualMaxValue;
1.176 + break;
1.177 + case ScalingStrategy.Linear:
1.178 + value0 = (fftBuffer[i] * ScaleFactorLinear) * actualMaxValue;
1.179 + break;
1.180 + case ScalingStrategy.Sqrt:
1.181 + value0 = ((Math.Sqrt(fftBuffer[i])) * ScaleFactorSqr) * actualMaxValue;
1.182 + break;
1.183 + }
1.184 +
1.185 + bool recalc = true;
1.186 +
1.187 + value = Math.Max(0, Math.Max(value0, value));
1.188 +
1.189 + while (spectrumPointIndex <= _spectrumIndexMax.Length - 1 &&
1.190 + i ==
1.191 + (IsXLogScale
1.192 + ? _spectrumLogScaleIndexMax[spectrumPointIndex]
1.193 + : _spectrumIndexMax[spectrumPointIndex]))
1.194 + {
1.195 + if (!recalc)
1.196 + value = lastValue;
1.197 +
1.198 + if (value > maxValue)
1.199 + value = maxValue;
1.200 +
1.201 + if (_useAverage && spectrumPointIndex > 0)
1.202 + value = (lastValue + value) / 2.0;
1.203 +
1.204 + dataPoints.Add(new SpectrumPointData {SpectrumPointIndex = spectrumPointIndex, Value = value});
1.205 +
1.206 + lastValue = value;
1.207 + value = 0.0;
1.208 + spectrumPointIndex++;
1.209 + recalc = false;
1.210 + }
1.211 +
1.212 + //value = 0;
1.213 + }
1.214 +
1.215 + return dataPoints.ToArray();
1.216 + }
1.217 +
1.218 + protected void RaisePropertyChanged(string propertyName)
1.219 + {
1.220 + if (PropertyChanged != null && !String.IsNullOrEmpty(propertyName))
1.221 + PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
1.222 + }
1.223 +
1.224 + [DebuggerDisplay("{Value}")]
1.225 + protected struct SpectrumPointData
1.226 + {
1.227 + public int SpectrumPointIndex;
1.228 + public double Value;
1.229 + }
1.230 + }
1.231 +}
1.232 \ No newline at end of file