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.
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" />