-
Notifications
You must be signed in to change notification settings - Fork 0
/
AudioDeviceManager.cs
125 lines (110 loc) · 5.08 KB
/
AudioDeviceManager.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
using CSCore.CoreAudioAPI;
using CSCore.Win32;
using System;
using System.Collections.ObjectModel;
using System.Runtime.InteropServices;
using static VoiceMod.AudioDeviceManager.DeviceUpdatedEventArgs;
namespace VoiceMod
{
public class AudioDeviceManager
{
private static readonly string TAG = "AudioDeviceManager";
public static readonly MMDeviceEnumerator DeviceEnumerator = new MMDeviceEnumerator();
public class DeviceUpdatedEventArgs : EventArgs
{
public enum DeviceUpdateKind
{
Added,
Removed,
Changed
}
public readonly DeviceUpdateKind Kind;
public readonly string DeviceId;
public DeviceUpdatedEventArgs(DeviceUpdateKind kind, string deviceId)
{
Kind = kind;
DeviceId = deviceId;
}
/// <summary>
/// Tries the get device associated with the <see cref="DeviceId"/>.
/// </summary>
/// <returns><c>true</c> if the associated device be successfully retrieved; false otherwise.</returns>
public MMDevice TryGetDevice()
{
try
{
using (var deviceEnumerator = new MMDeviceEnumerator())
{
return deviceEnumerator.GetDevice(DeviceId);
}
}
catch (Exception)
{
return null;
}
}
public override string ToString()
{
return $"{{ Kind={Kind}, DeviceId={DeviceId} }}";
}
}
public event EventHandler<DeviceUpdatedEventArgs> OnAudioDeviceUpdated;
public AudioDeviceManager()
{
// TODO:(pv) Figure out why DeviceAdded/DeviceRemoved ain't firing!!!!!
// TODO:(pv) Consider rolling my own MMDeviceEnumerator like I did in NightVsKnight.ps1 Audio.cs?
// NightVsKnight\desktop\scripts\NightVsKnight.ps1 USES WMI!
// NightVsKnight\desktop\scripts\Audio.cs USES IMMDeviceEnumerator (but does not register for event)
// https://docs.microsoft.com/en-us/windows/desktop/CoreAudio/device-events
// https://www.codeproject.com/Articles/14500/Detecting-Hardware-Insertion-and-or-Removal
// https://www.codeguru.com/columns/dotnet/detecting-usb-devices-using-c.html
DeviceEnumerator.DeviceStateChanged += DeviceEnumerator_DeviceStateChanged;
DeviceEnumerator.DeviceAdded += DeviceEnumerator_DeviceAdded; // not firing?!?!?!
DeviceEnumerator.DeviceRemoved += DeviceEnumerator_DeviceRemoved; // not firing?!?!?!
}
public MMDevice GetDefaultAudioEndpoint(DataFlow dataFlow, Role role)
{
return DeviceEnumerator.GetDefaultAudioEndpoint(dataFlow, role);
}
public MMDeviceCollection EnumAudioEndpoints(DataFlow dataFlow, DeviceState stateMask)
{
return DeviceEnumerator.EnumAudioEndpoints(dataFlow, stateMask);
}
private void DeviceEnumerator_DeviceStateChanged(object sender, DeviceStateChangedEventArgs e)
{
// https://docs.microsoft.com/en-us/windows/win32/coreaudio/device-state-xxx-constants
//var s = $"e={{ DeviceState={e.DeviceState}, DeviceId={e.DeviceId} }}";
//Console.WriteLine($"{TAG} Info OnDeviceStateChanged({sender}, {s})");
if ((e.DeviceState & DeviceState.Active) == DeviceState.Active)
{
OnAudioDeviceUpdated?.Invoke(this, new DeviceUpdatedEventArgs(DeviceUpdateKind.Added, e.DeviceId));
}
if (((e.DeviceState & DeviceState.NotPresent) == DeviceState.NotPresent) || ((e.DeviceState & DeviceState.NotPresent) == DeviceState.Disabled))
{
OnAudioDeviceUpdated?.Invoke(this, new DeviceUpdatedEventArgs(DeviceUpdateKind.Removed, e.DeviceId));
}
}
/// <summary>
/// TODO: Why doesn't this actually ever fire on add?
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void DeviceEnumerator_DeviceAdded(object sender, DeviceNotificationEventArgs e)
{
var s = $"e={{ DeviceId={e.DeviceId} }}";
Console.WriteLine($"{TAG} Info OnDeviceAdded({sender}, {e})");
OnAudioDeviceUpdated?.Invoke(this, new DeviceUpdatedEventArgs(DeviceUpdateKind.Added, e.DeviceId));
}
/// <summary>
/// TODO: Why doesn't this actually ever fire on remove?
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void DeviceEnumerator_DeviceRemoved(object sender, DeviceNotificationEventArgs e)
{
var s = $"e={{ DeviceId={e.DeviceId} }}";
Console.WriteLine($"{TAG} Info OnDeviceRemoved({sender}, {s})");
OnAudioDeviceUpdated?.Invoke(this, new DeviceUpdatedEventArgs(DeviceUpdateKind.Removed, e.DeviceId));
}
}
}