*
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