From f2ea66bd48d3494f53934a93e5b387393d87fbe0 Mon Sep 17 00:00:00 2001 From: Antoine Aflalo Date: Thu, 7 Jun 2018 16:16:48 -0400 Subject: [PATCH 1/3] Add new collection To manage all the needs of matching per Name or Id --- .../Device/DeviceInfoCollection.cs | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 SoundSwitch/Framework/Configuration/Device/DeviceInfoCollection.cs diff --git a/SoundSwitch/Framework/Configuration/Device/DeviceInfoCollection.cs b/SoundSwitch/Framework/Configuration/Device/DeviceInfoCollection.cs new file mode 100644 index 0000000000..f6d297c495 --- /dev/null +++ b/SoundSwitch/Framework/Configuration/Device/DeviceInfoCollection.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using NAudio.CoreAudioApi; + +namespace SoundSwitch.Framework.Configuration.Device +{ + public class DeviceInfoCollection : ICollection + { + private readonly Dictionary _deviceByName = new Dictionary(); + private readonly Dictionary _deviceById = new Dictionary(); + + private readonly HashSet _original; + + public DeviceInfoCollection(HashSet devices) + { + _original = devices; + foreach (var deviceInfo in devices) + { + AddItem(deviceInfo); + } + } + + /// + /// Intersect with MMDevices + /// + /// + /// + public IEnumerable IntersectWith(IEnumerable devices) + { + var result = devices.Join(_deviceById, device => device.ID, pair => pair.Key, (device, pair) => device); + if (result.GetEnumerator().Current != null) + { + return result; + } + + return devices.Join(_deviceByName, device => device.FriendlyName, pair => pair.Key, (device, pair) => device); + } + + public IEnumerator GetEnumerator() + { + return _deviceById.Values.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void Add(DeviceInfo item) + { + AddItem(item, true); + } + + /// + /// Add Item and update or not the original + /// + /// + /// + private void AddItem(DeviceInfo item, bool updateOriginal = false) + { + if (item == null) + { + return; + } + + try + { + _deviceById.Add(item.Id, item); + } + catch (ArgumentException) + { + } + + try + { + _deviceByName.Add(item.Name, item); + } + catch (ArgumentException) + { + } + + if (updateOriginal) + { + _original.Add(item); + } + } + + public void Clear() + { + _deviceById.Clear(); + _deviceByName.Clear(); + _original.Clear(); + } + + public bool Contains(DeviceInfo item) + { + return item != null && (_deviceById.ContainsKey(item.Id) || _deviceByName.ContainsKey(item.Name)); + } + + public void CopyTo(DeviceInfo[] array, int arrayIndex) + { + throw new System.NotImplementedException(); + } + + public bool Remove(DeviceInfo item) + { + if (item == null) + { + return false; + } + + var result = _original.RemoveWhere((info => info?.Id == item.Id || info?.Name == item.Name)) > 0; + + try + { + result &= _deviceById.Remove(item.Id); + } + catch (ArgumentException) + { + + } + + try + { + result &= _deviceByName.Remove(item.Name); + } + catch (ArgumentException) + { + + } + + return result; + } + + public int Count => _deviceById.Count; + + public bool IsReadOnly => false; + } +} \ No newline at end of file From 365998139f01cf29b4b51ab263c8f0c32f9eea70 Mon Sep 17 00:00:00 2001 From: Antoine Aflalo Date: Thu, 7 Jun 2018 16:16:58 -0400 Subject: [PATCH 2/3] Update project to use collection --- SoundSwitch/Model/AppModel.cs | 55 +++++++++++++++++++++----------- SoundSwitch/Model/IAppModel.cs | 2 +- SoundSwitch/SoundSwitch.csproj | 1 + SoundSwitch/UI/Forms/Settings.cs | 8 ++--- 4 files changed, 43 insertions(+), 23 deletions(-) diff --git a/SoundSwitch/Model/AppModel.cs b/SoundSwitch/Model/AppModel.cs index f96289c51a..bd59fb1255 100644 --- a/SoundSwitch/Model/AppModel.cs +++ b/SoundSwitch/Model/AppModel.cs @@ -52,6 +52,9 @@ private AppModel() public TrayIcon TrayIcon { get; set; } private CachedSound _customNotificationCachedSound; private readonly DeviceCyclerManager _deviceCyclerManager; + private DeviceInfoCollection _deviceSelectedList; + + public CachedSound CustomNotificationSound { @@ -102,7 +105,15 @@ public bool IncludeBetaVersions } - public HashSet SelectedDevices { get; } = AppConfigs.Configuration.SelectedDevices; + public DeviceInfoCollection SelectedDevices + { + get + { + if (_deviceSelectedList != null) + return _deviceSelectedList; + return _deviceSelectedList = new DeviceInfoCollection(AppConfigs.Configuration.SelectedDevices); + } + } public ICollection AvailablePlaybackDevices { @@ -110,7 +121,7 @@ public ICollection AvailablePlaybackDevices { using (var devices = ActiveAudioDeviceLister.GetPlaybackDevices()) { - return devices.Where((device) => SelectedDevices.Any((info => new DeviceInfo(device).Equals(info)))).ToList(); + return SelectedDevices.IntersectWith(devices).ToList(); } } } @@ -123,7 +134,7 @@ public ICollection AvailableRecordingDevices { using (var devices = ActiveAudioDeviceLister.GetRecordingDevices()) { - return devices.Where((device) => SelectedDevices.Any((info => new DeviceInfo(device).Equals(info)))).ToList(); + return SelectedDevices.IntersectWith(devices).ToList(); } } } @@ -288,17 +299,19 @@ private int SaveState(object state) /// public bool SelectDevice(MMDevice device) { - var result = false; - DeviceListChanged eventChanged = null; - result = SelectedDevices.Add(new DeviceInfo(device)); - eventChanged = new DeviceListChanged(SelectedDevices, (DeviceType)device.DataFlow); - - if (result) + try { - SelectedDeviceChanged?.Invoke(this, eventChanged); - AppConfigs.Configuration.Save(); + SelectedDevices.Add(new DeviceInfo(device)); } - return result; + catch (ArgumentException) + { + return false; + } + + SelectedDeviceChanged?.Invoke(this, new DeviceListChanged(SelectedDevices, (DeviceType)device.DataFlow)); + AppConfigs.Configuration.Save(); + + return true; } /// @@ -309,17 +322,23 @@ public bool SelectDevice(MMDevice device) public bool UnselectDevice(MMDevice device) { var result = false; - var deviceToRemove = new DeviceInfo(device); - DeviceListChanged eventChanged = null; - result = SelectedDevices.RemoveWhere((info => info.Equals(deviceToRemove))) > 0; - eventChanged = new DeviceListChanged(SelectedDevices, (DeviceType)device.DataFlow); + try + { + result = SelectedDevices.Remove(new DeviceInfo(device)); + } + catch (ArgumentException) + { + return false; + } if (result) { - SelectedDeviceChanged?.Invoke(this, eventChanged); + SelectedDeviceChanged?.Invoke(this, + new DeviceListChanged(SelectedDevices, (DeviceType) device.DataFlow)); AppConfigs.Configuration.Save(); } - return result; + + return true; } #endregion diff --git a/SoundSwitch/Model/IAppModel.cs b/SoundSwitch/Model/IAppModel.cs index 7adc0d2117..f348241a6b 100644 --- a/SoundSwitch/Model/IAppModel.cs +++ b/SoundSwitch/Model/IAppModel.cs @@ -35,7 +35,7 @@ public interface IAppModel /// /// Devices selected for Switching /// - HashSet SelectedDevices { get; } + DeviceInfoCollection SelectedDevices { get; } /// /// An union between the Active of Windows and /// diff --git a/SoundSwitch/SoundSwitch.csproj b/SoundSwitch/SoundSwitch.csproj index d1d812b282..6f51bf90c1 100644 --- a/SoundSwitch/SoundSwitch.csproj +++ b/SoundSwitch/SoundSwitch.csproj @@ -258,6 +258,7 @@ + diff --git a/SoundSwitch/UI/Forms/Settings.cs b/SoundSwitch/UI/Forms/Settings.cs index 2a454ad990..df3125544a 100644 --- a/SoundSwitch/UI/Forms/Settings.cs +++ b/SoundSwitch/UI/Forms/Settings.cs @@ -149,8 +149,8 @@ public SettingsForm() // Playback and Recording var audioDeviceLister = new AudioDeviceLister(DeviceState.All); - PopulateAudioList(playbackListView, AppModel.Instance.SelectedDevices.Where((device) => device.Type == DataFlow.Render), audioDeviceLister.GetPlaybackDevices()); - PopulateAudioList(recordingListView, AppModel.Instance.SelectedDevices.Where((device) => device.Type == DataFlow.Capture), audioDeviceLister.GetRecordingDevices()); + PopulateAudioList(playbackListView, AppModel.Instance.SelectedDevices, audioDeviceLister.GetPlaybackDevices()); + PopulateAudioList(recordingListView, AppModel.Instance.SelectedDevices, audioDeviceLister.GetRecordingDevices()); _loaded = true; } @@ -479,7 +479,7 @@ private ListViewItem GenerateListViewItem(MMDevice device, IEnumerable selectedDevice.Equals(info)))) + if (selected.Contains(selectedDevice)) { listViewItem.Checked = true; listViewItem.Group = listView.Groups["selectedGroup"]; @@ -522,7 +522,7 @@ private void ListViewItemChecked(object sender, ItemCheckEventArgs e) throw new ArgumentOutOfRangeException(); } } - catch (Exception) + catch (Exception exception) { e.NewValue = e.CurrentValue; } From b22da76efc1c119a8fe15888402fbe4f0746046e Mon Sep 17 00:00:00 2001 From: Antoine Aflalo Date: Thu, 7 Jun 2018 16:40:06 -0400 Subject: [PATCH 3/3] Improves IntersectWith code Take advantage of the Dictionnaries --- .../Configuration/Device/DeviceInfoCollection.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/SoundSwitch/Framework/Configuration/Device/DeviceInfoCollection.cs b/SoundSwitch/Framework/Configuration/Device/DeviceInfoCollection.cs index f6d297c495..61ffb3d897 100644 --- a/SoundSwitch/Framework/Configuration/Device/DeviceInfoCollection.cs +++ b/SoundSwitch/Framework/Configuration/Device/DeviceInfoCollection.cs @@ -30,13 +30,21 @@ public DeviceInfoCollection(HashSet devices) /// public IEnumerable IntersectWith(IEnumerable devices) { - var result = devices.Join(_deviceById, device => device.ID, pair => pair.Key, (device, pair) => device); - if (result.GetEnumerator().Current != null) + var devicesResult = new Dictionary(); + foreach (var mmDevice in devices) { - return result; + if (devicesResult.ContainsKey(mmDevice.ID)) + continue; + + if (!_deviceById.ContainsKey(mmDevice.ID) && !_deviceByName.ContainsKey(mmDevice.FriendlyName)) + continue; + + devicesResult.Add(mmDevice.ID, mmDevice); + } - return devices.Join(_deviceByName, device => device.FriendlyName, pair => pair.Key, (device, pair) => device); + return devicesResult.Values; + } public IEnumerator GetEnumerator()