Skip to content

Commit

Permalink
refactor: device lister is now in charge of notifying of default devi…
Browse files Browse the repository at this point in the history
…ce changed.
  • Loading branch information
Belphemur committed Apr 3, 2024
1 parent 144cff1 commit a647935
Show file tree
Hide file tree
Showing 8 changed files with 49 additions and 51 deletions.
25 changes: 22 additions & 3 deletions SoundSwitch/Framework/Audio/Lister/CachedAudioDeviceLister.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -37,6 +39,9 @@ public class CachedAudioDeviceLister : IAudioDeviceLister
/// <inheritdoc />
private Dictionary<string, DeviceFullInfo> RecordingDevices { get; set; } = new();

private readonly ISubject<DefaultDevicePayload> _defaultDeviceChanged = new Subject<DefaultDevicePayload>();
public IObservable<DefaultDevicePayload> DefaultDeviceChanged => _defaultDeviceChanged.AsObservable();

/// <summary>
/// Get devices per type and state
/// </summary>
Expand Down Expand Up @@ -97,15 +102,22 @@ private void DisposeDevice(DeviceFullInfo deviceFullInfo)
/// <exception cref="ArgumentOutOfRangeException"></exception>
public void ProcessDeviceUpdates(IEnumerable<DeviceChangedEvent> deviceChangedEvents)
{
void UpdateDeviceCache(DeviceChangedEvent deviceChangedEvent)
bool GetDevice(DeviceChangedEvent deviceChangedEvent, out DeviceFullInfo device)
{
var device = AudioSwitcher.Instance.GetAudioEndpoint(deviceChangedEvent.DeviceId);
device = AudioSwitcher.Instance.GetAudioEndpoint(deviceChangedEvent.DeviceId);
if (device == null)
{
_context.Warning("Can't get device {deviceId}", deviceChangedEvent.DeviceId);
return;
return true;
}

return false;
}

void UpdateDeviceCache(DeviceChangedEvent deviceChangedEvent)
{
if (GetDevice(deviceChangedEvent, out var device)) return;

switch (device.Type)
{
case DataFlow.Render:
Expand Down Expand Up @@ -157,6 +169,13 @@ void UpdateDeviceCache(DeviceChangedEvent deviceChangedEvent)
UpdateDeviceCache(deviceChangedEvent);
break;
case EventType.DefaultChanged:
if (!PlaybackDevices.TryGetValue(deviceChangedEvent.DeviceId, out var device) && !RecordingDevices.TryGetValue(deviceChangedEvent.DeviceId, out device))
{
_context.Warning("Can't get device {deviceId}", deviceChangedEvent.DeviceId);
break;
}

_defaultDeviceChanged.OnNext(new DefaultDevicePayload(device, ((DefaultDeviceChangedEvent)deviceChangedEvent).Role));
break;
default:
throw new ArgumentOutOfRangeException();
Expand Down
6 changes: 6 additions & 0 deletions SoundSwitch/Framework/Audio/Lister/DefaultDevicePayload.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using NAudio.CoreAudioApi;
using SoundSwitch.Common.Framework.Audio.Device;

namespace SoundSwitch.Framework.Audio.Lister;

public record struct DefaultDevicePayload(DeviceFullInfo Device, Role Role);
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ private record struct DeviceRole(DataFlow Flow, Role Role);

private readonly ConcurrentQueue<DeviceChangedEvent> _deviceChangedEvents = new();

public event EventHandler<DeviceDefaultChangedEvent> DefaultDeviceChanged;
public event EventHandler<DeviceChangedEventBase> DevicesChanged;
public event EventHandler<DeviceChangedEventBase> DeviceAdded;

/// <summary>
Expand Down Expand Up @@ -91,7 +89,7 @@ public void OnDefaultDeviceChanged(DataFlow flow, Role role, string deviceId)
}

_lastRoleDevice[deviceRole] = deviceId;
_deviceChangedEvents.Enqueue(new DeviceChangedEvent(EventType.DefaultChanged, deviceId));
_deviceChangedEvents.Enqueue(new DefaultDeviceChangedEvent(EventType.DefaultChanged, deviceId, role));
}

public void OnPropertyValueChanged(string pwstrDeviceId, PropertyKey key)
Expand Down
45 changes: 5 additions & 40 deletions SoundSwitch/Model/AppModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Job.Scheduler.Job;
using Job.Scheduler.Job.Action;
using Job.Scheduler.Job.Exception;
using NAudio.CoreAudioApi;
using RailSharp;
using Serilog;
Expand Down Expand Up @@ -55,55 +51,20 @@ public class AppModel : IAppModel
private readonly NotificationManager _notificationManager;
private UpdateChecker _updateChecker;
private DeviceCollection<DeviceInfo> _selectedDevices;

private class DefaultDeviceChangedJob : IDebounceJob
{
private readonly AppModel _appModel;
private readonly DeviceFullInfo _device;
private readonly Role _role;

public DefaultDeviceChangedJob(AppModel appModel, DeviceFullInfo device, Role role)
{
_appModel = appModel;
_device = device;
_role = role;
Key = $"default-device-changed-{device.Type}-{role}";
}

public Task ExecuteAsync(CancellationToken cancellationToken)
{
Log.Information(@"[WINAPI] Default device changed to {device}:{role}", _device, _role);
_appModel.DefaultDeviceChanged?.Invoke(_appModel, new DeviceDefaultChangedEvent(_device, _role, cancellationToken));
_device.Dispose();
return Task.CompletedTask;
}

public Task OnFailure(JobException exception)
{
return Task.CompletedTask;
}

public IRetryAction FailRule { get; } = new NoRetry();
public TimeSpan? MaxRuntime { get; }
public string Key { get; }
public TimeSpan DebounceTime { get; } = TimeSpan.FromMilliseconds(200);
}

private AppModel()
{
_notificationManager = new NotificationManager(this);

_deviceCyclerManager = new DeviceCyclerManager();
_selectedDevices = null;
MMNotificationClient.Instance.DefaultDeviceChanged += (sender, @event) => { JobScheduler.Instance.ScheduleJob(new DefaultDeviceChangedJob(this, @event.Device, @event.Role), @event.Token); };
MMNotificationClient.Instance.DeviceAdded += (sender, @event) =>
{
if (!AutoAddNewDevice)
{
return;
}

JobScheduler.Instance.ScheduleJob(new DeviceAddedJob(this, @event.DeviceId), @event.Token);
JobScheduler.Instance.ScheduleJob(new DeviceAddedJob(this, @event.DeviceId));
};
_microphoneMuteToggler = new MicrophoneMuteToggler(AudioSwitcher.Instance, _notificationManager);
_updateScheduler = new LimitedConcurrencyLevelTaskScheduler(1);
Expand Down Expand Up @@ -346,6 +307,10 @@ public void InitializeMain(IAudioDeviceLister active)

AudioDeviceLister = active;
JobScheduler.Instance.ScheduleJob(new ProcessNotificationEventsJob());
AudioDeviceLister.DefaultDeviceChanged.Subscribe((@event) =>
{
DefaultDeviceChanged?.Invoke(this, new DeviceDefaultChangedEvent(@event.Device, @event.Role));
});

RegisterHotKey(AppConfigs.Configuration.PlaybackHotKey);
var saveConfig = false;
Expand Down
10 changes: 10 additions & 0 deletions SoundSwitch/Model/DeviceChangedEvent.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using NAudio.CoreAudioApi;

namespace SoundSwitch.Model;

Expand All @@ -21,4 +22,13 @@ public int CompareTo(DeviceChangedEvent other)
if (actionComparison != 0) return actionComparison;
return string.Compare(DeviceId, other.DeviceId, StringComparison.Ordinal);
}
}

public record DefaultDeviceChangedEvent(EventType Action, string DeviceId, Role Role) : DeviceChangedEvent(Action, DeviceId), IComparable<DefaultDeviceChangedEvent>
{
// Don't compare the role in this case, we don't want multiple times the same event for the same device with different role
public int CompareTo(DefaultDeviceChangedEvent other)
{
return base.CompareTo(other);
}
}
7 changes: 2 additions & 5 deletions SoundSwitch/Model/Events.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

using System;
using System.Collections.Generic;
using System.Threading;
using NAudio.CoreAudioApi;
using SoundSwitch.Common.Framework.Audio.Device;
using SoundSwitch.Framework.Audio;
Expand Down Expand Up @@ -96,13 +95,11 @@ public NewReleaseAvailableEvent(AppRelease appRelease, UpdateMode updateMode) :
public class DeviceChangedEventBase : EventArgs
{
public string DeviceId { get; }
public CancellationToken Token { get; }


public DeviceChangedEventBase(string deviceId, CancellationToken token)
public DeviceChangedEventBase(string deviceId)
{
DeviceId = deviceId;
Token = token;
}
}

Expand All @@ -111,7 +108,7 @@ public class DeviceDefaultChangedEvent : DeviceChangedEventBase
public Role Role { get; }
public DeviceFullInfo Device { get; }

public DeviceDefaultChangedEvent(DeviceFullInfo device, Role role, CancellationToken token) : base(device.Id, token)
public DeviceDefaultChangedEvent(DeviceFullInfo device, Role role) : base(device.Id)
{
Device = device;
Role = role;
Expand Down
2 changes: 2 additions & 0 deletions SoundSwitch/Model/IAudioDeviceLister.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@
using NAudio.CoreAudioApi;
using SoundSwitch.Common.Framework.Audio.Collection;
using SoundSwitch.Common.Framework.Audio.Device;
using SoundSwitch.Framework.Audio.Lister;

namespace SoundSwitch.Model
{
public interface IAudioDeviceLister : IDisposable
{

bool Refreshing { get; }
IObservable<DefaultDevicePayload> DefaultDeviceChanged { get; }

void Refresh(CancellationToken token);

Expand Down
1 change: 1 addition & 0 deletions SoundSwitch/SoundSwitch.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
<PackageReference Include="System.Diagnostics.TraceSource" Version="4.3.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.3" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
<PackageReference Include="System.Reactive.Linq" Version="6.0.0" />
<PackageReference Include="System.Resources.Extensions" Version="8.0.0" />
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
</ItemGroup>
Expand Down

0 comments on commit a647935

Please sign in to comment.