Created Audio Manager class.
authorStephaneLenclud
Sat, 07 Jan 2017 20:21:42 +0100
changeset 27771ba0dd622a5
parent 276 7cd495550d5f
child 278 2481c46d1f93
Created Audio Manager class.
Clean up CScore audio usage.
Fixing broken audio device change handler.
Fixed various audio Dispose deadlock due to Invoke usage.
Thus now using BeginInvoke instead.
Server/AudioManager.cs
Server/FormMain.Designer.cs
Server/FormMain.cs
Server/SharpDisplayManager.csproj
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/Server/AudioManager.cs	Sat Jan 07 20:21:42 2017 +0100
     1.3 @@ -0,0 +1,184 @@
     1.4 +´╗┐using System;
     1.5 +using System.Collections.Generic;
     1.6 +using System.Linq;
     1.7 +using System.Text;
     1.8 +using System.Threading.Tasks;
     1.9 +
    1.10 +//CSCore
    1.11 +using CSCore;
    1.12 +using CSCore.Win32;
    1.13 +using CSCore.DSP;
    1.14 +using CSCore.Streams;
    1.15 +using CSCore.CoreAudioAPI;
    1.16 +using CSCore.SoundIn;
    1.17 +// Visualization
    1.18 +using Visualization;
    1.19 +
    1.20 +
    1.21 +namespace SharpDisplayManager
    1.22 +{
    1.23 +    class AudioManager
    1.24 +    {
    1.25 +        // Volume management
    1.26 +        private MMDeviceEnumerator iMultiMediaDeviceEnumerator;
    1.27 +        private MMNotificationClient iMultiMediaNotificationClient;
    1.28 +        private MMDevice iMultiMediaDevice;
    1.29 +        private AudioEndpointVolume iAudioEndpointVolume;
    1.30 +        private AudioEndpointVolumeCallback iAudioEndpointVolumeCallback;
    1.31 +        EventHandler<DefaultDeviceChangedEventArgs> iDefaultDeviceChangedHandler;
    1.32 +        EventHandler<AudioEndpointVolumeCallbackEventArgs> iVolumeChangedHandler;
    1.33 +
    1.34 +        // Audio visualization
    1.35 +        private WasapiCapture iSoundIn;
    1.36 +        private IWaveSource iWaveSource;
    1.37 +        private LineSpectrum iLineSpectrum;
    1.38 +
    1.39 +
    1.40 +        public LineSpectrum Spectrum { get { return iLineSpectrum; } }
    1.41 +        public AudioEndpointVolume Volume { get { return iAudioEndpointVolume; } }
    1.42 +        public MMDevice DefaultDevice { get { return iMultiMediaDevice; } }
    1.43 +
    1.44 +        /// <summary>
    1.45 +        /// 
    1.46 +        /// </summary>
    1.47 +        /// <param name="aDefaultDeviceChangedHandler"></param>
    1.48 +        /// <param name="aVolumeChangedHandler"></param>
    1.49 +        public void Open(   EventHandler<DefaultDeviceChangedEventArgs> aDefaultDeviceChangedHandler,
    1.50 +                            EventHandler<AudioEndpointVolumeCallbackEventArgs> aVolumeChangedHandler)
    1.51 +        {
    1.52 +            //Create device and register default device change notification
    1.53 +            iMultiMediaDeviceEnumerator = new MMDeviceEnumerator();
    1.54 +            iMultiMediaNotificationClient = new MMNotificationClient(iMultiMediaDeviceEnumerator);
    1.55 +            iMultiMediaNotificationClient.DefaultDeviceChanged += iDefaultDeviceChangedHandler = aDefaultDeviceChangedHandler;
    1.56 +            iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render,Role.Multimedia);
    1.57 +            //Register to get volume modifications
    1.58 +            iAudioEndpointVolume = AudioEndpointVolume.FromDevice(iMultiMediaDevice);
    1.59 +            iAudioEndpointVolumeCallback = new AudioEndpointVolumeCallback();
    1.60 +            iAudioEndpointVolumeCallback.NotifyRecived += iVolumeChangedHandler = aVolumeChangedHandler;
    1.61 +            iAudioEndpointVolume.RegisterControlChangeNotify(iAudioEndpointVolumeCallback);
    1.62 +
    1.63 +            StartAudioVisualization();
    1.64 +        }
    1.65 +
    1.66 +        /// <summary>
    1.67 +        /// 
    1.68 +        /// </summary>
    1.69 +        public void Close()
    1.70 +        {
    1.71 +            StopAudioVisualization();
    1.72 +
    1.73 +            // Client up our MM objects in reverse order
    1.74 +            if (iAudioEndpointVolumeCallback != null && iAudioEndpointVolume != null)
    1.75 +            {
    1.76 +                iAudioEndpointVolume.UnregisterControlChangeNotify(iAudioEndpointVolumeCallback);
    1.77 +            }
    1.78 +
    1.79 +            if (iAudioEndpointVolumeCallback != null)
    1.80 +            {
    1.81 +                iAudioEndpointVolumeCallback.NotifyRecived -= iVolumeChangedHandler;
    1.82 +                iAudioEndpointVolumeCallback = null;
    1.83 +            }
    1.84 +
    1.85 +            if (iAudioEndpointVolume != null)
    1.86 +            {
    1.87 +                iAudioEndpointVolume.Dispose();
    1.88 +                iAudioEndpointVolume = null;
    1.89 +            }
    1.90 +
    1.91 +            if (iMultiMediaDevice != null)
    1.92 +            {
    1.93 +                iMultiMediaDevice.Dispose();
    1.94 +                iMultiMediaDevice = null;
    1.95 +            }
    1.96 +
    1.97 +            if (iMultiMediaNotificationClient != null)
    1.98 +            {
    1.99 +                iMultiMediaNotificationClient.DefaultDeviceChanged -= iDefaultDeviceChangedHandler;
   1.100 +                iMultiMediaNotificationClient.Dispose();
   1.101 +                iMultiMediaNotificationClient = null;
   1.102 +            }
   1.103 +
   1.104 +            if (iMultiMediaDeviceEnumerator != null)
   1.105 +            {
   1.106 +                iMultiMediaDeviceEnumerator.Dispose();
   1.107 +                iMultiMediaDeviceEnumerator = null;
   1.108 +            }
   1.109 +
   1.110 +        }
   1.111 +
   1.112 +
   1.113 +        /// <summary>
   1.114 +        /// 
   1.115 +        /// </summary>
   1.116 +        private void StartAudioVisualization()
   1.117 +        {
   1.118 +            //Open the default device 
   1.119 +            iSoundIn = new WasapiLoopbackCapture();
   1.120 +            //Our loopback capture opens the default render device by default so the following is not needed
   1.121 +            //iSoundIn.Device = MMDeviceEnumerator.DefaultAudioEndpoint(DataFlow.Render, Role.Console);
   1.122 +            iSoundIn.Initialize();
   1.123 +
   1.124 +            SoundInSource soundInSource = new SoundInSource(iSoundIn);
   1.125 +            ISampleSource source = soundInSource.ToSampleSource();
   1.126 +
   1.127 +            const FftSize fftSize = FftSize.Fft2048;
   1.128 +            //create a spectrum provider which provides fft data based on some input
   1.129 +            BasicSpectrumProvider spectrumProvider = new BasicSpectrumProvider(source.WaveFormat.Channels, source.WaveFormat.SampleRate, fftSize);
   1.130 +
   1.131 +            //linespectrum and voiceprint3dspectrum used for rendering some fft data
   1.132 +            //in oder to get some fft data, set the previously created spectrumprovider 
   1.133 +            iLineSpectrum = new LineSpectrum(fftSize)
   1.134 +            {
   1.135 +                SpectrumProvider = spectrumProvider,
   1.136 +                UseAverage = false,
   1.137 +                BarCount = 16,
   1.138 +                BarSpacing = 1,
   1.139 +                IsXLogScale = true,
   1.140 +                ScalingStrategy = ScalingStrategy.Decibel
   1.141 +            };
   1.142 +
   1.143 +
   1.144 +            //the SingleBlockNotificationStream is used to intercept the played samples
   1.145 +            var notificationSource = new SingleBlockNotificationStream(source);
   1.146 +            //pass the intercepted samples as input data to the spectrumprovider (which will calculate a fft based on them)
   1.147 +            notificationSource.SingleBlockRead += (s, a) => spectrumProvider.Add(a.Left, a.Right);
   1.148 +
   1.149 +            iWaveSource = notificationSource.ToWaveSource(16);
   1.150 +
   1.151 +
   1.152 +            // We need to read from our source otherwise SingleBlockRead is never called and our spectrum provider is not populated
   1.153 +            byte[] buffer = new byte[iWaveSource.WaveFormat.BytesPerSecond / 2];
   1.154 +            soundInSource.DataAvailable += (s, aEvent) =>
   1.155 +            {
   1.156 +                int read;
   1.157 +                while ((read = iWaveSource.Read(buffer, 0, buffer.Length)) > 0) ;
   1.158 +            };
   1.159 +
   1.160 +
   1.161 +            //Start recording
   1.162 +            iSoundIn.Start();
   1.163 +        }
   1.164 +
   1.165 +        /// <summary>
   1.166 +        /// 
   1.167 +        /// </summary>
   1.168 +        private void StopAudioVisualization()
   1.169 +        {
   1.170 +            if (iWaveSource != null)
   1.171 +            {
   1.172 +                iWaveSource.Dispose();
   1.173 +                iWaveSource = null;
   1.174 +            }
   1.175 +
   1.176 +            if (iSoundIn != null)
   1.177 +            {
   1.178 +                iSoundIn.Stop();
   1.179 +                iSoundIn.Dispose();
   1.180 +                iSoundIn = null;
   1.181 +            }
   1.182 +
   1.183 +        }
   1.184 +
   1.185 +
   1.186 +    }
   1.187 +}
     2.1 --- a/Server/FormMain.Designer.cs	Fri Jan 06 18:27:19 2017 +0100
     2.2 +++ b/Server/FormMain.Designer.cs	Sat Jan 07 20:21:42 2017 +0100
     2.3 @@ -88,7 +88,7 @@
     2.4              this.checkBoxInverseColors = new System.Windows.Forms.CheckBox();
     2.5              this.checkBoxReverseScreen = new System.Windows.Forms.CheckBox();
     2.6              this.tabPageAudio = new System.Windows.Forms.TabPage();
     2.7 -            this.labelDefaultAudioDevice = new System.Windows.Forms.Label();
     2.8 +            this.iLabelDefaultAudioDevice = new System.Windows.Forms.Label();
     2.9              this.checkBoxShowVolumeLabel = new System.Windows.Forms.CheckBox();
    2.10              this.checkBoxMute = new System.Windows.Forms.CheckBox();
    2.11              this.trackBarMasterVolume = new System.Windows.Forms.TrackBar();
    2.12 @@ -708,7 +708,7 @@
    2.13              // 
    2.14              // tabPageAudio
    2.15              // 
    2.16 -            this.tabPageAudio.Controls.Add(this.labelDefaultAudioDevice);
    2.17 +            this.tabPageAudio.Controls.Add(this.iLabelDefaultAudioDevice);
    2.18              this.tabPageAudio.Controls.Add(this.checkBoxShowVolumeLabel);
    2.19              this.tabPageAudio.Controls.Add(this.checkBoxMute);
    2.20              this.tabPageAudio.Controls.Add(this.trackBarMasterVolume);
    2.21 @@ -720,15 +720,15 @@
    2.22              this.tabPageAudio.Text = "Audio";
    2.23              this.tabPageAudio.UseVisualStyleBackColor = true;
    2.24              // 
    2.25 -            // labelDefaultAudioDevice
    2.26 +            // iLabelDefaultAudioDevice
    2.27              // 
    2.28 -            this.labelDefaultAudioDevice.AutoSize = true;
    2.29 -            this.labelDefaultAudioDevice.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
    2.30 -            this.labelDefaultAudioDevice.Location = new System.Drawing.Point(3, 6);
    2.31 -            this.labelDefaultAudioDevice.Name = "labelDefaultAudioDevice";
    2.32 -            this.labelDefaultAudioDevice.Size = new System.Drawing.Size(120, 13);
    2.33 -            this.labelDefaultAudioDevice.TabIndex = 19;
    2.34 -            this.labelDefaultAudioDevice.Text = "Audio Device Unknown";
    2.35 +            this.iLabelDefaultAudioDevice.AutoSize = true;
    2.36 +            this.iLabelDefaultAudioDevice.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
    2.37 +            this.iLabelDefaultAudioDevice.Location = new System.Drawing.Point(3, 6);
    2.38 +            this.iLabelDefaultAudioDevice.Name = "iLabelDefaultAudioDevice";
    2.39 +            this.iLabelDefaultAudioDevice.Size = new System.Drawing.Size(120, 13);
    2.40 +            this.iLabelDefaultAudioDevice.TabIndex = 19;
    2.41 +            this.iLabelDefaultAudioDevice.Text = "Audio Device Unknown";
    2.42              // 
    2.43              // checkBoxShowVolumeLabel
    2.44              // 
    2.45 @@ -1361,7 +1361,7 @@
    2.46  		private System.Windows.Forms.TrackBar trackBarMasterVolume;
    2.47  		private System.Windows.Forms.CheckBox checkBoxMute;
    2.48  		private System.Windows.Forms.CheckBox checkBoxShowVolumeLabel;
    2.49 -		private System.Windows.Forms.Label labelDefaultAudioDevice;
    2.50 +		private System.Windows.Forms.Label iLabelDefaultAudioDevice;
    2.51  		private System.Windows.Forms.OpenFileDialog openFileDialog;
    2.52          private System.Windows.Forms.TabPage tabPageCec;
    2.53          private System.Windows.Forms.CheckBox iCheckBoxCecEnabled;
     3.1 --- a/Server/FormMain.cs	Fri Jan 06 18:27:19 2017 +0100
     3.2 +++ b/Server/FormMain.cs	Sat Jan 07 20:21:42 2017 +0100
     3.3 @@ -79,15 +79,13 @@
     3.4  
     3.5      public delegate void SetClientPriorityDelegate(string aSessionId, uint aPriority);
     3.6  
     3.7 -    public delegate void PlainUpdateDelegate();
     3.8 -
     3.9      public delegate void WndProcDelegate(ref Message aMessage);
    3.10  
    3.11      /// <summary>
    3.12      /// Our Display manager main form
    3.13      /// </summary>
    3.14      [System.ComponentModel.DesignerCategory("Form")]
    3.15 -    public partial class FormMain : FormMainHid, IMMNotificationClient
    3.16 +    public partial class FormMain : FormMainHid
    3.17      {
    3.18          //public Manager iManager = new Manager();        
    3.19          DateTime LastTickTime;
    3.20 @@ -111,15 +109,8 @@
    3.21          CoordinateTranslationDelegate iScreenX;
    3.22          //Function pointer for pixel Y coordinate intercept
    3.23          CoordinateTranslationDelegate iScreenY;
    3.24 -        //CSCore
    3.25 -        // Volume management
    3.26 -        private MMDeviceEnumerator iMultiMediaDeviceEnumerator;
    3.27 -        private MMDevice iMultiMediaDevice;
    3.28 -        private AudioEndpointVolume iAudioEndpointVolume;
    3.29 -        // Audio visualization
    3.30 -        private WasapiCapture iSoundIn;
    3.31 -        private IWaveSource iWaveSource;
    3.32 -        private LineSpectrum iLineSpectrum;
    3.33 +        //Audio
    3.34 +        private AudioManager iAudioManager;
    3.35  
    3.36          //Network
    3.37          private NetworkManager iNetworkManager;
    3.38 @@ -232,9 +223,7 @@
    3.39              }
    3.40  
    3.41              //CSCore
    3.42 -            iMultiMediaDeviceEnumerator = new MMDeviceEnumerator();
    3.43 -            iMultiMediaDeviceEnumerator.RegisterEndpointNotificationCallback(this);
    3.44 -            UpdateAudioDeviceAndMasterVolumeThreadSafe();
    3.45 +            CreateAudioManager();
    3.46  
    3.47              //Network
    3.48              iNetworkManager = new NetworkManager();
    3.49 @@ -291,6 +280,54 @@
    3.50              }
    3.51          }
    3.52  
    3.53 +
    3.54 +        private void CreateAudioManager()
    3.55 +        {
    3.56 +            iAudioManager = new AudioManager();
    3.57 +            iAudioManager.Open(OnDefaultMultiMediaDeviceChanged, OnVolumeNotification);
    3.58 +            UpdateAudioDeviceAndMasterVolumeThreadSafe();
    3.59 +        }
    3.60 +
    3.61 +        private void DestroyAudioManager()
    3.62 +        {
    3.63 +            if (iAudioManager != null)
    3.64 +            {
    3.65 +                iAudioManager.Close();
    3.66 +                iAudioManager = null;
    3.67 +            }
    3.68 +        }
    3.69 +
    3.70 +        /// <summary>
    3.71 +        /// 
    3.72 +        /// </summary>
    3.73 +        /// <param name="sender"></param>
    3.74 +        /// <param name="aEvent"></param>
    3.75 +        public void OnDefaultMultiMediaDeviceChanged(object sender, DefaultDeviceChangedEventArgs aEvent)
    3.76 +        {
    3.77 +            if (aEvent.DataFlow == DataFlow.Render && aEvent.Role == Role.Multimedia)
    3.78 +            {
    3.79 +                ResetAudioManagerThreadSafe();
    3.80 +            }
    3.81 +        }
    3.82 +
    3.83 +        /// <summary>
    3.84 +        /// 
    3.85 +        /// </summary>
    3.86 +        private void ResetAudioManagerThreadSafe()
    3.87 +        {
    3.88 +            if (InvokeRequired)
    3.89 +            {
    3.90 +                //Not in the proper thread, invoke ourselves
    3.91 +                BeginInvoke(new Action<FormMain>((sender) => { ResetAudioManagerThreadSafe(); }), this);
    3.92 +                return;
    3.93 +            }
    3.94 +
    3.95 +            //Proper thread, go ahead
    3.96 +            DestroyAudioManager();
    3.97 +            CreateAudioManager();
    3.98 +
    3.99 +        }
   3.100 +
   3.101          /// <summary>
   3.102          /// Called when our display is opened.
   3.103          /// </summary>
   3.104 @@ -511,7 +548,7 @@
   3.105          /// Receive volume change notification and reflect changes on our slider.
   3.106          /// </summary>
   3.107          /// <param name="data"></param>
   3.108 -        public void OnVolumeNotificationThreadSafe(object sender, AudioEndpointVolumeCallbackEventArgs aEvent)
   3.109 +        public void OnVolumeNotification(object sender, AudioEndpointVolumeCallbackEventArgs aEvent)
   3.110          {
   3.111              UpdateMasterVolumeThreadSafe();
   3.112          }
   3.113 @@ -524,9 +561,9 @@
   3.114          private void trackBarMasterVolume_Scroll(object sender, EventArgs e)
   3.115          {
   3.116              //Just like Windows Volume Mixer we unmute if the volume is adjusted
   3.117 -            iAudioEndpointVolume.IsMuted = false;
   3.118 +            iAudioManager.Volume.IsMuted = false;
   3.119              //Set volume level according to our volume slider new position
   3.120 -            iAudioEndpointVolume.MasterVolumeLevelScalar = trackBarMasterVolume.Value/100.0f;
   3.121 +            iAudioManager.Volume.MasterVolumeLevelScalar = trackBarMasterVolume.Value/100.0f;
   3.122          }
   3.123  
   3.124  
   3.125 @@ -537,54 +574,9 @@
   3.126          /// <param name="e"></param>
   3.127          private void checkBoxMute_CheckedChanged(object sender, EventArgs e)
   3.128          {
   3.129 -            iAudioEndpointVolume.IsMuted = checkBoxMute.Checked;
   3.130 +            iAudioManager.Volume.IsMuted = checkBoxMute.Checked;
   3.131          }
   3.132  
   3.133 -        /// <summary>
   3.134 -        /// Device State Changed
   3.135 -        /// </summary>
   3.136 -        public void OnDeviceStateChanged([MarshalAs(UnmanagedType.LPWStr)] string deviceId,
   3.137 -            [MarshalAs(UnmanagedType.I4)] DeviceState newState)
   3.138 -        {
   3.139 -        }
   3.140 -
   3.141 -        /// <summary>
   3.142 -        /// Device Added
   3.143 -        /// </summary>
   3.144 -        public void OnDeviceAdded([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId)
   3.145 -        {
   3.146 -        }
   3.147 -
   3.148 -        /// <summary>
   3.149 -        /// Device Removed
   3.150 -        /// </summary>
   3.151 -        public void OnDeviceRemoved([MarshalAs(UnmanagedType.LPWStr)] string deviceId)
   3.152 -        {
   3.153 -        }
   3.154 -
   3.155 -        /// <summary>
   3.156 -        /// Default Device Changed
   3.157 -        /// </summary>
   3.158 -        public void OnDefaultDeviceChanged(DataFlow flow, Role role,
   3.159 -            [MarshalAs(UnmanagedType.LPWStr)] string defaultDeviceId)
   3.160 -        {
   3.161 -            if (role == Role.Multimedia && flow == DataFlow.Render)
   3.162 -            {
   3.163 -                UpdateAudioDeviceAndMasterVolumeThreadSafe();
   3.164 -            }
   3.165 -        }
   3.166 -
   3.167 -        /// <summary>
   3.168 -        /// Property Value Changed
   3.169 -        /// </summary>
   3.170 -        /// <param name="pwstrDeviceId"></param>
   3.171 -        /// <param name="key"></param>
   3.172 -        public void OnPropertyValueChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, PropertyKey key)
   3.173 -        {
   3.174 -        }
   3.175 -
   3.176 -
   3.177 -
   3.178  
   3.179          /// <summary>
   3.180          /// Update master volume indicators based our current system states.
   3.181 @@ -592,19 +584,18 @@
   3.182          /// </summary>
   3.183          private void UpdateMasterVolumeThreadSafe()
   3.184          {
   3.185 -            if (this.InvokeRequired)
   3.186 +            if (InvokeRequired)
   3.187              {
   3.188                  //Not in the proper thread, invoke ourselves
   3.189 -                PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateMasterVolumeThreadSafe);
   3.190 -                this.Invoke(d, new object[] {});
   3.191 +                BeginInvoke(new Action<FormMain>((sender) => { UpdateMasterVolumeThreadSafe(); }), this);
   3.192                  return;
   3.193              }
   3.194  
   3.195              //Update volume slider
   3.196 -            float volumeLevelScalar = iAudioEndpointVolume.MasterVolumeLevelScalar;
   3.197 +            float volumeLevelScalar = iAudioManager.Volume.MasterVolumeLevelScalar;
   3.198              trackBarMasterVolume.Value = Convert.ToInt32(volumeLevelScalar*100);
   3.199              //Update mute checkbox
   3.200 -            checkBoxMute.Checked = iAudioEndpointVolume.IsMuted;
   3.201 +            checkBoxMute.Checked = iAudioManager.Volume.IsMuted;
   3.202  
   3.203              //If our display connection is open we need to update its icons
   3.204              if (iDisplay.IsOpen())
   3.205 @@ -646,83 +637,11 @@
   3.206                  }
   3.207  
   3.208                  //Take care of our mute icon
   3.209 -                iDisplay.SetIconOnOff(MiniDisplay.IconType.Mute, iAudioEndpointVolume.IsMuted);
   3.210 +                iDisplay.SetIconOnOff(MiniDisplay.IconType.Mute, iAudioManager.Volume.IsMuted);
   3.211              }
   3.212  
   3.213          }
   3.214  
   3.215 -        /// <summary>
   3.216 -        /// 
   3.217 -        /// </summary>
   3.218 -        private void StartAudioVisualization()
   3.219 -        {
   3.220 -            StopAudioVisualization();
   3.221 -            //Open the default device 
   3.222 -            iSoundIn = new WasapiLoopbackCapture();
   3.223 -            //Our loopback capture opens the default render device by default so the following is not needed
   3.224 -            //iSoundIn.Device = MMDeviceEnumerator.DefaultAudioEndpoint(DataFlow.Render, Role.Console);
   3.225 -            iSoundIn.Initialize();
   3.226 -
   3.227 -            SoundInSource soundInSource = new SoundInSource(iSoundIn);
   3.228 -            ISampleSource source = soundInSource.ToSampleSource();
   3.229 -
   3.230 -            const FftSize fftSize = FftSize.Fft2048;
   3.231 -            //create a spectrum provider which provides fft data based on some input
   3.232 -            BasicSpectrumProvider spectrumProvider = new BasicSpectrumProvider(source.WaveFormat.Channels, source.WaveFormat.SampleRate, fftSize);
   3.233 -
   3.234 -            //linespectrum and voiceprint3dspectrum used for rendering some fft data
   3.235 -            //in oder to get some fft data, set the previously created spectrumprovider 
   3.236 -            iLineSpectrum = new LineSpectrum(fftSize)
   3.237 -            {
   3.238 -                SpectrumProvider = spectrumProvider,
   3.239 -                UseAverage = false,
   3.240 -                BarCount = 16,
   3.241 -                BarSpacing = 1,
   3.242 -                IsXLogScale = true,
   3.243 -                ScalingStrategy = ScalingStrategy.Decibel
   3.244 -            };
   3.245 -
   3.246 -
   3.247 -            //the SingleBlockNotificationStream is used to intercept the played samples
   3.248 -            var notificationSource = new SingleBlockNotificationStream(source);
   3.249 -            //pass the intercepted samples as input data to the spectrumprovider (which will calculate a fft based on them)
   3.250 -            notificationSource.SingleBlockRead += (s, a) => spectrumProvider.Add(a.Left, a.Right);
   3.251 -
   3.252 -            iWaveSource = notificationSource.ToWaveSource(16);
   3.253 -
   3.254 -
   3.255 -            // We need to read from our source otherwise SingleBlockRead is never called and our spectrum provider is not populated
   3.256 -            byte[] buffer = new byte[iWaveSource.WaveFormat.BytesPerSecond / 2];
   3.257 -            soundInSource.DataAvailable += (s, aEvent) =>
   3.258 -            {
   3.259 -                int read;
   3.260 -                while ((read = iWaveSource.Read(buffer, 0, buffer.Length)) > 0) ;
   3.261 -            };
   3.262 -
   3.263 -
   3.264 -            //Start recording
   3.265 -            iSoundIn.Start();
   3.266 -        }
   3.267 -
   3.268 -        /// <summary>
   3.269 -        /// 
   3.270 -        /// </summary>
   3.271 -        private void StopAudioVisualization()
   3.272 -        {
   3.273 -
   3.274 -            if (iSoundIn != null)
   3.275 -            {
   3.276 -                iSoundIn.Stop();
   3.277 -                iSoundIn.Dispose();
   3.278 -                iSoundIn = null;
   3.279 -            }
   3.280 -            if (iWaveSource != null)
   3.281 -            {
   3.282 -                iWaveSource.Dispose();
   3.283 -                iWaveSource = null;
   3.284 -            }
   3.285 -
   3.286 -        }
   3.287  
   3.288  
   3.289          /// <summary>
   3.290 @@ -737,7 +656,7 @@
   3.291              }
   3.292  
   3.293              // Update our math
   3.294 -            if (!iLineSpectrum.Update())
   3.295 +            if (iAudioManager==null || !iAudioManager.Spectrum.Update())
   3.296              {
   3.297                  //Nothing changed no need to render
   3.298                  return;
   3.299 @@ -755,7 +674,7 @@
   3.300                      if (ctrl is PictureBox)
   3.301                      {
   3.302                          PictureBox pb = (PictureBox)ctrl;
   3.303 -                        if (iLineSpectrum.Render(pb.Image, Color.Black, Color.Black, Color.White, false))
   3.304 +                        if (iAudioManager.Spectrum.Render(pb.Image, Color.Black, Color.Black, Color.White, false))
   3.305                          {
   3.306                              pb.Invalidate();
   3.307                          }
   3.308 @@ -769,35 +688,24 @@
   3.309          /// 
   3.310          /// </summary>
   3.311          private void UpdateAudioDeviceAndMasterVolumeThreadSafe()
   3.312 -        {
   3.313 -            if (this.InvokeRequired)
   3.314 +        {            
   3.315 +            if (InvokeRequired)
   3.316              {
   3.317                  //Not in the proper thread, invoke ourselves
   3.318 -                PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateAudioDeviceAndMasterVolumeThreadSafe);
   3.319 -                this.Invoke(d, new object[] {});
   3.320 +                BeginInvoke(new Action<FormMain>((sender) => { UpdateAudioDeviceAndMasterVolumeThreadSafe(); }), this);
   3.321                  return;
   3.322              }
   3.323  
   3.324              //We are in the correct thread just go ahead.
   3.325              try
   3.326              {
   3.327 -                //Get our master volume
   3.328 -                iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
   3.329 -                iAudioEndpointVolume = AudioEndpointVolume.FromDevice(iMultiMediaDevice);
   3.330 -                
   3.331 +                  
   3.332                  //Update our label
   3.333 -                labelDefaultAudioDevice.Text = iMultiMediaDevice.FriendlyName;
   3.334 +                iLabelDefaultAudioDevice.Text = iAudioManager.DefaultDevice.FriendlyName;
   3.335  
   3.336                  //Show our volume in our track bar
   3.337                  UpdateMasterVolumeThreadSafe();
   3.338  
   3.339 -                //Register to get volume modifications
   3.340 -                AudioEndpointVolumeCallback callback = new AudioEndpointVolumeCallback();
   3.341 -                callback.NotifyRecived += OnVolumeNotificationThreadSafe;
   3.342 -                // Do we need to unregister?
   3.343 -                iAudioEndpointVolume.RegisterControlChangeNotify(callback);
   3.344 -                //
   3.345 -                StartAudioVisualization();
   3.346                  //
   3.347                  trackBarMasterVolume.Enabled = true;
   3.348              }
   3.349 @@ -1573,10 +1481,9 @@
   3.350  
   3.351          private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
   3.352          {
   3.353 -            //TODO: discard other CSCore audio objects
   3.354 -            StopAudioVisualization();
   3.355              iCecManager.Stop();
   3.356              iNetworkManager.Dispose();
   3.357 +            DestroyAudioManager();
   3.358              CloseDisplayConnection();
   3.359              StopServer();
   3.360              e.Cancel = iClosing;
     4.1 --- a/Server/SharpDisplayManager.csproj	Fri Jan 06 18:27:19 2017 +0100
     4.2 +++ b/Server/SharpDisplayManager.csproj	Sat Jan 07 20:21:42 2017 +0100
     4.3 @@ -174,6 +174,7 @@
     4.4      <Compile Include="Actions\ActionCecUserControlReleased.cs" />
     4.5      <Compile Include="Actions\ActionDisplayMessage.cs" />
     4.6      <Compile Include="Actions\ActionHarmonyCommand.cs" />
     4.7 +    <Compile Include="AudioManager.cs" />
     4.8      <Compile Include="Spectrum\BasicSpectrumProvider.cs" />
     4.9      <Compile Include="CbtHook.cs" />
    4.10      <Compile Include="CecClient.cs" />