From f2b9d7c0db7dd666a36ce7d212109462d720b559 Mon Sep 17 00:00:00 2001 From: l-proger Date: Tue, 13 May 2025 00:34:26 +0300 Subject: [PATCH] * --- .gitignore | 4 + MidiApiWindows.cs | 163 +++++++++++++++++++++++++++++++++++++ MidiChannel.cs | 26 ++++++ MidiControl.cs | 28 +++++++ MidiDevice.cs | 19 +++++ MidiDeviceException.cs | 13 +++ MidiInputDevice.cs | 135 ++++++++++++++++++++++++++++++ MidiInstrument.cs | 154 +++++++++++++++++++++++++++++++++++ MidiNote.cs | 35 ++++++++ MidiRealTimeMessageType.cs | 17 ++++ MidiShortMessage.cs | 106 ++++++++++++++++++++++++ RushMidiLibrary.csproj | 7 ++ 12 files changed, 707 insertions(+) create mode 100644 .gitignore create mode 100644 MidiApiWindows.cs create mode 100644 MidiChannel.cs create mode 100644 MidiControl.cs create mode 100644 MidiDevice.cs create mode 100644 MidiDeviceException.cs create mode 100644 MidiInputDevice.cs create mode 100644 MidiInstrument.cs create mode 100644 MidiNote.cs create mode 100644 MidiRealTimeMessageType.cs create mode 100644 MidiShortMessage.cs create mode 100644 RushMidiLibrary.csproj diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1e1639d --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/*.sln +/bin +/obj +/.vs diff --git a/MidiApiWindows.cs b/MidiApiWindows.cs new file mode 100644 index 0000000..fe54f6d --- /dev/null +++ b/MidiApiWindows.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace RushMidiLibrary { + public static class MidiApiWindows { + [DllImport("winmm.dll")] + public static extern uint midiOutGetNumDevs(); + [DllImport("winmm.dll")] + public static extern uint midiInGetNumDevs(); + + public enum MMRESULT : uint { + MMSYSERR_BASE = 0, + MMSYSERR_NOERROR = MMSYSERR_BASE + 0, + MMSYSERR_ERROR = MMSYSERR_BASE + 1, + MMSYSERR_BADDEVICEID = MMSYSERR_BASE + 2, + MMSYSERR_NOTENABLED = MMSYSERR_BASE + 3, + MMSYSERR_ALLOCATED = MMSYSERR_BASE + 4, + MMSYSERR_INVALHANDLE = MMSYSERR_BASE + 5, + MMSYSERR_NODRIVER = MMSYSERR_BASE + 6, + MMSYSERR_NOMEM = MMSYSERR_BASE + 7, + MMSYSERR_NOTSUPPORTED = MMSYSERR_BASE + 8, + MMSYSERR_BADERRNUM = MMSYSERR_BASE + 9, + MMSYSERR_INVALFLAG = MMSYSERR_BASE + 10, + MMSYSERR_INVALPARAM = MMSYSERR_BASE + 11, + MMSYSERR_HANDLEBUSY = MMSYSERR_BASE + 12, + MMSYSERR_INVALIDALIAS = MMSYSERR_BASE + 13, + MMSYSERR_BADDB = MMSYSERR_BASE + 14, + MMSYSERR_KEYNOTFOUND = MMSYSERR_BASE + 15, + MMSYSERR_READERROR = MMSYSERR_BASE + 16, + MMSYSERR_WRITEERROR = MMSYSERR_BASE + 17, + MMSYSERR_DELETEERROR = MMSYSERR_BASE + 18, + MMSYSERR_VALNOTFOUND = MMSYSERR_BASE + 19, + MMSYSERR_NODRIVERCB = MMSYSERR_BASE + 20, + MMSYSERR_MOREDATA = MMSYSERR_BASE + 21, + MMSYSERR_LASTERROR = MMSYSERR_BASE + 21, + + + MIDIERR_BASE = 64, + MIDIERR_UNPREPARED = MIDIERR_BASE + 0, + MIDIERR_STILLPLAYING = MIDIERR_BASE + 1, + MIDIERR_NOMAP = MIDIERR_BASE + 2, + MIDIERR_NOTREADY = MIDIERR_BASE + 3, + MIDIERR_NODEVICE = MIDIERR_BASE + 4, + MIDIERR_INVALIDSETUP = MIDIERR_BASE + 5, + MIDIERR_BADOPENMODE = MIDIERR_BASE + 6, + MIDIERR_DONT_CONTINUE = MIDIERR_BASE + 7, + MIDIERR_LASTERROR = MIDIERR_BASE + 7 + } + + + [DllImport("winmm.dll")] + public static extern MMRESULT midiInGetErrorText(MMRESULT errCode, StringBuilder message, uint sizeOfMessage); + + public static MMRESULT midiInGetErrorText(MMRESULT mmrError, StringBuilder lpText) { + return midiInGetErrorText(mmrError, lpText, (UInt32)lpText.Capacity); + } + + + + [DllImport("winmm.dll")] + private static extern MMRESULT midiInGetDevCaps(UIntPtr uDeviceID, out MIDIINCAPS caps, uint sizeOfMidiInCaps); + public static MMRESULT midiInGetDevCaps(UIntPtr uDeviceID, out MIDIINCAPS caps) { + return midiInGetDevCaps(uDeviceID, out caps, (uint)Marshal.SizeOf()); + } + + + [DllImport("winmm.dll")] + public static extern MMRESULT midiInOpen(out HMIDIIN phmi, UIntPtr uDeviceID, MidiInProc dwCallback, UIntPtr dwInstance, MidiOpenFlags fdwOpen); + + public static MMRESULT midiInOpen(out HMIDIIN phmi, UIntPtr uDeviceID, MidiInProc dwCallback, UIntPtr dwInstance) { + return midiInOpen(out phmi, uDeviceID, dwCallback, dwInstance, dwCallback == null ? MidiOpenFlags.CALLBACK_NULL : MidiOpenFlags.CALLBACK_FUNCTION); + } + + + [DllImport("winmm.dll")] + public static extern MMRESULT midiInClose(HMIDIIN hmi); + [DllImport("winmm.dll")] + public static extern MMRESULT midiInStart(HMIDIIN hmi); + [DllImport("winmm.dll")] + public static extern MMRESULT midiInStop(HMIDIIN hmi); + [DllImport("winmm.dll")] + public static extern MMRESULT midiInReset(HMIDIIN hmi); + [DllImport("winmm.dll")] + public static extern MMRESULT midiInAddBuffer(HMIDIIN hmi, ref MIDIHDR lpMidiHeader, uint wSize); + [DllImport("winmm.dll")] + public static extern MMRESULT midiInPrepareHeader(HMIDIIN handle, ref MIDIHDR lpMidiHeader, uint wSize); + [DllImport("winmm.dll")] + public static extern MMRESULT midiInUnprepareHeader(HMIDIIN handle, ref MIDIHDR lpMidiHeader, uint wSize); + + + [DllImport("Kernel32.dll", CallingConvention = CallingConvention.Winapi)] + public static extern void GetSystemTimePreciseAsFileTime(out long filetime); + + + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate void MidiInProc(HMIDIIN handle, MidiInMessage wMsg, UIntPtr dwInstance, UIntPtr dwParam1, UIntPtr dwParam2); + + + public struct HMIDIIN { + public IntPtr handle; + } + + public struct HMIDIOUT { + public IntPtr handle; + } + + public enum MidiInMessage : uint { + MIM_OPEN = 0x3C1, + MIM_CLOSE = 0x3C2, + MIM_DATA = 0x3C3, + MIM_LONGDATA = 0x3C4, + MIM_ERROR = 0x3C5, + MIM_LONGERROR = 0x3C6, + MIM_MOREDATA = 0x3CC + } + + public enum MidiOpenFlags : uint { + CALLBACK_TYPEMASK = 0x70000, + CALLBACK_NULL = 0x00000, + CALLBACK_WINDOW = 0x10000, + CALLBACK_TASK = 0x20000, + CALLBACK_FUNCTION = 0x30000, + CALLBACK_THREAD = CALLBACK_TASK, + CALLBACK_EVENT = 0x50000, + MIDI_IO_STATUS = 0x00020 + } + + + [StructLayout(LayoutKind.Sequential)] + public struct MIDIINCAPS { + public ushort wMid; + public ushort wPid; + public uint vDriverVersion; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + public string szPname; + public uint dwSupport; + } + + [StructLayout(LayoutKind.Sequential)] + public struct MIDIHDR { + public UIntPtr lpData; + public uint dwBufferLength; + public uint dwBytesRecorded; + public UIntPtr dwUser; + public uint dwFlags; + public UIntPtr lpNext; + public UIntPtr reserved; + public uint dwOffset; + public UIntPtr dwReserved0; + public UIntPtr dwReserved1; + public UIntPtr dwReserved2; + public UIntPtr dwReserved3; + public UIntPtr dwReserved4; + public UIntPtr dwReserved5; + public UIntPtr dwReserved6; + public UIntPtr dwReserved7; + } + } +} diff --git a/MidiChannel.cs b/MidiChannel.cs new file mode 100644 index 0000000..8afd50c --- /dev/null +++ b/MidiChannel.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RushMidiLibrary { + public enum MidiChannel { + Channel1 = 0, + Channel2 = 1, + Channel3 = 2, + Channel4 = 3, + Channel5 = 4, + Channel6 = 5, + Channel7 = 6, + Channel8 = 7, + Channel9 = 8, + Channel10 = 9, + Channel11 = 10, + Channel12 = 11, + Channel13 = 12, + Channel14 = 13, + Channel15 = 14, + Channel16 = 15 + }; +} diff --git a/MidiControl.cs b/MidiControl.cs new file mode 100644 index 0000000..bc12a77 --- /dev/null +++ b/MidiControl.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RushMidiLibrary { + public enum MidiControl { + ModulationWheel = 1, + DataEntryMSB = 6, + Volume = 7, + Pan = 10, + Expression = 11, + DataEntryLSB = 38, + SustainPedal = 64, + ReverbLevel = 91, + TremoloLevel = 92, + ChorusLevel = 93, + CelesteLevel = 94, + PhaserLevel = 95, + NonRegisteredParameterLSB = 98, + NonRegisteredParameterMSB = 99, + RegisteredParameterNumberLSB = 100, + RegisteredParameterNumberMSB = 101, + AllControllersOff = 121, + AllNotesOff = 123 + } +} diff --git a/MidiDevice.cs b/MidiDevice.cs new file mode 100644 index 0000000..a942554 --- /dev/null +++ b/MidiDevice.cs @@ -0,0 +1,19 @@ +using System.Runtime.InteropServices; +using System.Text; + +namespace RushMidiLibrary { + public class MidiDevice { + + + protected void CheckMMResult(MidiApiWindows.MMRESULT errorCode) { + if(errorCode != MidiApiWindows.MMRESULT.MMSYSERR_NOERROR) { + StringBuilder errorMessage = new StringBuilder(128); + errorCode = MidiApiWindows.midiInGetErrorText(errorCode, errorMessage); + if (errorCode != MidiApiWindows.MMRESULT.MMSYSERR_NOERROR) { + throw new MidiDeviceException("Unknown error"); + } + throw new MidiDeviceException(errorMessage.ToString()); + } + } + } +} diff --git a/MidiDeviceException.cs b/MidiDeviceException.cs new file mode 100644 index 0000000..fa0a1a5 --- /dev/null +++ b/MidiDeviceException.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RushMidiLibrary { + public class MidiDeviceException : Exception { + public MidiDeviceException(string message) : base(message) { + + } + } +} diff --git a/MidiInputDevice.cs b/MidiInputDevice.cs new file mode 100644 index 0000000..5c4c7df --- /dev/null +++ b/MidiInputDevice.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Concurrent; +using System.Threading; +using static RushMidiLibrary.MidiApiWindows; + +namespace RushMidiLibrary { + public class MidiInputDevice : MidiDevice { + private MidiApiWindows.MidiInProc _inputCallback; + private UIntPtr _deviceId; + private MidiApiWindows.HMIDIIN _deviceHandle; + private Thread _messageProcessingThread; + private bool _messageProcessingThreadExitRequest = false; + + readonly BlockingCollection _messageQueue = new BlockingCollection(); + + private struct Message { + public MidiApiWindows.HMIDIIN hMidiIn; + public MidiApiWindows.MidiInMessage wMsg; + public UIntPtr dwInstance; + public UIntPtr dwParam1; + public UIntPtr dwParam2; + } + + public MidiInputDevice(UIntPtr deviceId, MidiApiWindows.MIDIINCAPS caps) { + _deviceId = deviceId; + } + + public static MidiInputDevice[] Enumerate() { + var devs = MidiApiWindows.midiInGetNumDevs(); + MidiInputDevice[] result = new MidiInputDevice[devs]; + for (uint deviceId = 0; deviceId < devs; deviceId++) { + MidiApiWindows.MIDIINCAPS caps; + MidiApiWindows.midiInGetDevCaps((UIntPtr)deviceId, out caps); + result[deviceId] = new MidiInputDevice((UIntPtr)deviceId, caps); + } + return result; + } + + public void Open() { + _inputCallback = this.InputCallback; + + BeginMessageProcessing(); + try { + CheckMMResult(MidiApiWindows.midiInOpen(out _deviceHandle, _deviceId, _inputCallback, (UIntPtr)0)); + } + catch (Exception) { + EndMessageProcessing(); + throw; + } + + } + + public void Start() { + CheckMMResult(MidiApiWindows.midiInStart(_deviceHandle)); + } + + private void InputCallback(MidiApiWindows.HMIDIIN hMidiIn, MidiApiWindows.MidiInMessage wMsg, UIntPtr dwInstance, UIntPtr dwParam1, UIntPtr dwParam2) { + Message message = new Message(); + message.hMidiIn = hMidiIn; + message.wMsg = wMsg; + message.dwInstance = dwInstance; + message.dwParam1 = dwParam1; + message.dwParam2 = dwParam2; + _messageQueue.Add(message); + } + + + private void BeginMessageProcessing() { + _messageProcessingThreadExitRequest = false; + _messageProcessingThread = new Thread(MessageProcessingThreadHandler); + _messageProcessingThread.Start(); + } + + private void MessageProcessingThreadHandler() { + while (!_messageProcessingThreadExitRequest) { + Message message; + if (_messageQueue.TryTake(out message, 100)) { + HandleMessage(message); + } + } + } + + private void EndMessageProcessing() { + _messageProcessingThreadExitRequest = true; + _messageProcessingThread.Join(); + } + + private void HandleMessage(Message message) { + + switch (message.wMsg) { + case MidiInMessage.MIM_DATA: + HandleMessageData(new MidiShortMessage(message.dwParam1, message.dwParam2)); + break; + case MidiInMessage.MIM_LONGDATA: + HandleMessageLongData(message.dwParam1, message.dwParam2); + break; + default: + break; + } + } + + private void HandleMessageData(MidiShortMessage message) { + if (message.IsNoteOn) { + MidiChannel channel; + int noteId; + int velocity; + uint timestamp; + message.GetNoteOn(out channel, out noteId, out velocity, out timestamp); + + var note = new MidiNote(noteId); + + Console.WriteLine($"Received short message NoteOn: Name={note.Name} Octave={note.OctaveName} Channel={channel.ToString()}, NoteId={noteId}, Velocity={velocity}, Timestapm={timestamp}"); + + } else if (message.IsNoteOff) { + MidiChannel channel; + int noteId; + int velocity; + uint timestamp; + message.GetNoteOff(out channel, out noteId, out velocity, out timestamp); + + var note = new MidiNote(noteId); + + Console.WriteLine($"Received short message NoteOff: Name={note.Name} Octave={note.OctaveName} Channel={channel.ToString()}, NoteId={noteId}, Velocity={velocity}, Timestapm={timestamp}"); + + } else { + Console.WriteLine($"Received short message"); + } + + } + + private void HandleMessageLongData(UIntPtr dwParam1, UIntPtr dwParam2) { + Console.WriteLine($"Received long message"); + } + } +} diff --git a/MidiInstrument.cs b/MidiInstrument.cs new file mode 100644 index 0000000..133fe8e --- /dev/null +++ b/MidiInstrument.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RushMidiLibrary { + public enum MidiInstrument { + AcousticGrandPiano = 0, + BrightAcousticPiano = 1, + ElectricGrandPiano = 2, + HonkyTonkPiano = 3, + ElectricPiano1 = 4, + ElectricPiano2 = 5, + Harpsichord = 6, + Clavinet = 7, + + + Celesta = 8, + Glockenspiel = 9, + MusicBox = 10, + Vibraphone = 11, + Marimba = 12, + Xylophone = 13, + TubularBells = 14, + Dulcimer = 15, + + DrawbarOrgan = 16, + PercussiveOrgan = 17, + RockOrgan = 18, + ChurchOrgan = 19, + ReedOrgan = 20, + Accordion = 21, + Harmonica = 22, + TangoAccordion = 23, + + AcousticGuitarNylon = 24, + AcousticGuitarSteel = 25, + ElectricGuitarJazz = 26, + ElectricGuitarClean = 27, + ElectricGuitarMuted = 28, + OverdrivenGuitar = 29, + DistortionGuitar = 30, + GuitarHarmonics = 31, + + AcousticBass = 32, + ElectricBassFinger = 33, + ElectricBassPick = 34, + FretlessBass = 35, + SlapBass1 = 36, + SlapBass2 = 37, + SynthBass1 = 38, + SynthBass2 = 39, + + Violin = 40, + Viola = 41, + Cello = 42, + Contrabass = 43, + TremoloStrings = 44, + PizzicatoStrings = 45, + OrchestralHarp = 46, + Timpani = 47, + + StringEnsemble1 = 48, + StringEnsemble2 = 49, + SynthStrings1 = 50, + SynthStrings2 = 51, + ChoirAahs = 52, + VoiceOohs = 53, + SynthVoice = 54, + OrchestraHit = 55, + + Trumpet = 56, + Trombone = 57, + Tuba = 58, + MutedTrumpet = 59, + FrenchHorn = 60, + BrassSection = 61, + SynthBrass1 = 62, + SynthBrass2 = 63, + + SopranoSax = 64, + AltoSax = 65, + TenorSax = 66, + BaritoneSax = 67, + Oboe = 68, + EnglishHorn = 69, + Bassoon = 70, + Clarinet = 71, + + Piccolo = 72, + Flute = 73, + Recorder = 74, + PanFlute = 75, + BlownBottle = 76, + Shakuhachi = 77, + Whistle = 78, + Ocarina = 79, + + Lead1Square = 80, + Lead2Sawtooth = 81, + Lead3Calliope = 82, + Lead4Chiff = 83, + Lead5Charang = 84, + Lead6Voice = 85, + Lead7Fifths = 86, + Lead8BassPlusLead = 87, + + Pad1NewAge = 88, + Pad2Warm = 89, + Pad3Polysynth = 90, + Pad4Choir = 91, + Pad5Bowed = 92, + Pad6Metallic = 93, + Pad7Halo = 94, + Pad8Sweep = 95, + + FX1Rain = 96, + FX2Soundtrack = 97, + FX3Crystal = 98, + FX4Atmosphere = 99, + FX5Brightness = 100, + FX6Goblins = 101, + FX7Echoes = 102, + FX8SciFi = 103, + + Sitar = 104, + Banjo = 105, + Shamisen = 106, + Koto = 107, + Kalimba = 108, + Bagpipe = 109, + Fiddle = 110, + Shanai = 111, + + TinkleBell = 112, + Agogo = 113, + SteelDrums = 114, + Woodblock = 115, + TaikoDrum = 116, + MelodicTom = 117, + SynthDrum = 118, + ReverseCymbal = 119, + + GuitarFretNoise = 120, + BreathNoise = 121, + Seashore = 122, + BirdTweet = 123, + TelephoneRing = 124, + Helicopter = 125, + Applause = 126, + Gunshot = 127 + }; +} diff --git a/MidiNote.cs b/MidiNote.cs new file mode 100644 index 0000000..3041e95 --- /dev/null +++ b/MidiNote.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RushMidiLibrary { + public class MidiNote { + private static string[] _noteNames = new string[] { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" }; + private static string[] _octaveNames = new string[] { "Sub Contra", "Contra", "Great", "Small", "1 Line", "2 Line", "3 Line", "4 Line", "5 Line", "6 Line" }; + public int NoteId { get; init; } + + public MidiNote(int noteId) { + NoteId = noteId; + } + + public string Name { + get { + return _noteNames[NoteId % 12]; + } + } + + public int OctaveIndex { + get { + return NoteId / 12 - 1; + } + } + + public string OctaveName { + get { + return _octaveNames[OctaveIndex]; + } + } + } +} diff --git a/MidiRealTimeMessageType.cs b/MidiRealTimeMessageType.cs new file mode 100644 index 0000000..a71b7d4 --- /dev/null +++ b/MidiRealTimeMessageType.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RushMidiLibrary { + public enum MidiRealTimeMessageType { + TimingClock, + Start, + Continue, + Stop, + Undefined, + ActiveSensing, + SystemReset + } +} diff --git a/MidiShortMessage.cs b/MidiShortMessage.cs new file mode 100644 index 0000000..885e82b --- /dev/null +++ b/MidiShortMessage.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Metrics; +using System.Linq; +using System.Text; +using System.Threading.Channels; +using System.Threading.Tasks; + +namespace RushMidiLibrary { + public struct MidiShortMessage { + public UIntPtr dwParam1; + public UIntPtr dwParam2; + public MidiShortMessage(UIntPtr dwParam1, UIntPtr dwParam2) { + this.dwParam1 = dwParam1; + this.dwParam2 = dwParam2; + } + + public bool IsNoteOn { + get { return ((int)dwParam1 & 0xf0) == 0x90; } + } + + public void GetNoteOn(out MidiChannel channel, out int noteId, out int velocity, out uint timestamp) { + channel = (MidiChannel)((int)dwParam1 & 0x0f); + noteId = ((int)dwParam1 & 0xff00) >> 8; + velocity = ((int)dwParam1 & 0xff0000) >> 16; + timestamp = (uint)dwParam2; + } + + public bool IsNoteOff { + get { return ((int)dwParam1 & 0xf0) == 0x80; } + } + + public void GetNoteOff(out MidiChannel channel, out int noteId, out int velocity, out uint timestamp) { + channel = (MidiChannel)((int)dwParam1 & 0x0f); + noteId = ((int)dwParam1 & 0xff00) >> 8; + velocity = ((int)dwParam1 & 0xff0000) >> 16; + timestamp = (uint)dwParam2; + } + + public bool IsControlChange { + get { return ((int)dwParam1 & 0xf0) == 0xB0; } + } + + public void GetControlChange(out MidiChannel channel, out MidiControl control, out int value, out uint timestamp) { + channel = (MidiChannel)((int)dwParam1 & 0x0f); + control = (MidiControl)(((int)dwParam1 & 0xff00) >> 8); + value = ((int)dwParam1 & 0xff0000) >> 16; + timestamp = (uint)dwParam2; + } + + public bool IsProgramChange { + get { return ((int)dwParam1 & 0xf0) == 0xC0; } + } + + public void DecodeProgramChange(out MidiChannel channel, out MidiInstrument instrument, out uint timestamp) { + channel = (MidiChannel)((int)dwParam1 & 0x0f); + instrument = (MidiInstrument)(((int)dwParam1 & 0xff00) >> 8); + timestamp = (uint)dwParam2; + } + + public bool IsPitchBend { + get { return ((int)dwParam1 & 0xf0) == 0xE0; } + } + + public void GetPitchBend(out MidiChannel channel, out int value, out uint timestamp) { + channel = (MidiChannel)((int)dwParam1 & 0x0f); + value = ((((int)dwParam1 >> 9) & 0x3f80) | (((int)dwParam1 >> 8) & 0x7f)); + timestamp = (uint)dwParam2; + } + + public bool IsRealTime { + get { return ((int)dwParam1 & 0xf8) == 0xF8; } + } + + public void GetRealTime(out MidiRealTimeMessageType msgType, out uint timestamp) { + int messageType = (int)dwParam1 & 0x07; + switch (messageType) { + case 0: + msgType = MidiRealTimeMessageType.TimingClock; + break; + case 2: + msgType = MidiRealTimeMessageType.Start; + break; + case 3: + msgType = MidiRealTimeMessageType.Continue; + break; + case 4: + msgType = MidiRealTimeMessageType.Stop; + break; + case 6: + msgType = MidiRealTimeMessageType.ActiveSensing; + break; + case 7: + msgType = MidiRealTimeMessageType.SystemReset; + break; + case 1: + case 5: + msgType = MidiRealTimeMessageType.Undefined; + break; + default: + throw new ArgumentException("Unknown realtime message"); + } + timestamp = (uint)dwParam2; + } + } +} diff --git a/RushMidiLibrary.csproj b/RushMidiLibrary.csproj new file mode 100644 index 0000000..fff7812 --- /dev/null +++ b/RushMidiLibrary.csproj @@ -0,0 +1,7 @@ + + + + net6.0 + + +