Skip to content

Commit

Permalink
fix(DeviceLister): Fix concurrency issue with the TrayIcon
Browse files Browse the repository at this point in the history
Fixes #626
Fixes #622
  • Loading branch information
Belphemur committed May 8, 2021
1 parent 5ce24db commit 84ddc78
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 8 deletions.
28 changes: 20 additions & 8 deletions SoundSwitch/Framework/Audio/Lister/CachedAudioDeviceLister.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
********************************************************************/

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using NAudio.CoreAudioApi;
using Serilog;
using SoundSwitch.Common.Framework.Audio.Device;
Expand All @@ -26,16 +28,16 @@ namespace SoundSwitch.Framework.Audio.Lister
public class CachedAudioDeviceLister : IAudioDeviceLister
{
/// <inheritdoc />
public IReadOnlyCollection<DeviceFullInfo> PlaybackDevices => _playbackDevices;
public IReadOnlyCollection<DeviceFullInfo> PlaybackDevices => (IReadOnlyCollection<DeviceFullInfo>) _playbackDevices.Values;

/// <inheritdoc />
public IReadOnlyCollection<DeviceFullInfo> RecordingDevices => _recordingDevices;
public IReadOnlyCollection<DeviceFullInfo> RecordingDevices => (IReadOnlyCollection<DeviceFullInfo>) _recordingDevices.Values;

private readonly DeviceState _state;
private readonly DebounceDispatcher _dispatcher = new();
private readonly object _lock = new();
private readonly HashSet<DeviceFullInfo> _playbackDevices = new();
private readonly HashSet<DeviceFullInfo> _recordingDevices = new();
private readonly ConcurrentDictionary<string, DeviceFullInfo> _playbackDevices = new();
private readonly ConcurrentDictionary<string,DeviceFullInfo> _recordingDevices = new();

public CachedAudioDeviceLister(DeviceState state)
{
Expand All @@ -51,12 +53,12 @@ public void Refresh()
{
lock (_lock)
{
_recordingDevices.Clear();
_playbackDevices.Clear();
var ids = new HashSet<string>();
Log.Information("[{@State}] Refreshing all devices", _state);
using var enumerator = new MMDeviceEnumerator();
foreach (var endPoint in enumerator.EnumerateAudioEndPoints(DataFlow.All, _state))
{
ids.Add(endPoint.ID);
try
{
var deviceInfo = new DeviceFullInfo(endPoint);
Expand All @@ -68,10 +70,10 @@ public void Refresh()
switch (deviceInfo.Type)
{
case DataFlow.Render:
_playbackDevices.Add(deviceInfo);
_playbackDevices.AddOrUpdate(deviceInfo.Id, deviceInfo, (_, _) => deviceInfo);
break;
case DataFlow.Capture:
_recordingDevices.Add(deviceInfo);
_recordingDevices.AddOrUpdate(deviceInfo.Id, deviceInfo, (_, _) => deviceInfo);
break;
case DataFlow.All:
break;
Expand All @@ -85,6 +87,16 @@ public void Refresh()
}
}

foreach (var deviceId in _playbackDevices.Keys.Except(ids))
{
_playbackDevices.TryRemove(deviceId, out _);
}

foreach (var deviceId in _recordingDevices.Keys.Except(ids))
{
_recordingDevices.TryRemove(deviceId, out _);
}

Log.Information("[{@State}] Refreshed all devices. {@Recording}/rec, {@Playback}/play", _state, _recordingDevices.Count, _playbackDevices.Count);
}
}
Expand Down
2 changes: 2 additions & 0 deletions SoundSwitch/SoundSwitch.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
<TargetPlatformVersion>8.0</TargetPlatformVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<DefineConstants>TRACE;DEBUG</DefineConstants>
<Optimize>false</Optimize>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
<Optimize>true</Optimize>
Expand Down

0 comments on commit 84ddc78

Please sign in to comment.