diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetApplicationVolumeCommand.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetApplicationVolumeCommand.cs index 668050c2..cdb39cdc 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetApplicationVolumeCommand.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetApplicationVolumeCommand.cs @@ -34,16 +34,6 @@ public override void TurnOn() TurnOnWithAction(CommandConfig); } - private AudioDevice GetDeviceOrDefault(string deviceName) - { - var device = AudioManager.GetDevices().Where(d => d.FriendlyName == deviceName).FirstOrDefault(); - if (device != null) - return device; - - var defaultDeviceId = AudioManager.GetDefaultDeviceId(DeviceType.Output, DeviceRole.Multimedia | DeviceRole.Console); - return AudioManager.GetDevices().Where(d => d.Id == defaultDeviceId).FirstOrDefault(); - } - public override void TurnOnWithAction(string action) { State = "ON"; diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioInputCommand.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioInputCommand.cs index 1e79d0a5..1b6b1a35 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioInputCommand.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioInputCommand.cs @@ -42,23 +42,6 @@ public override void TurnOnWithAction(string action) try { -/* var audioDevices = AudioManager.GetDevices(); - var inputDevice = audioDevices - .Where(d => d.Type == DeviceType.Input) - .Where(d => d.FriendlyName == action) - .FirstOrDefault(); - - if (inputDevice == null) - { - Log.Warning("[SETAUDIOIN] No input device {device} found", action); - return; - } - - if(inputDevice.Default) - return; - - AudioManager.Activate(inputDevice);*/ - AudioManager.ActivateDevice(action); } catch (Exception ex) diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioOutputCommand.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioOutputCommand.cs index 82aa8eaa..51769946 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioOutputCommand.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Commands/InternalCommands/SetAudioOutputCommand.cs @@ -42,23 +42,6 @@ public override void TurnOnWithAction(string action) try { -/* var audioDevices = AudioManager.GetDevices(); - var outputDevice = audioDevices - .Where(d => d.Type == DeviceType.Output) - .Where(d => d.FriendlyName == action) - .FirstOrDefault(); - - if (outputDevice == null) - { - Log.Warning("[SETAUDIOOUT] No input device {device} found", action); - return; - } - - if (outputDevice.Default) - return; - - AudioManager.Activate(outputDevice);*/ - AudioManager.ActivateDevice(action); } catch (Exception ex) diff --git a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/CurrentVolumeSensor.cs b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/CurrentVolumeSensor.cs index e1a03ce9..659db848 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/CurrentVolumeSensor.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/HomeAssistant/Sensors/GeneralSensors/SingleValue/CurrentVolumeSensor.cs @@ -39,13 +39,10 @@ public override DiscoveryConfigModel GetAutoDiscoveryConfig() public override string GetState() { - var defaultDeviceId = AudioManager.GetDefaultDeviceId(DeviceType.Output, DeviceRole.Multimedia | DeviceRole.Console); - var audioDevice = AudioManager.GetDevices().Where(d => d.Id == defaultDeviceId).FirstOrDefault(); - if (audioDevice == null) - return "0"; + var volume = AudioManager.GetDefaultDeviceVolume(DeviceType.Output, DeviceRole.Multimedia | DeviceRole.Console); // return as percentage - return audioDevice.Volume.ToString(CultureInfo.InvariantCulture); + return volume.ToString(CultureInfo.InvariantCulture); } public override string GetAttributes() => string.Empty; diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioManager.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioManager.cs index d094c580..9dd1c225 100644 --- a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioManager.cs +++ b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/AudioManager.cs @@ -11,6 +11,7 @@ using Serilog; using NAudio.CoreAudioApi.Interfaces; using HidSharp; +using Microsoft.VisualBasic.ApplicationServices; namespace HASS.Agent.Shared.Managers.Audio; public static class AudioManager @@ -21,8 +22,6 @@ public static class AudioManager private static MMNotificationClient _notificationClient = null; private static readonly ConcurrentDictionary _devices = new(); - private static readonly ConcurrentQueue _devicesToBeRemoved = new(); - private static readonly ConcurrentQueue _devicesToBeAdded = new(); private static readonly Dictionary _applicationNameCache = new(); @@ -42,67 +41,53 @@ private static void InitializeDevices() _initialized = true; } + private static void AddDevice(string deviceId) + { + if (_devices.ContainsKey(deviceId)) + return; + + using var device = _enumerator.GetDevice(deviceId); + _devices[deviceId] = device.FriendlyName; + Log.Debug($"[AUDIOMGR] added device: {_devices[deviceId]}"); + } + + private static void RemoveDevice(string deviceId) + { + _devices.Remove(deviceId, out var removedDeviceName); + if (!string.IsNullOrWhiteSpace(removedDeviceName)) + Log.Debug($"[AUDIOMGR] removed device: {removedDeviceName}"); + } + + private static void DeviceRemoved(object sender, DeviceNotificationEventArgs e) => RemoveDevice(e.DeviceId); + private static void DeviceAdded(object sender, DeviceNotificationEventArgs e) => AddDevice(e.DeviceId); + private static void DeviceStateChanged(object sender, DeviceStateChangedEventArgs e) { switch (e.DeviceState) { case DeviceState.Active: - if (_devices.ContainsKey(e.DeviceId)) - return; - - _devices.Remove(e.DeviceId, out var _); - //_devicesToBeAdded.Enqueue(e.DeviceId); + AddDevice(e.DeviceId); break; case DeviceState.NotPresent: case DeviceState.Unplugged: case DeviceState.Disabled: - { - using var device = _enumerator.GetDevice(e.DeviceId); - _devices[e.DeviceId] = device.FriendlyName; - //_devicesToBeRemoved.Enqueue(e.DeviceId); - break; - } + RemoveDevice(e.DeviceId); + break; default: break; } } - private static void DeviceRemoved(object sender, DeviceNotificationEventArgs e) - { - _devicesToBeRemoved.Enqueue(e.DeviceId); - } - - private static void DeviceAdded(object sender, DeviceNotificationEventArgs e) - { - if (_devices.ContainsKey(e.DeviceId)) - return; - - _devicesToBeAdded.Enqueue(e.DeviceId); - } - private static bool CheckInitialization() { if (!_initialized) { Log.Warning("[AUDIOMGR] not yet initialized!"); - return false; } - /* while (_devicesToBeRemoved.TryDequeue(out var deviceId)) - { - if (_devices.Remove(deviceId, out var device)) - device.Dispose(); - } - - while (_devicesToBeAdded.TryDequeue(out var deviceId)) - { - var device = _enumerator.GetDevice(deviceId); - _devices[deviceId] = new InternalAudioDevice(device); - }*/ - return true; } @@ -125,7 +110,7 @@ private static string GetSessionDisplayName(InternalAudioSession session) private static List GetDeviceSessions(MMDevice mmDevice) { using var internalAudioSessionManager = new InternalAudioSessionManager(mmDevice.AudioSessionManager); - return GetDeviceSessions(mmDevice.FriendlyName, internalAudioSessionManager); + return GetDeviceSessions(_devices[mmDevice.ID], internalAudioSessionManager); } private static List GetDeviceSessions(string deviceName, InternalAudioSessionManager internalAudioSessionManager) @@ -187,26 +172,11 @@ public static void Initialize() Log.Information("[AUDIOMGR] initialized"); } - public static void ReInitialize() - { - if (_initialized) - CleanupDevices(); - - Log.Debug("[AUDIOMGR] re-initializing"); - - InitializeDevices(); - - Log.Information("[AUDIOMGR] re-initialized"); - } - public static void CleanupDevices() { Log.Debug("[AUDIOMGR] starting cleanup"); _initialized = false; - /* foreach (var device in _devices.Values) - device.Dispose();*/ - _notificationClient.DeviceAdded -= DeviceAdded; _notificationClient.DeviceRemoved -= DeviceRemoved; _notificationClient.DeviceStateChanged -= DeviceStateChanged; @@ -233,7 +203,7 @@ public static List GetDevices() var defaultInputDeviceId = defaultInputDevice.ID; var defaultOutputDeviceId = defaultOutputDevice.ID; - foreach (var deviceId in _devices.Keys) + foreach (var (deviceId, deviceName) in _devices) { using var device = _enumerator.GetDevice(deviceId); @@ -245,7 +215,7 @@ public static List GetDevices() State = GetReadableState(device.State), Type = device.DataFlow == DataFlow.Capture ? DeviceType.Input : DeviceType.Output, Id = device.ID, - FriendlyName = device.FriendlyName, + FriendlyName = deviceName, Volume = Convert.ToInt32(Math.Round(device.AudioEndpointVolume.MasterVolumeLevelScalar * 100, 0)), Muted = device.AudioEndpointVolume.Mute, PeakVolume = loudestSession == null ? 0 : loudestSession.PeakVolume, @@ -258,7 +228,7 @@ public static List GetDevices() } catch (Exception ex) { - Log.Debug(ex, "[AUDIOMGR] Failed to retrieve devices: {msg}", ex.Message); + Log.Debug(ex, "[AUDIOMGR] failed to retrieve devices: {msg}", ex.Message); } return audioDevices; @@ -277,6 +247,32 @@ public static string GetDefaultDeviceId(DeviceType deviceType, DeviceRole device return defaultDevice.ID; } + public static int GetDefaultDeviceVolume(DeviceType deviceType, DeviceRole deviceRole) + { + if (!CheckInitialization()) + return 0; + + var dataFlow = deviceType == DeviceType.Input ? DataFlow.Capture : DataFlow.Render; + var role = (Role)deviceRole; + + using var defaultDevice = _enumerator.GetDefaultAudioEndpoint(dataFlow, role); + + return Convert.ToInt32(Math.Round(defaultDevice.AudioEndpointVolume.MasterVolumeLevelScalar * 100, 0)); + } + + public static bool GetDefaultDeviceMute(DeviceType deviceType, DeviceRole deviceRole) + { + if (!CheckInitialization()) + return false; + + var dataFlow = deviceType == DeviceType.Input ? DataFlow.Capture : DataFlow.Render; + var role = (Role)deviceRole; + + using var defaultDevice = _enumerator.GetDefaultAudioEndpoint(dataFlow, role); + + return defaultDevice.AudioEndpointVolume.Mute; + } + public static void ActivateDevice(string deviceName) { if (!CheckInitialization()) @@ -288,6 +284,8 @@ public static void ActivateDevice(string deviceName) using var configClient = new CPolicyConfigVistaClient(); configClient.SetDefaultDevice(deviceId); + + Log.Debug($"[AUDIOMGR] device '{deviceName}' activated"); } public static void SetDeviceProperties(string deviceName, int volume, bool? mute) @@ -308,12 +306,14 @@ private static void SetDeviceProperties(MMDevice device, int volume, bool? mute) if (mute != null) { device.AudioEndpointVolume.Mute = (bool)mute; + Log.Debug($"[AUDIOMGR] mute for '{_devices[device.ID]}' set to '{mute}'"); } if (volume != -1) { var volumeScalar = volume / 100f; device.AudioEndpointVolume.MasterVolumeLevelScalar = volumeScalar; + Log.Debug($"[AUDIOMGR] volume for '{_devices[device.ID]}' set to '{volume}'"); } } @@ -336,7 +336,7 @@ public static void SetApplicationProperties(string deviceName, string applicatio using var device = _enumerator.GetDevice(deviceId); using var sessionManager = new InternalAudioSessionManager(device.AudioSessionManager); - var sessions = GetDeviceSessions(device.FriendlyName, sessionManager); + var sessions = GetDeviceSessions(deviceName, sessionManager); var applicationAudioSessions = sessions.Where(s => s.Application == applicationName @@ -356,7 +356,7 @@ public static void SetApplicationProperties(string deviceName, string applicatio { if (!sessionManager.Sessions.TryGetValue(sessionId, out var internalSession)) { - Log.Debug("[SETAPPVOLUME] No session {actionData.SessionId} found for device {device}", applicationName, deviceName); + Log.Debug("[AUDIOMGR] no session '{sessionId}' found for device '{device}'", sessionId, deviceName); return; } @@ -366,13 +366,17 @@ public static void SetApplicationProperties(string deviceName, string applicatio private static void SetSessionProperties(InternalAudioSession internalAudioSession, int volume, bool mute) { + var displayName = string.IsNullOrWhiteSpace(internalAudioSession.DisplayName) ? GetSessionDisplayName(internalAudioSession) : internalAudioSession.DisplayName; + internalAudioSession.Volume.Mute = mute; + Log.Debug("[AUDIOMGR] mute for '{sessionName}' ({sessionId}) set to '{mute}'", displayName, internalAudioSession.Control.GetSessionInstanceIdentifier, mute); if (volume == -1) return; var volumeScalar = volume / 100f; internalAudioSession.Volume.Volume = volumeScalar; + Log.Debug("[AUDIOMGR] volume for '{sessionName}' ({sessionId}) set to '{vol}'", displayName, internalAudioSession.Control.GetSessionInstanceIdentifier, volume); } public static void Shutdown() diff --git a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioDevice.cs b/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioDevice.cs deleted file mode 100644 index 276315a6..00000000 --- a/src/HASS.Agent/HASS.Agent.Shared/Managers/Audio/Internal/InternalAudioDevice.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using HidSharp; -using NAudio.CoreAudioApi; - -namespace HASS.Agent.Shared.Managers.Audio.Internal; -internal class InternalAudioDevice : IDisposable -{ - private readonly MMDeviceEnumerator _enumerator; - - //public MMDevice MMDevice { get; private set; } - //public AudioEndpointVolume AudioEndpointVolume { get; private set; } - //public InternalAudioSessionManager Manager { get; private set; } - - public string DeviceId { get; private set; } - public string FriendlyName { get; private set; } - public bool Reinitialized { get; private set; } - - public InternalAudioDevice(MMDeviceEnumerator enumerator, string deviceId) - { - _enumerator = enumerator; - DeviceId = deviceId; - //Manager = new InternalAudioSessionManager(device.AudioSessionManager); - //AudioEndpointVolume = device.AudioEndpointVolume; - - //DeviceId = device.ID; - - using var device = GetMMDevice(); - FriendlyName = device.FriendlyName; - } - - public void Activate() - { - using var configClient = new CPolicyConfigVistaClient(); - configClient.SetDefaultDevice(DeviceId); - } - - public void Dispose() - { - //AudioEndpointVolume?.Dispose(); - //Manager?.Dispose(); - //MMDevice?.Dispose(); - - GC.SuppressFinalize(this); - } - - public MMDevice GetMMDevice() - { - return _enumerator.GetDevice(DeviceId); - } - - ~InternalAudioDevice() - { - Dispose(); - } -} \ No newline at end of file diff --git a/src/HASS.Agent/HASS.Agent/Managers/SystemStateManager.cs b/src/HASS.Agent/HASS.Agent/Managers/SystemStateManager.cs index a8794708..3354bda2 100644 --- a/src/HASS.Agent/HASS.Agent/Managers/SystemStateManager.cs +++ b/src/HASS.Agent/HASS.Agent/Managers/SystemStateManager.cs @@ -202,8 +202,6 @@ private static void SystemEventsOnPowerModeChanged(object sender, PowerModeChang Task.Run(() => Variables.MqttManager.AnnounceAvailabilityAsync()); LastSystemStateEvent = SystemStateEvent.Resume; - AudioManager.ReInitialize(); - break; case PowerModes.Suspend: @@ -214,8 +212,6 @@ private static void SystemEventsOnPowerModeChanged(object sender, PowerModeChang Task.Run(() => Variables.MqttManager.AnnounceAvailabilityAsync(true)); LastSystemStateEvent = SystemStateEvent.Suspend; - AudioManager.CleanupDevices(); - break; } } diff --git a/src/HASS.Agent/HASS.Agent/Media/MediaManagerRequests.cs b/src/HASS.Agent/HASS.Agent/Media/MediaManagerRequests.cs index bdf93fca..ceff8721 100644 --- a/src/HASS.Agent/HASS.Agent/Media/MediaManagerRequests.cs +++ b/src/HASS.Agent/HASS.Agent/Media/MediaManagerRequests.cs @@ -19,12 +19,7 @@ internal static int GetVolume() { try { - var defaultDeviceId = AudioManager.GetDefaultDeviceId(DeviceType.Output, DeviceRole.Multimedia); - var audioDevice = AudioManager.GetDevices().Where(d => d.Id == defaultDeviceId).FirstOrDefault(); - if (audioDevice == null) - return 0; - - return audioDevice.Volume; + return AudioManager.GetDefaultDeviceVolume(DeviceType.Output, DeviceRole.Multimedia); } catch (Exception ex) { @@ -41,12 +36,7 @@ internal static bool GetMuteState() { try { - var defaultDeviceId = AudioManager.GetDefaultDeviceId(DeviceType.Output, DeviceRole.Multimedia); - var audioDevice = AudioManager.GetDevices().Where(d => d.Id == defaultDeviceId).FirstOrDefault(); - if (audioDevice == null) - return false; - - return audioDevice.Muted; + return AudioManager.GetDefaultDeviceMute(DeviceType.Output, DeviceRole.Multimedia); } catch (Exception ex) {