diff --git a/ProjectObsidian/Components/Devices/MIDI/MIDI_CC_Value.cs b/ProjectObsidian/Components/Devices/MIDI/MIDI_CC_Value.cs index 4630543..db3ad59 100644 --- a/ProjectObsidian/Components/Devices/MIDI/MIDI_CC_Value.cs +++ b/ProjectObsidian/Components/Devices/MIDI/MIDI_CC_Value.cs @@ -51,7 +51,7 @@ protected override void OnDispose() } } - private void OnControl(MIDI_InputDevice device, MIDI_CC_EventData eventData) + private void OnControl(IMidiInputListener sender, MIDI_CC_EventData eventData) { RunSynchronously(() => { diff --git a/ProjectObsidian/Components/Devices/MIDI/MIDI_InputDevice.cs b/ProjectObsidian/Components/Devices/MIDI/MIDI_InputDevice.cs index c8c7cec..df33282 100644 --- a/ProjectObsidian/Components/Devices/MIDI/MIDI_InputDevice.cs +++ b/ProjectObsidian/Components/Devices/MIDI/MIDI_InputDevice.cs @@ -6,12 +6,13 @@ using Commons.Music.Midi; using Obsidian.Elements; using Obsidian; +using System; namespace Components.Devices.MIDI; [Category(new string[] { "Obsidian/Devices/MIDI" })] [OldTypeName("Obsidian.MIDI_InputDevice")] -public class MIDI_InputDevice : Component +public class MIDI_InputDevice : Component, IMidiInputListener { [NoContinuousParsing] public readonly Sync DeviceName; @@ -22,12 +23,11 @@ public class MIDI_InputDevice : Component private bool _lastIsConnected; - private IMidiInput _inputDevice; + private MidiInputConnection Connection; private MIDI_Settings _settings => Settings.GetActiveSetting(); public event MIDI_NoteEventHandler NoteOn; - public event MIDI_NoteEventHandler NoteOff; // Pressure for whole keyboard @@ -35,48 +35,31 @@ public class MIDI_InputDevice : Component // Pressure for individual notes (polyphonic) public event MIDI_PolyphonicAftertouchEventHandler PolyphonicAftertouch; - public event MIDI_CC_EventHandler Control; - public event MIDI_PitchWheelEventHandler PitchWheel; - public event MIDI_ProgramEventHandler Program; - public event MIDI_SystemRealtimeEventHandler MidiClock; - public event MIDI_SystemRealtimeEventHandler MidiTick; - public event MIDI_SystemRealtimeEventHandler MidiStart; - public event MIDI_SystemRealtimeEventHandler MidiStop; - public event MIDI_SystemRealtimeEventHandler MidiContinue; - public event MIDI_SystemRealtimeEventHandler ActiveSense; - public event MIDI_SystemRealtimeEventHandler Reset; - private const bool DEBUG = false; - - private struct TimestampedMidiEvent - { - public MidiEvent midiEvent; - public long timestamp; - public TimestampedMidiEvent(MidiEvent _midiEvent, long _timestamp) - { - midiEvent = _midiEvent; - timestamp = _timestamp; - } - } - - // I am using this like a Queue so it could possibly be turned into a Queue instead... - private List _eventBuffer = new(); - - private const long MESSAGE_BUFFER_TIME_MILLISECONDS = 3; - - private long _lastMessageBufferStartTime = 0; - - private int _bufferedMessagesToHandle = 0; + public void TriggerNoteOn(MIDI_NoteEventData eventData) => NoteOn?.Invoke(this, eventData); + public void TriggerNoteOff(MIDI_NoteEventData eventData) => NoteOff?.Invoke(this, eventData); + public void TriggerChannelAftertouch(MIDI_ChannelAftertouchEventData eventData) => ChannelAftertouch?.Invoke(this, eventData); + public void TriggerPolyphonicAftertouch(MIDI_PolyphonicAftertouchEventData eventData) => PolyphonicAftertouch?.Invoke(this, eventData); + public void TriggerControl(MIDI_CC_EventData eventData) => Control?.Invoke(this, eventData); + public void TriggerPitchWheel(MIDI_PitchWheelEventData eventData) => PitchWheel?.Invoke(this, eventData); + public void TriggerProgram(MIDI_ProgramEventData eventData) => Program?.Invoke(this, eventData); + public void TriggerMidiClock(MIDI_SystemRealtimeEventData eventData) => MidiClock?.Invoke(this, eventData); + public void TriggerMidiTick(MIDI_SystemRealtimeEventData eventData) => MidiTick?.Invoke(this, eventData); + public void TriggerMidiStart(MIDI_SystemRealtimeEventData eventData) => MidiStart?.Invoke(this, eventData); + public void TriggerMidiStop(MIDI_SystemRealtimeEventData eventData) => MidiStop?.Invoke(this, eventData); + public void TriggerMidiContinue(MIDI_SystemRealtimeEventData eventData) => MidiContinue?.Invoke(this, eventData); + public void TriggerActiveSense(MIDI_SystemRealtimeEventData eventData) => ActiveSense?.Invoke(this, eventData); + public void TriggerReset(MIDI_SystemRealtimeEventData eventData) => Reset?.Invoke(this, eventData); protected override void OnStart() { @@ -104,35 +87,67 @@ protected override void OnChanges() Update(); } - private async Task ReleaseDeviceAsync() - { - UniLog.Log("Releasing device..."); - await Task.WhenAny(_inputDevice.CloseAsync(), Task.Delay(5000)); - UniLog.Log("Device released."); - _inputDevice = null; - _eventBuffer.Clear(); - _lastMessageBufferStartTime = 0; - } - - private async void ReleaseDeviceAndConnectAsync(IMidiAccess access, string deviceId) - { - if (_inputDevice != null) - { - await ReleaseDeviceAsync(); - } - _inputDevice = access.OpenInputAsync(deviceId).Result; - _inputDevice.MessageReceived += OnMessageReceived; - SetIsConnected(true); - UniLog.Log("Connected."); - } - protected override void OnDispose() { base.OnDispose(); - if (_inputDevice != null) + var emptyArray = new Delegate[] { }; + foreach (Delegate d in NoteOn?.GetInvocationList().ToArray() ?? emptyArray) + { + NoteOn -= (MIDI_NoteEventHandler)d; + } + foreach (Delegate d in NoteOff?.GetInvocationList().ToArray() ?? emptyArray) + { + NoteOff -= (MIDI_NoteEventHandler)d; + } + foreach (Delegate d in ChannelAftertouch?.GetInvocationList().ToArray() ?? emptyArray) { - ReleaseDeviceAsync(); + ChannelAftertouch -= (MIDI_ChannelAftertouchEventHandler)d; } + foreach (Delegate d in PolyphonicAftertouch?.GetInvocationList().ToArray() ?? emptyArray) + { + PolyphonicAftertouch -= (MIDI_PolyphonicAftertouchEventHandler)d; + } + foreach (Delegate d in Control?.GetInvocationList().ToArray() ?? emptyArray) + { + Control -= (MIDI_CC_EventHandler)d; + } + foreach (Delegate d in PitchWheel?.GetInvocationList().ToArray() ?? emptyArray) + { + PitchWheel -= (MIDI_PitchWheelEventHandler)d; + } + foreach (Delegate d in Program?.GetInvocationList().ToArray() ?? emptyArray) + { + Program -= (MIDI_ProgramEventHandler)d; + } + foreach (Delegate d in MidiClock?.GetInvocationList().ToArray() ?? emptyArray) + { + MidiClock -= (MIDI_SystemRealtimeEventHandler)d; + } + foreach (Delegate d in MidiTick?.GetInvocationList().ToArray() ?? emptyArray) + { + MidiTick -= (MIDI_SystemRealtimeEventHandler)d; + } + foreach (Delegate d in MidiStart?.GetInvocationList().ToArray() ?? emptyArray) + { + MidiStart -= (MIDI_SystemRealtimeEventHandler)d; + } + foreach (Delegate d in MidiStop?.GetInvocationList().ToArray() ?? emptyArray) + { + MidiStop -= (MIDI_SystemRealtimeEventHandler)d; + } + foreach (Delegate d in MidiContinue?.GetInvocationList().ToArray() ?? emptyArray) + { + MidiContinue -= (MIDI_SystemRealtimeEventHandler)d; + } + foreach (Delegate d in ActiveSense?.GetInvocationList().ToArray() ?? emptyArray) + { + ActiveSense -= (MIDI_SystemRealtimeEventHandler)d; + } + foreach (Delegate d in Reset?.GetInvocationList().ToArray() ?? emptyArray) + { + Reset -= (MIDI_SystemRealtimeEventHandler)d; + } + MidiDeviceConnectionManager.UnregisterInputListener(this); } private void SetIsConnected(bool val) @@ -145,29 +160,20 @@ private void Update() { if (HandlingUser.Target == null) { - if (_inputDevice != null) - { - ReleaseDeviceAsync(); - } + MidiDeviceConnectionManager.UnregisterInputListener(this); SetIsConnected(false); return; } if (LocalUser != HandlingUser.Target) { - if (_inputDevice != null) - { - ReleaseDeviceAsync(); - } + MidiDeviceConnectionManager.UnregisterInputListener(this); return; } if (!Enabled) { - if (_inputDevice != null) - { - ReleaseDeviceAsync(); - } + MidiDeviceConnectionManager.UnregisterInputListener(this); SetIsConnected(false); return; } @@ -178,20 +184,26 @@ private void Update() if (!_settings.InputDevices.Any(dev => dev.DeviceName.Value == DeviceName.Value && dev.AllowConnections.Value == true)) { UniLog.Log("Device connection not allowed."); - if (_inputDevice != null) - { - ReleaseDeviceAsync(); - } + MidiDeviceConnectionManager.UnregisterInputListener(this); SetIsConnected(false); return; } - if (_inputDevice != null - && (_inputDevice.Connection == MidiPortConnectionState.Open || _inputDevice.Connection == MidiPortConnectionState.Pending) - && _inputDevice.Details.Name == DeviceName.Value) + if (Connection != null + && (Connection.Input.Connection == MidiPortConnectionState.Open || Connection.Input.Connection == MidiPortConnectionState.Pending)) { - UniLog.Log("Already connected. Connection state: " + _inputDevice.Connection.ToString()); - return; + if (MidiAccessManager.Default.Inputs.Any(inp => inp.Name == DeviceName.Value)) + { + UniLog.Log("Already connected. Connection state: " + Connection.Input.Connection.ToString()); + return; + } + else + { + UniLog.Log("Device was removed after a conection."); + MidiDeviceConnectionManager.UnregisterInputListener(this); + SetIsConnected(false); + return; + } } var access = MidiAccessManager.Default; @@ -199,7 +211,9 @@ private void Update() if (targetDevice != null) { UniLog.Log("Found the target device."); - ReleaseDeviceAndConnectAsync(access, targetDevice.Id); + Connection = MidiDeviceConnectionManager.RegisterInputListener(this, targetDevice); + SetIsConnected(true); + UniLog.Log("Connected."); } else { @@ -209,219 +223,8 @@ private void Update() } else { - if (_inputDevice != null) - { - ReleaseDeviceAsync(); - } + MidiDeviceConnectionManager.UnregisterInputListener(this); SetIsConnected(false); } } - - // What - private ushort CombineBytes(byte First, byte Second) - { - ushort _14bit; - _14bit = Second; - _14bit <<= 7; - _14bit |= First; - return _14bit; - } - - private bool IsCCFineMessage() - { - if (_eventBuffer.Count == 0) return false; - long timestamp = _eventBuffer[0].timestamp; - if (_eventBuffer.Count >= 2 - && _eventBuffer[0].midiEvent.EventType == MidiEvent.CC && _eventBuffer[1].midiEvent.EventType == MidiEvent.CC - && _eventBuffer[0].midiEvent.Msb == _eventBuffer[1].midiEvent.Msb - 32) - { - return true; - } - return false; - } - - private void FlushMessageBuffer() - { - if (_eventBuffer.Count == 0) - { - UniLog.Log("Message buffer empty."); - return; - } - - var batchStartTime = _eventBuffer[0].timestamp; - if (DEBUG) UniLog.Log("Flushing message buffer from start time: " + batchStartTime.ToString()); - - while (_eventBuffer.Count > 0) - { - - while (IsCCFineMessage()) - { - var e1 = _eventBuffer[0].midiEvent; - if (DEBUG) UniLog.Log(e1.ToString()); - var e2 = _eventBuffer[1].midiEvent; - if (DEBUG) UniLog.Log(e2.ToString()); - var finalValue = CombineBytes(e2.Lsb, e1.Lsb); - if (DEBUG) UniLog.Log($"CC fine. Value: " + finalValue.ToString()); - Control?.Invoke(this, new MIDI_CC_EventData(e1.Channel, e1.Msb, finalValue, _coarse: false)); - _eventBuffer.RemoveRange(0, 2); - _bufferedMessagesToHandle -= 2; - } - - if (_eventBuffer.Count == 0) break; - - var e = _eventBuffer[0].midiEvent; - if (DEBUG) UniLog.Log(e.ToString()); - switch (e.EventType) - { - case MidiEvent.CC: - if (DEBUG) UniLog.Log("CC"); - Control?.Invoke(this, new MIDI_CC_EventData(e.Channel, e.Msb, e.Lsb, _coarse: true)); - break; - // Program events are buffered because they can be sent after a CC fine message for Bank Select, one of my devices sends consecutively: CC (Bank Select) -> CC (Bank Select Lsb) -> Program for some buttons - case MidiEvent.Program: - if (DEBUG) UniLog.Log("Program"); - Program?.Invoke(this, new MIDI_ProgramEventData(e.Channel, e.Msb)); - break; - - // Unhandled events: - - //SysEx events are probably not worth handling - case MidiEvent.SysEx1: - if (DEBUG) UniLog.Log("UnhandledEvent: SysEx1"); - break; - case MidiEvent.SysEx2: - // Same as EndSysEx - if (DEBUG) UniLog.Log("UnhandledEvent: SysEx2"); - break; - case MidiEvent.MtcQuarterFrame: - if (DEBUG) UniLog.Log("UnhandledEvent: MtcQuarterFrame"); - break; - case MidiEvent.SongPositionPointer: - if (DEBUG) UniLog.Log("UnhandledEvent: SongPositionPointer"); - break; - case MidiEvent.SongSelect: - if (DEBUG) UniLog.Log("UnhandledEvent: SongSelect"); - break; - case MidiEvent.TuneRequest: - if (DEBUG) UniLog.Log("UnhandledEvent: TuneRequest"); - break; - default: - break; - } - _eventBuffer.RemoveAt(0); - _bufferedMessagesToHandle -= 1; - } - if (DEBUG) UniLog.Log("Finished flushing message buffer from start time: " + batchStartTime.ToString()); - if (_bufferedMessagesToHandle != 0) - { - // Just in case some messages got lost somehow - UniLog.Warning("Did not handle all buffered messages! " + _bufferedMessagesToHandle.ToString()); - } - _bufferedMessagesToHandle = 0; - } - - private async void OnMessageReceived(object sender, MidiReceivedEventArgs args) - { - if (DEBUG) UniLog.Log($"*** New midi message"); - if (DEBUG) UniLog.Log($"* Received {args.Length} bytes"); - if (DEBUG) UniLog.Log($"* Timestamp: {args.Timestamp}"); - - var events = MidiEvent.Convert(args.Data, args.Start, args.Length); - - if (args.Length == 1) - { - // system realtime message, do not buffer these, execute immediately - if (DEBUG) UniLog.Log($"* System realtime message"); - foreach (var e in events) - { - var str = e.ToString(); - if (DEBUG) UniLog.Log("* " + str); - switch (e.StatusByte) - { - case MidiEvent.MidiClock: - if (DEBUG) UniLog.Log("* MidiClock"); - MidiClock?.Invoke(this, new MIDI_SystemRealtimeEventData()); - break; - case MidiEvent.MidiTick: - if (DEBUG) UniLog.Log("* MidiTick"); - MidiTick?.Invoke(this, new MIDI_SystemRealtimeEventData()); - break; - case MidiEvent.MidiStart: - if (DEBUG) UniLog.Log("* MidiStart"); - MidiStart?.Invoke(this, new MIDI_SystemRealtimeEventData()); - break; - case MidiEvent.MidiStop: - if (DEBUG) UniLog.Log("* MidiStop"); - MidiStop?.Invoke(this, new MIDI_SystemRealtimeEventData()); - break; - case MidiEvent.MidiContinue: - if (DEBUG) UniLog.Log("* MidiContinue"); - MidiContinue?.Invoke(this, new MIDI_SystemRealtimeEventData()); - break; - case MidiEvent.ActiveSense: - if (DEBUG) UniLog.Log("* ActiveSense"); - ActiveSense?.Invoke(this, new MIDI_SystemRealtimeEventData()); - break; - case MidiEvent.Reset: - // Same as Meta - if (DEBUG) UniLog.Log("* Reset"); - Reset?.Invoke(this, new MIDI_SystemRealtimeEventData()); - break; - } - } - return; - } - - // other types of messages: channel message (voice or channel mode), system common message, system exclusive message - foreach(var e in events) - { - var str = e.ToString(); - if (DEBUG) UniLog.Log("* " + str); - - switch (e.EventType) - { - case MidiEvent.NoteOn: - if (DEBUG) UniLog.Log("* NoteOn"); - if (e.Lsb == 0) - { - if (DEBUG) UniLog.Log("* Zero velocity, so it's actually a NoteOff"); - NoteOff?.Invoke(this, new MIDI_NoteEventData(e.Channel, e.Msb, e.Lsb)); - return; - } - NoteOn?.Invoke(this, new MIDI_NoteEventData(e.Channel, e.Msb, e.Lsb)); - return; - case MidiEvent.NoteOff: - if (DEBUG) UniLog.Log("* NoteOff"); - NoteOff?.Invoke(this, new MIDI_NoteEventData(e.Channel, e.Msb, e.Lsb)); - return; - case MidiEvent.CAf: - if (DEBUG) UniLog.Log("* CAf"); - ChannelAftertouch?.Invoke(this, new MIDI_ChannelAftertouchEventData(e.Channel, e.Msb)); - return; - case MidiEvent.Pitch: - if (DEBUG) UniLog.Log("* Pitch"); - PitchWheel?.Invoke(this, new MIDI_PitchWheelEventData(e.Channel, CombineBytes(e.Msb, e.Lsb))); - return; - case MidiEvent.PAf: - if (DEBUG) UniLog.Log("* PAf"); - PolyphonicAftertouch?.Invoke(this, new MIDI_PolyphonicAftertouchEventData(e.Channel, e.Msb, e.Lsb)); - return; - default: - break; - } - - // buffer CC messages because consecutive ones may need to be combined - // also buffer Program messages - _eventBuffer.Add(new TimestampedMidiEvent(e, args.Timestamp)); - _bufferedMessagesToHandle += 1; - } - - if (events.Count() > 0 && args.Timestamp - _lastMessageBufferStartTime > MESSAGE_BUFFER_TIME_MILLISECONDS) - { - _lastMessageBufferStartTime = args.Timestamp; - if (DEBUG) UniLog.Log("* New message batch created: " + args.Timestamp.ToString()); - await Task.Delay((int)MESSAGE_BUFFER_TIME_MILLISECONDS); - FlushMessageBuffer(); - } - } } \ No newline at end of file diff --git a/ProjectObsidian/Components/Devices/MIDI/MIDI_PitchWheel_Value.cs b/ProjectObsidian/Components/Devices/MIDI/MIDI_PitchWheel_Value.cs index 857cc32..20740b0 100644 --- a/ProjectObsidian/Components/Devices/MIDI/MIDI_PitchWheel_Value.cs +++ b/ProjectObsidian/Components/Devices/MIDI/MIDI_PitchWheel_Value.cs @@ -47,7 +47,7 @@ protected override void OnDispose() } } - private void OnPitchWheel(MIDI_InputDevice device, MIDI_PitchWheelEventData eventData) + private void OnPitchWheel(IMidiInputListener sender, MIDI_PitchWheelEventData eventData) { RunSynchronously(() => { diff --git a/ProjectObsidian/Elements/MIDI.cs b/ProjectObsidian/Elements/MIDI.cs index d5f2725..e8d0ed5 100644 --- a/ProjectObsidian/Elements/MIDI.cs +++ b/ProjectObsidian/Elements/MIDI.cs @@ -3,12 +3,349 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using Commons.Music.Midi; using Components.Devices.MIDI; using Elements.Core; using FrooxEngine; namespace Obsidian.Elements; +public struct TimestampedMidiEvent +{ + public MidiEvent midiEvent; + public long timestamp; + public TimestampedMidiEvent(MidiEvent _midiEvent, long _timestamp) + { + midiEvent = _midiEvent; + timestamp = _timestamp; + } +} + +public interface IMidiInputListener +{ + public event MIDI_NoteEventHandler NoteOn; + public event MIDI_NoteEventHandler NoteOff; + + // Pressure for whole keyboard + public event MIDI_ChannelAftertouchEventHandler ChannelAftertouch; + + // Pressure for individual notes (polyphonic) + public event MIDI_PolyphonicAftertouchEventHandler PolyphonicAftertouch; + public event MIDI_CC_EventHandler Control; + public event MIDI_PitchWheelEventHandler PitchWheel; + public event MIDI_ProgramEventHandler Program; + public event MIDI_SystemRealtimeEventHandler MidiClock; + public event MIDI_SystemRealtimeEventHandler MidiTick; + public event MIDI_SystemRealtimeEventHandler MidiStart; + public event MIDI_SystemRealtimeEventHandler MidiStop; + public event MIDI_SystemRealtimeEventHandler MidiContinue; + public event MIDI_SystemRealtimeEventHandler ActiveSense; + public event MIDI_SystemRealtimeEventHandler Reset; + + public void TriggerNoteOn(MIDI_NoteEventData eventData); + public void TriggerNoteOff(MIDI_NoteEventData eventData); + public void TriggerChannelAftertouch(MIDI_ChannelAftertouchEventData eventData); + public void TriggerPolyphonicAftertouch(MIDI_PolyphonicAftertouchEventData eventData); + public void TriggerControl(MIDI_CC_EventData eventData); + public void TriggerPitchWheel(MIDI_PitchWheelEventData eventData); + public void TriggerProgram(MIDI_ProgramEventData eventData); + public void TriggerMidiClock(MIDI_SystemRealtimeEventData eventData); + public void TriggerMidiTick(MIDI_SystemRealtimeEventData eventData); + public void TriggerMidiStart(MIDI_SystemRealtimeEventData eventData); + public void TriggerMidiStop(MIDI_SystemRealtimeEventData eventData); + public void TriggerMidiContinue(MIDI_SystemRealtimeEventData eventData); + public void TriggerActiveSense(MIDI_SystemRealtimeEventData eventData); + public void TriggerReset(MIDI_SystemRealtimeEventData eventData); +} + +public class MidiInputConnection +{ + public IMidiInput Input; + + public List Listeners = new(); + + // I am using this like a Queue so it could possibly be turned into a Queue instead... + private List _eventBuffer = new(); + + private const long MESSAGE_BUFFER_TIME_MILLISECONDS = 3; + + private long _lastMessageBufferStartTime = 0; + + private int _bufferedMessagesToHandle = 0; + + private const bool DEBUG = false; + + public void Initialize() + { + _eventBuffer.Clear(); + _lastMessageBufferStartTime = 0; + _bufferedMessagesToHandle = 0; + Listeners.Clear(); + } + + // What + private ushort CombineBytes(byte First, byte Second) + { + ushort _14bit; + _14bit = Second; + _14bit <<= 7; + _14bit |= First; + return _14bit; + } + + private bool IsCCFineMessage() + { + if (_eventBuffer.Count == 0) return false; + long timestamp = _eventBuffer[0].timestamp; + if (_eventBuffer.Count >= 2 + && _eventBuffer[0].midiEvent.EventType == MidiEvent.CC && _eventBuffer[1].midiEvent.EventType == MidiEvent.CC + && _eventBuffer[0].midiEvent.Msb == _eventBuffer[1].midiEvent.Msb - 32) + { + return true; + } + return false; + } + + private void FlushMessageBuffer() + { + if (_eventBuffer.Count == 0) + { + UniLog.Log("Message buffer empty."); + return; + } + + var batchStartTime = _eventBuffer[0].timestamp; + if (DEBUG) UniLog.Log("Flushing message buffer from start time: " + batchStartTime.ToString()); + + while (_eventBuffer.Count > 0) + { + + while (IsCCFineMessage()) + { + var e1 = _eventBuffer[0].midiEvent; + if (DEBUG) UniLog.Log(e1.ToString()); + var e2 = _eventBuffer[1].midiEvent; + if (DEBUG) UniLog.Log(e2.ToString()); + var finalValue = CombineBytes(e2.Lsb, e1.Lsb); + if (DEBUG) UniLog.Log($"CC fine. Value: " + finalValue.ToString()); + Listeners.ForEach(l => l.TriggerControl(new MIDI_CC_EventData(e1.Channel, e1.Msb, finalValue, _coarse: false))); + _eventBuffer.RemoveRange(0, 2); + _bufferedMessagesToHandle -= 2; + } + + if (_eventBuffer.Count == 0) break; + + var e = _eventBuffer[0].midiEvent; + if (DEBUG) UniLog.Log(e.ToString()); + switch (e.EventType) + { + case MidiEvent.CC: + if (DEBUG) UniLog.Log("CC"); + Listeners.ForEach(l => l.TriggerControl(new MIDI_CC_EventData(e.Channel, e.Msb, e.Lsb, _coarse: true))); + break; + // Program events are buffered because they can be sent after a CC fine message for Bank Select, one of my devices sends consecutively: CC (Bank Select) -> CC (Bank Select Lsb) -> Program for some buttons + case MidiEvent.Program: + if (DEBUG) UniLog.Log("Program"); + Listeners.ForEach(l => l.TriggerProgram(new MIDI_ProgramEventData(e.Channel, e.Msb))); + break; + + // Unhandled events: + + //SysEx events are probably not worth handling + case MidiEvent.SysEx1: + if (DEBUG) UniLog.Log("UnhandledEvent: SysEx1"); + break; + case MidiEvent.SysEx2: + // Same as EndSysEx + if (DEBUG) UniLog.Log("UnhandledEvent: SysEx2"); + break; + case MidiEvent.MtcQuarterFrame: + if (DEBUG) UniLog.Log("UnhandledEvent: MtcQuarterFrame"); + break; + case MidiEvent.SongPositionPointer: + if (DEBUG) UniLog.Log("UnhandledEvent: SongPositionPointer"); + break; + case MidiEvent.SongSelect: + if (DEBUG) UniLog.Log("UnhandledEvent: SongSelect"); + break; + case MidiEvent.TuneRequest: + if (DEBUG) UniLog.Log("UnhandledEvent: TuneRequest"); + break; + default: + break; + } + _eventBuffer.RemoveAt(0); + _bufferedMessagesToHandle -= 1; + } + if (DEBUG) UniLog.Log("Finished flushing message buffer from start time: " + batchStartTime.ToString()); + if (_bufferedMessagesToHandle != 0) + { + // Just in case some messages got lost somehow + UniLog.Warning("Did not handle all buffered messages! " + _bufferedMessagesToHandle.ToString()); + } + _bufferedMessagesToHandle = 0; + } + + public async void OnMessageReceived(object sender, MidiReceivedEventArgs args) + { + if (DEBUG) UniLog.Log($"*** New midi message"); + if (DEBUG) UniLog.Log($"* Received {args.Length} bytes"); + if (DEBUG) UniLog.Log($"* Timestamp: {args.Timestamp}"); + + var events = MidiEvent.Convert(args.Data, args.Start, args.Length); + + if (args.Length == 1) + { + // system realtime message, do not buffer these, execute immediately + if (DEBUG) UniLog.Log($"* System realtime message"); + foreach (var e in events) + { + var str = e.ToString(); + if (DEBUG) UniLog.Log("* " + str); + switch (e.StatusByte) + { + case MidiEvent.MidiClock: + if (DEBUG) UniLog.Log("* MidiClock"); + Listeners.ForEach(l => l.TriggerMidiClock(new MIDI_SystemRealtimeEventData())); + break; + case MidiEvent.MidiTick: + if (DEBUG) UniLog.Log("* MidiTick"); + Listeners.ForEach(l => l.TriggerMidiTick(new MIDI_SystemRealtimeEventData())); + break; + case MidiEvent.MidiStart: + if (DEBUG) UniLog.Log("* MidiStart"); + Listeners.ForEach(l => l.TriggerMidiStart(new MIDI_SystemRealtimeEventData())); + break; + case MidiEvent.MidiStop: + if (DEBUG) UniLog.Log("* MidiStop"); + Listeners.ForEach(l => l.TriggerMidiStop(new MIDI_SystemRealtimeEventData())); + break; + case MidiEvent.MidiContinue: + if (DEBUG) UniLog.Log("* MidiContinue"); + Listeners.ForEach(l => l.TriggerMidiContinue(new MIDI_SystemRealtimeEventData())); + break; + case MidiEvent.ActiveSense: + if (DEBUG) UniLog.Log("* ActiveSense"); + Listeners.ForEach(l => l.TriggerActiveSense(new MIDI_SystemRealtimeEventData())); + break; + case MidiEvent.Reset: + // Same as Meta + if (DEBUG) UniLog.Log("* Reset"); + Listeners.ForEach(l => l.TriggerReset(new MIDI_SystemRealtimeEventData())); + break; + } + } + return; + } + + // other types of messages: channel message (voice or channel mode), system common message, system exclusive message + foreach (var e in events) + { + var str = e.ToString(); + if (DEBUG) UniLog.Log("* " + str); + + switch (e.EventType) + { + case MidiEvent.NoteOn: + if (DEBUG) UniLog.Log("* NoteOn"); + if (e.Lsb == 0) + { + if (DEBUG) UniLog.Log("* Zero velocity, so it's actually a NoteOff"); + Listeners.ForEach(l => l.TriggerNoteOff(new MIDI_NoteEventData(e.Channel, e.Msb, e.Lsb))); + return; + } + Listeners.ForEach(l => l.TriggerNoteOn(new MIDI_NoteEventData(e.Channel, e.Msb, e.Lsb))); + return; + case MidiEvent.NoteOff: + if (DEBUG) UniLog.Log("* NoteOff"); + Listeners.ForEach(l => l.TriggerNoteOff(new MIDI_NoteEventData(e.Channel, e.Msb, e.Lsb))); + return; + case MidiEvent.CAf: + if (DEBUG) UniLog.Log("* CAf"); + Listeners.ForEach(l => l.TriggerChannelAftertouch(new MIDI_ChannelAftertouchEventData(e.Channel, e.Msb))); + return; + case MidiEvent.Pitch: + if (DEBUG) UniLog.Log("* Pitch"); + Listeners.ForEach(l => l.TriggerPitchWheel(new MIDI_PitchWheelEventData(e.Channel, CombineBytes(e.Msb, e.Lsb)))); + return; + case MidiEvent.PAf: + if (DEBUG) UniLog.Log("* PAf"); + Listeners.ForEach(l => l.TriggerPolyphonicAftertouch(new MIDI_PolyphonicAftertouchEventData(e.Channel, e.Msb, e.Lsb))); + return; + default: + break; + } + + // buffer CC messages because consecutive ones may need to be combined + // also buffer Program messages + _eventBuffer.Add(new TimestampedMidiEvent(e, args.Timestamp)); + _bufferedMessagesToHandle += 1; + } + + if (events.Count() > 0 && args.Timestamp - _lastMessageBufferStartTime > MESSAGE_BUFFER_TIME_MILLISECONDS) + { + _lastMessageBufferStartTime = args.Timestamp; + if (DEBUG) UniLog.Log("* New message batch created: " + args.Timestamp.ToString()); + await Task.Delay((int)MESSAGE_BUFFER_TIME_MILLISECONDS); + FlushMessageBuffer(); + } + } +} + +public static class MidiDeviceConnectionManager +{ + private static Dictionary _deviceConnectionMap = new(); + + private static Dictionary _listenerConnectionMap = new(); + + public static MidiInputConnection RegisterInputListener(IMidiInputListener listener, IMidiPortDetails details) + { + if (_deviceConnectionMap.TryGetValue(details.Name, out MidiInputConnection conn)) + { + conn.Listeners.Add(listener); + return conn; + } + var newConn = CreateInputConnection(details); + newConn.Listeners.Add(listener); + _listenerConnectionMap.Add(listener, newConn); + return newConn; + } + + public static void UnregisterInputListener(IMidiInputListener listener) + { + if (_listenerConnectionMap.TryGetValue(listener, out MidiInputConnection conn)) + { + conn.Listeners.Remove(listener); + _listenerConnectionMap.Remove(listener); + if (conn.Listeners.Count == 0) + { + UniLog.Log("No more listeners. Releasing input device connection. Device name: " + conn.Input.Details.Name); + Task.Run(() => ReleaseInputConnection(conn)); + } + } + } + + private static async Task ReleaseInputConnection(MidiInputConnection conn) + { + UniLog.Log("Releasing input device..."); + await Task.WhenAny(conn.Input.CloseAsync(), Task.Delay(5000)); + UniLog.Log("Device released."); + _deviceConnectionMap.Remove(conn.Input.Details.Name); + conn.Initialize(); + Pool.ReturnCleaned(ref conn); + } + + private static MidiInputConnection CreateInputConnection(IMidiPortDetails details) + { + var input = MidiAccessManager.Default.OpenInputAsync(details.Id).Result; + var conn = Pool.Borrow(); + conn.Input = input; + input.MessageReceived += conn.OnMessageReceived; + _deviceConnectionMap.Add(details.Name, conn); + return conn; + } +} + [DataModelType] public enum MIDI_CC_Definition { @@ -208,22 +545,22 @@ public MIDI_CC_EventData(in int _channel, in int _controller, in int _value, in } [DataModelType] -public delegate void MIDI_NoteEventHandler(MIDI_InputDevice device, MIDI_NoteEventData eventData); +public delegate void MIDI_NoteEventHandler(IMidiInputListener sender, MIDI_NoteEventData eventData); [DataModelType] -public delegate void MIDI_ChannelAftertouchEventHandler(MIDI_InputDevice device, MIDI_ChannelAftertouchEventData eventData); +public delegate void MIDI_ChannelAftertouchEventHandler(IMidiInputListener sender, MIDI_ChannelAftertouchEventData eventData); [DataModelType] -public delegate void MIDI_PolyphonicAftertouchEventHandler(MIDI_InputDevice device, MIDI_PolyphonicAftertouchEventData eventData); +public delegate void MIDI_PolyphonicAftertouchEventHandler(IMidiInputListener sender, MIDI_PolyphonicAftertouchEventData eventData); [DataModelType] -public delegate void MIDI_CC_EventHandler(MIDI_InputDevice device, MIDI_CC_EventData eventData); +public delegate void MIDI_CC_EventHandler(IMidiInputListener sender, MIDI_CC_EventData eventData); [DataModelType] -public delegate void MIDI_PitchWheelEventHandler(MIDI_InputDevice device, MIDI_PitchWheelEventData eventData); +public delegate void MIDI_PitchWheelEventHandler(IMidiInputListener sender, MIDI_PitchWheelEventData eventData); [DataModelType] -public delegate void MIDI_ProgramEventHandler(MIDI_InputDevice device, MIDI_ProgramEventData eventData); +public delegate void MIDI_ProgramEventHandler(IMidiInputListener sender, MIDI_ProgramEventData eventData); [DataModelType] -public delegate void MIDI_SystemRealtimeEventHandler(MIDI_InputDevice device, MIDI_SystemRealtimeEventData eventData); \ No newline at end of file +public delegate void MIDI_SystemRealtimeEventHandler(IMidiInputListener sender, MIDI_SystemRealtimeEventData eventData); \ No newline at end of file diff --git a/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_CC_Event.cs b/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_CC_Event.cs index 6da80c3..3314e20 100644 --- a/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_CC_Event.cs +++ b/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_CC_Event.cs @@ -50,11 +50,11 @@ private void OnDeviceChanged(MIDI_InputDevice device, FrooxEngineContext context { NodeContextPath path = context.CaptureContextPath(); context.GetEventDispatcher(out var dispatcher); - MIDI_CC_EventHandler value3 = delegate (MIDI_InputDevice dev, MIDI_CC_EventData e) + MIDI_CC_EventHandler value3 = delegate (IMidiInputListener sender, MIDI_CC_EventData e) { dispatcher.ScheduleEvent(path, delegate (FrooxEngineContext c) { - OnControl(dev, in e, c); + OnControl(sender, in e, c); }); }; _currentDevice.Write(device, context); @@ -84,7 +84,7 @@ private void WriteCCEventData(in MIDI_CC_EventData eventData, FrooxEngineContext NormalizedValue.Write(eventData.normalizedValue, context); } - private void OnControl(MIDI_InputDevice device, in MIDI_CC_EventData eventData, FrooxEngineContext context) + private void OnControl(IMidiInputListener sender, in MIDI_CC_EventData eventData, FrooxEngineContext context) { WriteCCEventData(in eventData, context); ControlChange.Execute(context); diff --git a/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_ChannelAftertouchEvent.cs b/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_ChannelAftertouchEvent.cs index 59363c9..0bfe175 100644 --- a/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_ChannelAftertouchEvent.cs +++ b/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_ChannelAftertouchEvent.cs @@ -47,11 +47,11 @@ private void OnDeviceChanged(MIDI_InputDevice device, FrooxEngineContext context { NodeContextPath path = context.CaptureContextPath(); context.GetEventDispatcher(out var dispatcher); - MIDI_ChannelAftertouchEventHandler value3 = delegate (MIDI_InputDevice dev, MIDI_ChannelAftertouchEventData e) + MIDI_ChannelAftertouchEventHandler value3 = delegate (IMidiInputListener sender, MIDI_ChannelAftertouchEventData e) { dispatcher.ScheduleEvent(path, delegate (FrooxEngineContext c) { - OnChannelAftertouch(dev, in e, c); + OnChannelAftertouch(sender, in e, c); }); }; _currentDevice.Write(device, context); @@ -72,7 +72,7 @@ private void WriteChannelAftertouchEventData(in MIDI_ChannelAftertouchEventData NormalizedPressure.Write(eventData.normalizedPressure, context); } - private void OnChannelAftertouch(MIDI_InputDevice device, in MIDI_ChannelAftertouchEventData eventData, FrooxEngineContext context) + private void OnChannelAftertouch(IMidiInputListener sender, in MIDI_ChannelAftertouchEventData eventData, FrooxEngineContext context) { WriteChannelAftertouchEventData(in eventData, context); ChannelAftertouch.Execute(context); diff --git a/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_NoteEvents.cs b/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_NoteEvents.cs index b775be6..0a72681 100644 --- a/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_NoteEvents.cs +++ b/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_NoteEvents.cs @@ -52,18 +52,18 @@ private void OnDeviceChanged(MIDI_InputDevice device, FrooxEngineContext context { NodeContextPath path = context.CaptureContextPath(); context.GetEventDispatcher(out var dispatcher); - MIDI_NoteEventHandler value = delegate (MIDI_InputDevice dev, MIDI_NoteEventData e) + MIDI_NoteEventHandler value = delegate (IMidiInputListener sender, MIDI_NoteEventData e) { dispatcher.ScheduleEvent(path, delegate (FrooxEngineContext c) { - OnNoteOn(dev, in e, c); + OnNoteOn(sender, in e, c); }); }; - MIDI_NoteEventHandler value2 = delegate (MIDI_InputDevice dev, MIDI_NoteEventData e) + MIDI_NoteEventHandler value2 = delegate (IMidiInputListener sender, MIDI_NoteEventData e) { dispatcher.ScheduleEvent(path, delegate (FrooxEngineContext c) { - OnNoteOff(dev, in e, c); + OnNoteOff(sender, in e, c); }); }; _currentDevice.Write(device, context); @@ -88,13 +88,13 @@ private void WriteNoteOnOffEventData(in MIDI_NoteEventData eventData, FrooxEngin NormalizedVelocity.Write(eventData.normalizedVelocity, context); } - private void OnNoteOn(MIDI_InputDevice device, in MIDI_NoteEventData eventData, FrooxEngineContext context) + private void OnNoteOn(IMidiInputListener sender, in MIDI_NoteEventData eventData, FrooxEngineContext context) { WriteNoteOnOffEventData(in eventData, context); NoteOn.Execute(context); } - private void OnNoteOff(MIDI_InputDevice device, in MIDI_NoteEventData eventData, FrooxEngineContext context) + private void OnNoteOff(IMidiInputListener sender, in MIDI_NoteEventData eventData, FrooxEngineContext context) { WriteNoteOnOffEventData(in eventData, context); NoteOff.Execute(context); diff --git a/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_PitchWheelEvent.cs b/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_PitchWheelEvent.cs index c32069b..3500854 100644 --- a/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_PitchWheelEvent.cs +++ b/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_PitchWheelEvent.cs @@ -45,11 +45,11 @@ private void OnDeviceChanged(MIDI_InputDevice device, FrooxEngineContext context { NodeContextPath path = context.CaptureContextPath(); context.GetEventDispatcher(out var dispatcher); - MIDI_PitchWheelEventHandler value3 = delegate (MIDI_InputDevice dev, MIDI_PitchWheelEventData e) + MIDI_PitchWheelEventHandler value3 = delegate (IMidiInputListener sender, MIDI_PitchWheelEventData e) { dispatcher.ScheduleEvent(path, delegate (FrooxEngineContext c) { - OnPitch(dev, in e, c); + OnPitch(sender, in e, c); }); }; _currentDevice.Write(device, context); @@ -72,7 +72,7 @@ private void WritePitchEventData(in MIDI_PitchWheelEventData eventData, FrooxEng NormalizedValue.Write(eventData.normalizedValue, context); } - private void OnPitch(MIDI_InputDevice device, in MIDI_PitchWheelEventData eventData, FrooxEngineContext context) + private void OnPitch(IMidiInputListener sender, in MIDI_PitchWheelEventData eventData, FrooxEngineContext context) { WritePitchEventData(in eventData, context); PitchWheel.Execute(context); diff --git a/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_PolyphonicAftertouchEvent.cs b/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_PolyphonicAftertouchEvent.cs index d65c205..83bc33b 100644 --- a/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_PolyphonicAftertouchEvent.cs +++ b/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_PolyphonicAftertouchEvent.cs @@ -49,11 +49,11 @@ private void OnDeviceChanged(MIDI_InputDevice device, FrooxEngineContext context { NodeContextPath path = context.CaptureContextPath(); context.GetEventDispatcher(out var dispatcher); - MIDI_PolyphonicAftertouchEventHandler value3 = delegate (MIDI_InputDevice dev, MIDI_PolyphonicAftertouchEventData e) + MIDI_PolyphonicAftertouchEventHandler value3 = delegate (IMidiInputListener sender, MIDI_PolyphonicAftertouchEventData e) { dispatcher.ScheduleEvent(path, delegate (FrooxEngineContext c) { - OnPolyphonicAftertouch(dev, in e, c); + OnPolyphonicAftertouch(sender, in e, c); }); }; _currentDevice.Write(device, context); @@ -75,7 +75,7 @@ private void WritePolyphonicAftertouchEventData(in MIDI_PolyphonicAftertouchEven NormalizedPressure.Write(eventData.normalizedPressure, context); } - private void OnPolyphonicAftertouch(MIDI_InputDevice device, in MIDI_PolyphonicAftertouchEventData eventData, FrooxEngineContext context) + private void OnPolyphonicAftertouch(IMidiInputListener sender, in MIDI_PolyphonicAftertouchEventData eventData, FrooxEngineContext context) { WritePolyphonicAftertouchEventData(in eventData, context); PolyphonicAftertouch.Execute(context); diff --git a/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_ProgramEvent.cs b/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_ProgramEvent.cs index ef80a5a..bc839c5 100644 --- a/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_ProgramEvent.cs +++ b/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_ProgramEvent.cs @@ -44,11 +44,11 @@ private void OnDeviceChanged(MIDI_InputDevice device, FrooxEngineContext context { NodeContextPath path = context.CaptureContextPath(); context.GetEventDispatcher(out var dispatcher); - MIDI_ProgramEventHandler value = delegate (MIDI_InputDevice dev, MIDI_ProgramEventData e) + MIDI_ProgramEventHandler value = delegate (IMidiInputListener sender, MIDI_ProgramEventData e) { dispatcher.ScheduleEvent(path, delegate (FrooxEngineContext c) { - OnProgram(dev, in e, c); + OnProgram(sender, in e, c); }); }; _currentDevice.Write(device, context); @@ -68,7 +68,7 @@ private void WriteProgramEventData(in MIDI_ProgramEventData eventData, FrooxEngi ProgramValue.Write(eventData.program, context); } - private void OnProgram(MIDI_InputDevice device, in MIDI_ProgramEventData eventData, FrooxEngineContext context) + private void OnProgram(IMidiInputListener sender, in MIDI_ProgramEventData eventData, FrooxEngineContext context) { WriteProgramEventData(in eventData, context); Program.Execute(context); diff --git a/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_SystemRealtimeEvents.cs b/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_SystemRealtimeEvents.cs index b8ff0ea..25e5373 100644 --- a/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_SystemRealtimeEvents.cs +++ b/ProjectObsidian/ProtoFlux/Devices/MIDI/MIDI_SystemRealtimeEvents.cs @@ -70,53 +70,53 @@ private void OnDeviceChanged(MIDI_InputDevice device, FrooxEngineContext context { NodeContextPath path = context.CaptureContextPath(); context.GetEventDispatcher(out var dispatcher); - MIDI_SystemRealtimeEventHandler value = delegate (MIDI_InputDevice dev, MIDI_SystemRealtimeEventData e) + MIDI_SystemRealtimeEventHandler value = delegate (IMidiInputListener sender, MIDI_SystemRealtimeEventData e) { dispatcher.ScheduleEvent(path, delegate (FrooxEngineContext c) { - OnClock(dev, in e, c); + OnClock(sender, in e, c); }); }; - MIDI_SystemRealtimeEventHandler value2 = delegate (MIDI_InputDevice dev, MIDI_SystemRealtimeEventData e) + MIDI_SystemRealtimeEventHandler value2 = delegate (IMidiInputListener sender, MIDI_SystemRealtimeEventData e) { dispatcher.ScheduleEvent(path, delegate (FrooxEngineContext c) { - OnTick(dev, in e, c); + OnTick(sender, in e, c); }); }; - MIDI_SystemRealtimeEventHandler value3 = delegate (MIDI_InputDevice dev, MIDI_SystemRealtimeEventData e) + MIDI_SystemRealtimeEventHandler value3 = delegate (IMidiInputListener sender, MIDI_SystemRealtimeEventData e) { dispatcher.ScheduleEvent(path, delegate (FrooxEngineContext c) { - OnStart(dev, in e, c); + OnStart(sender, in e, c); }); }; - MIDI_SystemRealtimeEventHandler value4 = delegate (MIDI_InputDevice dev, MIDI_SystemRealtimeEventData e) + MIDI_SystemRealtimeEventHandler value4 = delegate (IMidiInputListener sender, MIDI_SystemRealtimeEventData e) { dispatcher.ScheduleEvent(path, delegate (FrooxEngineContext c) { - OnStop(dev, in e, c); + OnStop(sender, in e, c); }); }; - MIDI_SystemRealtimeEventHandler value5 = delegate (MIDI_InputDevice dev, MIDI_SystemRealtimeEventData e) + MIDI_SystemRealtimeEventHandler value5 = delegate (IMidiInputListener sender, MIDI_SystemRealtimeEventData e) { dispatcher.ScheduleEvent(path, delegate (FrooxEngineContext c) { - OnContinue(dev, in e, c); + OnContinue(sender, in e, c); }); }; - MIDI_SystemRealtimeEventHandler value6 = delegate (MIDI_InputDevice dev, MIDI_SystemRealtimeEventData e) + MIDI_SystemRealtimeEventHandler value6 = delegate (IMidiInputListener sender, MIDI_SystemRealtimeEventData e) { dispatcher.ScheduleEvent(path, delegate (FrooxEngineContext c) { - OnActiveSense(dev, in e, c); + OnActiveSense(sender, in e, c); }); }; - MIDI_SystemRealtimeEventHandler value7 = delegate (MIDI_InputDevice dev, MIDI_SystemRealtimeEventData e) + MIDI_SystemRealtimeEventHandler value7 = delegate (IMidiInputListener sender, MIDI_SystemRealtimeEventData e) { dispatcher.ScheduleEvent(path, delegate (FrooxEngineContext c) { - OnReset(dev, in e, c); + OnReset(sender, in e, c); }); }; _currentDevice.Write(device, context); @@ -152,43 +152,43 @@ private void WriteSystemRealtimeEventData(in MIDI_SystemRealtimeEventData eventD { } - private void OnClock(MIDI_InputDevice device, in MIDI_SystemRealtimeEventData eventData, FrooxEngineContext context) + private void OnClock(IMidiInputListener sender, in MIDI_SystemRealtimeEventData eventData, FrooxEngineContext context) { WriteSystemRealtimeEventData(eventData, context); Clock.Execute(context); } - private void OnTick(MIDI_InputDevice device, in MIDI_SystemRealtimeEventData eventData, FrooxEngineContext context) + private void OnTick(IMidiInputListener sender, in MIDI_SystemRealtimeEventData eventData, FrooxEngineContext context) { WriteSystemRealtimeEventData(eventData, context); Tick.Execute(context); } - private void OnStart(MIDI_InputDevice device, in MIDI_SystemRealtimeEventData eventData, FrooxEngineContext context) + private void OnStart(IMidiInputListener sender, in MIDI_SystemRealtimeEventData eventData, FrooxEngineContext context) { WriteSystemRealtimeEventData(eventData, context); Start.Execute(context); } - private void OnStop(MIDI_InputDevice device, in MIDI_SystemRealtimeEventData eventData, FrooxEngineContext context) + private void OnStop(IMidiInputListener sender, in MIDI_SystemRealtimeEventData eventData, FrooxEngineContext context) { WriteSystemRealtimeEventData(eventData, context); Stop.Execute(context); } - private void OnContinue(MIDI_InputDevice device, in MIDI_SystemRealtimeEventData eventData, FrooxEngineContext context) + private void OnContinue(IMidiInputListener sender, in MIDI_SystemRealtimeEventData eventData, FrooxEngineContext context) { WriteSystemRealtimeEventData(eventData, context); Continue.Execute(context); } - private void OnActiveSense(MIDI_InputDevice device, in MIDI_SystemRealtimeEventData eventData, FrooxEngineContext context) + private void OnActiveSense(IMidiInputListener sender, in MIDI_SystemRealtimeEventData eventData, FrooxEngineContext context) { WriteSystemRealtimeEventData(eventData, context); ActiveSense.Execute(context); } - private void OnReset(MIDI_InputDevice device, in MIDI_SystemRealtimeEventData eventData, FrooxEngineContext context) + private void OnReset(IMidiInputListener sender, in MIDI_SystemRealtimeEventData eventData, FrooxEngineContext context) { WriteSystemRealtimeEventData(eventData, context); Reset.Execute(context);