MassiveKnob/Windows/MassiveKnob.Plugin.VoiceMeeter/InstanceRegister.cs

150 lines
4.3 KiB
C#

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Voicemeeter;
namespace MassiveKnob.Plugin.VoiceMeeter
{
public static class InstanceRegister
{
private static readonly object InstancesLock = new object();
private static readonly HashSet<IVoiceMeeterAction> Instances = new HashSet<IVoiceMeeterAction>();
private static Task initializeTask;
private static readonly object SubscribersLock = new object();
private static Parameters parameters;
private static IDisposable parametersSubscriber;
private static readonly List<Action> ParameterSubscriberActions = new List<Action>();
private static IDisposable voicemeeterClient;
// The VoiceMeeter Remote only connects to one instance, so all actions need to be in sync
private static RunVoicemeeterParam version;
public static RunVoicemeeterParam Version
{
get => version;
set
{
if (value == version)
return;
version = value;
Notify(action => action.VoiceMeeterVersionChanged());
initializeTask = Task.Run(async () =>
{
voicemeeterClient?.Dispose();
voicemeeterClient = await global::VoiceMeeter.Remote.Initialize(version);
});
}
}
public static Task InitializeVoicemeeter()
{
return initializeTask ?? Task.CompletedTask;
}
// For the same reason, we can only subscribe to the parameters once, as they will not be "dirty"
// for other subscribers otherwise
public static IDisposable SubscribeToParameterChanges(Action action)
{
lock (SubscribersLock)
{
if (parameters == null)
{
parameters = new Parameters();
parametersSubscriber = parameters.Subscribe(x => NotifyParameterSubscribers());
}
ParameterSubscriberActions.Add(action);
return new ParametersSubscriber(action);
}
}
private static void NotifyParameterSubscribers()
{
Action[] subscribers;
lock (SubscribersLock)
{
subscribers = ParameterSubscriberActions.ToArray();
}
foreach (var subscriber in subscribers)
subscriber();
}
private static void RemoveParameterSubscriber(Action action)
{
lock (SubscribersLock)
{
// ReSharper disable once InvertIf
if (ParameterSubscriberActions.Remove(action) && ParameterSubscriberActions.Count == 0)
{
parametersSubscriber.Dispose();
parametersSubscriber = null;
parameters.Dispose();
parameters = null;
}
}
}
public static void Register(IVoiceMeeterAction instance)
{
lock (InstancesLock)
{
Instances.Add(instance);
}
}
public static void Unregister(IVoiceMeeterAction instance)
{
lock (InstancesLock)
{
// ReSharper disable once InvertIf
if (Instances.Remove(instance) && Instances.Count == 0)
{
voicemeeterClient?.Dispose();
voicemeeterClient = null;
}
}
}
public static void Notify(Action<IVoiceMeeterAction> action)
{
lock (InstancesLock)
{
foreach (var instance in Instances)
action(instance);
}
}
private class ParametersSubscriber : IDisposable
{
private readonly Action action;
public ParametersSubscriber(Action action)
{
this.action = action;
}
public void Dispose()
{
InstanceRegister.RemoveParameterSubscriber(action);
}
}
}
}