summaryrefslogtreecommitdiff
path: root/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi
diff options
context:
space:
mode:
Diffstat (limited to 'VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi')
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi.meta8
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Event.cs17
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Event.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceInfo.cs34
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceInfo.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceManager.cs69
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceManager.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiErrorType.cs18
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiErrorType.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiEvent.cs31
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiEvent.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiException.cs22
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiException.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiFilter.cs30
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiFilter.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiInput.cs57
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiInput.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiMessage.cs20
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiMessage.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiOutput.cs67
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiOutput.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiStream.cs41
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiStream.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiTimeProcDelegate.cs7
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiTimeProcDelegate.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins.meta8
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins/Windows.meta8
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins/Windows/portmidi.dllbin0 -> 29184 bytes
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins/Windows/portmidi.dll.meta96
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmDeviceInfo.cs21
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmDeviceInfo.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmEvent.cs11
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmEvent.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PortMidiMarshal.cs105
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PortMidiMarshal.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/VRCPortMidi.cs116
-rw-r--r--VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/VRCPortMidi.cs.meta3
37 files changed, 954 insertions, 0 deletions
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi.meta
new file mode 100644
index 00000000..0b38e67b
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: c4ba92a5dc8e6c14bb9b8dfce6851a99
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Event.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Event.cs
new file mode 100644
index 00000000..110564e7
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Event.cs
@@ -0,0 +1,17 @@
+namespace PortMidi
+{
+ public class Event
+ {
+ public Event(PmEvent pmEvent)
+ {
+ this.Status = PortMidiMarshal.Pm_MessageStatus(pmEvent.message);
+ this.Data1 = PortMidiMarshal.Pm_MessageData1(pmEvent.message);
+ this.Data2 = PortMidiMarshal.Pm_MessageData2(pmEvent.message);
+ }
+
+ public long Timestamp { get; set; }
+ public long Status { get; set; }
+ public long Data1 { get; set; }
+ public long Data2 { get; set; }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Event.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Event.cs.meta
new file mode 100644
index 00000000..d02de3b3
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Event.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 61b00e22036b5cc49a565aaec91ca8cb
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceInfo.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceInfo.cs
new file mode 100644
index 00000000..001816fe
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceInfo.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace PortMidi
+{
+ public struct MidiDeviceInfo
+ {
+ PmDeviceInfo info;
+
+ internal MidiDeviceInfo(int id, IntPtr ptr)
+ {
+ ID = id;
+ this.info = (PmDeviceInfo) Marshal.PtrToStructure(ptr, typeof(PmDeviceInfo));
+ }
+
+ public int ID { get; set; }
+
+ public string Interface => Marshal.PtrToStringAnsi(info.Interface);
+
+ public string Name => Marshal.PtrToStringAnsi(info.Name);
+
+ public bool IsInput => info.Input != 0;
+
+ public bool IsOutput => info.Output != 0;
+
+ public bool IsOpened => info.Opened != 0;
+
+ public override string ToString()
+ {
+ return
+ $"{Interface} - {Name} ({(IsInput ? (IsOutput ? "I/O" : "Input") : (IsOutput ? "Output" : "N/A"))} {(IsOpened ? "open" : String.Empty)})";
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceInfo.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceInfo.cs.meta
new file mode 100644
index 00000000..60628362
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceInfo.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: bbb88a986ea685d41a9eac3aa3dd8c60
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceManager.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceManager.cs
new file mode 100644
index 00000000..4628afc1
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceManager.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Collections.Generic;
+using PmDeviceID = System.Int32;
+using PortMidiStream = System.IntPtr;
+using PmError = PortMidi.MidiErrorType;
+
+namespace PortMidi
+{
+ public static class MidiDeviceManager
+ {
+ private const int DefaultBufferSize = 1024;
+
+ static MidiDeviceManager()
+ {
+ PortMidiMarshal.Pm_Initialize();
+ AppDomain.CurrentDomain.DomainUnload += delegate { PortMidiMarshal.Pm_Terminate(); };
+ }
+
+ public static int DeviceCount => PortMidiMarshal.Pm_CountDevices();
+
+ public static int DefaultInputDeviceId => PortMidiMarshal.Pm_GetDefaultInputDeviceID();
+
+ public static int DefaultOutputDeviceId => PortMidiMarshal.Pm_GetDefaultOutputDeviceID();
+
+ public static IEnumerable<MidiDeviceInfo> AllDevices
+ {
+ get
+ {
+ for (var i = 0; i < DeviceCount; i++)
+ {
+ yield return GetDeviceInfo(i);
+ }
+ }
+ }
+
+ private static MidiDeviceInfo GetDeviceInfo(PmDeviceID id)
+ {
+ return new MidiDeviceInfo(id, PortMidiMarshal.Pm_GetDeviceInfo(id));
+ }
+
+ public static MidiInput OpenInput(PmDeviceID inputDevice)
+ {
+ return OpenInput(inputDevice, DefaultBufferSize);
+ }
+
+ private static MidiInput OpenInput(PmDeviceID inputDevice, int bufferSize)
+ {
+ PortMidiStream stream = default;
+ var e = PortMidiMarshal.Pm_OpenInput(out stream, inputDevice, IntPtr.Zero, bufferSize, null, IntPtr.Zero);
+ if (e != PmError.NoError)
+ {
+ throw new MidiException(e, $"Failed to open MIDI input device {e}");
+ }
+
+ return new MidiInput(stream, inputDevice);
+ }
+
+ public static MidiOutput OpenOutput(PmDeviceID outputDevice)
+ {
+ PortMidiStream stream;
+ var e = PortMidiMarshal.Pm_OpenOutput(out stream, outputDevice, IntPtr.Zero, 0, null, IntPtr.Zero, 0);
+ if (e != PmError.NoError)
+ {
+ throw new MidiException(e, $"Failed to open MIDI output device {e}");
+ }
+ return new MidiOutput(stream, outputDevice, 0);
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceManager.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceManager.cs.meta
new file mode 100644
index 00000000..3874e4b5
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiDeviceManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 02b0549f0815d8a4982133c0a99880cf
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiErrorType.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiErrorType.cs
new file mode 100644
index 00000000..e03dfdcf
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiErrorType.cs
@@ -0,0 +1,18 @@
+namespace PortMidi
+{
+ public enum MidiErrorType
+ {
+ NoError = 0,
+ NoData = 0,
+ GotData = 1,
+ HostError = -10000,
+ InvalidDeviceId,
+ InsufficientMemory,
+ BufferTooSmall,
+ BufferOverflow,
+ BadPointer,
+ BadData,
+ InternalError,
+ BufferMaxSize,
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiErrorType.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiErrorType.cs.meta
new file mode 100644
index 00000000..15768ae2
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiErrorType.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6b1e1637c177e0c43af16fefddc0826c
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiEvent.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiEvent.cs
new file mode 100644
index 00000000..b304cefd
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiEvent.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace PortMidi
+{
+ [StructLayout(LayoutKind.Sequential)]
+ public struct MidiEvent
+ {
+ MidiMessage msg;
+ Int32 ts;
+ [NonSerialized] byte[] sysex;
+
+ public MidiMessage Message
+ {
+ get => msg;
+ set => msg = value;
+ }
+
+ public Int32 Timestamp
+ {
+ get => ts;
+ set => ts = value;
+ }
+
+ public byte[] SysEx
+ {
+ get => sysex;
+ set => sysex = value;
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiEvent.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiEvent.cs.meta
new file mode 100644
index 00000000..cb252310
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiEvent.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b8f59aea3b1948f4ebb0d7835b69bbb7
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiException.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiException.cs
new file mode 100644
index 00000000..551297b2
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiException.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace PortMidi
+{
+ public class MidiException : Exception
+ {
+ MidiErrorType error_type;
+
+ public MidiException(MidiErrorType errorType, string message)
+ : this(errorType, message, null)
+ {
+ }
+
+ public MidiException(MidiErrorType errorType, string message, Exception innerException)
+ : base(message, innerException)
+ {
+ error_type = errorType;
+ }
+
+ public MidiErrorType ErrorType => error_type;
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiException.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiException.cs.meta
new file mode 100644
index 00000000..82da420f
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiException.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a9aa39367a5a6014e8ae35c4c0fc0fd0
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiFilter.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiFilter.cs
new file mode 100644
index 00000000..d270806a
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiFilter.cs
@@ -0,0 +1,30 @@
+using System;
+
+namespace PortMidi
+{
+ [Flags]
+ public enum MidiFilter : int
+ {
+ Active = 1 << 0x0E,
+ SysEx = 1,
+ Clock = 1 << 0x08,
+ Play = ((1 << 0x0A) | (1 << 0x0C) | (1 << 0x0B)),
+ Tick = (1 << 0x09),
+ FD = (1 << 0x0D),
+ Undefined = FD,
+ Reset = (1 << 0x0F),
+ RealTime = (Active | SysEx | Clock | Play | Undefined | Reset | Tick),
+ Note = ((1 << 0x19) | (1 << 0x18)),
+ CAF = (1 << 0x1D),
+ PAF = (1 << 0x1A),
+ AF = (CAF | PAF),
+ Program = (1 << 0x1C),
+ Control = (1 << 0x1B),
+ PitchBend = (1 << 0x1E),
+ MTC = (1 << 0x01),
+ SongPosition = (1 << 0x02),
+ SongSelect = (1 << 0x03),
+ Tune = (1 << 0x06),
+ SystemCommon = (MTC | SongPosition | SongSelect | Tune)
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiFilter.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiFilter.cs.meta
new file mode 100644
index 00000000..f553d7f5
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiFilter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9ddf13cca3c55164692f303b16a5fb2d
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiInput.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiInput.cs
new file mode 100644
index 00000000..dbb35623
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiInput.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace PortMidi
+{
+ public class MidiInput : MidiStream
+ {
+ public MidiInput(IntPtr stream, Int32 inputDevice)
+ : base(stream, inputDevice)
+ {
+ }
+
+ public bool HasData => PortMidiMarshal.Pm_Poll(stream) == MidiErrorType.GotData;
+
+ public int Read(byte[] buffer, int index, int length)
+ {
+ var gch = GCHandle.Alloc(buffer);
+ try
+ {
+ var ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, index);
+ int size = PortMidiMarshal.Pm_Read(stream, ptr, length);
+ if (size < 0)
+ {
+ throw new MidiException((MidiErrorType) size,
+ PortMidiMarshal.Pm_GetErrorText((MidiErrorType) size));
+ }
+ return size * 4;
+ }
+ finally
+ {
+ gch.Free();
+ }
+ }
+
+ public Event ReadEvent(byte[] buffer, int index, int length)
+ {
+ var gch = GCHandle.Alloc(buffer);
+ try
+ {
+ var ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, index);
+ int size = PortMidiMarshal.Pm_Read(stream, ptr, length);
+ if (size < 0)
+ {
+ throw new MidiException((MidiErrorType) size,
+ PortMidiMarshal.Pm_GetErrorText((MidiErrorType) size));
+ }
+
+ return new Event(Marshal.PtrToStructure<PmEvent>(ptr));
+ }
+ finally
+ {
+ gch.Free();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiInput.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiInput.cs.meta
new file mode 100644
index 00000000..c46ce4b1
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiInput.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 324be2ea40d796c49b74c60b8c2e5c97
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiMessage.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiMessage.cs
new file mode 100644
index 00000000..a7b7c9c4
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiMessage.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace PortMidi
+{
+ public struct MidiMessage
+ {
+ private int v;
+ public MidiMessage(int value)
+ {
+ v = value;
+ }
+
+ public MidiMessage(int status, int data1, int data2)
+ {
+ v = ((data2 << 16) & 0xFF0000) | ((data1 << 8) & 0xFF00) | (status & 0xFF);
+ }
+
+ public Int32 Value => v;
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiMessage.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiMessage.cs.meta
new file mode 100644
index 00000000..02f67375
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiMessage.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c18643a5c49be3649ade32f7c9b19e99
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiOutput.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiOutput.cs
new file mode 100644
index 00000000..fe56081c
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiOutput.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace PortMidi
+{
+ public class MidiOutput : MidiStream
+ {
+ public MidiOutput(IntPtr stream, Int32 outputDevice, int latency)
+ : base(stream, outputDevice)
+ {
+ }
+
+ public void Write(MidiEvent midiEvent)
+ {
+ if (midiEvent.SysEx != null)
+ {
+ WriteSysEx(midiEvent.Timestamp, midiEvent.SysEx);
+ }
+ else
+ {
+ Write(midiEvent.Timestamp, midiEvent.Message);
+ }
+ }
+
+ private void Write(Int32 when, MidiMessage msg)
+ {
+ var ret = PortMidiMarshal.Pm_WriteShort(stream, when, msg);
+ if (ret != MidiErrorType.NoError)
+ {
+ throw new MidiException(ret,
+ $"Failed to write message {msg.Value} : {PortMidiMarshal.Pm_GetErrorText((MidiErrorType) ret)}");
+ }
+ }
+
+ private void WriteSysEx(Int32 when, byte[] sysEx)
+ {
+ var ret = PortMidiMarshal.Pm_WriteSysEx(stream, when, sysEx);
+ if (ret != MidiErrorType.NoError)
+ throw new MidiException(ret,
+ $"Failed to write sysEx message : {PortMidiMarshal.Pm_GetErrorText((MidiErrorType) ret)}");
+ }
+
+ public void Write(MidiEvent[] buffer)
+ {
+ Write(buffer, 0, buffer.Length);
+ }
+
+ private void Write(MidiEvent[] buffer, int index, int length)
+ {
+ var gch = GCHandle.Alloc(buffer);
+ try
+ {
+ var ptr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, index);
+ var ret = PortMidiMarshal.Pm_Write(stream, ptr, length);
+ if (ret != MidiErrorType.NoError)
+ {
+ throw new MidiException(ret,
+ $"Failed to write messages : {PortMidiMarshal.Pm_GetErrorText((MidiErrorType) ret)}");
+ }
+ }
+ finally
+ {
+ gch.Free();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiOutput.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiOutput.cs.meta
new file mode 100644
index 00000000..cc6a9a22
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiOutput.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a806da9b51653af47b83c3c10a6eed65
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiStream.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiStream.cs
new file mode 100644
index 00000000..e4d80447
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiStream.cs
@@ -0,0 +1,41 @@
+using System;
+
+namespace PortMidi
+{
+ public abstract class MidiStream : IDisposable
+ {
+ internal IntPtr stream;
+ internal Int32 device;
+
+ protected MidiStream(IntPtr stream, Int32 deviceID)
+ {
+ this.stream = stream;
+ this.device = deviceID;
+ }
+
+ public void Abort()
+ {
+ PortMidiMarshal.Pm_Abort(stream);
+ }
+
+ public void Close()
+ {
+ Dispose();
+ }
+
+ public void Dispose()
+ {
+ PortMidiMarshal.Pm_Close(stream);
+ }
+
+ public void SetFilter(MidiFilter filters)
+ {
+ PortMidiMarshal.Pm_SetFilter(stream, filters);
+ }
+
+ public void SetChannelMask(int mask)
+ {
+ PortMidiMarshal.Pm_SetChannelMask(stream, mask);
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiStream.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiStream.cs.meta
new file mode 100644
index 00000000..a16d7e5c
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiStream.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f858f0a52902aaf4298247159434e1ae
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiTimeProcDelegate.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiTimeProcDelegate.cs
new file mode 100644
index 00000000..c0a21dc2
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiTimeProcDelegate.cs
@@ -0,0 +1,7 @@
+using System;
+using PmTimestamp = System.Int32;
+
+namespace PortMidi
+{
+ public delegate PmTimestamp MidiTimeProcDelegate(IntPtr timeInfo);
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiTimeProcDelegate.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiTimeProcDelegate.cs.meta
new file mode 100644
index 00000000..79fb10f5
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/MidiTimeProcDelegate.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 63050b27ae9f6ab4cb3837f915f15067
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins.meta
new file mode 100644
index 00000000..9828731f
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 743cfca463c6cc94fabfda636f4eb439
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins/Windows.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins/Windows.meta
new file mode 100644
index 00000000..fe5fb2cb
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins/Windows.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: bc4d155a8e6f6f144871b6ee03542b7c
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins/Windows/portmidi.dll b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins/Windows/portmidi.dll
new file mode 100644
index 00000000..ba5be4e5
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins/Windows/portmidi.dll
Binary files differ
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins/Windows/portmidi.dll.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins/Windows/portmidi.dll.meta
new file mode 100644
index 00000000..36f81d69
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/Plugins/Windows/portmidi.dll.meta
@@ -0,0 +1,96 @@
+fileFormatVersion: 2
+guid: 372656c38e5c12f48b819e12fa08ac7b
+PluginImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ iconMap: {}
+ executionOrder: {}
+ defineConstraints: []
+ isPreloaded: 0
+ isOverridable: 0
+ isExplicitlyReferenced: 0
+ validateReferences: 1
+ platformData:
+ - first:
+ '': Any
+ second:
+ enabled: 0
+ settings:
+ Exclude Android: 1
+ Exclude Editor: 0
+ Exclude Linux: 0
+ Exclude Linux64: 0
+ Exclude LinuxUniversal: 0
+ Exclude OSXUniversal: 0
+ Exclude WebGL: 1
+ Exclude Win: 0
+ Exclude Win64: 0
+ - first:
+ Android: Android
+ second:
+ enabled: 0
+ settings:
+ CPU: ARMv7
+ - first:
+ Any:
+ second:
+ enabled: 0
+ settings: {}
+ - first:
+ Editor: Editor
+ second:
+ enabled: 1
+ settings:
+ CPU: AnyCPU
+ DefaultValueInitialized: true
+ OS: AnyOS
+ - first:
+ Facebook: Win
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ - first:
+ Facebook: Win64
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ - first:
+ Standalone: Linux
+ second:
+ enabled: 1
+ settings:
+ CPU: x86
+ - first:
+ Standalone: Linux64
+ second:
+ enabled: 1
+ settings:
+ CPU: x86_64
+ - first:
+ Standalone: LinuxUniversal
+ second:
+ enabled: 1
+ settings: {}
+ - first:
+ Standalone: OSXUniversal
+ second:
+ enabled: 1
+ settings:
+ CPU: AnyCPU
+ - first:
+ Standalone: Win
+ second:
+ enabled: 1
+ settings:
+ CPU: AnyCPU
+ - first:
+ Standalone: Win64
+ second:
+ enabled: 1
+ settings:
+ CPU: AnyCPU
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmDeviceInfo.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmDeviceInfo.cs
new file mode 100644
index 00000000..e2ecccdd
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmDeviceInfo.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace PortMidi
+{
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PmDeviceInfo
+ {
+ public int StructVersion;
+ public IntPtr Interface;
+ public IntPtr Name;
+ public int Input;
+ public int Output;
+ public int Opened;
+
+ public override string ToString()
+ {
+ return $"{StructVersion}, {Interface}, {Name}, {Input}, {Output}, {Opened}";
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmDeviceInfo.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmDeviceInfo.cs.meta
new file mode 100644
index 00000000..048fa702
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmDeviceInfo.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e20f952b815e0fd4c89e244be795b605
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmEvent.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmEvent.cs
new file mode 100644
index 00000000..b5d68bce
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmEvent.cs
@@ -0,0 +1,11 @@
+using PmMessage = System.Int32;
+using PmTimestamp = System.Int32;
+
+namespace PortMidi
+{
+ public struct PmEvent
+ {
+ public PmMessage message;
+ public PmTimestamp timestamp;
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmEvent.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmEvent.cs.meta
new file mode 100644
index 00000000..d8ec66ee
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PmEvent.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 36b7824d7ad3b3f4fbb5ed6d2a5766ec
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PortMidiMarshal.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PortMidiMarshal.cs
new file mode 100644
index 00000000..58f02e11
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PortMidiMarshal.cs
@@ -0,0 +1,105 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace PortMidi
+{
+ internal class PortMidiMarshal
+ {
+ private const int Hdrlength = 50;
+ private const uint PmHostErrorMsgLen = 256;
+ const int PmNoDevice = -1;
+
+ [DllImport("portmidi")]
+ public static extern MidiErrorType Pm_Initialize();
+
+ [DllImport("portmidi")]
+ public static extern IntPtr Pm_GetDeviceInfo(Int32 id);
+
+ [DllImport("portmidi")]
+ public static extern int Pm_CountDevices();
+
+ [DllImport("portmidi")]
+ public static extern Int32 Pm_GetDefaultInputDeviceID();
+
+ [DllImport("portmidi")]
+ public static extern Int32 Pm_GetDefaultOutputDeviceID();
+
+ [DllImport("portmidi")]
+ public static extern MidiErrorType Pm_OpenInput(
+ out IntPtr stream,
+ Int32 inputDevice,
+ IntPtr inputDriverInfo,
+ int bufferSize,
+ MidiTimeProcDelegate timeProc,
+ IntPtr timeInfo);
+
+ [DllImport("portmidi")]
+ public static extern MidiErrorType Pm_OpenOutput(
+ out IntPtr stream,
+ Int32 outputDevice,
+ IntPtr outputDriverInfo,
+ int bufferSize,
+ MidiTimeProcDelegate time_proc,
+ IntPtr time_info,
+ int latency);
+
+ [DllImport("portmidi")]
+ public static extern MidiErrorType Pm_SetFilter(IntPtr stream, MidiFilter filters);
+
+ [DllImport("portmidi")]
+ public static extern MidiErrorType Pm_SetChannelMask(IntPtr stream, int mask);
+
+ [DllImport("portmidi")]
+ public static extern MidiErrorType Pm_Poll(IntPtr stream);
+
+ [DllImport("portmidi")]
+ public static extern int Pm_Read(IntPtr stream, IntPtr buffer, int length);
+
+ [DllImport("portmidi")]
+ public static extern MidiErrorType Pm_Write(IntPtr stream, IntPtr buffer, int length);
+
+ [DllImport("portmidi")]
+ public static extern MidiErrorType Pm_WriteSysEx(IntPtr stream, Int32 when, byte[] msg);
+
+ [DllImport("portmidi")]
+ public static extern MidiErrorType Pm_WriteShort(IntPtr stream, Int32 when, MidiMessage msg);
+
+ [DllImport("portmidi")]
+ public static extern MidiErrorType Pm_Abort(IntPtr stream);
+
+ [DllImport("portmidi")]
+ public static extern MidiErrorType Pm_Close(IntPtr stream);
+
+ [DllImport("portmidi")]
+ public static extern MidiErrorType Pm_Terminate();
+
+ [DllImport("portmidi")]
+ public static extern int Pm_HasHostError(IntPtr stream);
+
+ [DllImport("portmidi")]
+ public static extern string Pm_GetErrorText(MidiErrorType errnum);
+
+ [DllImport("portmidi")]
+ public static extern void Pm_GetHostErrorText(IntPtr msg, uint len);
+
+ public static int Pm_Channel(int channel)
+ {
+ return 1 << channel;
+ }
+
+ public static int Pm_MessageStatus(int msg)
+ {
+ return msg & 0xFF;
+ }
+
+ public static int Pm_MessageData1(int msg)
+ {
+ return (msg >> 8) & 0xFF;
+ }
+
+ public static int Pm_MessageData2(int msg)
+ {
+ return (msg >> 16) & 0xFF;
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PortMidiMarshal.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PortMidiMarshal.cs.meta
new file mode 100644
index 00000000..09c36972
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/PortMidi/PortMidiMarshal.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 5d4b9d0d9dfad1c4894df5c2a5530696
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/VRCPortMidi.cs b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/VRCPortMidi.cs
new file mode 100644
index 00000000..e4a6200b
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/VRCPortMidi.cs
@@ -0,0 +1,116 @@
+#if (UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN) && !UNITY_ANDROID
+using System;
+using System.Collections.Generic;
+using System.Linq;
+#if VRC_CLIENT
+using VRC.Core;
+#endif
+using PortMidi;
+using UnityEngine;
+using VRC.SDK3.Midi;
+
+
+namespace VRC.SDKBase.Midi
+{
+ public class VRCPortMidiInput: IVRCMidiInput
+ {
+
+ private MidiInput _input;
+ private byte[] _data;
+ private MidiDeviceInfo _info;
+
+ public bool OpenDevice(string name = null)
+ {
+ try
+ {
+ if (!string.IsNullOrWhiteSpace(name))
+ {
+ _info = MidiDeviceManager.AllDevices.FirstOrDefault(device =>
+ device.IsInput && device.Name.ToLower().Contains(name.ToLower()));
+ }
+ else
+ {
+ _info = MidiDeviceManager.AllDevices.FirstOrDefault(device =>
+ device.ID == MidiDeviceManager.DefaultInputDeviceId);
+ }
+
+ _input = MidiDeviceManager.OpenInput(_info.ID);
+ _input.SetFilter(MidiFilter.Active | MidiFilter.SysEx | MidiFilter.Clock | MidiFilter.Play | MidiFilter.Tick | MidiFilter.Undefined | MidiFilter.Reset | MidiFilter.RealTime | MidiFilter.AF | MidiFilter.Program | MidiFilter.PitchBend | MidiFilter.SystemCommon);
+ _data = new byte[1024];
+ return true;
+ }
+ catch (Exception e)
+ {
+ #if VRC_CLIENT
+ VRC.Core.Logger.LogError($"Error opening Default Device: {e.Message}");
+ #else
+ Debug.Log($"Error opening Default Device: {e.Message}");
+ #endif
+ return false;
+ }
+ }
+
+ public void Close()
+ {
+ if (_input != null)
+ {
+ _input.Close();
+ }
+ }
+
+ public void Update()
+ {
+ if (_input == null) return;
+
+ if (_input.HasData)
+ {
+ // Portmidi reports 4 bytes per event but the buffer has 8 bytes so we multiply count by 2
+ int count = (_input.Read(_data, 0, _data.Length)) * 2;
+ for (int i = 0; i < count; i+=8)
+ {
+ ConvertAndSend(_data[i], _data[i + 1], _data[i + 2]);
+ }
+ }
+ }
+
+ public IEnumerable<string> GetDeviceNames()
+ {
+ return MidiDeviceManager.AllDevices.Select(d => d.Name);
+ }
+
+ private void ConvertAndSend(byte status, byte data1, byte data2)
+ {
+ var command = status & 0xF0; // mask off all but top 4 bits
+
+ if (command >= 0x80 && command <= 0xE0) {
+ // it's a voice message
+ // find the channel by masking off all but the low 4 bits
+ var channel = status & 0x0F;
+
+ if (command == VRCMidiHandler.STATUS_NOTE_ON || command == VRCMidiHandler.STATUS_NOTE_OFF || command == VRCMidiHandler.STATUS_CONTROL_CHANGE)
+ {
+ OnMidiVoiceMessage?.Invoke(this, new MidiVoiceEventArgs(command, channel, data1, data2));
+ }
+ else
+ {
+#if VRC_CLIENT
+ VRC.Core.Logger.Log($"command:{command} channel:{channel} data1:{data1} data2:{data2}");
+#else
+ Debug.Log($"command:{command} channel:{channel} data1:{data1} data2:{data2}");
+#endif
+ }
+
+ }
+ else
+ {
+ // it's a system message, ignore for now
+ OnMidiRawMessage?.Invoke(this, new MidiRawEventArgs(status, data1, data2));
+ }
+ }
+
+ public string Name => _info.Name;
+ public event MidiVoiceMessageDelegate OnMidiVoiceMessage;
+ public event MidiRawMessageDelegate OnMidiRawMessage;
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/VRCPortMidi.cs.meta b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/VRCPortMidi.cs.meta
new file mode 100644
index 00000000..156b4665
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/VRCSDK/SDK3/Runtime/Midi/VRCPortMidi.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: fb33369bfa554472b08fc6828450a2c4
+timeCreated: 1607454401 \ No newline at end of file