From 655316a67c50b62ca4d9e1fdf1ac52d8c2b6a396 Mon Sep 17 00:00:00 2001 From: Vera Aguilera Puerto <6766154+Zumorica@users.noreply.github.com> Date: Thu, 4 Feb 2021 13:37:26 +0100 Subject: [PATCH] Add playback slider to instruments. (#3071) --- .../Instruments/InstrumentComponent.cs | 52 +++++++++++++------ .../Instruments/InstrumentMenu.xaml | 9 +++- .../Instruments/InstrumentMenu.xaml.cs | 44 +++++++++++++++- .../Instruments/InstrumentComponent.cs | 40 +++----------- 4 files changed, 90 insertions(+), 55 deletions(-) diff --git a/Content.Client/GameObjects/Components/Instruments/InstrumentComponent.cs b/Content.Client/GameObjects/Components/Instruments/InstrumentComponent.cs index 85d6119014..e24e60cf4e 100644 --- a/Content.Client/GameObjects/Components/Instruments/InstrumentComponent.cs +++ b/Content.Client/GameObjects/Components/Instruments/InstrumentComponent.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.Linq; using Content.Client.GameObjects.EntitySystems; -using Content.Shared; using Content.Shared.GameObjects.Components.Instruments; using Content.Shared.Physics; using Robust.Client.Audio.Midi; @@ -11,13 +10,11 @@ using Robust.Shared.Audio.Midi; using Robust.Shared.GameObjects; using Robust.Shared.GameObjects.Components.Timers; using Robust.Shared.GameObjects.Systems; -using Robust.Shared.Interfaces.Configuration; using Robust.Shared.Interfaces.Network; using Robust.Shared.Interfaces.Timing; using Robust.Shared.IoC; using Robust.Shared.Players; using Robust.Shared.Serialization; -using Robust.Shared.Timers; using Robust.Shared.ViewVariables; namespace Content.Client.GameObjects.Components.Instruments @@ -162,6 +159,41 @@ namespace Content.Client.GameObjects.Components.Instruments [ViewVariables] public bool IsRendererAlive => _renderer != null; + [ViewVariables] + public int PlayerTotalTick => _renderer?.PlayerTotalTick ?? 0; + + [ViewVariables] + public int PlayerTick + { + get => _renderer?.PlayerTick ?? 0; + set + { + if (!IsRendererAlive || _renderer!.Status != MidiRendererStatus.File) return; + + _midiEventBuffer.Clear(); + + _renderer.PlayerTick = value; + var tick = _renderer.SequencerTick; + + // We add a "all notes off" message. + for (byte i = 0; i < 16; i++) + { + _midiEventBuffer.Add(new MidiEvent() + { + Tick = tick, Type = 176, + Control = 123, Velocity = 0, Channel = i, + }); + } + + // Now we add a Reset All Controllers message. + _midiEventBuffer.Add(new MidiEvent() + { + Tick = tick, Type = 176, + Control = 121, Value = 0, + }); + } + } + public override void Initialize() { base.Initialize(); @@ -390,20 +422,6 @@ namespace Content.Client.GameObjects.Components.Instruments /// The received midi event private void RendererOnMidiEvent(MidiEvent midiEvent) { - // avoid of out-of-band, unimportant or unsupported events - switch (midiEvent.Type) - { - case 0x80: // NOTE_OFF - case 0x90: // NOTE_ON - case 0xa0: // KEY_PRESSURE - case 0xb0: // CONTROL_CHANGE - case 0xd0: // CHANNEL_PRESSURE - case 0xe0: // PITCH_BEND - break; - default: - return; - } - _midiEventBuffer.Add(midiEvent); } diff --git a/Content.Client/Instruments/InstrumentMenu.xaml b/Content.Client/Instruments/InstrumentMenu.xaml index 0825557f22..23e4b75a9b 100644 --- a/Content.Client/Instruments/InstrumentMenu.xaml +++ b/Content.Client/Instruments/InstrumentMenu.xaml @@ -1,18 +1,23 @@ - + - + + + + + + diff --git a/Content.Client/Instruments/InstrumentMenu.xaml.cs b/Content.Client/Instruments/InstrumentMenu.xaml.cs index e9debae7e1..9ead93cc77 100644 --- a/Content.Client/Instruments/InstrumentMenu.xaml.cs +++ b/Content.Client/Instruments/InstrumentMenu.xaml.cs @@ -14,11 +14,14 @@ using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; using Robust.Shared.Containers; +using Robust.Shared.Input; using Robust.Shared.IoC; using Robust.Shared.Localization; using Robust.Shared.Log; using Robust.Shared.Maths; using Robust.Shared.Timers; +using Robust.Shared.Timing; +using Range = Robust.Client.UserInterface.Controls.Range; namespace Content.Client.Instruments { @@ -46,6 +49,7 @@ namespace Content.Client.Instruments LoopButton.Disabled = !_owner.Instrument.IsMidiOpen; LoopButton.Pressed = _owner.Instrument.LoopMidi; StopButton.Disabled = !_owner.Instrument.IsMidiOpen; + PlaybackSlider.MouseFilter = _owner.Instrument.IsMidiOpen ? MouseFilterMode.Pass : MouseFilterMode.Ignore; if (!_midiManager.IsAvailable) { @@ -74,6 +78,8 @@ namespace Content.Client.Instruments FileButton.OnPressed += MidiFileButtonOnOnPressed; LoopButton.OnToggled += MidiLoopButtonOnOnToggled; StopButton.OnPressed += MidiStopButtonOnPressed; + PlaybackSlider.OnValueChanged += PlaybackSliderSeek; + PlaybackSlider.OnKeyBindUp += PlaybackSliderKeyUp; } private void InstrumentOnMidiPlaybackEnded() @@ -85,12 +91,15 @@ namespace Content.Client.Instruments { LoopButton.Disabled = disabled; StopButton.Disabled = disabled; + + // Whether to allow the slider to receive events.. + PlaybackSlider.MouseFilter = !disabled ? MouseFilterMode.Pass : MouseFilterMode.Ignore; } private async void MidiFileButtonOnOnPressed(BaseButton.ButtonEventArgs obj) { var filters = new FileDialogFilters(new FileDialogFilters.Group("mid", "midi")); - var file = await _fileDialogManager.OpenFile(filters); + await using var file = await _fileDialogManager.OpenFile(filters); // The following checks are only in place to prevent players from playing MIDI songs locally. // There are equivalents for these checks on the server. @@ -107,7 +116,7 @@ namespace Content.Client.Instruments return; MidiStopButtonOnPressed(null); - var memStream = new MemoryStream((int) file.Length); + await using var memStream = new MemoryStream((int) file.Length); // 100ms delay is due to a race condition or something idk. // While we're waiting, load it into memory. await Task.WhenAll(Timer.Delay(100), file.CopyToAsync(memStream)); @@ -165,5 +174,36 @@ namespace Content.Client.Instruments { _owner.Instrument.LoopMidi = obj.Pressed; } + + private void PlaybackSliderSeek(Range _) + { + // Do not seek while still grabbing. + if (PlaybackSlider.Grabbed) return; + + _owner.Instrument.PlayerTick = (int)Math.Ceiling(PlaybackSlider.Value); + } + + private void PlaybackSliderKeyUp(GUIBoundKeyEventArgs args) + { + if (args.Function != EngineKeyFunctions.UIClick) return; + _owner.Instrument.PlayerTick = (int)Math.Ceiling(PlaybackSlider.Value); + } + + protected override void Update(FrameEventArgs args) + { + base.Update(args); + + if (!_owner.Instrument.IsMidiOpen) + { + PlaybackSlider.MaxValue = 1; + PlaybackSlider.SetValueWithoutEvent(0); + return; + } + + if (PlaybackSlider.Grabbed) return; + + PlaybackSlider.MaxValue = _owner.Instrument.PlayerTotalTick; + PlaybackSlider.SetValueWithoutEvent(_owner.Instrument.PlayerTick); + } } } diff --git a/Content.Server/GameObjects/Components/Instruments/InstrumentComponent.cs b/Content.Server/GameObjects/Components/Instruments/InstrumentComponent.cs index 233874b533..6651e20708 100644 --- a/Content.Server/GameObjects/Components/Instruments/InstrumentComponent.cs +++ b/Content.Server/GameObjects/Components/Instruments/InstrumentComponent.cs @@ -4,9 +4,7 @@ using System.Linq; using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.EntitySystems; using Content.Server.Utility; -using Content.Shared; using Content.Shared.GameObjects.Components.Instruments; -using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems.ActionBlocker; using Content.Shared.Interfaces; using Content.Shared.Interfaces.GameObjects.Components; @@ -18,10 +16,7 @@ using Robust.Server.Player; using Robust.Shared.Enums; using Robust.Shared.GameObjects; using Robust.Shared.GameObjects.Systems; -using Robust.Shared.Interfaces.Configuration; using Robust.Shared.Interfaces.Network; -using Robust.Shared.Interfaces.Timing; -using Robust.Shared.IoC; using Robust.Shared.Localization; using Robust.Shared.Players; using Robust.Shared.Serialization; @@ -41,9 +36,6 @@ namespace Content.Server.GameObjects.Components.Instruments IUse, IThrown { - [Dependency] private readonly IGameTiming _gameTiming = default!; - - private static readonly TimeSpan OneSecAgo = TimeSpan.FromSeconds(-1); private InstrumentSystem _instrumentSystem = default!; /// @@ -60,9 +52,6 @@ namespace Content.Server.GameObjects.Components.Instruments [ViewVariables] private float _timer = 0f; - [ViewVariables(VVAccess.ReadOnly)] - private TimeSpan _lastMeasured = TimeSpan.MinValue; - [ViewVariables] private int _batchesDropped = 0; @@ -213,15 +202,6 @@ namespace Content.Server.GameObjects.Components.Instruments var minTick = midiEventMsg.MidiEvent.Min(x => x.Tick); if (_lastSequencerTick > minTick) { - var now = _gameTiming.RealTime; - var oneSecAGo = now.Add(OneSecAgo); - if (_lastMeasured < oneSecAGo) - { - _lastMeasured = now; - _laggedBatches = 0; - _batchesDropped = 0; - } - _laggedBatches++; if (_respectMidiLimits) @@ -246,15 +226,6 @@ namespace Content.Server.GameObjects.Components.Instruments if (++_midiEventCount > maxMidiEventsPerSecond || midiEventMsg.MidiEvent.Length > maxMidiEventsPerBatch) { - var now = _gameTiming.RealTime; - var oneSecAGo = now.Add(OneSecAgo); - if (_lastMeasured < oneSecAGo) - { - _lastMeasured = now; - _laggedBatches = 0; - _batchesDropped = 0; - } - _batchesDropped++; send = false; @@ -266,7 +237,7 @@ namespace Content.Server.GameObjects.Components.Instruments } var maxTick = midiEventMsg.MidiEvent.Max(x => x.Tick); - _lastSequencerTick = Math.Max(maxTick, minTick + 1); + _lastSequencerTick = Math.Max(maxTick, minTick); break; case InstrumentStartMidiMessage startMidi: if (session != _instrumentPlayer) @@ -386,15 +357,14 @@ namespace Content.Server.GameObjects.Components.Instruments UserInterface?.CloseAll(); + if(Handheld) + EntitySystem.Get().DropAllItemsInHands(mob, false); + if (mob != null && mob.TryGetComponent(out StunnableComponent? stun)) { stun.Stun(1); Clean(); } - else - { - EntitySystem.Get().DropAllItemsInHands(mob, false); - } InstrumentPlayer = null; @@ -406,6 +376,8 @@ namespace Content.Server.GameObjects.Components.Instruments _timer = 0f; _midiEventCount = 0; + _laggedBatches = 0; + _batchesDropped = 0; } }