StephaneLenclud@277: using System; StephaneLenclud@277: using System.Collections.Generic; StephaneLenclud@277: using System.Linq; StephaneLenclud@277: using System.Text; StephaneLenclud@277: using System.Threading.Tasks; StephaneLenclud@277: StephaneLenclud@277: //CSCore StephaneLenclud@277: using CSCore; StephaneLenclud@277: using CSCore.Win32; StephaneLenclud@277: using CSCore.DSP; StephaneLenclud@277: using CSCore.Streams; StephaneLenclud@277: using CSCore.CoreAudioAPI; StephaneLenclud@277: using CSCore.SoundIn; StephaneLenclud@277: // Visualization StephaneLenclud@277: using Visualization; StephaneLenclud@277: StephaneLenclud@277: StephaneLenclud@277: namespace SharpDisplayManager StephaneLenclud@277: { StephaneLenclud@277: class AudioManager StephaneLenclud@277: { StephaneLenclud@277: // Volume management StephaneLenclud@277: private MMDeviceEnumerator iMultiMediaDeviceEnumerator; StephaneLenclud@277: private MMNotificationClient iMultiMediaNotificationClient; StephaneLenclud@277: private MMDevice iMultiMediaDevice; StephaneLenclud@277: private AudioEndpointVolume iAudioEndpointVolume; StephaneLenclud@277: private AudioEndpointVolumeCallback iAudioEndpointVolumeCallback; StephaneLenclud@277: EventHandler iDefaultDeviceChangedHandler; StephaneLenclud@277: EventHandler iVolumeChangedHandler; StephaneLenclud@277: StephaneLenclud@277: // Audio visualization StephaneLenclud@277: private WasapiCapture iSoundIn; StephaneLenclud@277: private IWaveSource iWaveSource; StephaneLenclud@277: private LineSpectrum iLineSpectrum; StephaneLenclud@278: private int iVisualizerCount = 0; StephaneLenclud@277: StephaneLenclud@277: public LineSpectrum Spectrum { get { return iLineSpectrum; } } StephaneLenclud@277: public AudioEndpointVolume Volume { get { return iAudioEndpointVolume; } } StephaneLenclud@277: public MMDevice DefaultDevice { get { return iMultiMediaDevice; } } StephaneLenclud@277: StephaneLenclud@277: /// StephaneLenclud@278: /// Increment our visualizer count StephaneLenclud@278: /// StephaneLenclud@278: public void AddVisualizer() StephaneLenclud@278: { StephaneLenclud@278: if (iVisualizerCount == 0) StephaneLenclud@278: { StephaneLenclud@278: // If we need at least one visualizer then we need to start our engine. StephaneLenclud@278: StartAudioVisualization(); StephaneLenclud@278: } StephaneLenclud@278: StephaneLenclud@278: //TODO: Check bounds? StephaneLenclud@278: iVisualizerCount++; StephaneLenclud@278: } StephaneLenclud@278: StephaneLenclud@278: /// StephaneLenclud@278: /// Decrement our visualizer counter. StephaneLenclud@278: /// StephaneLenclud@278: public void RemoveVisualizer() StephaneLenclud@278: { StephaneLenclud@278: if (iVisualizerCount == 1) StephaneLenclud@278: { StephaneLenclud@278: // When reaching zero visualization is not need and we stop our engine StephaneLenclud@278: StopAudioVisualization(); StephaneLenclud@278: iVisualizerCount = 0; StephaneLenclud@278: } StephaneLenclud@278: // Defensive: Make sure we don't go below zero StephaneLenclud@278: else if (iVisualizerCount>0) StephaneLenclud@278: { StephaneLenclud@278: iVisualizerCount--; StephaneLenclud@278: } StephaneLenclud@278: } StephaneLenclud@278: StephaneLenclud@278: StephaneLenclud@278: /// StephaneLenclud@277: /// StephaneLenclud@277: /// StephaneLenclud@277: /// StephaneLenclud@277: /// StephaneLenclud@277: public void Open( EventHandler aDefaultDeviceChangedHandler, StephaneLenclud@277: EventHandler aVolumeChangedHandler) StephaneLenclud@277: { StephaneLenclud@277: //Create device and register default device change notification StephaneLenclud@277: iMultiMediaDeviceEnumerator = new MMDeviceEnumerator(); StephaneLenclud@277: iMultiMediaNotificationClient = new MMNotificationClient(iMultiMediaDeviceEnumerator); StephaneLenclud@277: iMultiMediaNotificationClient.DefaultDeviceChanged += iDefaultDeviceChangedHandler = aDefaultDeviceChangedHandler; StephaneLenclud@277: iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render,Role.Multimedia); StephaneLenclud@277: //Register to get volume modifications StephaneLenclud@277: iAudioEndpointVolume = AudioEndpointVolume.FromDevice(iMultiMediaDevice); StephaneLenclud@277: iAudioEndpointVolumeCallback = new AudioEndpointVolumeCallback(); StephaneLenclud@277: iAudioEndpointVolumeCallback.NotifyRecived += iVolumeChangedHandler = aVolumeChangedHandler; StephaneLenclud@277: iAudioEndpointVolume.RegisterControlChangeNotify(iAudioEndpointVolumeCallback); StephaneLenclud@277: StephaneLenclud@278: if (iVisualizerCount > 0) StephaneLenclud@278: { StephaneLenclud@278: // We probably got restarted, make sure visualization is running if needed StephaneLenclud@278: StartAudioVisualization(); StephaneLenclud@278: } StephaneLenclud@277: } StephaneLenclud@277: StephaneLenclud@277: /// StephaneLenclud@277: /// StephaneLenclud@277: /// StephaneLenclud@277: public void Close() StephaneLenclud@277: { StephaneLenclud@277: StopAudioVisualization(); StephaneLenclud@277: StephaneLenclud@277: // Client up our MM objects in reverse order StephaneLenclud@277: if (iAudioEndpointVolumeCallback != null && iAudioEndpointVolume != null) StephaneLenclud@277: { StephaneLenclud@277: iAudioEndpointVolume.UnregisterControlChangeNotify(iAudioEndpointVolumeCallback); StephaneLenclud@277: } StephaneLenclud@277: StephaneLenclud@277: if (iAudioEndpointVolumeCallback != null) StephaneLenclud@277: { StephaneLenclud@277: iAudioEndpointVolumeCallback.NotifyRecived -= iVolumeChangedHandler; StephaneLenclud@277: iAudioEndpointVolumeCallback = null; StephaneLenclud@277: } StephaneLenclud@277: StephaneLenclud@277: if (iAudioEndpointVolume != null) StephaneLenclud@277: { StephaneLenclud@277: iAudioEndpointVolume.Dispose(); StephaneLenclud@277: iAudioEndpointVolume = null; StephaneLenclud@277: } StephaneLenclud@277: StephaneLenclud@277: if (iMultiMediaDevice != null) StephaneLenclud@277: { StephaneLenclud@277: iMultiMediaDevice.Dispose(); StephaneLenclud@277: iMultiMediaDevice = null; StephaneLenclud@277: } StephaneLenclud@277: StephaneLenclud@277: if (iMultiMediaNotificationClient != null) StephaneLenclud@277: { StephaneLenclud@277: iMultiMediaNotificationClient.DefaultDeviceChanged -= iDefaultDeviceChangedHandler; StephaneLenclud@277: iMultiMediaNotificationClient.Dispose(); StephaneLenclud@277: iMultiMediaNotificationClient = null; StephaneLenclud@277: } StephaneLenclud@277: StephaneLenclud@277: if (iMultiMediaDeviceEnumerator != null) StephaneLenclud@277: { StephaneLenclud@277: iMultiMediaDeviceEnumerator.Dispose(); StephaneLenclud@277: iMultiMediaDeviceEnumerator = null; StephaneLenclud@277: } StephaneLenclud@277: StephaneLenclud@277: } StephaneLenclud@277: StephaneLenclud@277: StephaneLenclud@277: /// StephaneLenclud@277: /// StephaneLenclud@277: /// StephaneLenclud@277: private void StartAudioVisualization() StephaneLenclud@277: { StephaneLenclud@277: //Open the default device StephaneLenclud@277: iSoundIn = new WasapiLoopbackCapture(); StephaneLenclud@277: //Our loopback capture opens the default render device by default so the following is not needed StephaneLenclud@277: //iSoundIn.Device = MMDeviceEnumerator.DefaultAudioEndpoint(DataFlow.Render, Role.Console); StephaneLenclud@277: iSoundIn.Initialize(); StephaneLenclud@277: StephaneLenclud@277: SoundInSource soundInSource = new SoundInSource(iSoundIn); StephaneLenclud@277: ISampleSource source = soundInSource.ToSampleSource(); StephaneLenclud@277: StephaneLenclud@277: const FftSize fftSize = FftSize.Fft2048; StephaneLenclud@277: //create a spectrum provider which provides fft data based on some input StephaneLenclud@277: BasicSpectrumProvider spectrumProvider = new BasicSpectrumProvider(source.WaveFormat.Channels, source.WaveFormat.SampleRate, fftSize); StephaneLenclud@277: StephaneLenclud@277: //linespectrum and voiceprint3dspectrum used for rendering some fft data StephaneLenclud@277: //in oder to get some fft data, set the previously created spectrumprovider StephaneLenclud@277: iLineSpectrum = new LineSpectrum(fftSize) StephaneLenclud@277: { StephaneLenclud@277: SpectrumProvider = spectrumProvider, StephaneLenclud@278: UseAverage = false, // Does not matter since we hacked it StephaneLenclud@277: BarCount = 16, StephaneLenclud@277: BarSpacing = 1, StephaneLenclud@278: IsXLogScale = true, // Does not matter since we hacked it StephaneLenclud@278: ScalingStrategy = ScalingStrategy.Decibel // Does not matter since we hacked it StephaneLenclud@277: }; StephaneLenclud@277: StephaneLenclud@277: StephaneLenclud@277: //the SingleBlockNotificationStream is used to intercept the played samples StephaneLenclud@277: var notificationSource = new SingleBlockNotificationStream(source); StephaneLenclud@277: //pass the intercepted samples as input data to the spectrumprovider (which will calculate a fft based on them) StephaneLenclud@277: notificationSource.SingleBlockRead += (s, a) => spectrumProvider.Add(a.Left, a.Right); StephaneLenclud@277: StephaneLenclud@277: iWaveSource = notificationSource.ToWaveSource(16); StephaneLenclud@277: StephaneLenclud@277: StephaneLenclud@277: // We need to read from our source otherwise SingleBlockRead is never called and our spectrum provider is not populated StephaneLenclud@277: byte[] buffer = new byte[iWaveSource.WaveFormat.BytesPerSecond / 2]; StephaneLenclud@277: soundInSource.DataAvailable += (s, aEvent) => StephaneLenclud@277: { StephaneLenclud@277: int read; StephaneLenclud@277: while ((read = iWaveSource.Read(buffer, 0, buffer.Length)) > 0) ; StephaneLenclud@277: }; StephaneLenclud@277: StephaneLenclud@277: StephaneLenclud@277: //Start recording StephaneLenclud@277: iSoundIn.Start(); StephaneLenclud@277: } StephaneLenclud@277: StephaneLenclud@277: /// StephaneLenclud@277: /// StephaneLenclud@277: /// StephaneLenclud@277: private void StopAudioVisualization() StephaneLenclud@277: { StephaneLenclud@278: if (iSoundIn != null) StephaneLenclud@278: { StephaneLenclud@278: iSoundIn.Stop(); StephaneLenclud@278: } StephaneLenclud@278: StephaneLenclud@278: if (iWaveSource != null) StephaneLenclud@277: { StephaneLenclud@277: iWaveSource.Dispose(); StephaneLenclud@277: iWaveSource = null; StephaneLenclud@277: } StephaneLenclud@277: StephaneLenclud@277: if (iSoundIn != null) StephaneLenclud@277: { StephaneLenclud@277: iSoundIn.Dispose(); StephaneLenclud@277: iSoundIn = null; StephaneLenclud@277: } StephaneLenclud@277: StephaneLenclud@278: iLineSpectrum = null; StephaneLenclud@277: } StephaneLenclud@277: StephaneLenclud@277: StephaneLenclud@277: } StephaneLenclud@277: }