*
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
/*.sln
|
||||
/bin
|
||||
/obj
|
||||
/.vs
|
||||
@@ -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<MIDIINCAPS>());
|
||||
}
|
||||
|
||||
|
||||
[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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Message> _messageQueue = new BlockingCollection<Message>();
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
+35
@@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user