Server/AudioManager.cs
author StephaneLenclud
Sat, 07 Jan 2017 20:21:42 +0100
changeset 277 71ba0dd622a5
child 278 2481c46d1f93
permissions -rw-r--r--
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 ´╗┐using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 //CSCore
     8 using CSCore;
     9 using CSCore.Win32;
    10 using CSCore.DSP;
    11 using CSCore.Streams;
    12 using CSCore.CoreAudioAPI;
    13 using CSCore.SoundIn;
    14 // Visualization
    15 using Visualization;
    16 
    17 
    18 namespace SharpDisplayManager
    19 {
    20     class AudioManager
    21     {
    22         // Volume management
    23         private MMDeviceEnumerator iMultiMediaDeviceEnumerator;
    24         private MMNotificationClient iMultiMediaNotificationClient;
    25         private MMDevice iMultiMediaDevice;
    26         private AudioEndpointVolume iAudioEndpointVolume;
    27         private AudioEndpointVolumeCallback iAudioEndpointVolumeCallback;
    28         EventHandler<DefaultDeviceChangedEventArgs> iDefaultDeviceChangedHandler;
    29         EventHandler<AudioEndpointVolumeCallbackEventArgs> iVolumeChangedHandler;
    30 
    31         // Audio visualization
    32         private WasapiCapture iSoundIn;
    33         private IWaveSource iWaveSource;
    34         private LineSpectrum iLineSpectrum;
    35 
    36 
    37         public LineSpectrum Spectrum { get { return iLineSpectrum; } }
    38         public AudioEndpointVolume Volume { get { return iAudioEndpointVolume; } }
    39         public MMDevice DefaultDevice { get { return iMultiMediaDevice; } }
    40 
    41         /// <summary>
    42         /// 
    43         /// </summary>
    44         /// <param name="aDefaultDeviceChangedHandler"></param>
    45         /// <param name="aVolumeChangedHandler"></param>
    46         public void Open(   EventHandler<DefaultDeviceChangedEventArgs> aDefaultDeviceChangedHandler,
    47                             EventHandler<AudioEndpointVolumeCallbackEventArgs> aVolumeChangedHandler)
    48         {
    49             //Create device and register default device change notification
    50             iMultiMediaDeviceEnumerator = new MMDeviceEnumerator();
    51             iMultiMediaNotificationClient = new MMNotificationClient(iMultiMediaDeviceEnumerator);
    52             iMultiMediaNotificationClient.DefaultDeviceChanged += iDefaultDeviceChangedHandler = aDefaultDeviceChangedHandler;
    53             iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render,Role.Multimedia);
    54             //Register to get volume modifications
    55             iAudioEndpointVolume = AudioEndpointVolume.FromDevice(iMultiMediaDevice);
    56             iAudioEndpointVolumeCallback = new AudioEndpointVolumeCallback();
    57             iAudioEndpointVolumeCallback.NotifyRecived += iVolumeChangedHandler = aVolumeChangedHandler;
    58             iAudioEndpointVolume.RegisterControlChangeNotify(iAudioEndpointVolumeCallback);
    59 
    60             StartAudioVisualization();
    61         }
    62 
    63         /// <summary>
    64         /// 
    65         /// </summary>
    66         public void Close()
    67         {
    68             StopAudioVisualization();
    69 
    70             // Client up our MM objects in reverse order
    71             if (iAudioEndpointVolumeCallback != null && iAudioEndpointVolume != null)
    72             {
    73                 iAudioEndpointVolume.UnregisterControlChangeNotify(iAudioEndpointVolumeCallback);
    74             }
    75 
    76             if (iAudioEndpointVolumeCallback != null)
    77             {
    78                 iAudioEndpointVolumeCallback.NotifyRecived -= iVolumeChangedHandler;
    79                 iAudioEndpointVolumeCallback = null;
    80             }
    81 
    82             if (iAudioEndpointVolume != null)
    83             {
    84                 iAudioEndpointVolume.Dispose();
    85                 iAudioEndpointVolume = null;
    86             }
    87 
    88             if (iMultiMediaDevice != null)
    89             {
    90                 iMultiMediaDevice.Dispose();
    91                 iMultiMediaDevice = null;
    92             }
    93 
    94             if (iMultiMediaNotificationClient != null)
    95             {
    96                 iMultiMediaNotificationClient.DefaultDeviceChanged -= iDefaultDeviceChangedHandler;
    97                 iMultiMediaNotificationClient.Dispose();
    98                 iMultiMediaNotificationClient = null;
    99             }
   100 
   101             if (iMultiMediaDeviceEnumerator != null)
   102             {
   103                 iMultiMediaDeviceEnumerator.Dispose();
   104                 iMultiMediaDeviceEnumerator = null;
   105             }
   106 
   107         }
   108 
   109 
   110         /// <summary>
   111         /// 
   112         /// </summary>
   113         private void StartAudioVisualization()
   114         {
   115             //Open the default device 
   116             iSoundIn = new WasapiLoopbackCapture();
   117             //Our loopback capture opens the default render device by default so the following is not needed
   118             //iSoundIn.Device = MMDeviceEnumerator.DefaultAudioEndpoint(DataFlow.Render, Role.Console);
   119             iSoundIn.Initialize();
   120 
   121             SoundInSource soundInSource = new SoundInSource(iSoundIn);
   122             ISampleSource source = soundInSource.ToSampleSource();
   123 
   124             const FftSize fftSize = FftSize.Fft2048;
   125             //create a spectrum provider which provides fft data based on some input
   126             BasicSpectrumProvider spectrumProvider = new BasicSpectrumProvider(source.WaveFormat.Channels, source.WaveFormat.SampleRate, fftSize);
   127 
   128             //linespectrum and voiceprint3dspectrum used for rendering some fft data
   129             //in oder to get some fft data, set the previously created spectrumprovider 
   130             iLineSpectrum = new LineSpectrum(fftSize)
   131             {
   132                 SpectrumProvider = spectrumProvider,
   133                 UseAverage = false,
   134                 BarCount = 16,
   135                 BarSpacing = 1,
   136                 IsXLogScale = true,
   137                 ScalingStrategy = ScalingStrategy.Decibel
   138             };
   139 
   140 
   141             //the SingleBlockNotificationStream is used to intercept the played samples
   142             var notificationSource = new SingleBlockNotificationStream(source);
   143             //pass the intercepted samples as input data to the spectrumprovider (which will calculate a fft based on them)
   144             notificationSource.SingleBlockRead += (s, a) => spectrumProvider.Add(a.Left, a.Right);
   145 
   146             iWaveSource = notificationSource.ToWaveSource(16);
   147 
   148 
   149             // We need to read from our source otherwise SingleBlockRead is never called and our spectrum provider is not populated
   150             byte[] buffer = new byte[iWaveSource.WaveFormat.BytesPerSecond / 2];
   151             soundInSource.DataAvailable += (s, aEvent) =>
   152             {
   153                 int read;
   154                 while ((read = iWaveSource.Read(buffer, 0, buffer.Length)) > 0) ;
   155             };
   156 
   157 
   158             //Start recording
   159             iSoundIn.Start();
   160         }
   161 
   162         /// <summary>
   163         /// 
   164         /// </summary>
   165         private void StopAudioVisualization()
   166         {
   167             if (iWaveSource != null)
   168             {
   169                 iWaveSource.Dispose();
   170                 iWaveSource = null;
   171             }
   172 
   173             if (iSoundIn != null)
   174             {
   175                 iSoundIn.Stop();
   176                 iSoundIn.Dispose();
   177                 iSoundIn = null;
   178             }
   179 
   180         }
   181 
   182 
   183     }
   184 }