Reading audio output is stopped when audio visualizers are not in used.
authorStephaneLenclud
Sun, 08 Jan 2017 12:19:35 +0100
changeset 2782481c46d1f93
parent 277 71ba0dd622a5
child 279 10f0de70b69b
Reading audio output is stopped when audio visualizers are not in used.
Server/AudioManager.cs
Server/FormMain.cs
Server/MarqueeLabel.cs
Server/Spectrum/SpectrumBase.cs
     1.1 --- a/Server/AudioManager.cs	Sat Jan 07 20:21:42 2017 +0100
     1.2 +++ b/Server/AudioManager.cs	Sun Jan 08 12:19:35 2017 +0100
     1.3 @@ -32,13 +32,47 @@
     1.4          private WasapiCapture iSoundIn;
     1.5          private IWaveSource iWaveSource;
     1.6          private LineSpectrum iLineSpectrum;
     1.7 -
     1.8 +        private int iVisualizerCount = 0;
     1.9  
    1.10          public LineSpectrum Spectrum { get { return iLineSpectrum; } }
    1.11          public AudioEndpointVolume Volume { get { return iAudioEndpointVolume; } }
    1.12          public MMDevice DefaultDevice { get { return iMultiMediaDevice; } }
    1.13  
    1.14          /// <summary>
    1.15 +        /// Increment our visualizer count
    1.16 +        /// </summary>
    1.17 +        public void AddVisualizer()
    1.18 +        {
    1.19 +            if (iVisualizerCount == 0)
    1.20 +            {
    1.21 +                // If we need at least one visualizer then we need to start our engine.
    1.22 +                StartAudioVisualization();
    1.23 +            }
    1.24 +
    1.25 +            //TODO: Check bounds?
    1.26 +            iVisualizerCount++;
    1.27 +        }
    1.28 +
    1.29 +        /// <summary>
    1.30 +        /// Decrement our visualizer counter.
    1.31 +        /// </summary>
    1.32 +        public void RemoveVisualizer()
    1.33 +        {
    1.34 +            if (iVisualizerCount == 1)
    1.35 +            {
    1.36 +                // When reaching zero visualization is not need and we stop our engine
    1.37 +                StopAudioVisualization();
    1.38 +                iVisualizerCount = 0;
    1.39 +            }
    1.40 +            // Defensive: Make sure we don't go below zero
    1.41 +            else if (iVisualizerCount>0)
    1.42 +            {
    1.43 +                iVisualizerCount--;
    1.44 +            }
    1.45 +        }
    1.46 +
    1.47 +
    1.48 +        /// <summary>
    1.49          /// 
    1.50          /// </summary>
    1.51          /// <param name="aDefaultDeviceChangedHandler"></param>
    1.52 @@ -57,7 +91,11 @@
    1.53              iAudioEndpointVolumeCallback.NotifyRecived += iVolumeChangedHandler = aVolumeChangedHandler;
    1.54              iAudioEndpointVolume.RegisterControlChangeNotify(iAudioEndpointVolumeCallback);
    1.55  
    1.56 -            StartAudioVisualization();
    1.57 +            if (iVisualizerCount > 0)
    1.58 +            {
    1.59 +                // We probably got restarted, make sure visualization is running if needed
    1.60 +                StartAudioVisualization();
    1.61 +            }
    1.62          }
    1.63  
    1.64          /// <summary>
    1.65 @@ -130,11 +168,11 @@
    1.66              iLineSpectrum = new LineSpectrum(fftSize)
    1.67              {
    1.68                  SpectrumProvider = spectrumProvider,
    1.69 -                UseAverage = false,
    1.70 +                UseAverage = false, // Does not matter since we hacked it
    1.71                  BarCount = 16,
    1.72                  BarSpacing = 1,
    1.73 -                IsXLogScale = true,
    1.74 -                ScalingStrategy = ScalingStrategy.Decibel
    1.75 +                IsXLogScale = true, // Does not matter since we hacked it
    1.76 +                ScalingStrategy = ScalingStrategy.Decibel // Does not matter since we hacked it
    1.77              };
    1.78  
    1.79  
    1.80 @@ -164,7 +202,12 @@
    1.81          /// </summary>
    1.82          private void StopAudioVisualization()
    1.83          {
    1.84 -            if (iWaveSource != null)
    1.85 +            if (iSoundIn != null)
    1.86 +            {
    1.87 +                iSoundIn.Stop();
    1.88 +            }
    1.89 +
    1.90 +                if (iWaveSource != null)
    1.91              {
    1.92                  iWaveSource.Dispose();
    1.93                  iWaveSource = null;
    1.94 @@ -172,11 +215,11 @@
    1.95  
    1.96              if (iSoundIn != null)
    1.97              {
    1.98 -                iSoundIn.Stop();
    1.99                  iSoundIn.Dispose();
   1.100                  iSoundIn = null;
   1.101              }
   1.102  
   1.103 +            iLineSpectrum = null;
   1.104          }
   1.105  
   1.106  
     2.1 --- a/Server/FormMain.cs	Sat Jan 07 20:21:42 2017 +0100
     2.2 +++ b/Server/FormMain.cs	Sun Jan 08 12:19:35 2017 +0100
     2.3 @@ -256,6 +256,7 @@
     2.4  #if !DEBUG
     2.5      //When not debugging we want the screen to be empty until a client takes over
     2.6  			ClearLayout();
     2.7 +            iCurrentClientData = null;
     2.8  #else
     2.9              //When developing we want at least one client for testing
    2.10              StartNewClient("abcdefghijklmnopqrst-0123456789", "ABCDEFGHIJKLMNOPQRST-0123456789");
    2.11 @@ -656,7 +657,7 @@
    2.12              }
    2.13  
    2.14              // Update our math
    2.15 -            if (iAudioManager==null || !iAudioManager.Spectrum.Update())
    2.16 +            if (iAudioManager==null || iAudioManager.Spectrum==null || !iAudioManager.Spectrum.Update())
    2.17              {
    2.18                  //Nothing changed no need to render
    2.19                  return;
    2.20 @@ -1560,6 +1561,7 @@
    2.21              if (iClients.Count == 0)
    2.22              {
    2.23                  ClearLayout();
    2.24 +                iCurrentClientData = null;
    2.25              }
    2.26          }
    2.27  
    2.28 @@ -1568,10 +1570,22 @@
    2.29          /// </summary>
    2.30          private void ClearLayout()
    2.31          {
    2.32 +            // For each loop did not work as calling Dispose on a control removes it from the collection.
    2.33 +            // We make sure every control are disposed of notably to turn off visualizer when no more needed.
    2.34 +            // That's the only way we found to make sure Control.Disposed is called in a timely fashion.
    2.35 +            // Though that loop is admetitly dangerous as if one of the control does not removes itself from the list we end up with infinite loop.
    2.36 +            // That's what happened with our MarqueeLabel until we fixed it's Dispose override.
    2.37 +            while (iTableLayoutPanel.Controls.Count>0)
    2.38 +            {
    2.39 +                // Dispose our last item
    2.40 +                iTableLayoutPanel.Controls[iTableLayoutPanel.Controls.Count-1].Dispose();
    2.41 +            }
    2.42 +
    2.43              iTableLayoutPanel.Controls.Clear();
    2.44              iTableLayoutPanel.RowStyles.Clear();
    2.45              iTableLayoutPanel.ColumnStyles.Clear();
    2.46 -            iCurrentClientData = null;
    2.47 +            iTableLayoutPanel.RowCount = 0;
    2.48 +            iTableLayoutPanel.ColumnCount = 0;            
    2.49          }
    2.50  
    2.51          /// <summary>
    2.52 @@ -1751,6 +1765,7 @@
    2.53                  {
    2.54                      //Clear our screen when last client disconnects
    2.55                      ClearLayout();
    2.56 +                    iCurrentClientData = null;
    2.57  
    2.58                      if (iClosing)
    2.59                      {
    2.60 @@ -2208,11 +2223,7 @@
    2.61              TableLayout layout = aClient.Layout;
    2.62  
    2.63              //First clean our current panel
    2.64 -            iTableLayoutPanel.Controls.Clear();
    2.65 -            iTableLayoutPanel.RowStyles.Clear();
    2.66 -            iTableLayoutPanel.ColumnStyles.Clear();
    2.67 -            iTableLayoutPanel.RowCount = 0;
    2.68 -            iTableLayoutPanel.ColumnCount = 0;
    2.69 +            ClearLayout();
    2.70  
    2.71              //Then recreate our rows...
    2.72              while (iTableLayoutPanel.RowCount < layout.Rows.Count)
    2.73 @@ -2331,6 +2342,21 @@
    2.74                  picture.Location = new System.Drawing.Point(1, 1);
    2.75                  picture.Margin = new System.Windows.Forms.Padding(0);
    2.76                  picture.Name = "pictureBox" + aField;
    2.77 +
    2.78 +                // Make sure visualization is running
    2.79 +                iAudioManager.AddVisualizer();
    2.80 +
    2.81 +                // Notify audio manager when we don't need audio visualizer anymore
    2.82 +                picture.Disposed += (sender, e) =>
    2.83 +                {
    2.84 +                    if (iAudioManager != null)
    2.85 +                    {
    2.86 +                        // Make sure we stop visualization when not needed
    2.87 +                        iAudioManager.RemoveVisualizer();
    2.88 +                    }
    2.89 +                };
    2.90 +
    2.91 +                // Create a new bitmap when control size changes
    2.92                  picture.SizeChanged += (sender, e) =>
    2.93                  {
    2.94                      // Somehow bitmap created when our from is invisible are not working
     3.1 --- a/Server/MarqueeLabel.cs	Sat Jan 07 20:21:42 2017 +0100
     3.2 +++ b/Server/MarqueeLabel.cs	Sun Jan 08 12:19:35 2017 +0100
     3.3 @@ -411,12 +411,23 @@
     3.4              return false;
     3.5          }
     3.6  
     3.7 +        /// <summary>
     3.8 +        /// See Dispose Pattern reference:
     3.9 +        /// https://msdn.microsoft.com/en-us/library/b1yfkh5e%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
    3.10 +        /// </summary>
    3.11 +        /// <param name="disposing"></param>
    3.12          protected override void Dispose(bool disposing)
    3.13          {
    3.14 +            // My sure we hook the rest of the framework.
    3.15 +            // That's most important for controls since they do things like removing themselves from containers upon disposal.
    3.16 +            base.Dispose(disposing);
    3.17 +            // My understanding is that one is not supose to Dispose other objects unless disposing is true.
    3.18              if (disposing)
    3.19              {
    3.20                  if (Timer != null)
    3.21 +                {
    3.22                      Timer.Dispose();
    3.23 +                }
    3.24              }
    3.25              Timer = null;
    3.26          }
     4.1 --- a/Server/Spectrum/SpectrumBase.cs	Sat Jan 07 20:21:42 2017 +0100
     4.2 +++ b/Server/Spectrum/SpectrumBase.cs	Sun Jan 08 12:19:35 2017 +0100
     4.3 @@ -162,10 +162,10 @@
     4.4          {
     4.5              var dataPoints = new List<SpectrumPointData>();
     4.6  
     4.7 -            double value0 = 0, value = 0;
     4.8 -            double lastValue = 0;
     4.9 -            double actualMaxValue = maxValue;
    4.10 -            int spectrumPointIndex = 0;
    4.11 +            //double value0 = 0, value = 0;
    4.12 +            //double lastValue = 0;
    4.13 +            //double actualMaxValue = maxValue;
    4.14 +            //int spectrumPointIndex = 0;
    4.15  
    4.16              int b0 = _minimumFrequencyIndex;
    4.17              int x, y;