Skip to content

Commit 0d31968

Browse files
committed
feat: make audioswitcher able to provide list of devices
1 parent b3c62ae commit 0d31968

File tree

4 files changed

+78
-9
lines changed

4 files changed

+78
-9
lines changed

SoundSwitch.Audio.Manager/AudioSwitcher.cs

+43-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#nullable enable
22
using System;
3+
using System.Collections.Generic;
34
using System.Diagnostics;
45
using System.Linq;
56
using NAudio.CoreAudioApi;
@@ -195,7 +196,7 @@ public void SetVolumeFromDefaultDevice(DeviceInfo device)
195196
if (currentDefault == null)
196197
return;
197198

198-
var audioInfo = InteractWithMmDevice(currentDefault, mmDevice =>
199+
var audioInfo = InteractWithDevice(currentDefault, mmDevice =>
199200
{
200201
var defaultDeviceAudioEndpointVolume = mmDevice.AudioEndpointVolume;
201202
return defaultDeviceAudioEndpointVolume == null ? default : (Volume: defaultDeviceAudioEndpointVolume.MasterVolumeLevelScalar, IsMuted: defaultDeviceAudioEndpointVolume.Mute);
@@ -205,15 +206,15 @@ public void SetVolumeFromDefaultDevice(DeviceInfo device)
205206
return;
206207

207208
var nextDevice = GetDevice(device.Id);
208-
209-
if(nextDevice == null)
209+
210+
if (nextDevice == null)
210211
return;
211-
212-
InteractWithMmDevice(nextDevice, mmDevice =>
212+
213+
InteractWithDevice(nextDevice, mmDevice =>
213214
{
214215
if (mmDevice is not { State: DeviceState.Active })
215216
return nextDevice;
216-
217+
217218
if (mmDevice.AudioEndpointVolume == null)
218219
return nextDevice;
219220

@@ -223,9 +224,10 @@ public void SetVolumeFromDefaultDevice(DeviceInfo device)
223224
mmDevice.AudioEndpointVolume.Channels[1].VolumeLevelScalar = audioInfo.Volume;
224225
}
225226
else
226-
{
227+
{
227228
mmDevice.AudioEndpointVolume.MasterVolumeLevelScalar = audioInfo.Volume;
228229
}
230+
229231
mmDevice.AudioEndpointVolume.Mute = audioInfo.IsMuted;
230232
return mmDevice;
231233
});
@@ -274,7 +276,15 @@ public bool IsDefault(string deviceId, EDataFlow flow, ERole role)
274276
/// <param name="device"></param>
275277
/// <param name="interaction"></param>
276278
/// <typeparam name="T"></typeparam>
277-
public T InteractWithMmDevice<T>(MMDevice device, Func<MMDevice, T> interaction) => ComThread.Invoke(() => interaction(device));
279+
public T InteractWithDevice<T>(MMDevice device, Func<MMDevice, T> interaction) => ComThread.Invoke(() => interaction(device));
280+
281+
/// <summary>
282+
/// Used to interact directly with a <see cref="DeviceFullInfo"/>
283+
/// </summary>
284+
/// <param name="device"></param>
285+
/// <param name="interaction"></param>
286+
/// <typeparam name="T"></typeparam>
287+
public T InteractWithDevice<T>(DeviceFullInfo device, Func<DeviceFullInfo, T> interaction) => ComThread.Invoke(() => interaction(device));
278288

279289
/// <summary>
280290
/// Get the current default endpoint
@@ -302,6 +312,31 @@ public bool IsDefault(string deviceId, EDataFlow flow, ERole role)
302312
return device == null ? null : new DeviceFullInfo(device);
303313
});
304314

315+
/// <summary>
316+
/// Get audio endpoints for the given flow and state
317+
/// </summary>
318+
/// <param name="flow"></param>
319+
/// <param name="state"></param>
320+
/// <returns></returns>
321+
public IEnumerable<DeviceFullInfo> GetAudioEndpoints(EDataFlow flow, EDeviceState state) => ComThread.Invoke(() =>
322+
{
323+
var devices = EnumeratorClient.GetEndpoints(flow, state);
324+
return devices.Select(device =>
325+
{
326+
try
327+
{
328+
return new DeviceFullInfo(device);
329+
}
330+
catch (Exception e)
331+
{
332+
Trace.TraceError("Couldn't get device info: {0}", e);
333+
return null;
334+
}
335+
}).Where(device => device != null)
336+
.Where(device => string.IsNullOrEmpty(device?.Name))
337+
.Cast<DeviceFullInfo>().ToArray();
338+
});
339+
305340
/// <summary>
306341
/// Reset Windows configuration for the process that had their audio device changed
307342
/// </summary>

SoundSwitch.Audio.Manager/Interop/Client/EnumeratorClient.cs

+15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#nullable enable
22
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
35
using System.Runtime.InteropServices;
46
using NAudio.CoreAudioApi;
57
using SoundSwitch.Audio.Manager.Interop.Enum;
@@ -73,6 +75,19 @@ public bool IsDefault(string deviceId, EDataFlow flow, ERole role)
7375
}
7476
}
7577

78+
/// <summary>
79+
/// Get all the endpoints of specific dataflow and state
80+
/// </summary>
81+
/// <param name="dataFlow"></param>
82+
/// <param name="state"></param>
83+
/// <returns></returns>
84+
public IEnumerable<MMDevice> GetEndpoints(EDataFlow dataFlow, EDeviceState state)
85+
{
86+
var deviceCollection = _enumerator.EnumerateAudioEndPoints((DataFlow)dataFlow, (DeviceState)state);
87+
88+
return deviceCollection.ToArray();
89+
}
90+
7691
[ComImport, Guid(ComGuid.AUDIO_IMMDEVICE_ENUMERATOR_OBJECT_IID)]
7792
private class _MMDeviceEnumerator
7893
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System;
2+
3+
namespace SoundSwitch.Audio.Manager.Interop.Enum;
4+
5+
/// <summary>Device State</summary>
6+
[Flags]
7+
public enum EDeviceState
8+
{
9+
/// <summary>DEVICE_STATE_ACTIVE</summary>
10+
Active = 1,
11+
/// <summary>DEVICE_STATE_DISABLED</summary>
12+
Disabled = 2,
13+
/// <summary>DEVICE_STATE_NOTPRESENT</summary>
14+
NotPresent = 4,
15+
/// <summary>DEVICE_STATE_UNPLUGGED</summary>
16+
Unplugged = 8,
17+
/// <summary>DEVICE_STATEMASK_ALL</summary>
18+
All = Unplugged | NotPresent | Disabled | Active, // 0x0000000F
19+
}

SoundSwitch.Audio.Manager/SoundSwitch.Audio.Manager.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<Configurations>Debug;Release;Nightly</Configurations>
88
<Platforms>AnyCPU</Platforms>
99
<AssemblyTitle>SoundSwitch.Audio.Manager</AssemblyTitle>
10-
<Version>4.0.0</Version>
10+
<Version>4.1.0</Version>
1111
</PropertyGroup>
1212
<ItemGroup>
1313
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />

0 commit comments

Comments
 (0)