diff --git a/EonaCat.VolumeMixer.Tester/Program.cs b/EonaCat.VolumeMixer.Tester/Program.cs
index 81b7ab6..dc69901 100644
--- a/EonaCat.VolumeMixer.Tester/Program.cs
+++ b/EonaCat.VolumeMixer.Tester/Program.cs
@@ -99,15 +99,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 +115,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 +145,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..2fafa65 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.3
+ 1.0.0.3
+ 1.0.0.3
icon.png
diff --git a/EonaCat.VolumeMixer/Interfaces/IAudioSessionControlExtended.cs b/EonaCat.VolumeMixer/Interfaces/IAudioSessionControl2.cs
similarity index 100%
rename from EonaCat.VolumeMixer/Interfaces/IAudioSessionControlExtended.cs
rename to EonaCat.VolumeMixer/Interfaces/IAudioSessionControl2.cs
diff --git a/EonaCat.VolumeMixer/Interfaces/IAudioSessionEnumerator.cs b/EonaCat.VolumeMixer/Interfaces/IAudioSessionEnumerator.cs
index 632e641..cd8373f 100644
--- a/EonaCat.VolumeMixer/Interfaces/IAudioSessionEnumerator.cs
+++ b/EonaCat.VolumeMixer/Interfaces/IAudioSessionEnumerator.cs
@@ -11,6 +11,7 @@ namespace EonaCat.VolumeMixer.Interfaces
internal interface IAudioSessionEnumerator
{
int GetCount(out int SessionCount);
- int GetSession(int SessionNumber, out IAudioSessionControlExtended Session);
+ int GetSession(int SessionNumber, out IAudioSessionControl Session);
}
+
}
\ No newline at end of file
diff --git a/EonaCat.VolumeMixer/Interfaces/IAudioSessionManager.cs b/EonaCat.VolumeMixer/Interfaces/IAudioSessionManager2.cs
similarity index 100%
rename from EonaCat.VolumeMixer/Interfaces/IAudioSessionManager.cs
rename to EonaCat.VolumeMixer/Interfaces/IAudioSessionManager2.cs
diff --git a/EonaCat.VolumeMixer/Interfaces/IAudioVolume.cs b/EonaCat.VolumeMixer/Interfaces/IAudioVolume.cs
index 6791d91..ba24589 100644
--- a/EonaCat.VolumeMixer/Interfaces/IAudioVolume.cs
+++ b/EonaCat.VolumeMixer/Interfaces/IAudioVolume.cs
@@ -8,11 +8,11 @@ namespace EonaCat.VolumeMixer.Interfaces
[ComImport]
[Guid("87CE5498-68D6-44E5-9215-6DA47EF883D8")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
- internal interface IAudioVolume
+ interface IAudioVolume
{
int SetMasterVolume(float fLevel, ref Guid EventContext);
int GetMasterVolume(out float pfLevel);
- int SetMute([MarshalAs(UnmanagedType.Bool)] bool bMute, ref Guid EventContext);
- int GetMute([MarshalAs(UnmanagedType.Bool)] out bool pbMute);
+ int SetMute(bool bMute, ref Guid EventContext);
+ int GetMute(out bool pbMute);
}
}
\ No newline at end of file
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..e1c576c 100644
--- a/EonaCat.VolumeMixer/Managers/VolumeMixerManager.cs
+++ b/EonaCat.VolumeMixer/Managers/VolumeMixerManager.cs
@@ -30,6 +30,7 @@ namespace EonaCat.VolumeMixer.Managers
}
}
+ // --- Get Devices ---
public async Task> GetAudioDevicesAsync(DataFlow dataFlow = DataFlow.Output)
{
return await Task.Run(() =>
@@ -37,9 +38,7 @@ namespace EonaCat.VolumeMixer.Managers
var devices = new List();
if (_deviceEnumerator == null)
- {
return devices;
- }
lock (_syncLock)
{
@@ -47,13 +46,12 @@ namespace EonaCat.VolumeMixer.Managers
{
var result = _deviceEnumerator.EnumAudioEndpoints(dataFlow, DeviceState.Active, out var deviceCollection);
if (result != 0 || deviceCollection == null)
- {
return devices;
- }
result = deviceCollection.GetCount(out var count);
if (result != 0)
{
+ ComHelper.ReleaseComObject(deviceCollection);
return devices;
}
@@ -110,14 +108,13 @@ namespace EonaCat.VolumeMixer.Managers
});
}
+ // --- Get Default Device ---
public async Task GetDefaultAudioDeviceAsync(DataFlow dataFlow = DataFlow.Output)
{
return await Task.Run(() =>
{
if (_deviceEnumerator == null)
- {
return null;
- }
lock (_syncLock)
{
@@ -173,19 +170,17 @@ namespace EonaCat.VolumeMixer.Managers
return "Unknown Device";
}
+ // --- System (Output) Volume and Mute ---
+
public async Task SetSystemVolumeAsync(float volume)
{
if (volume < 0f || volume > 1f)
- {
return false;
- }
- AudioDevice defaultDevice = await GetDefaultAudioDeviceAsync();
+ AudioDevice defaultDevice = await GetDefaultAudioDeviceAsync(DataFlow.Output);
if (defaultDevice == null)
- {
return false;
- }
try
{
@@ -199,12 +194,10 @@ namespace EonaCat.VolumeMixer.Managers
public async Task GetSystemVolumeAsync()
{
- AudioDevice defaultDevice = await GetDefaultAudioDeviceAsync();
+ AudioDevice defaultDevice = await GetDefaultAudioDeviceAsync(DataFlow.Output);
if (defaultDevice == null)
- {
return 0f;
- }
try
{
@@ -218,12 +211,10 @@ namespace EonaCat.VolumeMixer.Managers
public async Task SetSystemMuteAsync(bool mute)
{
- AudioDevice defaultDevice = await GetDefaultAudioDeviceAsync();
+ AudioDevice defaultDevice = await GetDefaultAudioDeviceAsync(DataFlow.Output);
if (defaultDevice == null)
- {
return false;
- }
try
{
@@ -237,12 +228,10 @@ namespace EonaCat.VolumeMixer.Managers
public async Task GetSystemMuteAsync()
{
- AudioDevice defaultDevice = await GetDefaultAudioDeviceAsync();
+ AudioDevice defaultDevice = await GetDefaultAudioDeviceAsync(DataFlow.Output);
if (defaultDevice == null)
- {
return false;
- }
try
{
@@ -254,13 +243,113 @@ namespace EonaCat.VolumeMixer.Managers
}
}
+ // --- Microphone (Input) Volume and Mute ---
+
+ public async Task SetMicrophoneVolumeAsync(float volume)
+ {
+ if (volume < 0f || volume > 1f)
+ return false;
+
+ AudioDevice defaultMic = await GetDefaultAudioDeviceAsync(DataFlow.Input);
+
+ if (defaultMic == null)
+ return false;
+
+ try
+ {
+ return await defaultMic.SetMasterVolumeAsync(volume);
+ }
+ finally
+ {
+ defaultMic.Dispose();
+ }
+ }
+
+ public async Task GetMicrophoneVolumeAsync()
+ {
+ AudioDevice defaultMic = await GetDefaultAudioDeviceAsync(DataFlow.Input);
+
+ if (defaultMic == null)
+ return 0f;
+
+ try
+ {
+ return await defaultMic.GetMasterVolumeAsync();
+ }
+ finally
+ {
+ defaultMic.Dispose();
+ }
+ }
+
+ public async Task SetMicrophoneMuteAsync(bool mute)
+ {
+ AudioDevice defaultMic = await GetDefaultAudioDeviceAsync(DataFlow.Input);
+
+ if (defaultMic == null)
+ return false;
+
+ try
+ {
+ return await defaultMic.SetMasterMuteAsync(mute);
+ }
+ finally
+ {
+ defaultMic.Dispose();
+ }
+ }
+
+ public async Task GetMicrophoneMuteAsync()
+ {
+ AudioDevice defaultMic = await GetDefaultAudioDeviceAsync(DataFlow.Input);
+
+ 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 GetAudioDevicesAsync(DataFlow.Input);
+
+ foreach (var mic in microphones)
+ {
+ try
+ {
+ if (mic.Name.IndexOf(microphoneName, StringComparison.OrdinalIgnoreCase) >= 0)
+ {
+ return await mic.SetMasterVolumeAsync(volume);
+ }
+ }
+ finally
+ {
+ mic.Dispose();
+ }
+ }
+
+ return false;
+ }
+
+ // --- Audio Sessions (All devices) ---
+
public async Task> GetAllActiveSessionsAsync()
{
return await Task.Run(async () =>
{
var allSessions = new List();
- List devices = await GetAudioDevicesAsync();
+ List devices = await GetAudioDevicesAsync(DataFlow.Output);
foreach (var device in devices)
{
@@ -282,6 +371,18 @@ namespace EonaCat.VolumeMixer.Managers
});
}
+ public async Task> GetMicrophonesAsync()
+ {
+ return await GetAudioDevicesAsync(DataFlow.Input);
+ }
+
+ public async Task GetDefaultMicrophoneAsync()
+ {
+ return await GetDefaultAudioDeviceAsync(DataFlow.Input);
+ }
+
+ // --- Dispose ---
+
public void Dispose()
{
lock (_syncLock)
diff --git a/EonaCat.VolumeMixer/Models/AudioDevice.cs b/EonaCat.VolumeMixer/Models/AudioDevice.cs
index ec5c711..c950a29 100644
--- a/EonaCat.VolumeMixer/Models/AudioDevice.cs
+++ b/EonaCat.VolumeMixer/Models/AudioDevice.cs
@@ -2,6 +2,7 @@
using EonaCat.VolumeMixer.Interfaces;
using System;
using System.Collections.Generic;
+using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace EonaCat.VolumeMixer.Models
@@ -10,6 +11,7 @@ namespace EonaCat.VolumeMixer.Models
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
public class AudioDevice : IDisposable
{
+ internal Guid AudioController2Guid = new Guid("bfb7ff88-7239-4fc9-8fa2-07c950be9c6d");
private readonly IMultiMediaDevice _device;
private IAudioEndpointVolume _endpointVolume;
private IAudioSessionManager _sessionManager;
@@ -79,8 +81,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
{
@@ -214,7 +216,15 @@ namespace EonaCat.VolumeMixer.Models
result = sessionEnum.GetSession(i, out var sessionControl);
if (result == 0 && sessionControl != null)
{
- sessions.Add(new AudioSession(sessionControl, _sessionManager));
+ var sessionControl2 = GetSessionControl2(sessionControl);
+ if (sessionControl2 != null)
+ {
+ sessions.Add(new AudioSession(sessionControl2, _sessionManager));
+ Marshal.ReleaseComObject(sessionControl2);
+ }
+
+ // Release the original sessionControl COM object
+ Marshal.ReleaseComObject(sessionControl);
}
}
catch
@@ -233,6 +243,30 @@ namespace EonaCat.VolumeMixer.Models
});
}
+ private IAudioSessionControlExtended GetSessionControl2(object sessionControl)
+ {
+ if (sessionControl == null)
+ return null;
+
+ var unknownPtr = Marshal.GetIUnknownForObject(sessionControl);
+ try
+ {
+ IntPtr sessionControl2Ptr;
+ int result = Marshal.QueryInterface(unknownPtr, ref AudioController2Guid, out sessionControl2Ptr);
+ if (result == 0 && sessionControl2Ptr != IntPtr.Zero)
+ {
+ var sessionControl2 = (IAudioSessionControlExtended)Marshal.GetObjectForIUnknown(sessionControl2Ptr);
+ Marshal.Release(sessionControl2Ptr);
+ return sessionControl2;
+ }
+ }
+ finally
+ {
+ Marshal.Release(unknownPtr);
+ }
+ return null;
+ }
+
public async Task StepUpAsync(int delayMs = 20)
{
bool success = false;
diff --git a/EonaCat.VolumeMixer/Models/AudioSession.cs b/EonaCat.VolumeMixer/Models/AudioSession.cs
index c25328f..db0d648 100644
--- a/EonaCat.VolumeMixer/Models/AudioSession.cs
+++ b/EonaCat.VolumeMixer/Models/AudioSession.cs
@@ -2,12 +2,11 @@
using EonaCat.VolumeMixer.Interfaces;
using System;
using System.Diagnostics;
+using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace EonaCat.VolumeMixer.Models
{
- // 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 AudioSession : IDisposable
{
private readonly IAudioSessionControlExtended _sessionControl;
@@ -24,24 +23,27 @@ namespace EonaCat.VolumeMixer.Models
{
_sessionControl = sessionControl;
LoadSessionInfo();
- InitializeVolume(sessionManager);
+ InitializeSimpleAudioVolume();
}
- private void InitializeVolume(IAudioSessionManager sessionManager)
+ private void InitializeSimpleAudioVolume()
{
lock (_syncLock)
{
try
{
- var result = _sessionControl.GetGroupingParam(out var guid);
- if (result == 0)
+ var sessionControlUnknown = Marshal.GetIUnknownForObject(_sessionControl);
+ var iidSimpleAudioVolume = typeof(IAudioVolume).GUID;
+ IntPtr simpleAudioVolumePtr = IntPtr.Zero;
+
+ int result = Marshal.QueryInterface(sessionControlUnknown, ref iidSimpleAudioVolume, out simpleAudioVolumePtr);
+ if (result == 0 && simpleAudioVolumePtr != IntPtr.Zero)
{
- result = sessionManager.GetAudioVolume(ref guid, 0, out var ptr);
- if (result == 0 && ptr != IntPtr.Zero)
- {
- _audioVolume = ComHelper.GetInterface(ptr);
- }
+ _audioVolume = (IAudioVolume)Marshal.GetObjectForIUnknown(simpleAudioVolumePtr);
+ Marshal.Release(simpleAudioVolumePtr);
}
+
+ Marshal.Release(sessionControlUnknown);
}
catch
{
@@ -91,7 +93,7 @@ namespace EonaCat.VolumeMixer.Models
try
{
- var result = _audioVolume.GetMasterVolume(out var volume);
+ int result = _audioVolume.GetMasterVolume(out var volume);
return result == 0 ? volume : 0f;
}
catch
@@ -109,7 +111,7 @@ namespace EonaCat.VolumeMixer.Models
return false;
}
- IAudioVolume audioVolCopy;
+ IAudioVolume simpleAudioVolCopy;
lock (_syncLock)
{
if (_isDisposed || _audioVolume == null)
@@ -117,7 +119,7 @@ namespace EonaCat.VolumeMixer.Models
return false;
}
- audioVolCopy = _audioVolume;
+ simpleAudioVolCopy = _audioVolume;
}
var guid = Guid.Empty;
@@ -126,8 +128,8 @@ namespace EonaCat.VolumeMixer.Models
{
try
{
- var hr = audioVolCopy.SetMasterVolume(volume, ref guid);
- if (hr == 0)
+ var result = simpleAudioVolCopy.SetMasterVolume(volume, ref guid);
+ if (result == 0)
{
await Task.Delay(delayMs);
@@ -175,7 +177,7 @@ namespace EonaCat.VolumeMixer.Models
public async Task SetMuteAsync(bool mute)
{
- IAudioVolume audioVolCopy;
+ IAudioVolume simpleAudioVolCopy;
lock (_syncLock)
{
if (_isDisposed || _audioVolume == null)
@@ -183,13 +185,13 @@ namespace EonaCat.VolumeMixer.Models
return false;
}
- audioVolCopy = _audioVolume;
+ simpleAudioVolCopy = _audioVolume;
}
try
{
var guid = Guid.Empty;
- return await Task.Run(() => audioVolCopy.SetMute(mute, ref guid) == 0);
+ return await Task.Run(() => simpleAudioVolCopy.SetMute(mute, ref guid) == 0);
}
catch
{
@@ -221,6 +223,13 @@ namespace EonaCat.VolumeMixer.Models
});
}
+ public async Task GetEffectiveVolumeAsync(AudioDevice device)
+ {
+ var deviceVolume = await device.GetMasterVolumeAsync();
+ var sessionVolume = await GetVolumeAsync();
+ return deviceVolume * sessionVolume;
+ }
+
public void Dispose()
{
lock (_syncLock)
@@ -230,7 +239,11 @@ namespace EonaCat.VolumeMixer.Models
return;
}
- ComHelper.ReleaseComObject(_audioVolume);
+ if (_audioVolume != null)
+ {
+ Marshal.ReleaseComObject(_audioVolume);
+ _audioVolume = null;
+ }
ComHelper.ReleaseComObject(_sessionControl);
_isDisposed = true;
}