Server/AudioManager.cs
author StephaneLenclud
Mon, 06 Mar 2017 15:11:27 +0100
changeset 282 29b4bdeda281
parent 277 71ba0dd622a5
permissions -rw-r--r--
Published v1.4.7.0
Adding back runtime dependencies.
     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         private int iVisualizerCount = 0;
    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         /// Increment our visualizer count
    43         /// </summary>
    44         public void AddVisualizer()
    45         {
    46             if (iVisualizerCount == 0)
    47             {
    48                 // If we need at least one visualizer then we need to start our engine.
    49                 StartAudioVisualization();
    50             }
    51 
    52             //TODO: Check bounds?
    53             iVisualizerCount++;
    54         }
    55 
    56         /// <summary>
    57         /// Decrement our visualizer counter.
    58         /// </summary>
    59         public void RemoveVisualizer()
    60         {
    61             if (iVisualizerCount == 1)
    62             {
    63                 // When reaching zero visualization is not need and we stop our engine
    64                 StopAudioVisualization();
    65                 iVisualizerCount = 0;
    66             }
    67             // Defensive: Make sure we don't go below zero
    68             else if (iVisualizerCount>0)
    69             {
    70                 iVisualizerCount--;
    71             }
    72         }
    73 
    74 
    75         /// <summary>
    76         /// 
    77         /// </summary>
    78         /// <param name="aDefaultDeviceChangedHandler"></param>
    79         /// <param name="aVolumeChangedHandler"></param>
    80         public void Open(   EventHandler<DefaultDeviceChangedEventArgs> aDefaultDeviceChangedHandler,
    81                             EventHandler<AudioEndpointVolumeCallbackEventArgs> aVolumeChangedHandler)
    82         {
    83             //Create device and register default device change notification
    84             iMultiMediaDeviceEnumerator = new MMDeviceEnumerator();
    85             iMultiMediaNotificationClient = new MMNotificationClient(iMultiMediaDeviceEnumerator);
    86             iMultiMediaNotificationClient.DefaultDeviceChanged += iDefaultDeviceChangedHandler = aDefaultDeviceChangedHandler;
    87             iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render,Role.Multimedia);
    88             //Register to get volume modifications
    89             iAudioEndpointVolume = AudioEndpointVolume.FromDevice(iMultiMediaDevice);
    90             iAudioEndpointVolumeCallback = new AudioEndpointVolumeCallback();
    91             iAudioEndpointVolumeCallback.NotifyRecived += iVolumeChangedHandler = aVolumeChangedHandler;
    92             iAudioEndpointVolume.RegisterControlChangeNotify(iAudioEndpointVolumeCallback);
    93 
    94             if (iVisualizerCount > 0)
    95             {
    96                 // We probably got restarted, make sure visualization is running if needed
    97                 StartAudioVisualization();
    98             }
    99         }
   100 
   101         /// <summary>
   102         /// 
   103         /// </summary>
   104         public void Close()
   105         {
   106             StopAudioVisualization();
   107 
   108             // Client up our MM objects in reverse order
   109             if (iAudioEndpointVolumeCallback != null && iAudioEndpointVolume != null)
   110             {
   111                 iAudioEndpointVolume.UnregisterControlChangeNotify(iAudioEndpointVolumeCallback);
   112             }
   113 
   114             if (iAudioEndpointVolumeCallback != null)
   115             {
   116                 iAudioEndpointVolumeCallback.NotifyRecived -= iVolumeChangedHandler;
   117                 iAudioEndpointVolumeCallback = null;
   118             }
   119 
   120             if (iAudioEndpointVolume != null)
   121             {
   122                 iAudioEndpointVolume.Dispose();
   123                 iAudioEndpointVolume = null;
   124             }
   125 
   126             if (iMultiMediaDevice != null)
   127             {
   128                 iMultiMediaDevice.Dispose();
   129                 iMultiMediaDevice = null;
   130             }
   131 
   132             if (iMultiMediaNotificationClient != null)
   133             {
   134                 iMultiMediaNotificationClient.DefaultDeviceChanged -= iDefaultDeviceChangedHandler;
   135                 iMultiMediaNotificationClient.Dispose();
   136                 iMultiMediaNotificationClient = null;
   137             }
   138 
   139             if (iMultiMediaDeviceEnumerator != null)
   140             {
   141                 iMultiMediaDeviceEnumerator.Dispose();
   142                 iMultiMediaDeviceEnumerator = null;
   143             }
   144 
   145         }
   146 
   147 
   148         /// <summary>
   149         /// 
   150         /// </summary>
   151         private void StartAudioVisualization()
   152         {
   153             //Open the default device 
   154             iSoundIn = new WasapiLoopbackCapture();
   155             //Our loopback capture opens the default render device by default so the following is not needed
   156             //iSoundIn.Device = MMDeviceEnumerator.DefaultAudioEndpoint(DataFlow.Render, Role.Console);
   157             iSoundIn.Initialize();
   158 
   159             SoundInSource soundInSource = new SoundInSource(iSoundIn);
   160             ISampleSource source = soundInSource.ToSampleSource();
   161 
   162             const FftSize fftSize = FftSize.Fft2048;
   163             //create a spectrum provider which provides fft data based on some input
   164             BasicSpectrumProvider spectrumProvider = new BasicSpectrumProvider(source.WaveFormat.Channels, source.WaveFormat.SampleRate, fftSize);
   165 
   166             //linespectrum and voiceprint3dspectrum used for rendering some fft data
   167             //in oder to get some fft data, set the previously created spectrumprovider 
   168             iLineSpectrum = new LineSpectrum(fftSize)
   169             {
   170                 SpectrumProvider = spectrumProvider,
   171                 UseAverage = false, // Does not matter since we hacked it
   172                 BarCount = 16,
   173                 BarSpacing = 1,
   174                 IsXLogScale = true, // Does not matter since we hacked it
   175                 ScalingStrategy = ScalingStrategy.Decibel // Does not matter since we hacked it
   176             };
   177 
   178 
   179             //the SingleBlockNotificationStream is used to intercept the played samples
   180             var notificationSource = new SingleBlockNotificationStream(source);
   181             //pass the intercepted samples as input data to the spectrumprovider (which will calculate a fft based on them)
   182             notificationSource.SingleBlockRead += (s, a) => spectrumProvider.Add(a.Left, a.Right);
   183 
   184             iWaveSource = notificationSource.ToWaveSource(16);
   185 
   186 
   187             // We need to read from our source otherwise SingleBlockRead is never called and our spectrum provider is not populated
   188             byte[] buffer = new byte[iWaveSource.WaveFormat.BytesPerSecond / 2];
   189             soundInSource.DataAvailable += (s, aEvent) =>
   190             {
   191                 int read;
   192                 while ((read = iWaveSource.Read(buffer, 0, buffer.Length)) > 0) ;
   193             };
   194 
   195 
   196             //Start recording
   197             iSoundIn.Start();
   198         }
   199 
   200         /// <summary>
   201         /// 
   202         /// </summary>
   203         private void StopAudioVisualization()
   204         {
   205             if (iSoundIn != null)
   206             {
   207                 iSoundIn.Stop();
   208             }
   209 
   210                 if (iWaveSource != null)
   211             {
   212                 iWaveSource.Dispose();
   213                 iWaveSource = null;
   214             }
   215 
   216             if (iSoundIn != null)
   217             {
   218                 iSoundIn.Dispose();
   219                 iSoundIn = null;
   220             }
   221 
   222             iLineSpectrum = null;
   223         }
   224 
   225 
   226     }
   227 }