Skip to content

Commit 136e288

Browse files
committed
Removed DeviceIOType in favor of existing DataFlow enum.
Added implementation for filtering, listing, and selecting audio endpoints in EditDeviceViewModel. Additional improvements to AudioEndpointInfo.
1 parent ea7291e commit 136e288

8 files changed

Lines changed: 295 additions & 62 deletions

File tree

WinAudioAssistant/Models/AudioEndpointInfo.cs

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,33 @@
88

99
namespace WinAudioAssistant.Models
1010
{
11+
public enum EndpointFormFactor
12+
{
13+
RemoteNetworkDevice = 0,
14+
Speakers = 1,
15+
LineLevel = 2,
16+
Headphones = 3,
17+
Microphone = 4,
18+
Headset = 5,
19+
Handset = 6,
20+
UnknownDigitalPassthrough = 7,
21+
SPDIF = 8,
22+
DigitalAudioDisplayDevice = 9,
23+
UnknownFormFactor = 10,
24+
}
25+
1126
public struct AudioEndpointInfo
1227
{
1328
public readonly DataFlow DataFlow { get; }
14-
public readonly Guid AudioEndpoint_GUID { get; }
29+
public readonly Guid AudioEndpoint_GUID { get; } // Globally unique to this endpoint
1530
public DeviceState? DeviceState { get; private set;}
16-
public int? AudioEndpoint_FormFactor { get; private set; }
17-
public string? AudioEndpoint_JackSubType { get; private set; }
18-
public Guid? Device_ContainerId { get; private set; }
19-
public string? Device_DeviceDesc { get; private set; }
31+
public EndpointFormFactor? AudioEndpoint_FormFactor { get; private set; } // Speakers, headphones, headset, SPDIF, etc.
32+
public Guid? AudioEndpoint_JackSubType { get; private set; } // Contains a GUID for a type of jack, more specific than FormFactor
33+
public Guid? Device_ContainerId { get; private set; } // Perhaps points to the parent device? Not populated for virtual devices
34+
public string? Device_DeviceDesc { get; private set; } // The endpoint's name, which can be changed in the control panel
2035
public string? DeviceClass_IconPath { get; private set; }
21-
public string? DeviceInterface_FriendlyName { get; private set; }
36+
public string? DeviceInterface_FriendlyName { get; private set; } // Set by the driver, but may have a different value if there are duplicate devices
37+
public string? HostDeviceDesc { get; private set; } // (Actual property name unkown) Appears to be the name of the host device. Usually same as DeviceInterface_FriendlyName.
2238

2339
public readonly string ID => (DataFlow == DataFlow.Render ? "{0.0.0.00000000}.{" : "{0.0.1.00000000}.{") + AudioEndpoint_GUID.ToString() + "}";
2440

@@ -28,12 +44,13 @@ public struct AudioEndpointInfo
2844
/// <remarks>Test remark.</remarks>
2945
public AudioEndpointInfo(DataFlow dataFlow,
3046
Guid audioEndpoint_GUID,
31-
int? audioEndpoint_FormFactor = null,
32-
string? audioEndpoint_JackSubType = null,
47+
EndpointFormFactor? audioEndpoint_FormFactor = null,
48+
Guid? audioEndpoint_JackSubType = null,
3349
Guid? device_ContainerId = null,
3450
string? device_DeviceDesc = null,
3551
string? deviceClass_IconPath = null,
36-
string? deviceInterface_FriendlyName = null)
52+
string? deviceInterface_FriendlyName = null,
53+
string? hostDeviceDesc = null)
3754
{
3855
Trace.Assert(dataFlow != DataFlow.All, "AudioEndpointInfo created with DataFlow.All");
3956
DataFlow = dataFlow;
@@ -44,12 +61,20 @@ public AudioEndpointInfo(DataFlow dataFlow,
4461
Device_DeviceDesc = device_DeviceDesc;
4562
DeviceClass_IconPath = deviceClass_IconPath;
4663
DeviceInterface_FriendlyName = deviceInterface_FriendlyName;
64+
HostDeviceDesc = hostDeviceDesc;
4765
}
4866
public AudioEndpointInfo(MMDevice device)
4967
{
5068
Trace.Assert(device.DataFlow != DataFlow.All, "AudioEndpointInfo created with DataFlow.All");
5169
DataFlow = device.DataFlow;
52-
AudioEndpoint_GUID = device.Properties[propertyKeys.AudioEndpoint_GUID].Value is Guid guid ? guid : Guid.Empty;
70+
if (Guid.TryParse(device.Properties[propertyKeys.AudioEndpoint_GUID]?.Value as string, out var guid))
71+
{
72+
AudioEndpoint_GUID = guid;
73+
}
74+
else
75+
{
76+
AudioEndpoint_GUID = Guid.Empty;
77+
}
5378
Debug.Assert(AudioEndpoint_GUID != Guid.Empty, "AudioEndpointInfo created with empty GUID");
5479
UpdateFromDevice(device);
5580
}
@@ -75,9 +100,9 @@ public bool UpdateFromSystem()
75100
public void UpdateFromDevice(MMDevice device)
76101
{
77102
DeviceState = device.State;
78-
if (device.Properties[propertyKeys.AudioEndpoint_FormFactor]?.Value is int formFactor)
79-
AudioEndpoint_FormFactor = formFactor;
80-
if (device.Properties[propertyKeys.AudioEndpoint_JackSubType]?.Value is string jackSubType)
103+
if (device.Properties[propertyKeys.AudioEndpoint_FormFactor]?.Value is uint formFactor)
104+
AudioEndpoint_FormFactor = (EndpointFormFactor)formFactor;
105+
if (Guid.TryParse(device.Properties[propertyKeys.AudioEndpoint_JackSubType]?.Value as string, out var jackSubType))
81106
AudioEndpoint_JackSubType = jackSubType;
82107
if (device.Properties[propertyKeys.Device_ContainerId]?.Value is Guid containerId &&
83108
containerId != new Guid(0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff))
@@ -88,6 +113,8 @@ public void UpdateFromDevice(MMDevice device)
88113
DeviceClass_IconPath = iconPath;
89114
if (device.Properties[propertyKeys.DeviceInterface_FriendlyName]?.Value is string friendlyName)
90115
DeviceInterface_FriendlyName = friendlyName;
116+
if (device.Properties[propertyKeys.Device_DeviceDesc]?.Value is string hostDeviceDesc)
117+
HostDeviceDesc = hostDeviceDesc;
91118
}
92119

93120
private static class propertyKeys
@@ -99,6 +126,7 @@ private static class propertyKeys
99126
internal static readonly PropertyKey Device_DeviceDesc = new(new Guid(0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0), 2);
100127
internal static readonly PropertyKey DeviceClass_IconPath = new(new Guid(0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66), 12);
101128
internal static readonly PropertyKey DeviceInterface_FriendlyName = new(new Guid(0x026e516e, 0xb814, 0x414b, 0x83, 0xcd, 0x85, 0x6d, 0x6f, 0xef, 0x48, 0x22), 2);
129+
internal static readonly PropertyKey HostDeviceDesc = new(new Guid(0xb3f8fa53, 0x0004, 0x438e, 0x90, 0x03, 0x51, 0xa4, 0x6e, 0x13, 0x9b, 0xfc), 6);
102130
}
103131

104132
}

WinAudioAssistant/Models/AudioEndpointManager.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using NAudio.CoreAudioApi;
22
using System;
33
using System.Collections.Generic;
4+
using System.Collections.ObjectModel;
45
using System.IO;
56
using System.Linq;
67
using System.Text;
@@ -13,8 +14,8 @@ namespace WinAudioAssistant.Models
1314
/// </summary>
1415
public class AudioEndpointManager
1516
{
16-
private List<AudioEndpointInfo> _cachedEndpoints { get; } = new();
17-
public IReadOnlyList<AudioEndpointInfo> CachedEndpoints { get { return _cachedEndpoints.AsReadOnly(); } }
17+
private ObservableCollection<AudioEndpointInfo> _cachedEndpoints { get; } = new();
18+
public ReadOnlyObservableCollection<AudioEndpointInfo> CachedEndpoints => new(_cachedEndpoints);
1819

1920
public AudioEndpointManager()
2021
{
Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,53 @@
1-
using System;
1+
using NAudio.CoreAudioApi;
2+
using System;
23
using System.Collections.Generic;
4+
using System.Diagnostics;
35
using System.Linq;
46
using System.Text;
57
using System.Threading.Tasks;
68

79
namespace WinAudioAssistant.Models
810
{
9-
public enum DeviceIOType
10-
{
11-
Input,
12-
Output
13-
}
14-
1511
public abstract class ManagedDevice
1612
{
1713
public string Name { get; set; } = "";
18-
public abstract DeviceIOType Type();
14+
public AudioEndpointInfo EndpointInfo { get; protected set; }
15+
16+
public abstract DataFlow DataFlow();
17+
public abstract void SetEndpoint(AudioEndpointInfo endpointInfo);
1918
}
2019

2120
public class ManagedInputDevice : ManagedDevice
2221
{
23-
public ManagedInputDevice() { }
22+
public ManagedInputDevice(AudioEndpointInfo endpointInfo)
23+
{
24+
Trace.Assert(endpointInfo.DataFlow == DataFlow(), "ManagedInputDevice created with mismatched DataFlow");
25+
EndpointInfo = endpointInfo;
26+
}
27+
28+
public override DataFlow DataFlow() => NAudio.CoreAudioApi.DataFlow.Capture;
2429

25-
public override DeviceIOType Type() => DeviceIOType.Input;
30+
public override void SetEndpoint(AudioEndpointInfo endpointInfo)
31+
{
32+
Trace.Assert(endpointInfo.DataFlow == DataFlow(), "ManagedInputDevice endpoint set to mismatched DataFlow");
33+
EndpointInfo = endpointInfo;
34+
}
2635
}
2736

2837
public class ManagedOutputDevice : ManagedDevice
2938
{
30-
public ManagedOutputDevice() { }
39+
public ManagedOutputDevice(AudioEndpointInfo endpointInfo)
40+
{
41+
Trace.Assert(endpointInfo.DataFlow == DataFlow(), "ManagedInputDevice created with mismatched DataFlow");
42+
EndpointInfo = endpointInfo;
43+
}
44+
45+
public override DataFlow DataFlow() => NAudio.CoreAudioApi.DataFlow.Render;
3146

32-
public override DeviceIOType Type() => DeviceIOType.Output;
47+
public override void SetEndpoint(AudioEndpointInfo endpointInfo)
48+
{
49+
Trace.Assert(endpointInfo.DataFlow == DataFlow(), "ManagedInputDevice endpoint set to mismatched DataFlow");
50+
EndpointInfo = endpointInfo;
51+
}
3352
}
3453
}

WinAudioAssistant/ViewModels/DevicePriorityViewModel.cs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using GongSolutions.Wpf.DragDrop;
22
using Microsoft.VisualBasic;
3+
using NAudio.CoreAudioApi;
34
using System;
45
using System.Collections.Generic;
56
using System.Collections.ObjectModel;
@@ -17,7 +18,7 @@ namespace WinAudioAssistant.ViewModels
1718
// Assigned to each ListBox in the view code-behind
1819
public struct ListBoxTag
1920
{
20-
public DeviceIOType Type;
21+
public DataFlow DataFlow;
2122
public bool IsComms;
2223
}
2324

@@ -38,7 +39,7 @@ dropInfo.VisualTarget is ListBox targetBox &&
3839
targetBox.Tag is ListBoxTag targetTag)
3940
{
4041
// Confirm that the device I/O type matches the listbox I/O type
41-
if (device.Type() == targetTag.Type)
42+
if (device.DataFlow() == targetTag.DataFlow)
4243
{
4344
// Show the caret when dragging over the listbox
4445
dropInfo.DropTargetAdorner = DropTargetAdorners.Insert;
@@ -110,7 +111,7 @@ private void AddDevice(object? parameter)
110111
if (ContextMenuListBox?.Tag is ListBoxTag tag)
111112
{
112113
var editDeviceView = new EditDeviceView();
113-
((EditDeviceViewModel)editDeviceView.DataContext).Initialize(tag.Type, tag.IsComms);
114+
((EditDeviceViewModel)editDeviceView.DataContext).Initialize(tag.DataFlow, tag.IsComms);
114115
editDeviceView.Show();
115116
}
116117
}
@@ -121,9 +122,20 @@ private void EditDevice(object? parameter)
121122
Debug.Assert(ContextMenuListBox.Tag is ListBoxTag);
122123
if (ContextMenuListBox?.SelectedItem is ManagedDevice device && ContextMenuListBox.Tag is ListBoxTag tag)
123124
{
124-
var editDeviceView = new EditDeviceView();
125-
((EditDeviceViewModel)editDeviceView.DataContext).Initialize(device, tag.IsComms);
126-
editDeviceView.Show();
125+
// Check if device is already being edited, and if so focus the window.
126+
foreach (var window in App.Current.Windows)
127+
{
128+
if (window is EditDeviceView editDeviceView &&
129+
editDeviceView.DataContext is EditDeviceViewModel editDeviceViewModel &&
130+
editDeviceViewModel.ManagedDevice == device)
131+
{
132+
editDeviceView.Focus();
133+
return;
134+
}
135+
}
136+
var newEditDeviceView = new EditDeviceView();
137+
((EditDeviceViewModel)newEditDeviceView.DataContext).Initialize(device, tag.IsComms);
138+
newEditDeviceView.Show();
127139
}
128140
}
129141

0 commit comments

Comments
 (0)