diff --git a/Content.Client/GameObjects/Components/Instruments/InstrumentComponent.cs b/Content.Client/GameObjects/Components/Instruments/InstrumentComponent.cs index 27a45c89a8..1f35d766bc 100644 --- a/Content.Client/GameObjects/Components/Instruments/InstrumentComponent.cs +++ b/Content.Client/GameObjects/Components/Instruments/InstrumentComponent.cs @@ -47,8 +47,6 @@ namespace Content.Client.GameObjects.Components.Instruments [ViewVariables] private float _timer = 0f; - private TimeSpan? _lastEvent = null; - /// /// Whether a midi song will loop or not. /// @@ -129,8 +127,12 @@ namespace Content.Client.GameObjects.Components.Instruments if (IsMidiOpen) CloseMidi(); + _renderer?.StopAllNotes(); + var renderer = _renderer; - Timer.Spawn(1000, () => { renderer?.Dispose(); }); + + // We dispose of the synth two seconds from now to allow the last notes to stop from playing. + Timer.Spawn(2000, () => { renderer?.Dispose(); }); _renderer = null; _midiQueue.Clear(); } @@ -156,38 +158,32 @@ namespace Content.Client.GameObjects.Components.Instruments case InstrumentMidiEventMessage midiEventMessage: // If we're the ones sending the MidiEvents, we ignore this message. if (!IsRendererAlive || IsInputOpen || IsMidiOpen) break; - var curTime = _gameTiming.CurTime; - Logger.Info($"NEW BATCH!!! LENGTH:{midiEventMessage.MidiEvent.Length} QUEUED:{_midiQueue.Count} LAST:{_lastEvent}"); for (var i = 0; i < midiEventMessage.MidiEvent.Length; i++) { var ev = midiEventMessage.MidiEvent[i]; - var delta = i != 0 ? - ev.Timestamp.Subtract(midiEventMessage.MidiEvent[i-1].Timestamp) : _lastEvent.HasValue ? ev.Timestamp.Subtract(_lastEvent.Value) : TimeSpan.Zero; - ev.Timestamp = curTime + TimeSpan.FromSeconds(TimeBetweenNetMessages*1.25); - Logger.Info($"DT:{delta} TIM:{ev.Timestamp} TIMR:{midiEventMessage.MidiEvent[i].Timestamp} LST:{midiEventMessage.MidiEvent[Math.Max(0, i-1)].Timestamp}"); - _midiQueue.Enqueue(ev); - _lastEvent = ev.Timestamp; + var delta = ((uint)TimeBetweenNetMessages*1250) + ev.Timestamp; - //var j = i; - //Timer.Spawn((int)ev.Timestamp.Subtract(_gameTiming.CurTime).TotalMilliseconds, - // () => _renderer?.SendMidiEvent(midiEventMessage.MidiEvent[j])); + _renderer?.ScheduleMidiEvent(ev, delta, true); } - - - - break; - - case InstrumentStopMidiMessage _: - EndRenderer(); - break; - - case InstrumentStartMidiMessage _: - SetupRenderer(); - Logger.Info("INITIALIZED MIDI RENDERER. I HOPE."); break; } } + public override void HandleComponentState(ComponentState curState, ComponentState nextState) + { + base.HandleComponentState(curState, nextState); + if (!(curState is InstrumentState state)) return; + + if (state.Playing) + { + Logger.Info($"WE GOT STATE: {state.Playing} {state.SequencerTick}"); + SetupRenderer(); + if (_renderer != null) _renderer.SequencerTick = state.SequencerTick; + } + else + EndRenderer(); + } + /// public bool OpenInput() { @@ -250,43 +246,25 @@ namespace Content.Client.GameObjects.Components.Instruments /// The received midi event private void RendererOnMidiEvent(MidiEvent midiEvent) { - midiEvent.Timestamp = _gameTiming.CurTime; _midiQueue.Enqueue(midiEvent); } public override void Update(float delta) { + if (!IsMidiOpen && !IsInputOpen) + return; + _timer -= delta; if (_timer > 0f) return; - if (!IsMidiOpen && !IsInputOpen) - { - UpdatePlaying(delta); - return; - } - SendAllMidiMessages(); _timer = TimeBetweenNetMessages; } - private void UpdatePlaying(float delta) - { - while (true) - { - if (_renderer == null || _midiQueue.Count == 0) return; - var midiEvent = _midiQueue.Dequeue(); - _renderer.SendMidiEvent(midiEvent); - _timer = _midiQueue.Count != 0 ? (MathF.Max((float) _midiQueue.Peek().Timestamp.Subtract(_gameTiming.CurTime).TotalSeconds, 0f)) : 0; - if (_timer <= 0f) continue; - break; - } - } - private void SendAllMidiMessages() { - var count = _midiQueue.Count; - if (count == 0) return; + if (_midiQueue.Count == 0) return; var events = _midiQueue.ToArray(); _midiQueue.Clear(); diff --git a/Content.Server/GameObjects/Components/Instruments/InstrumentComponent.cs b/Content.Server/GameObjects/Components/Instruments/InstrumentComponent.cs index 1fd5e8eff6..81be788b5a 100644 --- a/Content.Server/GameObjects/Components/Instruments/InstrumentComponent.cs +++ b/Content.Server/GameObjects/Components/Instruments/InstrumentComponent.cs @@ -24,14 +24,19 @@ namespace Content.Server.GameObjects.Components.Instruments /// /// The client channel currently playing the instrument, or null if there's none. /// + [ViewVariables] private ICommonSession _instrumentPlayer; private bool _handheld; [ViewVariables] private bool _playing = false; + [ViewVariables] private float _timer = 0f; + [ViewVariables] + public uint _lastSequencerTick = 0; + [ViewVariables] private int _midiEventCount = 0; @@ -44,6 +49,20 @@ namespace Content.Server.GameObjects.Components.Instruments [ViewVariables] public bool Handheld => _handheld; + /// + /// Whether the instrument is currently playing or not. + /// + [ViewVariables] + public bool Playing + { + get => _playing; + set + { + _playing = value; + Dirty(); + } + } + public override void Initialize() { base.Initialize(); @@ -57,6 +76,11 @@ namespace Content.Server.GameObjects.Components.Instruments serializer.DataField(ref _handheld, "handheld", false); } + public override ComponentState GetComponentState() + { + return new InstrumentState(Playing, _lastSequencerTick); + } + public override void HandleNetworkMessage(ComponentMessage message, INetChannel channel, ICommonSession session = null) { base.HandleNetworkMessage(message, channel, session); @@ -66,25 +90,25 @@ namespace Content.Server.GameObjects.Components.Instruments switch (message) { case InstrumentMidiEventMessage midiEventMsg: - if (!_playing) + if (!Playing) return; if(++_midiEventCount <= MaxMidiEventsPerSecond) SendNetworkMessage(midiEventMsg); + + _lastSequencerTick = midiEventMsg.MidiEvent[-1].Timestamp; break; case InstrumentStartMidiMessage startMidi: - _playing = true; - SendNetworkMessage(startMidi); + Playing = true; break; case InstrumentStopMidiMessage stopMidi: - _playing = false; - SendNetworkMessage(stopMidi); + Playing = false; break; } } public void Dropped(DroppedEventArgs eventArgs) { - _playing = false; + Playing = false; SendNetworkMessage(new InstrumentStopMidiMessage()); _instrumentPlayer = null; _userInterface.CloseAll(); @@ -92,7 +116,7 @@ namespace Content.Server.GameObjects.Components.Instruments public void Thrown(ThrownEventArgs eventArgs) { - _playing = false; + Playing = false; SendNetworkMessage(new InstrumentStopMidiMessage()); _instrumentPlayer = null; _userInterface.CloseAll(); @@ -109,7 +133,7 @@ namespace Content.Server.GameObjects.Components.Instruments public void HandDeselected(HandDeselectedEventArgs eventArgs) { - _playing = false; + Playing = false; SendNetworkMessage(new InstrumentStopMidiMessage()); _userInterface.CloseAll(); } @@ -142,7 +166,7 @@ namespace Content.Server.GameObjects.Components.Instruments { _instrumentPlayer = null; SendNetworkMessage(new InstrumentStopMidiMessage()); - _playing = false; + Playing = false; } } diff --git a/Content.Shared/GameObjects/Components/Instruments/SharedInstrumentComponent.cs b/Content.Shared/GameObjects/Components/Instruments/SharedInstrumentComponent.cs index 385a5a9b48..ef94e31e86 100644 --- a/Content.Shared/GameObjects/Components/Instruments/SharedInstrumentComponent.cs +++ b/Content.Shared/GameObjects/Components/Instruments/SharedInstrumentComponent.cs @@ -1,4 +1,5 @@ using System; +using Content.Shared.BodySystem; using Robust.Shared.Audio.Midi; using Robust.Shared.GameObjects; using Robust.Shared.Serialization; @@ -47,6 +48,19 @@ namespace Content.Shared.GameObjects.Components.Instruments } } + [Serializable, NetSerializable] + public class InstrumentState : ComponentState + { + public bool Playing { get; } + public uint SequencerTick { get; } + + public InstrumentState(bool playing, uint sequencerTick = 0) : base(ContentNetIDs.INSTRUMENTS) + { + Playing = playing; + SequencerTick = sequencerTick; + } + } + [NetSerializable, Serializable] public enum InstrumentUiKey {