diff --git a/Assets/Scripts/NativeMethods.cs b/Assets/Scripts/NativeMethods.cs index a3b0c5d..257733f 100644 --- a/Assets/Scripts/NativeMethods.cs +++ b/Assets/Scripts/NativeMethods.cs @@ -55,6 +55,18 @@ namespace PortMidi [DllImport(NativeLibrary, EntryPoint = "Pm_Abort", CallingConvention = CallingConvention.Cdecl)] private static extern PmError Pm_Abort(IntPtr stream); + [DllImport(NativeLibrary, EntryPoint = "Pm_Poll", CallingConvention = CallingConvention.Cdecl)] + private static extern PmError Pm_Poll(IntPtr stream); + + [DllImport(NativeLibrary, EntryPoint = "Pm_Synchronize", CallingConvention = CallingConvention.Cdecl)] + private static extern PmError Pm_Synchronize(IntPtr stream); + + [DllImport(NativeLibrary, EntryPoint = "Pm_SetFilter", CallingConvention = CallingConvention.Cdecl)] + private static extern PmError Pm_SetFilter(IntPtr stream, int filters); + + [DllImport(NativeLibrary, EntryPoint = "Pm_Read", CallingConvention = CallingConvention.Cdecl)] + private static extern int Pm_Read(IntPtr stream, PmEvent* e, int length); + [DllImport(NativeLibrary, EntryPoint = "Pt_Time", CallingConvention = CallingConvention.Cdecl)] private static extern int Pt_Time(); @@ -262,6 +274,100 @@ namespace PortMidi } } + // Reads from the input stream, the max number events to be read are + // determined by max. + public static Event[] Read(Stream stream, int max) + { + Event[] events = new Event[0]; + if (max > stream.bufferSize) throw new Exception("Out of bounds"); + PmEvent[] pm_events = new PmEvent[max]; + fixed (PmEvent* p = &pm_events[0]) + { + int numEvents = Pm_Read(stream.pmStream, p, max); + events = new Event[numEvents]; + + for (int i = 0; i < numEvents; i++) + { + Event e = new Event(); + e.Timestamp = pm_events[i].timestamp; + e.Status = (int)(pm_events[i].message) & 0xFF; + e.Data1 = (int)(pm_events[i].message >> 8) & 0xFF; + e.Data2 = (int)(pm_events[i].message >> 16) & 0xFF; + events[i] = e; + } + } + return events; + } + + // ReadSysExBytes reads 4*max sysex bytes from the input stream. + public static byte[] ReadSysExBytes(Stream stream, int max) + { + byte[] msg = new byte[0]; + if (max > stream.bufferSize) throw new Exception("Out of bounds"); + PmEvent[] pm_events = new PmEvent[max]; + fixed (PmEvent* p = &pm_events[0]) + { + int numEvents = Pm_Read(stream.pmStream, p, max); + msg = new byte[4 * numEvents]; + + for (int i = 0; i < numEvents; i++) + { + msg[4 * i + 0] = (byte)(pm_events[i].message & 0xFF); + msg[4 * i + 1] = (byte)((pm_events[i].message >> 8) & 0xFF); + msg[4 * i + 2] = (byte)((pm_events[i].message >> 16) & 0xFF); + msg[4 * i + 3] = (byte)((pm_events[i].message >> 24) & 0xFF); + } + } + return msg; + } + + // Poll reports whether there is input available in the stream. + public static bool Poll(Stream stream) + { + PmError error = Pm_Poll(stream.pmStream); + if (error < 0) + { + throw new Exception(ConvertToError(error)); + } + return error > 0; + } + + // instructs PortMidi to (re)synchronize to the time_proc passed when the stream was opened. + public static bool Synchronize(Stream stream) + { + PmError error = Pm_Synchronize(stream.pmStream); + if (error < 0) + { + throw new Exception(ConvertToError(error)); + } + return error > 0; + } + + // sets filters on an open input stream to drop selected input types. By default, only active sensing messages are filtered + public static void SetFilter(Stream stream, PortMidiFilter[] filters) + { + if (filters.Length == 0) + { + PmError err = Pm_SetFilter(stream.pmStream, (int)PortMidiFilter.PM_FILT_ACTIVE); + if (err != 0) + { + throw new Exception(ConvertToError(err)); + } + } + + int filtersMask = 0; + foreach (PortMidiFilter filter in filters) + { + filtersMask |= (int)filter; + } + + PmError error = Pm_SetFilter(stream.pmStream, (int)PortMidiFilter.PM_FILT_ACTIVE); + if (error != 0) + { + throw new Exception(ConvertToError(error)); + } + } + public static byte[] StringToByteArrayFastest(string hex) { if (hex.Length % 2 == 1) diff --git a/Assets/Scripts/PortMidiConst.cs b/Assets/Scripts/PortMidiConst.cs new file mode 100644 index 0000000..6b2f4af --- /dev/null +++ b/Assets/Scripts/PortMidiConst.cs @@ -0,0 +1,49 @@ +namespace PortMidi +{ + // Filter bit-mask definitions + public enum PortMidiFilter + { + // filter active sensing messages (0xFE) + PM_FILT_ACTIVE = (1 << 0x0E), + // filter system exclusive messages (0xF0) + PM_FILT_SYSEX = (1 << 0x00), + // filter MIDI clock message (0xF8) + PM_FILT_CLOCK = (1 << 0x08), + // filter play messages (start 0xFA, stop 0xFC, continue 0xFB) + PM_FILT_PLAY = ((1 << 0x0A) | (1 << 0x0C) | (1 << 0x0B)), + // filter tick messages (0xF9) + PM_FILT_TICK = (1 << 0x09), + // filter undefined FD messages + PM_FILT_FD = (1 << 0x0D), + // filter undefined real-time messages + PM_FILT_UNDEFINED = PM_FILT_FD, + // filter reset messages (0xFF) + PM_FILT_RESET = (1 << 0x0F), + // filter all real-time messages + PM_FILT_REALTIME = (PM_FILT_ACTIVE | PM_FILT_SYSEX | PM_FILT_CLOCK | PM_FILT_PLAY | PM_FILT_UNDEFINED | PM_FILT_RESET | PM_FILT_TICK), + // filter note-on and note-off (0x90-0x9F and 0x80-0x8F + PM_FILT_NOTE = ((1 << 0x19) | (1 << 0x18)), + // filter channel aftertouch (most midi controllers use this) (0xD0-0xDF) + PM_FILT_CHANNEL_AFTERTOUCH = (1 << 0x1D), + // per-note aftertouch (0xA0-0xAF) + PM_FILT_POLY_AFTERTOUCH = (1 << 0x1A), + // filter both channel and poly aftertouch + PM_FILT_AFTERTOUCH = (PM_FILT_CHANNEL_AFTERTOUCH | PM_FILT_POLY_AFTERTOUCH), + // Program changes (0xC0-0xCF) + PM_FILT_PROGRAM = (1 << 0x1C), + // Control Changes (CC's) (0xB0-0xBF) + PM_FILT_CONTROL = (1 << 0x1B), + // Pitch Bender (0xE0-0xEF) + PM_FILT_PITCHBEND = (1 << 0x1E), + // MIDI Time Code (0xF1) + PM_FILT_MTC = (1 << 0x01), + // Song Position (0xF2) + PM_FILT_SONG_POSITION = (1 << 0x02), + // Song Select (0xF3) + PM_FILT_SONG_SELECT = (1 << 0x03), + // Tuning request (0xF6) + PM_FILT_TUNE = (1 << 0x06), + // All System Common messages (mtc, song position, song select, tune request) + PM_FILT_SYSTEMCOMMON = (PM_FILT_MTC | PM_FILT_SONG_POSITION | PM_FILT_SONG_SELECT | PM_FILT_TUNE), + } +}