using NAudio.CoreAudioApi; using NAudio.CoreAudioApi.Interfaces; using Serilog; using System.Collections.Generic; namespace WorkIndicator.Audio; internal enum MicrophoneStatus { NotUsed, UsedMuted, UsedNotMuted } internal class AudioWatcher : IMMNotificationClient { private readonly ILogger _logger = Log.ForContext(); private readonly MMDeviceEnumerator _deviceEnumerator = new(); private readonly Dictionary _inputDevices = new(); public delegate void MicrophoneStatusChangedDelegate(MicrophoneStatus microphoneStatus); public event MicrophoneStatusChangedDelegate MicrophoneStatusChanged; public void Start() { _logger.Information("Start"); _deviceEnumerator.RegisterEndpointNotificationCallback(this); _logger.Information("Getting capture devices"); var captureDevices = _deviceEnumerator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.Active); foreach (var captureDevice in captureDevices) { _logger.Information("Capture device: {name}", captureDevice.DeviceFriendlyName); HandleAddedDevice(captureDevice); } } public void LogState() { _logger.Information("---"); foreach (var inputDevicePair in _inputDevices) { _logger.Information("Device: {name} {muted}", inputDevicePair.Value.FriendlyName, inputDevicePair.Value.IsMuted); foreach (var sessionPair in inputDevicePair.Value.Sessions) { var session = sessionPair.Value; _logger.Information("Session: {id} {friendlyName} {state}", session.Id, session.FriendlyName, session.State); } } _logger.Information("---"); } private void HandleAddedDevice(MMDevice captureDevice) { _logger.Information("Device added: {name}", captureDevice.DeviceFriendlyName); var audioDevice = new AudioDevice(captureDevice); audioDevice.AudioSessionsChanged += AudioDevice_AudioSessionsChanged; _inputDevices[captureDevice.ID] = audioDevice; MicrophoneStatusChanged?.Invoke(GetMicrophoneStatus()); } private void AudioDevice_AudioSessionsChanged() { MicrophoneStatusChanged?.Invoke(GetMicrophoneStatus()); } private void HandleRemovedDevice(string deviceId) { _logger.Information("Device removed: {id}", deviceId); _inputDevices.Remove(deviceId); MicrophoneStatusChanged?.Invoke(GetMicrophoneStatus()); } public void Stop() { _logger.Information("Stop"); _deviceEnumerator.UnregisterEndpointNotificationCallback(this); _deviceEnumerator.Dispose(); } public MicrophoneStatus GetMicrophoneStatus() { foreach (var inputDevice in _inputDevices.Values) { if (inputDevice.ActiveSessionCount > 0) return inputDevice.IsMuted ? MicrophoneStatus.UsedMuted : MicrophoneStatus.UsedNotMuted; } return MicrophoneStatus.NotUsed; } void IMMNotificationClient.OnDeviceStateChanged(string deviceId, DeviceState newState) { _logger.Information("OnDeviceStateChanged: {deviceId} {newState}", deviceId, newState); } void IMMNotificationClient.OnDeviceAdded(string deviceId) { _logger.Information("OnDeviceAdded: {deviceId}", deviceId); var device = _deviceEnumerator.GetDevice(deviceId); if (device.DataFlow is DataFlow.Capture or DataFlow.All) { HandleAddedDevice(device); } } void IMMNotificationClient.OnDeviceRemoved(string deviceId) { _logger.Information("OnDeviceRemoved: {deviceId}", deviceId); HandleRemovedDevice(deviceId); } void IMMNotificationClient.OnDefaultDeviceChanged(DataFlow flow, Role role, string defaultDeviceId) { _logger.Information("OnDefaultDeviceChanged: {defaultDeviceId} {flow} {role}", defaultDeviceId, flow, role); } void IMMNotificationClient.OnPropertyValueChanged(string pwstrDeviceId, PropertyKey key) { _logger.Debug("OnPropertyValueChanged: {deviceId} {propertyId}", pwstrDeviceId, key.propertyId); } }