Draft audio spectrum visualizer.
1.1 --- a/Server/FormMain.cs Mon Jan 02 15:50:50 2017 +0100
1.2 +++ b/Server/FormMain.cs Mon Jan 02 18:43:45 2017 +0100
1.3 @@ -37,8 +37,14 @@
1.4 using System.Runtime.InteropServices;
1.5 using System.Security;
1.6 //CSCore
1.7 +using CSCore;
1.8 +using CSCore.Win32;
1.9 +using CSCore.DSP;
1.10 +using CSCore.Streams;
1.11 using CSCore.CoreAudioAPI;
1.12 -
1.13 +using CSCore.SoundIn;
1.14 +// Visualization
1.15 +using Visualization;
1.16 // CEC
1.17 using CecSharp;
1.18 //Network
1.19 @@ -49,7 +55,7 @@
1.20 using MiniDisplayInterop;
1.21 using SharpLib.Display;
1.22 using Ear = SharpLib.Ear;
1.23 -using CSCore.Win32;
1.24 +
1.25
1.26 namespace SharpDisplayManager
1.27 {
1.28 @@ -105,9 +111,15 @@
1.29 //Function pointer for pixel Y coordinate intercept
1.30 CoordinateTranslationDelegate iScreenY;
1.31 //CSCore
1.32 + // Volume management
1.33 private MMDeviceEnumerator iMultiMediaDeviceEnumerator;
1.34 private MMDevice iMultiMediaDevice;
1.35 private AudioEndpointVolume iAudioEndpointVolume;
1.36 + // Audio visualization
1.37 + private WasapiCapture iSoundIn;
1.38 + private IWaveSource iWaveSource;
1.39 + private LineSpectrum iLineSpectrum;
1.40 +
1.41 //Network
1.42 private NetworkManager iNetworkManager;
1.43
1.44 @@ -641,6 +653,106 @@
1.45 /// <summary>
1.46 ///
1.47 /// </summary>
1.48 + private void StartAudioVisualization()
1.49 + {
1.50 + StopAudioVisualization();
1.51 + //Open the default device
1.52 + iSoundIn = new WasapiLoopbackCapture();
1.53 + //Our loopback capture opens the default render device by default so the following is not needed
1.54 + //iSoundIn.Device = MMDeviceEnumerator.DefaultAudioEndpoint(DataFlow.Render, Role.Console);
1.55 + iSoundIn.Initialize();
1.56 +
1.57 + SoundInSource soundInSource = new SoundInSource(iSoundIn);
1.58 + ISampleSource source = soundInSource.ToSampleSource();
1.59 +
1.60 + const FftSize fftSize = FftSize.Fft4096;
1.61 + //create a spectrum provider which provides fft data based on some input
1.62 + BasicSpectrumProvider spectrumProvider = new BasicSpectrumProvider(source.WaveFormat.Channels, source.WaveFormat.SampleRate, fftSize);
1.63 +
1.64 + //linespectrum and voiceprint3dspectrum used for rendering some fft data
1.65 + //in oder to get some fft data, set the previously created spectrumprovider
1.66 + iLineSpectrum = new LineSpectrum(fftSize)
1.67 + {
1.68 + SpectrumProvider = spectrumProvider,
1.69 + UseAverage = true,
1.70 + BarCount = 32,
1.71 + BarSpacing = 0,
1.72 + IsXLogScale = true,
1.73 + ScalingStrategy = ScalingStrategy.Sqrt
1.74 + };
1.75 +
1.76 +
1.77 + //the SingleBlockNotificationStream is used to intercept the played samples
1.78 + var notificationSource = new SingleBlockNotificationStream(source);
1.79 + //pass the intercepted samples as input data to the spectrumprovider (which will calculate a fft based on them)
1.80 + notificationSource.SingleBlockRead += (s, a) => spectrumProvider.Add(a.Left, a.Right);
1.81 +
1.82 + iWaveSource = notificationSource.ToWaveSource(16);
1.83 +
1.84 +
1.85 + // We need to read from our source otherwise SingleBlockRead is never called and our spectrum provider is not populated
1.86 + byte[] buffer = new byte[iWaveSource.WaveFormat.BytesPerSecond / 2];
1.87 + soundInSource.DataAvailable += (s, aEvent) =>
1.88 + {
1.89 + int read;
1.90 + while ((read = iWaveSource.Read(buffer, 0, buffer.Length)) > 0) ;
1.91 + };
1.92 +
1.93 +
1.94 + //Start recording
1.95 + iSoundIn.Start();
1.96 + }
1.97 +
1.98 + /// <summary>
1.99 + ///
1.100 + /// </summary>
1.101 + private void StopAudioVisualization()
1.102 + {
1.103 +
1.104 + if (iSoundIn != null)
1.105 + {
1.106 + iSoundIn.Stop();
1.107 + iSoundIn.Dispose();
1.108 + iSoundIn = null;
1.109 + }
1.110 + if (iWaveSource != null)
1.111 + {
1.112 + iWaveSource.Dispose();
1.113 + iWaveSource = null;
1.114 + }
1.115 +
1.116 + }
1.117 +
1.118 +
1.119 + /// <summary>
1.120 + ///
1.121 + /// </summary>
1.122 + private void GenerateAudioVisualization()
1.123 + {
1.124 + // For demo draft purposes just fetch the firt picture box control and update it with current audio spectrum
1.125 + foreach (Control ctrl in iTableLayoutPanel.Controls)
1.126 + {
1.127 + if (ctrl is PictureBox)
1.128 + {
1.129 + PictureBox pb = (PictureBox)ctrl;
1.130 + Image image = pb.Image;
1.131 + var newImage = iLineSpectrum.CreateSpectrumLine(pb.Size, Color.Black, Color.Black, Color.White, false);
1.132 + if (newImage != null)
1.133 + {
1.134 + pb.Image = newImage;
1.135 + if (image != null)
1.136 + image.Dispose();
1.137 + }
1.138 +
1.139 + break;
1.140 + }
1.141 + }
1.142 + }
1.143 +
1.144 +
1.145 + /// <summary>
1.146 + ///
1.147 + /// </summary>
1.148 private void UpdateAudioDeviceAndMasterVolumeThreadSafe()
1.149 {
1.150 if (this.InvokeRequired)
1.151 @@ -657,6 +769,7 @@
1.152 //Get our master volume
1.153 iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
1.154 iAudioEndpointVolume = AudioEndpointVolume.FromDevice(iMultiMediaDevice);
1.155 +
1.156 //Update our label
1.157 labelDefaultAudioDevice.Text = iMultiMediaDevice.FriendlyName;
1.158
1.159 @@ -667,7 +780,9 @@
1.160 AudioEndpointVolumeCallback callback = new AudioEndpointVolumeCallback();
1.161 callback.NotifyRecived += OnVolumeNotificationThreadSafe;
1.162 // Do we need to unregister?
1.163 - iAudioEndpointVolume.RegisterControlChangeNotify(callback);
1.164 + iAudioEndpointVolume.RegisterControlChangeNotify(callback);
1.165 + //
1.166 + StartAudioVisualization();
1.167 //
1.168 trackBarMasterVolume.Enabled = true;
1.169 }
1.170 @@ -1105,8 +1220,10 @@
1.171 }
1.172 }
1.173
1.174 + GenerateAudioVisualization();
1.175 +
1.176 //Compute instant FPS
1.177 - toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " +
1.178 + toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " +
1.179 (1000/iTimerDisplay.Interval).ToString() + " FPS";
1.180
1.181 LastTickTime = NewTickTime;
1.182 @@ -1441,6 +1558,8 @@
1.183
1.184 private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
1.185 {
1.186 + //TODO: discard other CSCore audio objects
1.187 + StopAudioVisualization();
1.188 iCecManager.Stop();
1.189 iNetworkManager.Dispose();
1.190 CloseDisplayConnection();
2.1 --- a/Server/SharpDisplayManager.csproj Mon Jan 02 15:50:50 2017 +0100
2.2 +++ b/Server/SharpDisplayManager.csproj Mon Jan 02 18:43:45 2017 +0100
2.3 @@ -174,11 +174,15 @@
2.4 <Compile Include="Actions\ActionCecUserControlReleased.cs" />
2.5 <Compile Include="Actions\ActionDisplayMessage.cs" />
2.6 <Compile Include="Actions\ActionHarmonyCommand.cs" />
2.7 + <Compile Include="Spectrum\BasicSpectrumProvider.cs" />
2.8 <Compile Include="CbtHook.cs" />
2.9 <Compile Include="CecClient.cs" />
2.10 <Compile Include="ConsumerElectronicControl.cs" />
2.11 <Compile Include="ClientData.cs" />
2.12 <Compile Include="EarManager.cs" />
2.13 + <Compile Include="Spectrum\ISpectrumProvider.cs" />
2.14 + <Compile Include="Spectrum\LineSpectrum.cs" />
2.15 + <Compile Include="Spectrum\ScalingStrategy.cs" />
2.16 <Compile Include="Secure.cs" />
2.17 <Compile Include="Events\EventHid.cs" />
2.18 <Compile Include="FormEditObject.cs">
2.19 @@ -223,6 +227,7 @@
2.20 <Compile Include="RichTextBoxTraceListener.cs" />
2.21 <Compile Include="Session.cs" />
2.22 <Compile Include="Settings.cs" />
2.23 + <Compile Include="Spectrum\SpectrumBase.cs" />
2.24 <Compile Include="StartupManager.cs" />
2.25 <Compile Include="TaskScheduler.cs" />
2.26 <Compile Include="Win32API.cs" />
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/Server/Spectrum/BasicSpectrumProvider.cs Mon Jan 02 18:43:45 2017 +0100
3.3 @@ -0,0 +1,54 @@
3.4 +using System;
3.5 +using System.Collections.Generic;
3.6 +using CSCore.DSP;
3.7 +
3.8 +namespace Visualization
3.9 +{
3.10 + /// <summary>
3.11 + /// BasicSpectrumProvider
3.12 + /// </summary>
3.13 + public class BasicSpectrumProvider : FftProvider, ISpectrumProvider
3.14 + {
3.15 + private readonly int _sampleRate;
3.16 + private readonly List<object> _contexts = new List<object>();
3.17 +
3.18 + public BasicSpectrumProvider(int channels, int sampleRate, FftSize fftSize)
3.19 + : base(channels, fftSize)
3.20 + {
3.21 + if (sampleRate <= 0)
3.22 + throw new ArgumentOutOfRangeException("sampleRate");
3.23 + _sampleRate = sampleRate;
3.24 + }
3.25 +
3.26 + public int GetFftBandIndex(float frequency)
3.27 + {
3.28 + int fftSize = (int)FftSize;
3.29 + double f = _sampleRate / 2.0;
3.30 + // ReSharper disable once PossibleLossOfFraction
3.31 + return (int)((frequency / f) * (fftSize / 2));
3.32 + }
3.33 +
3.34 + public bool GetFftData(float[] fftResultBuffer, object context)
3.35 + {
3.36 + if (_contexts.Contains(context))
3.37 + return false;
3.38 +
3.39 + _contexts.Add(context);
3.40 + GetFftData(fftResultBuffer);
3.41 + return true;
3.42 + }
3.43 +
3.44 + public override void Add(float[] samples, int count)
3.45 + {
3.46 + base.Add(samples, count);
3.47 + if (count > 0)
3.48 + _contexts.Clear();
3.49 + }
3.50 +
3.51 + public override void Add(float left, float right)
3.52 + {
3.53 + base.Add(left, right);
3.54 + _contexts.Clear();
3.55 + }
3.56 + }
3.57 +}
3.58 \ No newline at end of file
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/Server/Spectrum/ISpectrumProvider.cs Mon Jan 02 18:43:45 2017 +0100
4.3 @@ -0,0 +1,8 @@
4.4 +namespace Visualization
4.5 +{
4.6 + public interface ISpectrumProvider
4.7 + {
4.8 + bool GetFftData(float[] fftBuffer, object context);
4.9 + int GetFftBandIndex(float frequency);
4.10 + }
4.11 +}
4.12 \ No newline at end of file
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/Server/Spectrum/LineSpectrum.cs Mon Jan 02 18:43:45 2017 +0100
5.3 @@ -0,0 +1,166 @@
5.4 +using System;
5.5 +using System.ComponentModel;
5.6 +using System.Drawing;
5.7 +using System.Drawing.Drawing2D;
5.8 +using System.Drawing.Text;
5.9 +using CSCore.DSP;
5.10 +
5.11 +namespace Visualization
5.12 +{
5.13 + public class LineSpectrum : SpectrumBase
5.14 + {
5.15 + private int _barCount;
5.16 + private double _barSpacing;
5.17 + private double _barWidth;
5.18 + private Size _currentSize;
5.19 +
5.20 + public LineSpectrum(FftSize fftSize)
5.21 + {
5.22 + FftSize = fftSize;
5.23 + }
5.24 +
5.25 + [Browsable(false)]
5.26 + public double BarWidth
5.27 + {
5.28 + get { return _barWidth; }
5.29 + }
5.30 +
5.31 + public double BarSpacing
5.32 + {
5.33 + get { return _barSpacing; }
5.34 + set
5.35 + {
5.36 + if (value < 0)
5.37 + throw new ArgumentOutOfRangeException("value");
5.38 + _barSpacing = value;
5.39 + UpdateFrequencyMapping();
5.40 +
5.41 + RaisePropertyChanged("BarSpacing");
5.42 + RaisePropertyChanged("BarWidth");
5.43 + }
5.44 + }
5.45 +
5.46 + public int BarCount
5.47 + {
5.48 + get { return _barCount; }
5.49 + set
5.50 + {
5.51 + if (value <= 0)
5.52 + throw new ArgumentOutOfRangeException("value");
5.53 + _barCount = value;
5.54 + SpectrumResolution = value;
5.55 + UpdateFrequencyMapping();
5.56 +
5.57 + RaisePropertyChanged("BarCount");
5.58 + RaisePropertyChanged("BarWidth");
5.59 + }
5.60 + }
5.61 +
5.62 + [BrowsableAttribute(false)]
5.63 + public Size CurrentSize
5.64 + {
5.65 + get { return _currentSize; }
5.66 + protected set
5.67 + {
5.68 + _currentSize = value;
5.69 + RaisePropertyChanged("CurrentSize");
5.70 + }
5.71 + }
5.72 +
5.73 + public Bitmap CreateSpectrumLine(Size size, Brush brush, Color background, bool highQuality)
5.74 + {
5.75 + if (!UpdateFrequencyMappingIfNessesary(size))
5.76 + return null;
5.77 +
5.78 + var fftBuffer = new float[(int)FftSize];
5.79 +
5.80 + //get the fft result from the spectrum provider
5.81 + if (SpectrumProvider.GetFftData(fftBuffer, this))
5.82 + {
5.83 + using (var pen = new Pen(brush, (float)_barWidth))
5.84 + {
5.85 + var bitmap = new Bitmap(size.Width, size.Height);
5.86 +
5.87 + using (Graphics graphics = Graphics.FromImage(bitmap))
5.88 + {
5.89 + PrepareGraphics(graphics, highQuality);
5.90 + graphics.Clear(background);
5.91 +
5.92 + CreateSpectrumLineInternal(graphics, pen, fftBuffer, size);
5.93 + }
5.94 +
5.95 + return bitmap;
5.96 + }
5.97 + }
5.98 + return null;
5.99 + }
5.100 +
5.101 + public Bitmap CreateSpectrumLine(Size size, Color color1, Color color2, Color background, bool highQuality)
5.102 + {
5.103 + if (!UpdateFrequencyMappingIfNessesary(size))
5.104 + return null;
5.105 +
5.106 + using (
5.107 + Brush brush = new LinearGradientBrush(new RectangleF(0, 0, (float)_barWidth, size.Height), color2,
5.108 + color1, LinearGradientMode.Vertical))
5.109 + {
5.110 + return CreateSpectrumLine(size, brush, background, highQuality);
5.111 + }
5.112 + }
5.113 +
5.114 + private void CreateSpectrumLineInternal(Graphics graphics, Pen pen, float[] fftBuffer, Size size)
5.115 + {
5.116 + int height = size.Height;
5.117 + //prepare the fft result for rendering
5.118 + SpectrumPointData[] spectrumPoints = CalculateSpectrumPoints(height, fftBuffer);
5.119 +
5.120 + //connect the calculated points with lines
5.121 + for (int i = 0; i < spectrumPoints.Length; i++)
5.122 + {
5.123 + SpectrumPointData p = spectrumPoints[i];
5.124 + int barIndex = p.SpectrumPointIndex;
5.125 + double xCoord = BarSpacing * (barIndex + 1) + (_barWidth * barIndex) + _barWidth / 2;
5.126 +
5.127 + var p1 = new PointF((float)xCoord, height);
5.128 + var p2 = new PointF((float)xCoord, height - (float)p.Value - 1);
5.129 +
5.130 + graphics.DrawLine(pen, p1, p2);
5.131 + }
5.132 + }
5.133 +
5.134 + protected override void UpdateFrequencyMapping()
5.135 + {
5.136 + _barWidth = Math.Max(((_currentSize.Width - (BarSpacing * (BarCount + 1))) / BarCount), 0.00001);
5.137 + base.UpdateFrequencyMapping();
5.138 + }
5.139 +
5.140 + private bool UpdateFrequencyMappingIfNessesary(Size newSize)
5.141 + {
5.142 + if (newSize != CurrentSize)
5.143 + {
5.144 + CurrentSize = newSize;
5.145 + UpdateFrequencyMapping();
5.146 + }
5.147 +
5.148 + return newSize.Width > 0 && newSize.Height > 0;
5.149 + }
5.150 +
5.151 + private void PrepareGraphics(Graphics graphics, bool highQuality)
5.152 + {
5.153 + if (highQuality)
5.154 + {
5.155 + graphics.SmoothingMode = SmoothingMode.AntiAlias;
5.156 + graphics.CompositingQuality = CompositingQuality.AssumeLinear;
5.157 + graphics.PixelOffsetMode = PixelOffsetMode.Default;
5.158 + graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
5.159 + }
5.160 + else
5.161 + {
5.162 + graphics.SmoothingMode = SmoothingMode.HighSpeed;
5.163 + graphics.CompositingQuality = CompositingQuality.HighSpeed;
5.164 + graphics.PixelOffsetMode = PixelOffsetMode.None;
5.165 + graphics.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit;
5.166 + }
5.167 + }
5.168 + }
5.169 +}
5.170 \ No newline at end of file
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/Server/Spectrum/ScalingStrategy.cs Mon Jan 02 18:43:45 2017 +0100
6.3 @@ -0,0 +1,9 @@
6.4 +namespace Visualization
6.5 +{
6.6 + public enum ScalingStrategy
6.7 + {
6.8 + Decibel,
6.9 + Linear,
6.10 + Sqrt
6.11 + }
6.12 +}
6.13 \ No newline at end of file
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/Server/Spectrum/SpectrumBase.cs Mon Jan 02 18:43:45 2017 +0100
7.3 @@ -0,0 +1,228 @@
7.4 +using System;
7.5 +using System.Collections.Generic;
7.6 +using System.ComponentModel;
7.7 +using System.Diagnostics;
7.8 +using CSCore;
7.9 +using CSCore.DSP;
7.10 +
7.11 +namespace Visualization
7.12 +{
7.13 + public class SpectrumBase : INotifyPropertyChanged
7.14 + {
7.15 + private const int ScaleFactorLinear = 9;
7.16 + protected const int ScaleFactorSqr = 2;
7.17 + protected const double MinDbValue = -90;
7.18 + protected const double MaxDbValue = 0;
7.19 + protected const double DbScale = (MaxDbValue - MinDbValue);
7.20 +
7.21 + private int _fftSize;
7.22 + private bool _isXLogScale;
7.23 + private int _maxFftIndex;
7.24 + private int _maximumFrequency = 20000;
7.25 + private int _maximumFrequencyIndex;
7.26 + private int _minimumFrequency = 20; //Default spectrum from 20Hz to 20kHz
7.27 + private int _minimumFrequencyIndex;
7.28 + private ScalingStrategy _scalingStrategy;
7.29 + private int[] _spectrumIndexMax;
7.30 + private int[] _spectrumLogScaleIndexMax;
7.31 + private ISpectrumProvider _spectrumProvider;
7.32 +
7.33 + protected int SpectrumResolution;
7.34 + private bool _useAverage;
7.35 +
7.36 + public int MaximumFrequency
7.37 + {
7.38 + get { return _maximumFrequency; }
7.39 + set
7.40 + {
7.41 + if (value <= MinimumFrequency)
7.42 + {
7.43 + throw new ArgumentOutOfRangeException("value",
7.44 + "Value must not be less or equal the MinimumFrequency.");
7.45 + }
7.46 + _maximumFrequency = value;
7.47 + UpdateFrequencyMapping();
7.48 +
7.49 + RaisePropertyChanged("MaximumFrequency");
7.50 + }
7.51 + }
7.52 +
7.53 + public int MinimumFrequency
7.54 + {
7.55 + get { return _minimumFrequency; }
7.56 + set
7.57 + {
7.58 + if (value < 0)
7.59 + throw new ArgumentOutOfRangeException("value");
7.60 + _minimumFrequency = value;
7.61 + UpdateFrequencyMapping();
7.62 +
7.63 + RaisePropertyChanged("MinimumFrequency");
7.64 + }
7.65 + }
7.66 +
7.67 + [BrowsableAttribute(false)]
7.68 + public ISpectrumProvider SpectrumProvider
7.69 + {
7.70 + get { return _spectrumProvider; }
7.71 + set
7.72 + {
7.73 + if (value == null)
7.74 + throw new ArgumentNullException("value");
7.75 + _spectrumProvider = value;
7.76 +
7.77 + RaisePropertyChanged("SpectrumProvider");
7.78 + }
7.79 + }
7.80 +
7.81 + public bool IsXLogScale
7.82 + {
7.83 + get { return _isXLogScale; }
7.84 + set
7.85 + {
7.86 + _isXLogScale = value;
7.87 + UpdateFrequencyMapping();
7.88 + RaisePropertyChanged("IsXLogScale");
7.89 + }
7.90 + }
7.91 +
7.92 + public ScalingStrategy ScalingStrategy
7.93 + {
7.94 + get { return _scalingStrategy; }
7.95 + set
7.96 + {
7.97 + _scalingStrategy = value;
7.98 + RaisePropertyChanged("ScalingStrategy");
7.99 + }
7.100 + }
7.101 +
7.102 + public bool UseAverage
7.103 + {
7.104 + get { return _useAverage; }
7.105 + set
7.106 + {
7.107 + _useAverage = value;
7.108 + RaisePropertyChanged("UseAverage");
7.109 + }
7.110 + }
7.111 +
7.112 + [BrowsableAttribute(false)]
7.113 + public FftSize FftSize
7.114 + {
7.115 + get { return (FftSize) _fftSize; }
7.116 + protected set
7.117 + {
7.118 + if ((int) Math.Log((int) value, 2) % 1 != 0)
7.119 + throw new ArgumentOutOfRangeException("value");
7.120 +
7.121 + _fftSize = (int) value;
7.122 + _maxFftIndex = _fftSize / 2 - 1;
7.123 +
7.124 + RaisePropertyChanged("FFTSize");
7.125 + }
7.126 + }
7.127 +
7.128 + public event PropertyChangedEventHandler PropertyChanged;
7.129 +
7.130 + protected virtual void UpdateFrequencyMapping()
7.131 + {
7.132 + _maximumFrequencyIndex = Math.Min(_spectrumProvider.GetFftBandIndex(MaximumFrequency) + 1, _maxFftIndex);
7.133 + _minimumFrequencyIndex = Math.Min(_spectrumProvider.GetFftBandIndex(MinimumFrequency), _maxFftIndex);
7.134 +
7.135 + int actualResolution = SpectrumResolution;
7.136 +
7.137 + int indexCount = _maximumFrequencyIndex - _minimumFrequencyIndex;
7.138 + double linearIndexBucketSize = Math.Round(indexCount / (double) actualResolution, 3);
7.139 +
7.140 + _spectrumIndexMax = _spectrumIndexMax.CheckBuffer(actualResolution, true);
7.141 + _spectrumLogScaleIndexMax = _spectrumLogScaleIndexMax.CheckBuffer(actualResolution, true);
7.142 +
7.143 + double maxLog = Math.Log(actualResolution, actualResolution);
7.144 + for (int i = 1; i < actualResolution; i++)
7.145 + {
7.146 + int logIndex =
7.147 + (int) ((maxLog - Math.Log((actualResolution + 1) - i, (actualResolution + 1))) * indexCount) +
7.148 + _minimumFrequencyIndex;
7.149 +
7.150 + _spectrumIndexMax[i - 1] = _minimumFrequencyIndex + (int) (i * linearIndexBucketSize);
7.151 + _spectrumLogScaleIndexMax[i - 1] = logIndex;
7.152 + }
7.153 +
7.154 + if (actualResolution > 0)
7.155 + {
7.156 + _spectrumIndexMax[_spectrumIndexMax.Length - 1] =
7.157 + _spectrumLogScaleIndexMax[_spectrumLogScaleIndexMax.Length - 1] = _maximumFrequencyIndex;
7.158 + }
7.159 + }
7.160 +
7.161 + protected virtual SpectrumPointData[] CalculateSpectrumPoints(double maxValue, float[] fftBuffer)
7.162 + {
7.163 + var dataPoints = new List<SpectrumPointData>();
7.164 +
7.165 + double value0 = 0, value = 0;
7.166 + double lastValue = 0;
7.167 + double actualMaxValue = maxValue;
7.168 + int spectrumPointIndex = 0;
7.169 +
7.170 + for (int i = _minimumFrequencyIndex; i <= _maximumFrequencyIndex; i++)
7.171 + {
7.172 + switch (ScalingStrategy)
7.173 + {
7.174 + case ScalingStrategy.Decibel:
7.175 + value0 = (((20 * Math.Log10(fftBuffer[i])) - MinDbValue) / DbScale) * actualMaxValue;
7.176 + break;
7.177 + case ScalingStrategy.Linear:
7.178 + value0 = (fftBuffer[i] * ScaleFactorLinear) * actualMaxValue;
7.179 + break;
7.180 + case ScalingStrategy.Sqrt:
7.181 + value0 = ((Math.Sqrt(fftBuffer[i])) * ScaleFactorSqr) * actualMaxValue;
7.182 + break;
7.183 + }
7.184 +
7.185 + bool recalc = true;
7.186 +
7.187 + value = Math.Max(0, Math.Max(value0, value));
7.188 +
7.189 + while (spectrumPointIndex <= _spectrumIndexMax.Length - 1 &&
7.190 + i ==
7.191 + (IsXLogScale
7.192 + ? _spectrumLogScaleIndexMax[spectrumPointIndex]
7.193 + : _spectrumIndexMax[spectrumPointIndex]))
7.194 + {
7.195 + if (!recalc)
7.196 + value = lastValue;
7.197 +
7.198 + if (value > maxValue)
7.199 + value = maxValue;
7.200 +
7.201 + if (_useAverage && spectrumPointIndex > 0)
7.202 + value = (lastValue + value) / 2.0;
7.203 +
7.204 + dataPoints.Add(new SpectrumPointData {SpectrumPointIndex = spectrumPointIndex, Value = value});
7.205 +
7.206 + lastValue = value;
7.207 + value = 0.0;
7.208 + spectrumPointIndex++;
7.209 + recalc = false;
7.210 + }
7.211 +
7.212 + //value = 0;
7.213 + }
7.214 +
7.215 + return dataPoints.ToArray();
7.216 + }
7.217 +
7.218 + protected void RaisePropertyChanged(string propertyName)
7.219 + {
7.220 + if (PropertyChanged != null && !String.IsNullOrEmpty(propertyName))
7.221 + PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
7.222 + }
7.223 +
7.224 + [DebuggerDisplay("{Value}")]
7.225 + protected struct SpectrumPointData
7.226 + {
7.227 + public int SpectrumPointIndex;
7.228 + public double Value;
7.229 + }
7.230 + }
7.231 +}
7.232 \ No newline at end of file