diff --git a/EonaCat.VolumeMixer.Tester/Program.cs b/EonaCat.VolumeMixer.Tester/Program.cs index 81b7ab6..a03765c 100644 --- a/EonaCat.VolumeMixer.Tester/Program.cs +++ b/EonaCat.VolumeMixer.Tester/Program.cs @@ -16,7 +16,7 @@ class Program try { // Get all audio PLAYBACK devices - var devices = await volumeMixer.GetAudioDevicesAsync(DataFlow.Output); + var devices = await volumeMixer.GetAudioDevicesAsync(DataFlow.Input); Console.WriteLine($"Found {devices.Count} playback devices:"); foreach (var device in devices) @@ -36,6 +36,9 @@ class Program // Get all audio sessions var sessions = await defaultDevice.GetAudioSessionsAsync(); + var sessions2 = await defaultDevice.GetAudioSessionsAsync(); + var sessions3 = await defaultDevice.GetAudioSessionsAsync(); + var sessions4 = await defaultDevice.GetAudioSessionsAsync(); Console.WriteLine($"Active sessions: {sessions.Count}"); foreach (var session in sessions) @@ -99,15 +102,11 @@ class Program { Console.WriteLine($"Error: {ex.Message}"); } - } - // Microphone management example - using (var micManager = new MicrophoneManager()) - { try { // Get all microphones - var microphones = await micManager.GetMicrophonesAsync(); + var microphones = await volumeMixer.GetMicrophonesAsync(); Console.WriteLine($"Found {microphones.Count} microphones:"); foreach (var mic in microphones) @@ -119,7 +118,7 @@ class Program } // Default microphone - using (var defaultMic = await micManager.GetDefaultMicrophoneAsync()) + using (var defaultMic = await volumeMixer.GetDefaultMicrophoneAsync()) { Console.WriteLine($"\nSetting default microphone volume to 1%"); bool success = await defaultMic.SetMasterVolumeAsync(0.1f); @@ -149,15 +148,15 @@ class Program } } - Console.WriteLine($"Default mic volume: {await micManager.GetMicrophoneVolumeAsync():P0}"); - Console.WriteLine($"Default mic muted: {await micManager.GetMicrophoneMuteAsync()}"); + Console.WriteLine($"Default mic volume: {await volumeMixer.GetMicrophoneVolumeAsync():P0}"); + Console.WriteLine($"Default mic muted: {await volumeMixer.GetMicrophoneMuteAsync()}"); // Set microphone volume to 60% - bool result = await micManager.SetMicrophoneVolumeAsync(0.6f); + bool result = await volumeMixer.SetMicrophoneVolumeAsync(0.6f); Console.WriteLine($"Set microphone volume to 60%: {result}"); // Set specific microphone by name - result = await micManager.SetMicrophoneVolumeByNameAsync("USB", 0.7f); + result = await volumeMixer.SetMicrophoneVolumeByNameAsync("USB", 0.7f); Console.WriteLine($"Set USB microphone volume to 70%: {result}"); } diff --git a/EonaCat.VolumeMixer/EonaCat.VolumeMixer.csproj b/EonaCat.VolumeMixer/EonaCat.VolumeMixer.csproj index 09be1ff..d184bee 100644 --- a/EonaCat.VolumeMixer/EonaCat.VolumeMixer.csproj +++ b/EonaCat.VolumeMixer/EonaCat.VolumeMixer.csproj @@ -18,9 +18,9 @@ EonaCat, Audio, Volume, Mixer .NET Standard, Jeroen, Saey EonaCat VolumeMixer - 1.0.0 - 1.0.0.0 - 1.0.0.0 + 1.0.1 + 1.0.0.1 + 1.0.0.1 icon.png diff --git a/EonaCat.VolumeMixer/Managers/MicrophoneManager.cs b/EonaCat.VolumeMixer/Managers/MicrophoneManager.cs deleted file mode 100644 index b50b1b7..0000000 --- a/EonaCat.VolumeMixer/Managers/MicrophoneManager.cs +++ /dev/null @@ -1,146 +0,0 @@ -using EonaCat.VolumeMixer.Models; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace EonaCat.VolumeMixer.Managers -{ - // This file is part of the EonaCat project(s) which is released under the Apache License. - // See the LICENSE file or go to https://EonaCat.com/License for full license details. - public class MicrophoneManager : IDisposable - { - private readonly VolumeMixerManager _volumeMixer; - private readonly object _syncLock = new(); - - public MicrophoneManager() - { - _volumeMixer = new VolumeMixerManager(); - } - - public async Task> GetMicrophonesAsync() - { - return await _volumeMixer.GetAudioDevicesAsync(DataFlow.Input); - } - - public async Task GetDefaultMicrophoneAsync() - { - return await _volumeMixer.GetDefaultAudioDeviceAsync(DataFlow.Input); - } - - public async Task SetMicrophoneVolumeAsync(float volume) - { - if (volume < 0f || volume > 1f) - { - return false; - } - - AudioDevice defaultMic = await GetDefaultMicrophoneAsync(); - - if (defaultMic == null) - { - return false; - } - - try - { - return await defaultMic.SetMasterVolumeAsync(volume); - } - finally - { - defaultMic.Dispose(); - } - } - - public async Task GetMicrophoneVolumeAsync() - { - AudioDevice defaultMic = await GetDefaultMicrophoneAsync(); - - if (defaultMic == null) - { - return 0f; - } - - try - { - return await defaultMic.GetMasterVolumeAsync(); - } - finally - { - defaultMic.Dispose(); - } - } - - public async Task SetMicrophoneMuteAsync(bool mute) - { - AudioDevice defaultMic = await GetDefaultMicrophoneAsync(); - - if (defaultMic == null) - { - return false; - } - - try - { - return await defaultMic.SetMasterMuteAsync(mute); - } - finally - { - defaultMic.Dispose(); - } - } - - public async Task GetMicrophoneMuteAsync() - { - AudioDevice defaultMic = await GetDefaultMicrophoneAsync(); - - if (defaultMic == null) - { - return false; - } - - try - { - return await defaultMic.GetMasterMuteAsync(); - } - finally - { - defaultMic.Dispose(); - } - } - - public async Task SetMicrophoneVolumeByNameAsync(string microphoneName, float volume) - { - if (string.IsNullOrWhiteSpace(microphoneName) || volume < 0f || volume > 1f) - { - return false; - } - - List microphones = await GetMicrophonesAsync(); - - foreach (var mic in microphones) - { - try - { - if (mic.Name.IndexOf(microphoneName, StringComparison.OrdinalIgnoreCase) >= 0) - { - return await mic.SetMasterVolumeAsync(volume); - } - } - finally - { - mic.Dispose(); - } - } - - return false; - } - - public void Dispose() - { - lock (_syncLock) - { - _volumeMixer?.Dispose(); - } - } - } -} diff --git a/EonaCat.VolumeMixer/Managers/VolumeMixerManager.cs b/EonaCat.VolumeMixer/Managers/VolumeMixerManager.cs index ae0040b..bb40db3 100644 --- a/EonaCat.VolumeMixer/Managers/VolumeMixerManager.cs +++ b/EonaCat.VolumeMixer/Managers/VolumeMixerManager.cs @@ -92,18 +92,12 @@ namespace EonaCat.VolumeMixer.Managers } } } - catch - { - // Skip individual device on error - } + catch { } } ComHelper.ReleaseComObject(deviceCollection); } - catch - { - // Ignore all and return partial/empty result - } + catch { } } return devices; @@ -132,45 +126,78 @@ namespace EonaCat.VolumeMixer.Managers var name = GetDeviceName(device); return new AudioDevice(device, id, name, true, dataFlow); } - ComHelper.ReleaseComObject(device); } } - catch - { - // Ignore and return null - } + catch { } return null; } }); } - private string GetDeviceName(IMultiMediaDevice device) + public async Task> GetMicrophonesAsync() { - try - { - var result = device.OpenPropertyStore(0, out var propertyStore); - if (result == 0 && propertyStore != null) - { - var propertyKey = PKEY_Device_FriendlyName; - result = propertyStore.GetValue(ref propertyKey, out var propVariant); - if (result == 0 && propVariant.data != IntPtr.Zero) - { - string name = Marshal.PtrToStringUni(propVariant.data); - ComHelper.ReleaseComObject(propertyStore); - return !string.IsNullOrEmpty(name) ? name : "Unknown Device"; - } + return await GetAudioDevicesAsync(DataFlow.Input); + } - ComHelper.ReleaseComObject(propertyStore); + public async Task GetDefaultMicrophoneAsync() + { + return await GetDefaultAudioDeviceAsync(DataFlow.Input); + } + + public async Task SetAudioDeviceVolumeByNameAsync(string audioDevice, float volume) + { + if (string.IsNullOrWhiteSpace(audioDevice) || volume < 0f || volume > 1f) + { + return false; + } + + List audioDevices = await GetAudioDevicesAsync(); + + foreach (var mic in audioDevices) + { + try + { + if (mic.Name.IndexOf(audioDevice, StringComparison.OrdinalIgnoreCase) >= 0) + { + return await mic.SetMasterVolumeAsync(volume); + } + } + finally + { + mic.Dispose(); } } - catch + + return false; + } + + public async Task SetMicrophoneVolumeByNameAsync(string microphoneName, float volume) + { + if (string.IsNullOrWhiteSpace(microphoneName) || volume < 0f || volume > 1f) { - // Ignore and fall through + return false; } - return "Unknown Device"; + List microphones = await GetMicrophonesAsync(); + + foreach (var mic in microphones) + { + try + { + if (mic.Name.IndexOf(microphoneName, StringComparison.OrdinalIgnoreCase) >= 0) + { + return await mic.SetMasterVolumeAsync(volume); + } + } + finally + { + mic.Dispose(); + } + } + + return false; } public async Task SetSystemVolumeAsync(float volume) @@ -254,6 +281,87 @@ namespace EonaCat.VolumeMixer.Managers } } + public async Task GetMicrophoneVolumeAsync() + { + AudioDevice defaultMic = await GetDefaultMicrophoneAsync(); + + if (defaultMic == null) + { + return 0f; + } + + try + { + return await defaultMic.GetMasterVolumeAsync(); + } + finally + { + defaultMic.Dispose(); + } + } + + public async Task SetMicrophoneVolumeAsync(float volume) + { + if (volume < 0f || volume > 1f) + { + return false; + } + + AudioDevice defaultMic = await GetDefaultMicrophoneAsync(); + + if (defaultMic == null) + { + return false; + } + + try + { + return await defaultMic.SetMasterVolumeAsync(volume); + } + finally + { + defaultMic.Dispose(); + } + } + + public async Task SetMicrophoneMuteAsync(bool mute) + { + AudioDevice defaultMic = await GetDefaultMicrophoneAsync(); + + if (defaultMic == null) + { + return false; + } + + try + { + return await defaultMic.SetMasterMuteAsync(mute); + } + finally + { + defaultMic.Dispose(); + } + } + + public async Task GetMicrophoneMuteAsync() + { + AudioDevice defaultMic = await GetDefaultMicrophoneAsync(); + + if (defaultMic == null) + { + return false; + } + + try + { + return await defaultMic.GetMasterMuteAsync(); + } + finally + { + defaultMic.Dispose(); + } + } + public async Task> GetAllActiveSessionsAsync() { return await Task.Run(async () => @@ -268,10 +376,7 @@ namespace EonaCat.VolumeMixer.Managers { allSessions.AddRange(await device.GetAudioSessionsAsync()); } - catch - { - // Skip device on error - } + catch { } finally { device.Dispose(); @@ -282,6 +387,30 @@ namespace EonaCat.VolumeMixer.Managers }); } + private string GetDeviceName(IMultiMediaDevice device) + { + try + { + var result = device.OpenPropertyStore(0, out var propertyStore); + if (result == 0 && propertyStore != null) + { + var propertyKey = PKEY_Device_FriendlyName; + result = propertyStore.GetValue(ref propertyKey, out var propVariant); + if (result == 0 && propVariant.data != IntPtr.Zero) + { + string name = Marshal.PtrToStringUni(propVariant.data); + ComHelper.ReleaseComObject(propertyStore); + return !string.IsNullOrEmpty(name) ? name : "Unknown Device"; + } + + ComHelper.ReleaseComObject(propertyStore); + } + } + catch { } + + return "Unknown Device"; + } + public void Dispose() { lock (_syncLock) diff --git a/EonaCat.VolumeMixer/Models/AudioDevice.cs b/EonaCat.VolumeMixer/Models/AudioDevice.cs index ec5c711..fd068f4 100644 --- a/EonaCat.VolumeMixer/Models/AudioDevice.cs +++ b/EonaCat.VolumeMixer/Models/AudioDevice.cs @@ -79,8 +79,8 @@ namespace EonaCat.VolumeMixer.Models try { - var hr = _endpointVolume.GetMasterVolumeLevelScalar(out var volume); - return hr == 0 ? volume : 0f; + var result = _endpointVolume.GetMasterVolumeLevelScalar(out var volume); + return result == 0 ? volume : 0f; } catch { diff --git a/EonaCat.VolumeMixer/Models/AudioSession.cs b/EonaCat.VolumeMixer/Models/AudioSession.cs index c25328f..6068cdc 100644 --- a/EonaCat.VolumeMixer/Models/AudioSession.cs +++ b/EonaCat.VolumeMixer/Models/AudioSession.cs @@ -10,6 +10,7 @@ namespace EonaCat.VolumeMixer.Models // See the LICENSE file or go to https://EonaCat.com/License for full license details. public class AudioSession : IDisposable { + private static Guid SharedAppSessionGuid = new Guid("c0a3a9d0-b4dc-58b7-b95e-dcc407dbd8db"); private readonly IAudioSessionControlExtended _sessionControl; private IAudioVolume _audioVolume; private bool _isDisposed = false; @@ -33,14 +34,11 @@ namespace EonaCat.VolumeMixer.Models { try { - var result = _sessionControl.GetGroupingParam(out var guid); - if (result == 0) + // Always use the shared static GUID + var result = sessionManager.GetAudioVolume(ref SharedAppSessionGuid, 0, out var ptr); + if (result == 0 && ptr != IntPtr.Zero) { - result = sessionManager.GetAudioVolume(ref guid, 0, out var ptr); - if (result == 0 && ptr != IntPtr.Zero) - { - _audioVolume = ComHelper.GetInterface(ptr); - } + _audioVolume = ComHelper.GetInterface(ptr); } } catch @@ -126,8 +124,8 @@ namespace EonaCat.VolumeMixer.Models { try { - var hr = audioVolCopy.SetMasterVolume(volume, ref guid); - if (hr == 0) + var result = audioVolCopy.SetMasterVolume(volume, ref guid); + if (result == 0) { await Task.Delay(delayMs); diff --git a/README.md b/README.md index 36c07de..4f4cf84 100644 --- a/README.md +++ b/README.md @@ -88,18 +88,18 @@ using System.Threading.Tasks; class Program { - // This file is part of the EonaCat project(s) which is released under the Apache License. + // This file is part of the EonaCat project(s) which is released under the Apache License. // See the LICENSE file or go to https://EonaCat.com/License for full license details. [STAThread] static async Task Main() { - // Playback management example + // 1. Create the volume mixer manager using (var volumeMixer = new VolumeMixerManager()) { try { - // Get all audio PLAYBACK devices - var devices = await volumeMixer.GetAudioDevicesAsync(DataFlow.Output); + // 2. Get all audio PLAYBACK devices + var devices = await volumeMixer.GetAudioDevicesAsync(DataFlow.Input); Console.WriteLine($"Found {devices.Count} playback devices:"); foreach (var device in devices) @@ -119,6 +119,9 @@ class Program // Get all audio sessions var sessions = await defaultDevice.GetAudioSessionsAsync(); + var sessions2 = await defaultDevice.GetAudioSessionsAsync(); + var sessions3 = await defaultDevice.GetAudioSessionsAsync(); + var sessions4 = await defaultDevice.GetAudioSessionsAsync(); Console.WriteLine($"Active sessions: {sessions.Count}"); foreach (var session in sessions) @@ -182,33 +185,11 @@ class Program { Console.WriteLine($"Error: {ex.Message}"); } - } - } -} -``` - -## Microphone management example: -```csharp -using EonaCat.VolumeMixer.Managers; -using EonaCat.VolumeMixer.Models; -using System; -using System.Threading.Tasks; - -class Program -{ - // This file is part of the EonaCat project(s) which is released under the Apache License. - // See the LICENSE file or go to https://EonaCat.com/License for full license details. - [STAThread] - static async Task Main() - { - // Microphone management example - using (var micManager = new MicrophoneManager()) - { try { - // Get all microphones - var microphones = await micManager.GetMicrophonesAsync(); + // 3. Get all microphones + var microphones = await volumeMixer.GetMicrophonesAsync(); Console.WriteLine($"Found {microphones.Count} microphones:"); foreach (var mic in microphones) @@ -220,7 +201,7 @@ class Program } // Default microphone - using (var defaultMic = await micManager.GetDefaultMicrophoneAsync()) + using (var defaultMic = await volumeMixer.GetDefaultMicrophoneAsync()) { Console.WriteLine($"\nSetting default microphone volume to 1%"); bool success = await defaultMic.SetMasterVolumeAsync(0.1f); @@ -250,15 +231,15 @@ class Program } } - Console.WriteLine($"Default mic volume: {await micManager.GetMicrophoneVolumeAsync():P0}"); - Console.WriteLine($"Default mic muted: {await micManager.GetMicrophoneMuteAsync()}"); + Console.WriteLine($"Default mic volume: {await volumeMixer.GetMicrophoneVolumeAsync():P0}"); + Console.WriteLine($"Default mic muted: {await volumeMixer.GetMicrophoneMuteAsync()}"); // Set microphone volume to 60% - bool result = await micManager.SetMicrophoneVolumeAsync(0.6f); + bool result = await volumeMixer.SetMicrophoneVolumeAsync(0.6f); Console.WriteLine($"Set microphone volume to 60%: {result}"); // Set specific microphone by name - result = await micManager.SetMicrophoneVolumeByNameAsync("USB", 0.7f); + result = await volumeMixer.SetMicrophoneVolumeByNameAsync("USB", 0.7f); Console.WriteLine($"Set USB microphone volume to 70%: {result}"); } @@ -268,5 +249,4 @@ class Program } } } -} ``` \ No newline at end of file