Skip to content

Commit 2956bc6

Browse files
committed
boost(Device::Switching): Be sure the order of switching device follow the selection made by the user in the settings menu
1 parent 1a49f4e commit 2956bc6

File tree

12 files changed

+68
-76
lines changed

12 files changed

+68
-76
lines changed

SoundSwitch.Common/Framework/Audio/Device/DeviceFullInfo.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using NAudio.CoreAudioApi;
1+
using System;
2+
using NAudio.CoreAudioApi;
23
using Newtonsoft.Json;
34
using SoundSwitch.Common.Framework.Audio.Icon;
45

@@ -13,7 +14,7 @@ public class DeviceFullInfo : DeviceInfo
1314
public System.Drawing.Icon SmallIcon => AudioDeviceIconExtractor.ExtractIconFromPath(IconPath, Type, false);
1415

1516
[JsonConstructor]
16-
public DeviceFullInfo(string name, string id, DataFlow type, string iconPath, DeviceState state, bool isUsb) : base(name, id, type, isUsb)
17+
public DeviceFullInfo(string name, string id, DataFlow type, string iconPath, DeviceState state, bool isUsb) : base(name, id, type, isUsb, DateTime.UtcNow)
1718
{
1819
IconPath = iconPath;
1920
State = state;

SoundSwitch.Common/Framework/Audio/Device/DeviceInfo.cs

+23-21
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ public class DeviceInfo : IEquatable<DeviceInfo>, IComparable<DeviceInfo>
2121

2222
public bool IsUsb { get; } = true;
2323

24+
public DateTime DiscoveredAt { get; set; } = DateTime.UtcNow;
25+
2426
public string NameClean
2527
{
2628
get
@@ -43,15 +45,6 @@ public string NameClean
4345
}
4446
}
4547

46-
[JsonConstructor]
47-
public DeviceInfo(string name, string id, DataFlow type, bool isUsb)
48-
{
49-
Name = name;
50-
Id = id;
51-
Type = type;
52-
IsUsb = isUsb;
53-
}
54-
5548
public DeviceInfo(MMDevice device)
5649
{
5750
Name = device.FriendlyName;
@@ -62,6 +55,16 @@ public DeviceInfo(MMDevice device)
6255
IsUsb = enumerator == "USB";
6356
}
6457

58+
[JsonConstructor]
59+
public DeviceInfo(string name, string id, DataFlow type, bool isUsb, DateTime discoveredAt)
60+
{
61+
Name = name;
62+
Id = id;
63+
Type = type;
64+
IsUsb = isUsb;
65+
DiscoveredAt = discoveredAt;
66+
}
67+
6568

6669
public static bool operator ==(DeviceInfo left, DeviceInfo right)
6770
{
@@ -79,18 +82,6 @@ public override string ToString()
7982
return NameClean;
8083
}
8184

82-
public int CompareTo(DeviceInfo other)
83-
{
84-
if (ReferenceEquals(this, other)) return 0;
85-
if (ReferenceEquals(null, other)) return 1;
86-
var nameComparison = string.Compare(NameClean, other.NameClean, StringComparison.Ordinal);
87-
if (nameComparison != 0) return nameComparison;
88-
var idComparison = string.Compare(Id, other.Id, StringComparison.Ordinal);
89-
if (idComparison != 0) return idComparison;
90-
return Type.CompareTo(other.Type);
91-
}
92-
93-
9485

9586
public bool Equals(DeviceInfo other)
9687
{
@@ -119,5 +110,16 @@ public override int GetHashCode()
119110
return hashCode;
120111
}
121112
}
113+
114+
public int CompareTo(DeviceInfo other)
115+
{
116+
if (ReferenceEquals(this, other)) return 0;
117+
if (ReferenceEquals(null, other)) return 1;
118+
var discoveredAtComparison = DiscoveredAt.CompareTo(other.DiscoveredAt);
119+
if (discoveredAtComparison != 0) return discoveredAtComparison;
120+
var nameComparison = string.Compare(Name, other.Name, StringComparison.Ordinal);
121+
if (nameComparison != 0) return nameComparison;
122+
return string.Compare(Id, other.Id, StringComparison.Ordinal);
123+
}
122124
}
123125
}

SoundSwitch/Framework/Configuration/ISoundSwitchConfiguration.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public interface ISoundSwitchConfiguration : IConfiguration
3636
[Obsolete]
3737
HashSet<string> SelectedRecordingDeviceListId { get; }
3838

39-
HashSet<DeviceInfo> SelectedDevices { get; }
39+
SortedSet<DeviceInfo> SelectedDevices { get; }
4040

4141
bool FirstRun { get; set; }
4242
HotKey PlaybackHotKey { get; set; }

SoundSwitch/Framework/Configuration/SoundSwitchConfiguration.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,15 @@ public SoundSwitchConfiguration()
6060
RecordingHotKey = new HotKey(Keys.F7, HotKey.ModifierKeys.Alt | HotKey.ModifierKeys.Control);
6161
MuteRecordingHotKey = new HotKey(Keys.M, HotKey.ModifierKeys.Control | HotKey.ModifierKeys.Alt);
6262

63-
SelectedDevices = new HashSet<DeviceInfo>();
63+
SelectedDevices = new SortedSet<DeviceInfo>();
6464
SwitchIcon = IconChangerFactory.ActionEnum.Never;
6565
MigratedFields = new HashSet<string>();
6666
}
6767

6868

6969
public HashSet<string> SelectedPlaybackDeviceListId { get; }
7070
public HashSet<string> SelectedRecordingDeviceListId { get; }
71-
public HashSet<DeviceInfo> SelectedDevices { get; }
71+
public SortedSet<DeviceInfo> SelectedDevices { get; }
7272
public bool FirstRun { get; set; }
7373
public HotKey PlaybackHotKey { get; set; }
7474
public HotKey RecordingHotKey { get; set; }
@@ -124,15 +124,15 @@ public bool Migrate()
124124
if (SelectedPlaybackDeviceListId.Count > 0)
125125
{
126126
SelectedDevices.UnionWith(
127-
SelectedPlaybackDeviceListId.Select((s => new DeviceInfo("", s, DataFlow.Render, false))));
127+
SelectedPlaybackDeviceListId.Select((s => new DeviceInfo("", s, DataFlow.Render, false, DateTime.UtcNow))));
128128
SelectedPlaybackDeviceListId.Clear();
129129
migrated = true;
130130
}
131131

132132
if (SelectedRecordingDeviceListId.Count > 0)
133133
{
134134
SelectedDevices.UnionWith(
135-
SelectedRecordingDeviceListId.Select((s => new DeviceInfo("", s, DataFlow.Capture, false))));
135+
SelectedRecordingDeviceListId.Select((s => new DeviceInfo("", s, DataFlow.Capture, false, DateTime.UtcNow))));
136136
SelectedRecordingDeviceListId.Clear();
137137
migrated = true;
138138
}

SoundSwitch/Framework/DeviceCyclerManager/DeviceCycler/ADeviceCycler.cs

+7-7
Original file line numberDiff line numberDiff line change
@@ -40,21 +40,21 @@ public abstract class ADeviceCycler : IDeviceCycler
4040
/// <param name="audioDevices"></param>
4141
/// <param name="type"></param>
4242
/// <returns></returns>
43-
protected DeviceFullInfo GetNextDevice(IReadOnlyCollection<DeviceFullInfo> audioDevices, DataFlow type)
43+
protected DeviceInfo GetNextDevice(IEnumerable<DeviceInfo> audioDevices, DataFlow type)
4444
{
45-
46-
var defaultDev = audioDevices.FirstOrDefault(device => AudioSwitcher.Instance.IsDefault(device.Id, (EDataFlow)type, ERole.eConsole)) ??
47-
audioDevices.Last();
48-
var next = audioDevices.SkipWhile((device, i) => device.Id != defaultDev.Id).Skip(1).FirstOrDefault() ??
49-
audioDevices.ElementAt(0);
45+
var deviceInfos = audioDevices as DeviceInfo[] ?? audioDevices.ToArray();
46+
var defaultDev = deviceInfos.FirstOrDefault(device => AudioSwitcher.Instance.IsDefault(device.Id, (EDataFlow)type, ERole.eConsole)) ??
47+
deviceInfos.Last();
48+
var next = deviceInfos.SkipWhile((device, i) => device.Id != defaultDev.Id).Skip(1).FirstOrDefault() ??
49+
deviceInfos.ElementAt(0);
5050
return next;
5151
}
5252

5353
/// <summary>
5454
/// Attempts to set active device to the specified name
5555
/// </summary>
5656
/// <param name="device"></param>
57-
public bool SetActiveDevice(DeviceFullInfo device)
57+
public bool SetActiveDevice(DeviceInfo device)
5858
{
5959

6060
Log.Information("Set Default device: {Device}", device);

SoundSwitch/Framework/DeviceCyclerManager/DeviceCycler/DeviceCyclerAvailable.cs

+12-22
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,10 @@
1313
********************************************************************/
1414

1515
using System;
16-
using System.Collections.Generic;
16+
using System.Linq;
1717
using NAudio.CoreAudioApi;
18-
using SoundSwitch.Common.Framework.Audio.Device;
19-
using SoundSwitch.Model;
2018
using SoundSwitch.Localization;
19+
using SoundSwitch.Model;
2120

2221
namespace SoundSwitch.Framework.DeviceCyclerManager.DeviceCycler
2322
{
@@ -32,28 +31,19 @@ public class DeviceCyclerAvailable : ADeviceCycler
3231
/// <param name="type"></param>
3332
public override bool CycleAudioDevice(DataFlow type)
3433
{
35-
IReadOnlyCollection<DeviceFullInfo> audioDevices;
36-
switch (type)
34+
var audioDevices = (type switch
3735
{
38-
case DataFlow.Render:
39-
audioDevices = AppModel.Instance.AvailablePlaybackDevices;
40-
break;
41-
case DataFlow.Capture:
42-
audioDevices = AppModel.Instance.AvailableRecordingDevices;
43-
break;
44-
default:
45-
throw new ArgumentOutOfRangeException(nameof(type), type, null);
46-
}
36+
DataFlow.Render => AppModel.Instance.AvailablePlaybackDevices,
37+
DataFlow.Capture => AppModel.Instance.AvailableRecordingDevices,
38+
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
39+
}).ToArray();
4740

48-
switch (audioDevices.Count)
41+
return audioDevices switch
4942
{
50-
case 0:
51-
throw new AppModel.NoDevicesException();
52-
case 1:
53-
return false;
54-
}
55-
56-
return SetActiveDevice(GetNextDevice(audioDevices, type));
43+
{Length: 0} => throw new AppModel.NoDevicesException(),
44+
{Length: 1} => false,
45+
_ => SetActiveDevice(GetNextDevice(audioDevices, type))
46+
};
5747
}
5848
}
5949
}

SoundSwitch/Framework/DeviceCyclerManager/DeviceCycler/IDeviceCycler.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,6 @@ public interface IDeviceCycler : IEnumImpl<DeviceCyclerTypeEnum>
3030
/// Attempts to set active device to the specified name
3131
/// </summary>
3232
/// <param name="device"></param>
33-
bool SetActiveDevice(DeviceFullInfo device);
33+
bool SetActiveDevice(DeviceInfo device);
3434
}
3535
}

SoundSwitch/Framework/DeviceCyclerManager/DeviceCyclerManager.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public bool CycleDevice(DataFlow type)
5151
/// </summary>
5252
/// <param name="device"></param>
5353
/// <returns></returns>
54-
public bool SetAsDefault(DeviceFullInfo device)
54+
public bool SetAsDefault(DeviceInfo device)
5555
{
5656
return _deviceCyclerFactory.Get(CurrentCycler).SetActiveDevice(device);
5757
}

SoundSwitch/Model/AppModel.cs

+6-7
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,9 @@ public bool IncludeBetaVersions
123123

124124
public ISet<DeviceInfo> SelectedDevices => AppConfigs.Configuration.SelectedDevices;
125125

126-
public IReadOnlyCollection<DeviceFullInfo> AvailablePlaybackDevices =>
127-
ActiveAudioDeviceLister.PlaybackDevices.Where(device => SelectedDevices.FirstOrDefault(device.Equals) != null).ToArray();
126+
public IEnumerable<DeviceInfo> AvailablePlaybackDevices => SelectedDevices.Intersect(ActiveAudioDeviceLister.PlaybackDevices);
128127

129-
130-
public IReadOnlyCollection<DeviceFullInfo> AvailableRecordingDevices =>
131-
ActiveAudioDeviceLister.RecordingDevices.Where(device => SelectedDevices.FirstOrDefault(device.Equals) != null).ToArray();
128+
public IEnumerable<DeviceInfo> AvailableRecordingDevices => SelectedDevices.Intersect(ActiveAudioDeviceLister.RecordingDevices);
132129

133130
public bool SetCommunications
134131
{
@@ -332,6 +329,7 @@ public bool SelectDevice(DeviceFullInfo device)
332329
{
333330
try
334331
{
332+
device.DiscoveredAt = DateTime.UtcNow;
335333
SelectedDevices.Add(device);
336334
}
337335
catch (ArgumentException)
@@ -355,7 +353,8 @@ public bool UnselectDevice(DeviceFullInfo device)
355353
bool result;
356354
try
357355
{
358-
result = SelectedDevices.Where(device.Equals).Aggregate(true, (current, deviceInfo) => current & SelectedDevices.Remove(deviceInfo));
356+
var list = SelectedDevices.Where(device.Equals).ToArray();
357+
result = list.Aggregate(true, (b, info) => b & SelectedDevices.Remove(info));
359358
}
360359
catch (ArgumentException)
361360
{
@@ -475,7 +474,7 @@ private void HandleHotkeyPress(object sender, WindowsAPIAdapter.KeyPressedEventA
475474
/// Attempts to set active device to the specified name
476475
/// </summary>
477476
/// <param name="device"></param>
478-
public bool SetActiveDevice(DeviceFullInfo device)
477+
public bool SetActiveDevice(DeviceInfo device)
479478
{
480479
try
481480
{

SoundSwitch/Model/IAppModel.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ public interface IAppModel : IDisposable
3939
/// <summary>
4040
/// An union between the Active <see cref="IAudioDevice" /> of Windows and <see cref="SelectedPlaybackDevicesList" />
4141
/// </summary>
42-
IReadOnlyCollection<DeviceFullInfo> AvailablePlaybackDevices { get; }
42+
IEnumerable<DeviceInfo> AvailablePlaybackDevices { get; }
4343

4444
/// <summary>
4545
/// An union between the Active <see cref="IAudioDevice" /> of Windows and <see cref="SelectedRecordingDevicesList" />
4646
/// </summary>
47-
IReadOnlyCollection<DeviceFullInfo> AvailableRecordingDevices { get; }
47+
IEnumerable<DeviceInfo> AvailableRecordingDevices { get; }
4848

4949
/// <summary>
5050
/// If the Playback device need also to be set for Communications.
@@ -191,7 +191,7 @@ public interface IAppModel : IDisposable
191191
/// Attempts to set active device to the specified name
192192
/// </summary>
193193
/// <param name="device"></param>
194-
bool SetActiveDevice(DeviceFullInfo device);
194+
bool SetActiveDevice(DeviceInfo device);
195195

196196
/// <summary>
197197
/// Cycles the active device to the next device. Returns true if succesfully switched (at least

SoundSwitch/UI/Component/TrayIcon.cs

+5-5
Original file line numberDiff line numberDiff line change
@@ -302,8 +302,8 @@ public void UpdateDeviceSelectionList()
302302
{
303303
Log.Information("Set tray icon menu devices");
304304
_selectionMenu.Items.Clear();
305-
var playbackDevices = AppModel.Instance.AvailablePlaybackDevices;
306-
var recordingDevices = AppModel.Instance.AvailableRecordingDevices;
305+
var playbackDevices = AppModel.Instance.AvailablePlaybackDevices.ToArray();
306+
var recordingDevices = AppModel.Instance.AvailableRecordingDevices.ToArray();
307307
var profiles = _profileTrayIconBuilder.GetMenuItems().ToArray();
308308

309309
if (profiles.Length > 0)
@@ -312,8 +312,8 @@ public void UpdateDeviceSelectionList()
312312
_selectionMenu.Items.Add(new ToolStripSeparator());
313313
}
314314

315-
if (playbackDevices.Count < 0 &&
316-
recordingDevices.Count < 0)
315+
if (playbackDevices.Length < 0 &&
316+
recordingDevices.Length < 0)
317317
{
318318
Log.Information("Device list empty");
319319
return;
@@ -322,7 +322,7 @@ public void UpdateDeviceSelectionList()
322322
var defaultPlayback = AudioSwitcher.Instance.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eConsole);
323323
_selectionMenu.Items.AddRange(playbackDevices.Select(info => new ToolStripDeviceItem(DeviceClicked, info, info.Equals(defaultPlayback))).ToArray());
324324

325-
if (recordingDevices.Count > 0)
325+
if (recordingDevices.Length > 0)
326326
{
327327
var defaultRecording = AudioSwitcher.Instance.GetDefaultAudioEndpoint(EDataFlow.eCapture, ERole.eConsole);
328328

SoundSwitch/Util/ToolStripDeviceItem.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ internal class ToolStripDeviceItem : ToolStripMenuItem
2525
{
2626
private readonly bool _isDefault;
2727

28-
public ToolStripDeviceItem(EventHandler onClick, DeviceFullInfo audioDevice, bool isDefault)
28+
public ToolStripDeviceItem(EventHandler onClick, DeviceInfo audioDevice, bool isDefault)
2929
: base(audioDevice.NameClean, null, onClick)
3030
{
3131
AudioDevice = audioDevice;
@@ -34,6 +34,6 @@ public ToolStripDeviceItem(EventHandler onClick, DeviceFullInfo audioDevice, boo
3434

3535
public override Image Image => _isDefault ? Resources.Check : null;
3636

37-
public DeviceFullInfo AudioDevice { get; }
37+
public DeviceInfo AudioDevice { get; }
3838
}
3939
}

0 commit comments

Comments
 (0)