Server/Spectrum/SpectrumBase.cs
author StephaneLenclud
Mon, 02 Jan 2017 18:43:45 +0100
changeset 273 e5f85a895a62
child 274 920fea7a6427
permissions -rw-r--r--
Draft audio spectrum visualizer.
     1 using System;
     2 using System.Collections.Generic;
     3 using System.ComponentModel;
     4 using System.Diagnostics;
     5 using CSCore;
     6 using CSCore.DSP;
     7 
     8 namespace Visualization
     9 {
    10     public class SpectrumBase : INotifyPropertyChanged
    11     {
    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);
    17 
    18         private int _fftSize;
    19         private bool _isXLogScale;
    20         private int _maxFftIndex;
    21         private int _maximumFrequency = 20000;
    22         private int _maximumFrequencyIndex;
    23         private int _minimumFrequency = 20; //Default spectrum from 20Hz to 20kHz
    24         private int _minimumFrequencyIndex;
    25         private ScalingStrategy _scalingStrategy;
    26         private int[] _spectrumIndexMax;
    27         private int[] _spectrumLogScaleIndexMax;
    28         private ISpectrumProvider _spectrumProvider;
    29 
    30         protected int SpectrumResolution;
    31         private bool _useAverage;
    32 
    33         public int MaximumFrequency
    34         {
    35             get { return _maximumFrequency; }
    36             set
    37             {
    38                 if (value <= MinimumFrequency)
    39                 {
    40                     throw new ArgumentOutOfRangeException("value",
    41                         "Value must not be less or equal the MinimumFrequency.");
    42                 }
    43                 _maximumFrequency = value;
    44                 UpdateFrequencyMapping();
    45 
    46                 RaisePropertyChanged("MaximumFrequency");
    47             }
    48         }
    49 
    50         public int MinimumFrequency
    51         {
    52             get { return _minimumFrequency; }
    53             set
    54             {
    55                 if (value < 0)
    56                     throw new ArgumentOutOfRangeException("value");
    57                 _minimumFrequency = value;
    58                 UpdateFrequencyMapping();
    59 
    60                 RaisePropertyChanged("MinimumFrequency");
    61             }
    62         }
    63 
    64         [BrowsableAttribute(false)]
    65         public ISpectrumProvider SpectrumProvider
    66         {
    67             get { return _spectrumProvider; }
    68             set
    69             {
    70                 if (value == null)
    71                     throw new ArgumentNullException("value");
    72                 _spectrumProvider = value;
    73 
    74                 RaisePropertyChanged("SpectrumProvider");
    75             }
    76         }
    77 
    78         public bool IsXLogScale
    79         {
    80             get { return _isXLogScale; }
    81             set
    82             {
    83                 _isXLogScale = value;
    84                 UpdateFrequencyMapping();
    85                 RaisePropertyChanged("IsXLogScale");
    86             }
    87         }
    88 
    89         public ScalingStrategy ScalingStrategy
    90         {
    91             get { return _scalingStrategy; }
    92             set
    93             {
    94                 _scalingStrategy = value;
    95                 RaisePropertyChanged("ScalingStrategy");
    96             }
    97         }
    98 
    99         public bool UseAverage
   100         {
   101             get { return _useAverage; }
   102             set
   103             {
   104                 _useAverage = value;
   105                 RaisePropertyChanged("UseAverage");
   106             }
   107         }
   108 
   109         [BrowsableAttribute(false)]
   110         public FftSize FftSize
   111         {
   112             get { return (FftSize) _fftSize; }
   113             protected set
   114             {
   115                 if ((int) Math.Log((int) value, 2) % 1 != 0)
   116                     throw new ArgumentOutOfRangeException("value");
   117 
   118                 _fftSize = (int) value;
   119                 _maxFftIndex = _fftSize / 2 - 1;
   120 
   121                 RaisePropertyChanged("FFTSize");
   122             }
   123         }
   124 
   125         public event PropertyChangedEventHandler PropertyChanged;
   126 
   127         protected virtual void UpdateFrequencyMapping()
   128         {
   129             _maximumFrequencyIndex = Math.Min(_spectrumProvider.GetFftBandIndex(MaximumFrequency) + 1, _maxFftIndex);
   130             _minimumFrequencyIndex = Math.Min(_spectrumProvider.GetFftBandIndex(MinimumFrequency), _maxFftIndex);
   131 
   132             int actualResolution = SpectrumResolution;
   133 
   134             int indexCount = _maximumFrequencyIndex - _minimumFrequencyIndex;
   135             double linearIndexBucketSize = Math.Round(indexCount / (double) actualResolution, 3);
   136 
   137             _spectrumIndexMax = _spectrumIndexMax.CheckBuffer(actualResolution, true);
   138             _spectrumLogScaleIndexMax = _spectrumLogScaleIndexMax.CheckBuffer(actualResolution, true);
   139 
   140             double maxLog = Math.Log(actualResolution, actualResolution);
   141             for (int i = 1; i < actualResolution; i++)
   142             {
   143                 int logIndex =
   144                     (int) ((maxLog - Math.Log((actualResolution + 1) - i, (actualResolution + 1))) * indexCount) +
   145                     _minimumFrequencyIndex;
   146 
   147                 _spectrumIndexMax[i - 1] = _minimumFrequencyIndex + (int) (i * linearIndexBucketSize);
   148                 _spectrumLogScaleIndexMax[i - 1] = logIndex;
   149             }
   150 
   151             if (actualResolution > 0)
   152             {
   153                 _spectrumIndexMax[_spectrumIndexMax.Length - 1] =
   154                     _spectrumLogScaleIndexMax[_spectrumLogScaleIndexMax.Length - 1] = _maximumFrequencyIndex;
   155             }
   156         }
   157 
   158         protected virtual SpectrumPointData[] CalculateSpectrumPoints(double maxValue, float[] fftBuffer)
   159         {
   160             var dataPoints = new List<SpectrumPointData>();
   161 
   162             double value0 = 0, value = 0;
   163             double lastValue = 0;
   164             double actualMaxValue = maxValue;
   165             int spectrumPointIndex = 0;
   166 
   167             for (int i = _minimumFrequencyIndex; i <= _maximumFrequencyIndex; i++)
   168             {
   169                 switch (ScalingStrategy)
   170                 {
   171                     case ScalingStrategy.Decibel:
   172                         value0 = (((20 * Math.Log10(fftBuffer[i])) - MinDbValue) / DbScale) * actualMaxValue;
   173                         break;
   174                     case ScalingStrategy.Linear:
   175                         value0 = (fftBuffer[i] * ScaleFactorLinear) * actualMaxValue;
   176                         break;
   177                     case ScalingStrategy.Sqrt:
   178                         value0 = ((Math.Sqrt(fftBuffer[i])) * ScaleFactorSqr) * actualMaxValue;
   179                         break;
   180                 }
   181 
   182                 bool recalc = true;
   183 
   184                 value = Math.Max(0, Math.Max(value0, value));
   185 
   186                 while (spectrumPointIndex <= _spectrumIndexMax.Length - 1 &&
   187                        i ==
   188                        (IsXLogScale
   189                            ? _spectrumLogScaleIndexMax[spectrumPointIndex]
   190                            : _spectrumIndexMax[spectrumPointIndex]))
   191                 {
   192                     if (!recalc)
   193                         value = lastValue;
   194 
   195                     if (value > maxValue)
   196                         value = maxValue;
   197 
   198                     if (_useAverage && spectrumPointIndex > 0)
   199                         value = (lastValue + value) / 2.0;
   200 
   201                     dataPoints.Add(new SpectrumPointData {SpectrumPointIndex = spectrumPointIndex, Value = value});
   202 
   203                     lastValue = value;
   204                     value = 0.0;
   205                     spectrumPointIndex++;
   206                     recalc = false;
   207                 }
   208 
   209                 //value = 0;
   210             }
   211 
   212             return dataPoints.ToArray();
   213         }
   214 
   215         protected void RaisePropertyChanged(string propertyName)
   216         {
   217             if (PropertyChanged != null && !String.IsNullOrEmpty(propertyName))
   218                 PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
   219         }
   220 
   221         [DebuggerDisplay("{Value}")]
   222         protected struct SpectrumPointData
   223         {
   224             public int SpectrumPointIndex;
   225             public double Value;
   226         }
   227     }
   228 }