From e1ca9f67ac40284caee3db1ae5aa75e97afde447 Mon Sep 17 00:00:00 2001 From: Chris Kaczor Date: Fri, 16 Apr 2021 21:38:38 -0400 Subject: [PATCH] WIP - microphone in use detection --- AudioWatcher.cs | 92 ++++++++++++++++ Delcom/Delcom.cs | 2 +- Delcom/StoplightIndicator.cs | 8 +- LightController.cs | 118 ++------------------- Options/WindowPatternWindow.xaml | 87 --------------- Options/WindowPatternWindow.xaml.cs | 66 ------------ Options/WindowPatternsOptionsPanel.xaml | 79 -------------- Options/WindowPatternsOptionsPanel.xaml.cs | 114 -------------------- TrayIcon.cs | 8 +- WindowPattern.cs | 9 -- WindowPatterns.cs | 47 -------- WorkIndicator.csproj | 22 +--- WorkIndicator.sln.DotSettings | 2 + 13 files changed, 116 insertions(+), 538 deletions(-) create mode 100644 AudioWatcher.cs delete mode 100644 Options/WindowPatternWindow.xaml delete mode 100644 Options/WindowPatternWindow.xaml.cs delete mode 100644 Options/WindowPatternsOptionsPanel.xaml delete mode 100644 Options/WindowPatternsOptionsPanel.xaml.cs delete mode 100644 WindowPattern.cs delete mode 100644 WindowPatterns.cs create mode 100644 WorkIndicator.sln.DotSettings diff --git a/AudioWatcher.cs b/AudioWatcher.cs new file mode 100644 index 0000000..7048201 --- /dev/null +++ b/AudioWatcher.cs @@ -0,0 +1,92 @@ +using CSCore.CoreAudioAPI; +using System; +using System.Diagnostics; +using System.Threading; + +namespace WorkIndicator +{ + public static class AudioWatcher + { + public delegate void MicrophoneInUseChangedDelegate(bool microphoneInUse); + + private static ManualResetEvent _manualResetEvent; + + private static int _activeSessionCount; + + public static event MicrophoneInUseChangedDelegate MicrophoneInUseChanged; + + public static void Start() + { + _manualResetEvent = new ManualResetEvent(false); + + var thread = new Thread(delegate () + { + var deviceEnumerator = new MMDeviceEnumerator(); + + foreach (var device in deviceEnumerator.EnumAudioEndpoints(DataFlow.Capture, DeviceState.Active)) + { + var sessionManager = AudioSessionManager2.FromMMDevice(device); + + var sessionEnumerator = sessionManager.GetSessionEnumerator(); + + sessionManager.SessionCreated += HandleAudioSessionCreated; + + foreach (var audioSessionControl in sessionEnumerator) + { + HandleAudioStateChanged(audioSessionControl, new AudioSessionStateChangedEventArgs(audioSessionControl.SessionState)); + + audioSessionControl.StateChanged += HandleAudioStateChanged; + } + } + + _manualResetEvent.WaitOne(); + }); + + thread.SetApartmentState(ApartmentState.MTA); + thread.Start(); + } + + public static void Stop() + { + _manualResetEvent?.Set(); + } + + private static void HandleAudioSessionCreated(object sender, SessionCreatedEventArgs e) + { + HandleAudioStateChanged(null, new AudioSessionStateChangedEventArgs(e.NewSession.SessionState)); + + e.NewSession.StateChanged += HandleAudioStateChanged; + } + + private static void HandleAudioStateChanged(object sender, AudioSessionStateChangedEventArgs e) + { + Debug.WriteLine($"{e.NewState}"); + + switch (e.NewState) + { + case AudioSessionState.AudioSessionStateActive: + _activeSessionCount++; + break; + case AudioSessionState.AudioSessionStateInactive: + _activeSessionCount--; + break; + case AudioSessionState.AudioSessionStateExpired: + break; + default: + throw new ArgumentOutOfRangeException(); + } + + if (_activeSessionCount < 0) + _activeSessionCount = 0; + + Debug.WriteLine($"{_activeSessionCount}"); + + MicrophoneInUseChanged?.Invoke(MicrophoneInUse()); + } + + public static bool MicrophoneInUse() + { + return _activeSessionCount > 0; + } + } +} diff --git a/Delcom/Delcom.cs b/Delcom/Delcom.cs index 28ead9b..40fa653 100644 --- a/Delcom/Delcom.cs +++ b/Delcom/Delcom.cs @@ -154,7 +154,7 @@ namespace WorkIndicator.Delcom { try { - if (!_deviceHandle.IsClosed) + if (_deviceHandle != null && !_deviceHandle.IsClosed) _deviceHandle.Close(); return 0; diff --git a/Delcom/StoplightIndicator.cs b/Delcom/StoplightIndicator.cs index e7cd731..aafe777 100644 --- a/Delcom/StoplightIndicator.cs +++ b/Delcom/StoplightIndicator.cs @@ -1,6 +1,6 @@ -using System; - -using Common.Extensions; +using Common.Extensions; +using System; +using System.Diagnostics; namespace WorkIndicator.Delcom { @@ -80,6 +80,8 @@ namespace WorkIndicator.Delcom _yellow = yellow; _green = green; + Debug.WriteLine($"Red: {_red}, Yellow: {_yellow}, Green: {_green}"); + port1 = port1.SetBitValue((int) Light.Green, green == LightState.Off); port1 = port1.SetBitValue((int) Light.Yellow, yellow == LightState.Off); port1 = port1.SetBitValue((int) Light.Red, red == LightState.Off); diff --git a/LightController.cs b/LightController.cs index aadeb91..f49c7d0 100644 --- a/LightController.cs +++ b/LightController.cs @@ -1,8 +1,4 @@ -using Common.Native; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; +using System; using WorkIndicator.Delcom; namespace WorkIndicator @@ -18,46 +14,32 @@ namespace WorkIndicator public static class LightController { - private static WindowPatterns _windowPatterns; private static StoplightIndicator _stoplightIndicator; private static bool _initialized; private static Status _status = Status.Auto; public static void Initialize() { - WindowPatterns.Changed += HandleWindowPatternsChanged; - _stoplightIndicator = new StoplightIndicator(); - _stoplightIndicator.SetLight(StoplightIndicator.Light.Green, StoplightIndicator.LightState.On); + _stoplightIndicator.SetLight(StoplightIndicator.Light.Yellow, StoplightIndicator.LightState.On); - LoadPatterns(); + AudioWatcher.MicrophoneInUseChanged += AudioWatcher_MicrophoneInUseChanged; + AudioWatcher.Start(); _initialized = true; } - private static void LoadPatterns() + private static void AudioWatcher_MicrophoneInUseChanged(bool microphoneInUse) { - _windowPatterns = WindowPatterns.Load(); - - if (_initialized) - TerminateWindowDetection(); - - InitializeWindowDetection(); - UpdateLights(); } - private static void HandleWindowPatternsChanged(object sender, EventArgs e) - { - LoadPatterns(); - } - public static void Dispose() { if (!_initialized) return; - TerminateWindowDetection(); + AudioWatcher.Stop(); _stoplightIndicator.SetLights(StoplightIndicator.LightState.Off, StoplightIndicator.LightState.Off, StoplightIndicator.LightState.Off); _stoplightIndicator.Dispose(); @@ -83,13 +65,13 @@ namespace WorkIndicator if (_status == Status.Auto) { - if (WindowHandles.Count > 0) + if (AudioWatcher.MicrophoneInUse()) { - yellow = StoplightIndicator.LightState.On; + red = StoplightIndicator.LightState.On; } else { - green = StoplightIndicator.LightState.On; + yellow = StoplightIndicator.LightState.On; } } else @@ -115,87 +97,5 @@ namespace WorkIndicator _stoplightIndicator.SetLights(red, yellow, green); } - - #region Window detection - - private static readonly WinEvent.WinEventDelegate ProcDelegate = WinEventProc; - - private static readonly List WindowEventHooks = new List(); - private static readonly List WindowHandles = new List(); - - private static readonly List WindowMatchRegexList = new List(); - - private static string WildcardToRegexPattern(string value) - { - return "^" + Regex.Escape(value).Replace("\\?", ".").Replace("\\*", ".*") + "$"; - } - - private static void InitializeWindowDetection() - { - foreach (var windowPattern in _windowPatterns.Where(w => w.Enabled)) - WindowMatchRegexList.Add(new Regex(WildcardToRegexPattern(windowPattern.Pattern))); - - Functions.User32.EnumWindows(EnumWindowProc, IntPtr.Zero); - - IntPtr hook = WinEvent.SetWinEventHook(WinEvent.Event.ObjectCreate, WinEvent.Event.ObjectDestroy, IntPtr.Zero, ProcDelegate, 0, 0, WinEvent.SetWinEventHookFlags.OutOfContext | WinEvent.SetWinEventHookFlags.SkipOwnProcess); - if (hook != IntPtr.Zero) - WindowEventHooks.Add(hook); - - hook = WinEvent.SetWinEventHook(WinEvent.Event.ObjectNameChange, WinEvent.Event.ObjectNameChange, IntPtr.Zero, ProcDelegate, 0, 0, WinEvent.SetWinEventHookFlags.OutOfContext | WinEvent.SetWinEventHookFlags.SkipOwnProcess); - if (hook != IntPtr.Zero) - WindowEventHooks.Add(hook); - } - - private static void TerminateWindowDetection() - { - WindowEventHooks.ForEach(h => WinEvent.UnhookWinEvent(h)); - - WindowMatchRegexList.Clear(); - - WindowHandles.Clear(); - } - - private static bool EnumWindowProc(IntPtr hWnd, IntPtr lParam) - { - WinEventProc(IntPtr.Zero, WinEvent.Event.ObjectCreate, hWnd, WinEvent.ObjectIdentifier.Window, 0, 0, 0); - return true; - } - - private static void WinEventProc(IntPtr hWinEventHook, WinEvent.Event eventType, IntPtr hwnd, WinEvent.ObjectIdentifier idObject, int idChild, uint dwEventThread, uint dwmsEventTime) - { - if (idObject == WinEvent.ObjectIdentifier.Window && idChild == (int) WinEvent.ChildIdentifier.Self) - { - switch (eventType) - { - case WinEvent.Event.ObjectCreate: - case WinEvent.Event.ObjectNameChange: - - if (WindowHandles.Contains(hwnd)) - WindowHandles.Remove(hwnd); - - foreach (var regex in WindowMatchRegexList) - { - if (regex.IsMatch(Functions.Window.GetText(hwnd))) - { - WindowHandles.Add(hwnd); - UpdateLights(); - } - } - - break; - - case WinEvent.Event.ObjectDestroy: - if (WindowHandles.Contains(hwnd)) - { - WindowHandles.Remove(hwnd); - UpdateLights(); - } - - break; - } - } - } - - #endregion } } diff --git a/Options/WindowPatternWindow.xaml b/Options/WindowPatternWindow.xaml deleted file mode 100644 index 8083b16..0000000 --- a/Options/WindowPatternWindow.xaml +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - -