Skip to content

Instantly share code, notes, and snippets.

@RupertAvery
Last active September 8, 2022 20:56
Show Gist options
  • Save RupertAvery/b1a5297d99f498e44b534b8a5190d9d3 to your computer and use it in GitHub Desktop.
Save RupertAvery/b1a5297d99f498e44b534b8a5190d9d3 to your computer and use it in GitHub Desktop.
AudioDeviceManager
namespace MyAudio
{
public class AudioDevice
{
public string Name { get; set; }
public string Direction { get; set; }
public bool IsEnabled { get; set; }
public string DeviceId { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
namespace MyAudio
{
public class AudioDeviceManager : IDisposable
{
private IMMDeviceEnumerator pMMDeviceEnumerator;
private NotificationClient _notificationClient;
private List<AudioDevice> _audioDevices = new List<AudioDevice>();
public IEnumerable<AudioDevice> AudioDevices => _audioDevices;
private Dictionary<string, AudioDevice> _audioDeviceLookup = new Dictionary<string, AudioDevice>();
public Action<AudioDevice> DefaultDeviceChanged { get; set; }
public Action<AudioDevice> DeviceStateChanged { get; set; }
public AudioDeviceManager()
{
Guid CLSID_MMDeviceEnumerator = new Guid("{BCDE0395-E52F-467C-8E3D-C4579291692E}");
Type MMDeviceEnumeratorType = Type.GetTypeFromCLSID(CLSID_MMDeviceEnumerator, true);
pMMDeviceEnumerator = (IMMDeviceEnumerator)Activator.CreateInstance(MMDeviceEnumeratorType);
_notificationClient = new NotificationClient();
_notificationClient.DefaultDeviceChanged += (sender, args) =>
{
if(_audioDeviceLookup.TryGetValue(args.DefaultDeviceId, out var device))
{
DefaultDeviceChanged?.Invoke(device);
}
};
_notificationClient.DeviceStateChanged += (sender, args) =>
{
if (_audioDeviceLookup.TryGetValue(args.DeviceId, out var device))
{
DeviceStateChanged?.Invoke(device);
}
};
RegisterNotificationClient(_notificationClient);
}
public void RegisterNotificationClient([In][MarshalAs(UnmanagedType.Interface)] IMMNotificationClient client)
{
var hr = pMMDeviceEnumerator.RegisterEndpointNotificationCallback(client);
if (hr != HRESULT.S_OK)
{
throw new Exception("Could not register notification client!");
}
}
public void UnRegisterEndpointNotificationCallback([In][MarshalAs(UnmanagedType.Interface)] IMMNotificationClient client)
{
//DeviceEnum declared below
var hr = pMMDeviceEnumerator.UnregisterEndpointNotificationCallback(client);
if (hr != HRESULT.S_OK)
{
throw new Exception("Could not unregister notification client!");
}
}
public void GetAudioDevices()
{
HRESULT hr = HRESULT.E_FAIL;
_audioDevices = new List<AudioDevice>();
if (pMMDeviceEnumerator != null)
{
IMMDeviceCollection pDeviceCollection = null;
hr = pMMDeviceEnumerator.EnumAudioEndpoints(EDataFlow.eAll,
Win32.DEVICE_STATE_ACTIVE | Win32.DEVICE_STATE_UNPLUGGED, out pDeviceCollection);
if (hr == HRESULT.S_OK)
{
uint nDevices = 0;
hr = pDeviceCollection.GetCount(out nDevices);
for (uint i = 0; i < nDevices; i++)
{
IMMDevice pDevice = null;
hr = pDeviceCollection.Item(i, out pDevice);
if (hr == HRESULT.S_OK)
{
int nState = 0;
hr = pDevice.GetState(out nState);
var isEnabled = false;
if (hr == HRESULT.S_OK)
{
if (nState == Win32.DEVICE_STATE_ACTIVE)
{
isEnabled = true;
}
else
{
isEnabled = false;
}
}
IntPtr nDeviceId;
hr = pDevice.GetId(out nDeviceId);
string deviceId = null;
if (hr == HRESULT.S_OK)
{
deviceId = Marshal.PtrToStringUni(nDeviceId);
}
IPropertyStore pPropertyStore = null;
hr = pDevice.OpenPropertyStore(Win32.STGM_READ, out pPropertyStore);
if (hr == HRESULT.S_OK)
{
string sFriendlyName = null;
string sDesc = null;
PROPVARIANT pv = new PROPVARIANT();
hr = pPropertyStore.GetValue(ref Win32.PKEY_Device_FriendlyName, out pv);
if (hr == HRESULT.S_OK)
{
sFriendlyName = Marshal.PtrToStringUni(pv.pwszVal);
}
hr = pPropertyStore.GetValue(ref Win32.PKEY_Device_DeviceDesc, out pv);
if (hr == HRESULT.S_OK)
{
sDesc = Marshal.PtrToStringUni(pv.pwszVal);
}
hr = pPropertyStore.GetValue(ref Win32.PKEY_Device_DeviceDesc, out pv);
if (hr == HRESULT.S_OK)
{
sDesc = Marshal.PtrToStringUni(pv.pwszVal);
}
//Console.WriteLine("Device: {0}", sFriendlyName);
//IntPtr hGlobal = Marshal.AllocHGlobal(260);
//hr = pDevice.GetId(out hGlobal);
//string sId = Marshal.PtrToStringUni(hGlobal);
//Marshal.FreeHGlobal(hGlobal);
//IntPtr pAudioEndpointVolumePtr = IntPtr.Zero;
//PROPVARIANT pvActivate = new PROPVARIANT();
//hr = pDevice.Activate(typeof(IAudioEndpointVolume).GUID, 0, ref pvActivate, out pAudioEndpointVolumePtr);
//if (hr == HRESULT.S_OK)
//{
// IAudioEndpointVolume pAudioEndpointVolume = Marshal.GetObjectForIUnknown(pAudioEndpointVolumePtr) as IAudioEndpointVolume;
// float nVolume = 0;
// hr = pAudioEndpointVolume.GetMasterVolumeLevelScalar(out nVolume);
// System.Diagnostics.Trace.WriteLine("\tMaster Volume : " + nVolume.ToString());
// if (sFriendlyName == "Mixage stéréo (Realtek High Definition Audio)")
// {
// hr = pAudioEndpointVolume.SetMasterVolumeLevelScalar(0.5f, Guid.Empty);
// hr = pAudioEndpointVolume.GetMasterVolumeLevelScalar(out nVolume);
// System.Diagnostics.Trace.WriteLine("\tNew Master Volume : " + nVolume.ToString());
// }
// Marshal.ReleaseComObject(pAudioEndpointVolume);
//}
IMMEndpoint pEndpoint = null;
pEndpoint = (IMMEndpoint)pDevice;
EDataFlow eDirection = EDataFlow.eAll;
hr = pEndpoint.GetDataFlow(out eDirection);
//System.Diagnostics.Trace.WriteLine("\tDirection : " + eDirection.ToString());
string sDirection = "";
if (eDirection == EDataFlow.eRender)
sDirection = "Playback";
else if (eDirection == EDataFlow.eCapture)
sDirection = "Recording";
_audioDevices.Add(new AudioDevice()
{
Name = sFriendlyName,
Direction = sDirection,
IsEnabled = isEnabled,
DeviceId = deviceId
});
Marshal.ReleaseComObject(pPropertyStore);
}
Marshal.ReleaseComObject(pDevice);
}
}
_audioDeviceLookup = _audioDevices.ToDictionary(d => d.DeviceId);
}
Marshal.ReleaseComObject(pDeviceCollection);
}
}
public void Dispose()
{
UnRegisterEndpointNotificationCallback(_notificationClient);
}
}
}
using System.Runtime.InteropServices;
namespace MyAudio
{
[ComImport]
[Guid("7991EEC9-7E89-4D85-8390-6C703CEC60C0")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IMMNotificationClient
{
void OnDeviceStateChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, int dwNewState);
void OnDeviceAdded([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId);
void OnDeviceRemoved([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId);
void OnDefaultDeviceChanged(EDataFlow flow, ERole role, [MarshalAs(UnmanagedType.LPWStr)] string pwstrDefaultDeviceId);
void OnPropertyValueChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, ref PROPERTYKEY key);
}
}
using System.Collections.ObjectModel;
using System.Data;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace MyAudio
{
public partial class MainWindow : Window
{
private AudioDeviceManager _audioDeviceManager;
public MainWindow()
{
InitializeComponent();
_audioDeviceManager = new AudioDeviceManager();
_audioDeviceManager.GetAudioDevices();
_audioDeviceManager.DefaultDeviceChanged = device =>
{
// Doesn't work yet, you might need to do proper binding
this.cmb1.SelectedItem = device;
};
ListCollectionView lcv = new ListCollectionView(new ObservableCollection<AudioDevice>(_audioDeviceManager.AudioDevices));
lcv.GroupDescriptions.Add(new PropertyGroupDescription("Direction"));
this.cmb1.ItemsSource = lcv;
}
}
}
using System;
using System.Data;
using System.Runtime.InteropServices;
namespace MyAudio
{
public class DefaultDeviceChangedEventArgs
{
public EDataFlow Flow { get; set; }
public ERole Role { get; set; }
public string DefaultDeviceId { get; set; }
}
public class DeviceStateChangedEventArgs
{
public int NewState { get; set; }
public string DeviceId { get; set; }
}
public class NotificationClient : IMMNotificationClient
{
public EventHandler<DefaultDeviceChangedEventArgs> DefaultDeviceChanged { get; set; }
public EventHandler<DeviceStateChangedEventArgs> DeviceStateChanged { get; set; }
public Action<string> DeviceAdded { get; set; }
public void OnDeviceStateChanged(string pwstrDeviceId, int dwNewState)
{
DeviceStateChanged.Invoke(this, new DeviceStateChangedEventArgs() { DeviceId = pwstrDeviceId, NewState = dwNewState });
}
public void OnDeviceAdded(string pwstrDeviceId)
{
}
public void OnDeviceRemoved(string pwstrDeviceId)
{
}
public void OnDefaultDeviceChanged(EDataFlow flow, ERole role, string pwstrDefaultDeviceId)
{
DefaultDeviceChanged.Invoke(this, new DefaultDeviceChangedEventArgs() { Flow = flow, Role = role, DefaultDeviceId = pwstrDefaultDeviceId });
}
public void OnPropertyValueChanged(string pwstrDeviceId, ref PROPERTYKEY key)
{
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment