Merged the managers to 1 volumeMixer manager.

Create a bugFix where the wrong interface was applied as a check.
This commit is contained in:
EonaCat 2025-07-22 19:13:51 +02:00
parent c2043acbd3
commit 6ecc177c73
7 changed files with 200 additions and 240 deletions

View File

@ -16,7 +16,7 @@ class Program
try try
{ {
// Get all audio PLAYBACK devices // 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:"); Console.WriteLine($"Found {devices.Count} playback devices:");
foreach (var device in devices) foreach (var device in devices)
@ -36,6 +36,9 @@ class Program
// Get all audio sessions // Get all audio sessions
var sessions = await defaultDevice.GetAudioSessionsAsync(); 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}"); Console.WriteLine($"Active sessions: {sessions.Count}");
foreach (var session in sessions) foreach (var session in sessions)
@ -99,15 +102,11 @@ class Program
{ {
Console.WriteLine($"Error: {ex.Message}"); Console.WriteLine($"Error: {ex.Message}");
} }
}
// Microphone management example
using (var micManager = new MicrophoneManager())
{
try try
{ {
// Get all microphones // Get all microphones
var microphones = await micManager.GetMicrophonesAsync(); var microphones = await volumeMixer.GetMicrophonesAsync();
Console.WriteLine($"Found {microphones.Count} microphones:"); Console.WriteLine($"Found {microphones.Count} microphones:");
foreach (var mic in microphones) foreach (var mic in microphones)
@ -119,7 +118,7 @@ class Program
} }
// Default microphone // Default microphone
using (var defaultMic = await micManager.GetDefaultMicrophoneAsync()) using (var defaultMic = await volumeMixer.GetDefaultMicrophoneAsync())
{ {
Console.WriteLine($"\nSetting default microphone volume to 1%"); Console.WriteLine($"\nSetting default microphone volume to 1%");
bool success = await defaultMic.SetMasterVolumeAsync(0.1f); 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 volume: {await volumeMixer.GetMicrophoneVolumeAsync():P0}");
Console.WriteLine($"Default mic muted: {await micManager.GetMicrophoneMuteAsync()}"); Console.WriteLine($"Default mic muted: {await volumeMixer.GetMicrophoneMuteAsync()}");
// Set microphone volume to 60% // 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}"); Console.WriteLine($"Set microphone volume to 60%: {result}");
// Set specific microphone by name // 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}"); Console.WriteLine($"Set USB microphone volume to 70%: {result}");
} }

View File

@ -18,9 +18,9 @@
<PackageTags>EonaCat, Audio, Volume, Mixer .NET Standard, Jeroen, Saey</PackageTags> <PackageTags>EonaCat, Audio, Volume, Mixer .NET Standard, Jeroen, Saey</PackageTags>
<PackageReleaseNotes></PackageReleaseNotes> <PackageReleaseNotes></PackageReleaseNotes>
<Description>EonaCat VolumeMixer</Description> <Description>EonaCat VolumeMixer</Description>
<Version>1.0.0</Version> <Version>1.0.1</Version>
<AssemblyVersion>1.0.0.0</AssemblyVersion> <AssemblyVersion>1.0.0.1</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion> <FileVersion>1.0.0.1</FileVersion>
<PackageIcon>icon.png</PackageIcon> <PackageIcon>icon.png</PackageIcon>
</PropertyGroup> </PropertyGroup>

View File

@ -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<List<AudioDevice>> GetMicrophonesAsync()
{
return await _volumeMixer.GetAudioDevicesAsync(DataFlow.Input);
}
public async Task<AudioDevice> GetDefaultMicrophoneAsync()
{
return await _volumeMixer.GetDefaultAudioDeviceAsync(DataFlow.Input);
}
public async Task<bool> 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<float> GetMicrophoneVolumeAsync()
{
AudioDevice defaultMic = await GetDefaultMicrophoneAsync();
if (defaultMic == null)
{
return 0f;
}
try
{
return await defaultMic.GetMasterVolumeAsync();
}
finally
{
defaultMic.Dispose();
}
}
public async Task<bool> SetMicrophoneMuteAsync(bool mute)
{
AudioDevice defaultMic = await GetDefaultMicrophoneAsync();
if (defaultMic == null)
{
return false;
}
try
{
return await defaultMic.SetMasterMuteAsync(mute);
}
finally
{
defaultMic.Dispose();
}
}
public async Task<bool> GetMicrophoneMuteAsync()
{
AudioDevice defaultMic = await GetDefaultMicrophoneAsync();
if (defaultMic == null)
{
return false;
}
try
{
return await defaultMic.GetMasterMuteAsync();
}
finally
{
defaultMic.Dispose();
}
}
public async Task<bool> SetMicrophoneVolumeByNameAsync(string microphoneName, float volume)
{
if (string.IsNullOrWhiteSpace(microphoneName) || volume < 0f || volume > 1f)
{
return false;
}
List<AudioDevice> 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();
}
}
}
}

View File

@ -92,18 +92,12 @@ namespace EonaCat.VolumeMixer.Managers
} }
} }
} }
catch catch { }
{
// Skip individual device on error
}
} }
ComHelper.ReleaseComObject(deviceCollection); ComHelper.ReleaseComObject(deviceCollection);
} }
catch catch { }
{
// Ignore all and return partial/empty result
}
} }
return devices; return devices;
@ -132,45 +126,78 @@ namespace EonaCat.VolumeMixer.Managers
var name = GetDeviceName(device); var name = GetDeviceName(device);
return new AudioDevice(device, id, name, true, dataFlow); return new AudioDevice(device, id, name, true, dataFlow);
} }
ComHelper.ReleaseComObject(device); ComHelper.ReleaseComObject(device);
} }
} }
catch catch { }
{
// Ignore and return null
}
return null; return null;
} }
}); });
} }
private string GetDeviceName(IMultiMediaDevice device) public async Task<List<AudioDevice>> GetMicrophonesAsync()
{ {
try return await GetAudioDevicesAsync(DataFlow.Input);
{ }
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); public async Task<AudioDevice> GetDefaultMicrophoneAsync()
{
return await GetDefaultAudioDeviceAsync(DataFlow.Input);
}
public async Task<bool> SetAudioDeviceVolumeByNameAsync(string audioDevice, float volume)
{
if (string.IsNullOrWhiteSpace(audioDevice) || volume < 0f || volume > 1f)
{
return false;
}
List<AudioDevice> 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<bool> SetMicrophoneVolumeByNameAsync(string microphoneName, float volume)
{
if (string.IsNullOrWhiteSpace(microphoneName) || volume < 0f || volume > 1f)
{ {
// Ignore and fall through return false;
} }
return "Unknown Device"; List<AudioDevice> 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<bool> SetSystemVolumeAsync(float volume) public async Task<bool> SetSystemVolumeAsync(float volume)
@ -254,6 +281,87 @@ namespace EonaCat.VolumeMixer.Managers
} }
} }
public async Task<float> GetMicrophoneVolumeAsync()
{
AudioDevice defaultMic = await GetDefaultMicrophoneAsync();
if (defaultMic == null)
{
return 0f;
}
try
{
return await defaultMic.GetMasterVolumeAsync();
}
finally
{
defaultMic.Dispose();
}
}
public async Task<bool> 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<bool> SetMicrophoneMuteAsync(bool mute)
{
AudioDevice defaultMic = await GetDefaultMicrophoneAsync();
if (defaultMic == null)
{
return false;
}
try
{
return await defaultMic.SetMasterMuteAsync(mute);
}
finally
{
defaultMic.Dispose();
}
}
public async Task<bool> GetMicrophoneMuteAsync()
{
AudioDevice defaultMic = await GetDefaultMicrophoneAsync();
if (defaultMic == null)
{
return false;
}
try
{
return await defaultMic.GetMasterMuteAsync();
}
finally
{
defaultMic.Dispose();
}
}
public async Task<List<AudioSession>> GetAllActiveSessionsAsync() public async Task<List<AudioSession>> GetAllActiveSessionsAsync()
{ {
return await Task.Run(async () => return await Task.Run(async () =>
@ -268,10 +376,7 @@ namespace EonaCat.VolumeMixer.Managers
{ {
allSessions.AddRange(await device.GetAudioSessionsAsync()); allSessions.AddRange(await device.GetAudioSessionsAsync());
} }
catch catch { }
{
// Skip device on error
}
finally finally
{ {
device.Dispose(); 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() public void Dispose()
{ {
lock (_syncLock) lock (_syncLock)

View File

@ -79,8 +79,8 @@ namespace EonaCat.VolumeMixer.Models
try try
{ {
var hr = _endpointVolume.GetMasterVolumeLevelScalar(out var volume); var result = _endpointVolume.GetMasterVolumeLevelScalar(out var volume);
return hr == 0 ? volume : 0f; return result == 0 ? volume : 0f;
} }
catch catch
{ {

View File

@ -10,6 +10,7 @@ namespace EonaCat.VolumeMixer.Models
// See the LICENSE file or go to https://EonaCat.com/License for full license details. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
public class AudioSession : IDisposable public class AudioSession : IDisposable
{ {
private static Guid SharedAppSessionGuid = new Guid("c0a3a9d0-b4dc-58b7-b95e-dcc407dbd8db");
private readonly IAudioSessionControlExtended _sessionControl; private readonly IAudioSessionControlExtended _sessionControl;
private IAudioVolume _audioVolume; private IAudioVolume _audioVolume;
private bool _isDisposed = false; private bool _isDisposed = false;
@ -33,14 +34,11 @@ namespace EonaCat.VolumeMixer.Models
{ {
try try
{ {
var result = _sessionControl.GetGroupingParam(out var guid); // Always use the shared static GUID
if (result == 0) 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); _audioVolume = ComHelper.GetInterface<IAudioVolume>(ptr);
if (result == 0 && ptr != IntPtr.Zero)
{
_audioVolume = ComHelper.GetInterface<IAudioVolume>(ptr);
}
} }
} }
catch catch
@ -126,8 +124,8 @@ namespace EonaCat.VolumeMixer.Models
{ {
try try
{ {
var hr = audioVolCopy.SetMasterVolume(volume, ref guid); var result = audioVolCopy.SetMasterVolume(volume, ref guid);
if (hr == 0) if (result == 0)
{ {
await Task.Delay(delayMs); await Task.Delay(delayMs);

View File

@ -88,18 +88,18 @@ using System.Threading.Tasks;
class Program 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. // See the LICENSE file or go to https://EonaCat.com/License for full license details.
[STAThread] [STAThread]
static async Task Main() static async Task Main()
{ {
// Playback management example // 1. Create the volume mixer manager
using (var volumeMixer = new VolumeMixerManager()) using (var volumeMixer = new VolumeMixerManager())
{ {
try try
{ {
// Get all audio PLAYBACK devices // 2. 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:"); Console.WriteLine($"Found {devices.Count} playback devices:");
foreach (var device in devices) foreach (var device in devices)
@ -119,6 +119,9 @@ class Program
// Get all audio sessions // Get all audio sessions
var sessions = await defaultDevice.GetAudioSessionsAsync(); 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}"); Console.WriteLine($"Active sessions: {sessions.Count}");
foreach (var session in sessions) foreach (var session in sessions)
@ -182,33 +185,11 @@ class Program
{ {
Console.WriteLine($"Error: {ex.Message}"); 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 try
{ {
// Get all microphones // 3. Get all microphones
var microphones = await micManager.GetMicrophonesAsync(); var microphones = await volumeMixer.GetMicrophonesAsync();
Console.WriteLine($"Found {microphones.Count} microphones:"); Console.WriteLine($"Found {microphones.Count} microphones:");
foreach (var mic in microphones) foreach (var mic in microphones)
@ -220,7 +201,7 @@ class Program
} }
// Default microphone // Default microphone
using (var defaultMic = await micManager.GetDefaultMicrophoneAsync()) using (var defaultMic = await volumeMixer.GetDefaultMicrophoneAsync())
{ {
Console.WriteLine($"\nSetting default microphone volume to 1%"); Console.WriteLine($"\nSetting default microphone volume to 1%");
bool success = await defaultMic.SetMasterVolumeAsync(0.1f); 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 volume: {await volumeMixer.GetMicrophoneVolumeAsync():P0}");
Console.WriteLine($"Default mic muted: {await micManager.GetMicrophoneMuteAsync()}"); Console.WriteLine($"Default mic muted: {await volumeMixer.GetMicrophoneMuteAsync()}");
// Set microphone volume to 60% // 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}"); Console.WriteLine($"Set microphone volume to 60%: {result}");
// Set specific microphone by name // 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}"); Console.WriteLine($"Set USB microphone volume to 70%: {result}");
} }
@ -268,5 +249,4 @@ class Program
} }
} }
} }
}
``` ```