diff --git a/WaveLinkPlugin/Adjustments/InputMonitorMixAdjustment.cs b/WaveLinkPlugin/Adjustments/InputMonitorMixAdjustment.cs index d6d1359..24f7e7e 100644 --- a/WaveLinkPlugin/Adjustments/InputMonitorMixAdjustment.cs +++ b/WaveLinkPlugin/Adjustments/InputMonitorMixAdjustment.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using ElgatoWaveSDK; using ElgatoWaveSDK.Models; namespace Loupedeck.WaveLinkPlugin.Adjustments @@ -8,7 +7,7 @@ namespace Loupedeck.WaveLinkPlugin.Adjustments class InputMonitorMixAdjustment : PluginDynamicAdjustment { private WaveLinkPlugin _plugin; - private ElgatoWaveClient _client; + private WaveLinkClient _client; private readonly Dictionary _states; @@ -87,8 +86,6 @@ protected override void RunCommand(string actionParameter) inputMix.IsLocalInMuted = !inputMix.IsLocalInMuted; inputMix.IsLocalInMuted = _client.SetInputMixer(inputMix, MixType.LocalMix) - .GetAwaiter() - .GetResult() ?.IsLocalInMuted; base.ActionImageChanged(); @@ -114,7 +111,6 @@ protected override void ApplyAdjustment(string actionParameter, int diff) inputMix.LocalVolumeIn = volume; inputMix.LocalVolumeIn = _client.SetInputMixer(inputMix, MixType.LocalMix) - .GetAwaiter().GetResult() ?.LocalVolumeIn; base.AdjustmentValueChanged(actionParameter); diff --git a/WaveLinkPlugin/Adjustments/InputStreamMixAdjustment.cs b/WaveLinkPlugin/Adjustments/InputStreamMixAdjustment.cs index af96375..1c81639 100644 --- a/WaveLinkPlugin/Adjustments/InputStreamMixAdjustment.cs +++ b/WaveLinkPlugin/Adjustments/InputStreamMixAdjustment.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using ElgatoWaveSDK; using ElgatoWaveSDK.Models; namespace Loupedeck.WaveLinkPlugin.Adjustments @@ -8,7 +7,7 @@ namespace Loupedeck.WaveLinkPlugin.Adjustments class InputStreamMixAdjustment : PluginDynamicAdjustment { private WaveLinkPlugin _plugin; - private ElgatoWaveClient _client; + private WaveLinkClient _client; private readonly Dictionary _states; @@ -87,8 +86,6 @@ protected override void RunCommand(string actionParameter) inputMix.IsStreamInMuted = !inputMix.IsStreamInMuted; inputMix.IsStreamInMuted = _client.SetInputMixer(inputMix, MixType.StreamMix) - .GetAwaiter() - .GetResult() ?.IsStreamInMuted; base.ActionImageChanged(); @@ -114,7 +111,6 @@ protected override void ApplyAdjustment(string actionParameter, int diff) inputMix.StreamVolumeIn = volume; inputMix.StreamVolumeIn = _client.SetInputMixer(inputMix, MixType.StreamMix) - .GetAwaiter().GetResult() ?.StreamVolumeIn; base.AdjustmentValueChanged(actionParameter); diff --git a/WaveLinkPlugin/Adjustments/OutputMonitorMixAdjustment.cs b/WaveLinkPlugin/Adjustments/OutputMonitorMixAdjustment.cs index 73fb3cd..1315b47 100644 --- a/WaveLinkPlugin/Adjustments/OutputMonitorMixAdjustment.cs +++ b/WaveLinkPlugin/Adjustments/OutputMonitorMixAdjustment.cs @@ -1,12 +1,11 @@ -using ElgatoWaveSDK; -using ElgatoWaveSDK.Models; +using ElgatoWaveSDK.Models; namespace Loupedeck.WaveLinkPlugin.Adjustments { class OutputMonitorMixAdjustment : PluginDynamicAdjustment { private WaveLinkPlugin _plugin; - private ElgatoWaveClient _client; + private WaveLinkClient _client; private MonitoringState _state; @@ -50,7 +49,6 @@ protected override void RunCommand(string actionParameter) _state.IsLocalOutMuted = !_state.IsLocalOutMuted; _state.IsLocalOutMuted = _client.SetOutputMixer(_state) - .GetAwaiter().GetResult() ?.IsLocalOutMuted; base.ActionImageChanged(actionParameter); @@ -73,7 +71,6 @@ protected override void ApplyAdjustment(string actionParameter, int diff) _state.LocalVolumeOut = volume; _state.LocalVolumeOut = _client.SetOutputMixer(_state) - .GetAwaiter().GetResult() ?.LocalVolumeOut; base.AdjustmentValueChanged(actionParameter); diff --git a/WaveLinkPlugin/Adjustments/OutputStreamMixAdjustment.cs b/WaveLinkPlugin/Adjustments/OutputStreamMixAdjustment.cs index 2a8d64b..261bab3 100644 --- a/WaveLinkPlugin/Adjustments/OutputStreamMixAdjustment.cs +++ b/WaveLinkPlugin/Adjustments/OutputStreamMixAdjustment.cs @@ -1,12 +1,11 @@ -using ElgatoWaveSDK; -using ElgatoWaveSDK.Models; +using ElgatoWaveSDK.Models; namespace Loupedeck.WaveLinkPlugin.Adjustments { class OutputStreamMixAdjustment : PluginDynamicAdjustment { private WaveLinkPlugin _plugin; - private ElgatoWaveClient _client; + private WaveLinkClient _client; private MonitoringState _state; @@ -50,7 +49,6 @@ protected override void RunCommand(string actionParameter) _state.IsStreamOutMuted = !_state.IsStreamOutMuted; _state.IsStreamOutMuted = _client.SetOutputMixer(_state) - .GetAwaiter().GetResult() ?.IsStreamOutMuted; base.ActionImageChanged(actionParameter); @@ -72,7 +70,6 @@ protected override void ApplyAdjustment(string actionParameter, int diff) _state.StreamVolumeOut = volume; _state.StreamVolumeOut = _client.SetOutputMixer(_state) - .GetAwaiter().GetResult() ?.StreamVolumeOut; base.AdjustmentValueChanged(actionParameter); diff --git a/WaveLinkPlugin/App.config b/WaveLinkPlugin/App.config index a804072..7986f39 100644 --- a/WaveLinkPlugin/App.config +++ b/WaveLinkPlugin/App.config @@ -1,4 +1,4 @@ - + @@ -8,7 +8,7 @@ - + diff --git a/WaveLinkPlugin/Commands/SetOutputMonitorMixCommand.cs b/WaveLinkPlugin/Commands/SetOutputMonitorMixCommand.cs index ecf0da5..db05179 100644 --- a/WaveLinkPlugin/Commands/SetOutputMonitorMixCommand.cs +++ b/WaveLinkPlugin/Commands/SetOutputMonitorMixCommand.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using ElgatoWaveSDK; using ElgatoWaveSDK.Models; namespace Loupedeck.WaveLinkPlugin.Commands @@ -8,16 +7,16 @@ namespace Loupedeck.WaveLinkPlugin.Commands class SetOutputMonitorMixCommand : PluginDynamicCommand { private WaveLinkPlugin _plugin; - private ElgatoWaveClient _client; + private WaveLinkClient _client; private string _monitorMix; protected override bool OnLoad() { _plugin = (WaveLinkPlugin)base.Plugin; - _plugin.LocalMonitorOutputFetched += LocalMonitorOutputFetched; _client = _plugin.Client; + _client.LocalMonitorOutputFetched += LocalMonitorOutputFetched; _client.LocalMonitorOutputChanged += LocalMonitorOutputChanged; return true; @@ -25,8 +24,7 @@ protected override bool OnLoad() protected override bool OnUnload() { - _plugin.LocalMonitorOutputFetched -= LocalMonitorOutputFetched; - + _client.LocalMonitorOutputFetched -= LocalMonitorOutputFetched; _client.LocalMonitorOutputChanged -= LocalMonitorOutputChanged; return true; @@ -73,7 +71,6 @@ protected override void RunCommand(string actionParameter) return; _monitorMix = _client.SetMonitorMixOutput(actionParameter) - .GetAwaiter().GetResult() ?.MonitorMix; base.ActionImageChanged(); diff --git a/WaveLinkPlugin/WaveLinkClient.cs b/WaveLinkPlugin/WaveLinkClient.cs new file mode 100644 index 0000000..86d9f43 --- /dev/null +++ b/WaveLinkPlugin/WaveLinkClient.cs @@ -0,0 +1,190 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Channels; +using System.Threading.Tasks; +using ElgatoWaveSDK; +using ElgatoWaveSDK.Models; + +namespace Loupedeck.WaveLinkPlugin +{ + class WaveLinkClient : IDisposable + { + private readonly WaveLinkPlugin _plugin; + private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); + private readonly Channel _channel; + + private bool _shouldConnect = false; + + private ElgatoWaveClient _client; + internal bool IsConnected => _client?.IsConnected ?? false; + + internal event EventHandler> LocalMonitorOutputFetched; + internal event EventHandler OutputMixerChanged; + internal event EventHandler LocalMonitorOutputChanged; + internal event EventHandler> ChannelsChanged; + internal event EventHandler InputMixerChanged; + + public WaveLinkClient(WaveLinkPlugin plugin) + { + _plugin = plugin; + + _channel = Channel.CreateUnbounded(); + + var reconnectThread = new Thread(Reconnect); + reconnectThread.Start(); + + var readChannelThread = new Thread(ReadChannel); + readChannelThread.Start(); + } + + private async Task UpdateWaveLinkState() + { + if (_client is null) + return; + + var channelWriter = _channel.Writer; + + var monitoringState = await _client.GetMonitoringState(); + channelWriter.TryWrite(monitoringState); + + var mixOutputList = await _client.GetMonitorMixOutputList(); + channelWriter.TryWrite(mixOutputList?.MonitorMixList); + channelWriter.TryWrite(mixOutputList?.MonitorMix); + + var channels = await _client.GetAllChannelInfo(); + channelWriter.TryWrite(channels); + } + + private void ReadChannel() + { + while (!_cancellationTokenSource.IsCancellationRequested) + { + var channelReader = _channel.Reader; + if (!channelReader.TryRead(out var item)) + { + Thread.Sleep(TimeSpan.FromSeconds(1)); + continue; + } + + switch (item) + { + case List monitorMixList: + LocalMonitorOutputFetched?.Invoke(this, monitorMixList); + continue; + case MonitoringState state: + OutputMixerChanged?.Invoke(this, state); + continue; + case string str: + LocalMonitorOutputChanged?.Invoke(this, str); + continue; + case List list: + ChannelsChanged?.Invoke(this, list); + continue; + case ChannelInfo info: + InputMixerChanged?.Invoke(this, info); + continue; + } + } + } + + private void RefreshClient() + { + _client?.Disconnect(); + + var channelWriter = _channel.Writer; + _client = new ElgatoWaveClient(); + _client.OutputMixerChanged += (sender, state) => channelWriter.TryWrite(state); + _client.LocalMonitorOutputChanged += (sender, s) => channelWriter.TryWrite(s); + _client.ChannelsChanged += (sender, list) => channelWriter.TryWrite(list); + _client.InputMixerChanged += (sender, info) => channelWriter.TryWrite(info); + } + + public void Start() + { + _shouldConnect = true; + } + + public void Stop() + { + _shouldConnect = false; + RefreshClient(); + } + + private void Reconnect() + { + while (!_cancellationTokenSource.IsCancellationRequested) + { + try + { + if (!_shouldConnect) + continue; + + if (_client is null) + RefreshClient(); + + if (_client?.IsConnected != false) + continue; + + var success = _client.ConnectAsync() + .GetAwaiter() + .GetResult(); + + if (success) + { + UpdateWaveLinkState() + .GetAwaiter() + .GetResult(); + + _plugin.ConnectedStatus(); + } + else + { + _plugin.DisconnectedStatus(); + } + } + catch (ElgatoException exception) + when (exception.Message == "Looped through possible ports 2 times and couldn't connect [1824-1834]") + { + _plugin.DisconnectedStatus(); + + RefreshClient(); + } + catch (Exception exception) + { + _plugin.ErrorStatus(exception.Message); + + RefreshClient(); + } + finally + { + Thread.Sleep(TimeSpan.FromSeconds(5)); + } + } + } + + public void Dispose() + { + _cancellationTokenSource.Cancel(); + _client?.Disconnect(); + } + + public MonitorMixOutputList SetMonitorMixOutput(string mixOutput) + { + return _client.SetMonitorMixOutput(mixOutput) + .GetAwaiter().GetResult(); + } + + public ChannelInfo SetInputMixer(ChannelInfo info, MixType mixType) + { + return _client.SetInputMixer(info, mixType) + .GetAwaiter().GetResult(); + } + + public MonitoringState SetOutputMixer(MonitoringState state) + { + return _client.SetOutputMixer(state) + .GetAwaiter().GetResult(); + } + } +} diff --git a/WaveLinkPlugin/WaveLinkPlugin.cs b/WaveLinkPlugin/WaveLinkPlugin.cs index 8efb712..08fa9bb 100644 --- a/WaveLinkPlugin/WaveLinkPlugin.cs +++ b/WaveLinkPlugin/WaveLinkPlugin.cs @@ -1,10 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using ElgatoWaveSDK; -using ElgatoWaveSDK.Models; - namespace Loupedeck.WaveLinkPlugin { public class WaveLinkPlugin : Plugin @@ -12,31 +5,22 @@ public class WaveLinkPlugin : Plugin public override bool UsesApplicationApiOnly => true; public override bool HasNoApplication => true; - public ElgatoWaveClient Client; - private CancellationTokenSource _cancellationTokenSource; - - public event EventHandler> LocalMonitorOutputFetched; + internal WaveLinkClient Client; public WaveLinkPlugin() { - Client = new ElgatoWaveClient(); + Client = new WaveLinkClient(this); } public override void Load() { - var x = this.DynamicCommands; this.LoadPluginIcons(); - - _cancellationTokenSource = new CancellationTokenSource(); - - var token = _cancellationTokenSource.Token; - _ = Task.Run(() => ConnectAsync(token), token); + Client.Start(); } public override void Unload() { - _cancellationTokenSource.Cancel(); - Client.Disconnect(); + Client.Dispose(); } public override void RunCommand(string commandName, string parameter) @@ -49,56 +33,34 @@ public override void ApplyAdjustment(string adjustmentName, string parameter, in private void LoadPluginIcons() { - //var resources = this.Assembly.GetManifestResourceNames(); this.Info.Icon16x16 = EmbeddedResources.ReadImage("Loupedeck.WaveLinkPlugin.Resources.Icons.Icon-16.png"); this.Info.Icon32x32 = EmbeddedResources.ReadImage("Loupedeck.WaveLinkPlugin.Resources.Icons.Icon-32.png"); this.Info.Icon48x48 = EmbeddedResources.ReadImage("Loupedeck.WaveLinkPlugin.Resources.Icons.Icon-48.png"); this.Info.Icon256x256 = EmbeddedResources.ReadImage("Loupedeck.WaveLinkPlugin.Resources.Icons.Icon-256.png"); } - - private async Task ConnectAsync(CancellationToken cancellationToken) + + internal void ConnectedStatus() { - while (!cancellationToken.IsCancellationRequested) - { - try - { - if (!Client.IsConnected) - await ConnectAndSetStatusAsync(); - - await UpdateStatesAsync(); - await Task.Delay(TimeSpan.FromSeconds(60), cancellationToken); - } - catch - { - await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken); - } - } + base.OnPluginStatusChanged(Loupedeck.PluginStatus.Normal, + "Connected.", + "https://github.com/oddbear/Loupedeck.WaveLink.Plugin/", + "Plugin GitHub page"); } - private async Task ConnectAndSetStatusAsync() + internal void DisconnectedStatus() { - var connected = await Client.ConnectAsync(); - if (connected) - return; - - base.OnPluginStatusChanged( - Loupedeck.PluginStatus.Warning, + base.OnPluginStatusChanged(Loupedeck.PluginStatus.Warning, "Could not connect to WaveLink.", "https://github.com/oddbear/Loupedeck.WaveLink.Plugin/", "Plugin GitHub page"); } - - private async Task UpdateStatesAsync() + + internal void ErrorStatus(string message) { - var monitoringState = await Client.GetMonitoringState(); - Client.OutputMixerChanged?.Invoke(this, monitoringState); - - var mixOutputList = await Client.GetMonitorMixOutputList(); - LocalMonitorOutputFetched?.Invoke(this, mixOutputList?.MonitorMixList); - Client.LocalMonitorOutputChanged?.Invoke(this, mixOutputList?.MonitorMix); - - var channels = await Client.GetAllChannelInfo(); - Client.ChannelsChanged?.Invoke(this, channels); + base.OnPluginStatusChanged(Loupedeck.PluginStatus.Error, + $"Error: {message}", + "https://github.com/oddbear/Loupedeck.WaveLink.Plugin/", + "Plugin GitHub page"); } } } \ No newline at end of file diff --git a/WaveLinkPlugin/WaveLinkPlugin.csproj b/WaveLinkPlugin/WaveLinkPlugin.csproj index f8a84cc..98be81f 100644 --- a/WaveLinkPlugin/WaveLinkPlugin.csproj +++ b/WaveLinkPlugin/WaveLinkPlugin.csproj @@ -50,6 +50,15 @@ + + ..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Threading.Channels.6.0.0\lib\net461\System.Threading.Channels.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + @@ -66,6 +75,7 @@ + diff --git a/WaveLinkPlugin/packages.config b/WaveLinkPlugin/packages.config index 0a5a15b..2e1a59a 100644 --- a/WaveLinkPlugin/packages.config +++ b/WaveLinkPlugin/packages.config @@ -2,4 +2,7 @@ + + + \ No newline at end of file