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