//-- Play a note by number (0-127), specifying a velocity value (0-127) public void PlayNote(byte Note, byte Velocity) { byte Msg = 0; Int32 MidiMsg = default(Int32); if (_Engaged == false) { return; } Msg = StuffByte(GetByte(MIDIStatusMessages.NoteOn), _OutputChannel); MidiMsg = StuffInt32(Msg, GetByte(Note + _Transpose), Velocity, 0); try { MIDI.midiOutShortMsg(hMidiOUT, MidiMsg); if (_NoteDuration > 0) { NotesOff no = default(NotesOff); no.Note = GetByte(Note + _Transpose); no.ShutoffTime = DateAndTime.DateAdd(DateInterval.Second, _NoteDuration, DateAndTime.Now); lock (notesToTurnOff) { notesToTurnOff.Add(no); } } } catch (Exception ex) { throw ex; } }
//-- This function opens a MIDI Output port. No callback is needed private Int32 OpenMIDIOutPort(ref int DeviceID, bool Open) { int midiError = 0; if (Open == true) { //midiError = MIDI.midiOutOpen(ref hMidiOUT, DeviceID, VariantType.Null, 0, MIDI.CALLBACK_NULL); midiError = MIDI.midiOutOpen(ref hMidiOUT, DeviceID, (int)VariantType.Null, 0, MIDI.CALLBACK_NULL); if (midiError != MIDI.MMSYSERR_NOERROR) { ThrowMidiException("midiOUT_Open", ref midiError); } } else { if (hMidiOUT != 0) { midiError = MIDI.midiOutClose(hMidiOUT); hMidiOUT = 0; if (midiError != MIDI.MMSYSERR_NOERROR) { ThrowMidiException("midiOUT_Close", ref midiError); } } } return(hMidiOUT); }
//-- Send a controller change by using an Enumeration public void SendControllerChange(MIDIControllers Controller, ref byte Data1, byte Data2 = 0) { int MidiMsg = 0; byte CmdAndChannel = 0; CmdAndChannel = StuffByte(GetByte(MIDIStatusMessages.ControllerChange), OutputChannel); MidiMsg = StuffInt32(CmdAndChannel, GetByte(Controller), Data1, Data2); MIDI.midiOutShortMsg(hMidiOUT, MidiMsg); }
//-- Send MIDI Data by specifying a message from an Enumeration public void SendMessage(MIDIStatusMessages Msg, ref byte Data1, byte Data2 = 0, byte Data3 = 0) { int MidiMsg = 0; byte CmdAndChannel = 0; CmdAndChannel = StuffByte(GetByte(Msg), OutputChannel); MidiMsg = StuffInt32(CmdAndChannel, Data1, Data2, Data3); MIDI.midiOutShortMsg(hMidiOUT, MidiMsg); }
//-- Sends MIDI Data public void Send(byte Channel, byte Status, byte Data1 = 0, byte Data2 = 0, byte Data3 = 0) { int MidiMsg = 0; byte CmdAndChannel = 0; CmdAndChannel = StuffByte(Status, Channel); MidiMsg = StuffInt32(CmdAndChannel, Data1, Data2, Data3); MIDI.midiOutShortMsg(hMidiOUT, MidiMsg); }
//-- This is called by the thread to shut off a note after it has played. // Only called when NoteDuration is set to non-zero protected void ShutoffNoteCallback() { NotesOff no = default(NotesOff); bool there = false; byte Msg = 0; Int32 MidiMsg = default(Int32); while (!(_Engaged == false)) { lock (notesToTurnOff) { if (notesToTurnOff.Count > 0) { no = (NotesOff)notesToTurnOff[0]; there = true; } else { there = false; } } if (there) { while (!(DateAndTime.Now >= no.ShutoffTime)) { System.Threading.Thread.Sleep(1); if (_Engaged == false) { notesToTurnOff.Clear(); ev.Set(); return; } } Msg = StuffByte(GetByte(MIDIStatusMessages.NoteOff), _OutputChannel); MidiMsg = StuffInt32(Msg, no.Note, 64, 0); try { MIDI.midiOutShortMsg(hMidiOUT, MidiMsg); } catch (Exception ex) { break; // TODO: might not be correct. Was : Exit Do } lock (notesToTurnOff) { notesToTurnOff.RemoveAt(0); } } System.Threading.Thread.Sleep(1); } notesToTurnOff.Clear(); ev.Set(); }
//-- This is a private function to open and close a MIDI Input port. private Int32 OpenMIDIInPort(ref int DeviceID, bool Open) { int midiError = 0; if (Open == true) { //-- This call opens the MIDI port using a callback function (MidiInProc) midiError = MIDI.midiInOpen(ref hMidiIN, DeviceID, dlgMIDIIn, 0, MIDI.CALLBACK_FUNCTION); if (midiError != MIDI.MMSYSERR_NOERROR) { ThrowMidiException("midiIN_Open", ref midiError); } else { midiError = MIDI.midiInStart(hMidiIN); if (midiError != MIDI.MMSYSERR_NOERROR) { ThrowMidiException("midiIN_Start", ref midiError); } } } else { if (hMidiIN != 0) { midiError = MIDI.midiInStop(hMidiIN); if (midiError != MIDI.MMSYSERR_NOERROR) { ThrowMidiException("midiIN_Start", ref midiError); } else { midiError = MIDI.midiInClose(hMidiIN); if (midiError != MIDI.MMSYSERR_NOERROR) { ThrowMidiException("midiIN_Close", ref midiError); } else { hMidiIN = 0; } } } } return(hMidiIN); }
//-- Turn off all notes currently playing public void AllNotesOff() { byte Msg = 0; Int32 MidiMsg = default(Int32); if (_Engaged == false) { return; } try { Msg = StuffByte(GetByte(MIDIStatusMessages.ChannelModeMessage), _OutputChannel); MidiMsg = StuffInt32(Msg, 0x7b, 0, 0); MIDI.midiOutShortMsg(hMidiOUT, MidiMsg); } catch (Exception ex) { throw ex; } }
//-- Return a list of output device names as a string array public static string[] OutDeviceNames() { int num = 0; int i = 0; MIDI.MIDIOUTCAPS Caps = default(MIDI.MIDIOUTCAPS); string[] names = null; num = MIDI.midiOutGetNumDevs(); if (num > 0) { names = new string[num]; for (i = 0; i <= num - 1; i++) { MIDI.midiOutGetDevCaps(i, ref Caps, Strings.Len(Caps)); names[i] = Caps.szPname; } } return(names); }
//-- Stop playing a note by passing the note number (0-127) and optionally velocity (0-127) public void StopNote(byte Note, byte Velocity = 64) { byte Msg = 0; Int32 MidiMsg = default(Int32); if (_Engaged == false) { return; } Msg = StuffByte(GetByte(MIDIStatusMessages.NoteOff), _OutputChannel); MidiMsg = StuffInt32(Msg, GetByte(Note + _Transpose), Velocity, 0); try { MIDI.midiOutShortMsg(hMidiOUT, MidiMsg); } catch (Exception ex) { throw ex; } }
//-- This is the Input proc that gets called when MIDI Data is received from the // input device. protected void MidiInProc(Int32 MidiInHandle, Int32 NewMsg, Int32 Instance, Int32 wParam, Int32 lParam) { byte chan = 0; byte Msg = 0; byte Status = 0; byte Data1 = 0; byte Data2 = 0; byte Data3 = 0; int MidiStatus = 0; bool Cancel = false; //-- We're only interested in MIDI Data messages if (NewMsg == MIDI.MM_MIM_DATA) { //-- Parse the data into a message byte and three data bytes SplitInt32(wParam, ref Msg, ref Data1, ref Data2, ref Data3); //-- The message byte is a combination of the channel and a Status byte. Parse SplitByte(Msg, ref chan, ref Status); Trace.WriteLine(" In: " + chan.ToString() + " " + Status.ToString() + " " + Data1.ToString() + " " + Data2.ToString() + " " + Data3.ToString()); //-- Is this coming in on our Input Channel? if (chan == _InputChannel) { //-- What MIDI Command was sent? switch (Status) { case MIDI.NOTE_ON: case MIDI.NOTE_OFF: //-- Transpose the note Int32 DTest = Convert.ToInt32(Data1) + Transpose; if (DTest < 0) { DTest = 0; } else if (DTest > 127) { DTest = 127; } Data1 = GetByte(DTest); //-- No output device? if (hMidiOUT == 0) { //-- Fire the appropriate event if (Status == MIDI.NOTE_ON) { if (NoteOn != null) { NoteOn(ref chan, ref Data1, ref Data2, ref Cancel); } } else if (Status == MIDI.NOTE_OFF) { if (NoteOff != null) { NoteOff(ref chan, ref Data1, ref Data2, ref Cancel); } } } else { //-- Change the channel to the output channel chan = _OutputChannel; } break; case MIDI.CHANNEL_PRESSURE: //-- Channel Pressure if (FilterAfterTouch == true) { //-- Cancel this data Cancel = true; } else if (hMidiOUT == 0) { //-- No output device. Fire the Receive event if (Receive != null) { Receive(ref chan, ref Status, ref Data1, ref Data2, ref Data3); } } else { //-- Change the channel to the output channel chan = _OutputChannel; } break; default: if (hMidiOUT == 0) { //-- No output device. Fire the Receive event if (Receive != null) { Receive(ref chan, ref Status, ref Data1, ref Data2, ref Data3); } } else { //-- Change the channel to the output channel chan = _OutputChannel; } break; } } //-- Prepare the message NewMsg = StuffByte(Status, chan); lParam = StuffInt32(Convert.ToByte(NewMsg), Data1, Data2, Data3); //-- The programmer can set Cancel to true to cancel this note. if (Cancel == false) { if (hMidiOUT != 0) { //-- We have an output device! Trace.WriteLine("Out: " + chan.ToString() + " " + Status.ToString() + " " + Data1.ToString() + " " + Data2.ToString() + " " + Data3.ToString() + " lParam=" + lParam.ToString()); //-- Send the MIDI data out the output device MIDI.midiOutShortMsg(hMidiOUT, lParam); } } } }
//-- Code to get the last MIDI message and throw as an exception private void ThrowMidiException(string InFunct, ref Int32 MMErr) { string Msg = Strings.Space(255); if (Strings.InStr(1, InFunct, "out", CompareMethod.Text) == 0) { MIDI.midiInGetErrorText(MMErr, Msg, 255); } else { MIDI.midiOutGetErrorText(MMErr, Msg, 255); } Msg = InFunct + Constants.vbCrLf + Msg + Constants.vbCrLf; switch (MMErr) { case MIDI.MMSYSERR_NOERROR: Msg = Msg + "no error"; break; case MIDI.MMSYSERR_ERROR: Msg = Msg + "unspecified error"; break; case MIDI.MMSYSERR_BADDEVICEID: Msg = Msg + "device ID out of range"; break; case MIDI.MMSYSERR_NOTENABLED: Msg = Msg + "driver failed enable"; break; case MIDI.MMSYSERR_ALLOCATED: Msg = Msg + "device already allocated"; break; case MIDI.MMSYSERR_INVALHANDLE: Msg = Msg + "device handle is invalid"; break; case MIDI.MMSYSERR_NODRIVER: Msg = Msg + "no device driver present"; break; case MIDI.MMSYSERR_NOMEM: Msg = Msg + "memory allocation error"; break; case MIDI.MMSYSERR_NOTSUPPORTED: Msg = Msg + "function isn't supported"; break; case MIDI.MMSYSERR_BADERRNUM: Msg = Msg + "error value out of range"; break; case MIDI.MMSYSERR_INVALFLAG: Msg = Msg + "invalid flag passed"; break; case MIDI.MMSYSERR_INVALPARAM: Msg = Msg + "invalid parameter passed"; break; case MIDI.MMSYSERR_HANDLEBUSY: Msg = Msg + "handle being used simultaneously on another thread (eg callback)"; break; case MIDI.MMSYSERR_INVALIDALIAS: Msg = Msg + "Specified alias not found in WIN.INI"; break; //case MIDI.MMSYSERR_LASTERROR: // Msg = Msg + "last error in range"; // break; } throw new Exception(Msg); }