diff --git a/deployment/VL.Audio.VST.nuspec b/deployment/VL.Audio.VST.nuspec index c24736c..6074167 100644 --- a/deployment/VL.Audio.VST.nuspec +++ b/deployment/VL.Audio.VST.nuspec @@ -3,7 +3,7 @@ VL.Audio.VST - 0.0.8-alpha + 0.0.9-alpha VL.Audio.VST vvvv https://github.com/vvvv/VL.Audio.VST diff --git a/src/EffectHost.UI.cs b/src/EffectHost.UI.cs index 2e9fb1b..15f7a35 100644 --- a/src/EffectHost.UI.cs +++ b/src/EffectHost.UI.cs @@ -72,7 +72,7 @@ private void ShowEditor() void SaveCurrentWindowBounds() { - if (window is null || window.IsDisposed) + if (window is null || window.IsDisposed || boundsPin.Value is null) return; var position = window.DesktopLocation; diff --git a/src/EffectHost.cs b/src/EffectHost.cs index d30c281..7903ceb 100644 --- a/src/EffectHost.cs +++ b/src/EffectHost.cs @@ -60,6 +60,7 @@ internal partial class EffectHost : FactoryBasedVLNode, IVLNode, IComponentHandl private readonly Pin applyPin; private PluginState? state; + private bool stateIsBeingSet; private RectangleF bounds; private AudioPin audioInputPin, audioOutputPin; private IObservable? midiInput; @@ -81,6 +82,7 @@ internal partial class EffectHost : FactoryBasedVLNode, IVLNode, IComponentHandl private readonly ProcessSetup processSetup; private readonly AudioOutput audioOutput; + private readonly StatePin statePin; public EffectHost(NodeContext nodeContext, IVLNodeDescription nodeDescription, string modulePath, ClassInfo info) : base(nodeContext) { @@ -132,7 +134,7 @@ public EffectHost(NodeContext nodeContext, IVLNodeDescription nodeDescription, s var i = 0; var o = 0; - Inputs[i] = new StatePin(); + Inputs[i] = statePin = new StatePin(); i++; Inputs[i++] = boundsPin = new Pin>(); Inputs[i++] = audioInputPin = new AudioPin(); @@ -195,10 +197,17 @@ public void Dispose() private void SavePluginState() { - var state = PluginState.From(plugProvider.ClassInfo.ID, component, controller); - var statePin = (StatePin)Inputs[0]; + if (stateIsBeingSet) + return; + var channel = statePin.Value; - SaveToChannelOrPin(channel, StateInputPinName, state); + if (channel is null) + return; + + // Acknowledge the new state, we don't want to trigger a SetState on the controller in the next update + var state = PluginState.From(plugProvider.ClassInfo.ID, component, controller); + if (Acknowledge(ref this.state, state)) + SaveToChannelOrPin(channel, StateInputPinName, state); } private static bool Acknowledge(ref T current, T value) @@ -219,17 +228,25 @@ public void Update() for (int i = 0; i < inputAudioSignals.Length; i++) inputAudioSignals[i] = aduioInputSignals[i]; - if (Acknowledge(ref state, ((StatePin)Inputs[0]).Value?.Value)) + if (Acknowledge(ref state, statePin.Value?.Value)) { - if (state != null && state.Id == plugProvider.ClassInfo.ID) + stateIsBeingSet = true; + try { - if (state.HasComponentData) + if (state != null && state.Id == plugProvider.ClassInfo.ID) { - component.IgnoreNotImplementedException(c => c.setState(state.GetComponentStream())); - controller?.IgnoreNotImplementedException(c => c.setComponentState(state.GetComponentStream())); + if (state.HasComponentData) + { + component.IgnoreNotImplementedException(c => c.setState(state.GetComponentStream())); + controller?.IgnoreNotImplementedException(c => c.setComponentState(state.GetComponentStream())); + } + if (state.HasControllerData) + controller?.IgnoreNotImplementedException(c => c.setState(state.GetControllerStream())); } - if (state.HasControllerData) - controller?.IgnoreNotImplementedException(c => c.setState(state.GetControllerStream())); + } + finally + { + stateIsBeingSet = false; } } @@ -592,16 +609,16 @@ void IComponentHandler2.finishGroupEdit() logger.LogTrace("Finishing group edit"); } - private void SaveToChannelOrPin(IChannel? channel, string pinName, T value) + private void SaveToChannelOrPin(IChannel channel, string pinName, T value) { // Only write changes to the channel. Avoids document marked as dirty on open. - if (channel != null && Equals(channel.Value, value)) + if (Equals(channel.Value, value)) return; - if (channel is null || channel.IsSystemGenerated()) + channel.Value = value; + + if (channel.IsSystemGenerated()) SaveToPin(pinName, value); - else - channel.Value = value; } private void SaveToPin(string pinName, T value) diff --git a/src/PluginState.cs b/src/PluginState.cs index b9aee4f..a1f2161 100644 --- a/src/PluginState.cs +++ b/src/PluginState.cs @@ -6,10 +6,22 @@ namespace VL.Audio.VST; -public record PluginState(Guid Id, ImmutableArray Component, ImmutableArray Controller) +public sealed record PluginState(Guid Id, ImmutableArray Component, ImmutableArray Controller) { public static readonly PluginState Default = new PluginState(default, ImmutableArray.Empty, ImmutableArray.Empty); + public bool Equals(PluginState? other) + { + if (other is null) + return false; + + return Id == other.Id + && Component.SequenceEqual(other.Component) + && Controller.SequenceEqual(other.Controller); + } + + public override int GetHashCode() => Id.GetHashCode(); + internal bool HasComponentData => Component.Length > 0; internal bool HasControllerData => Controller.Length > 0;