Getting signals from a MIDI port in C#

gideonrv picture gideonrv · Jan 2, 2010 · Viewed 24.9k times · Source

I bought a MIDI keyboard for my birthday. I found a program (MidiPiano) that gets signals from the MIDI input and translates it into music, but I'd rather like to write one myself.

Where can I find documentation for doing such a task? The MIDI protocol is well documented, but not the MIDI ports.

I checked two projects from CodeProject (Project MIDI and C# MIDI Toolkit), but spent many hours without getting close to my goal.

A reference to a project will be fine, but please, only C#.

Answer

DaveyM69 picture DaveyM69 · Jan 2, 2010

You need to wrap all the needed functions listed at http://msdn.microsoft.com/en-us/library/dd757277(VS.85).aspx

It's not too difficult if you're just using short messages, things get a little more complicated if you want to do SysEx or output streaming.

All you need for a basic input Port is to get the valid input IDs (InputCount -1), pass a valid ID to Open, Start the input, receive the messages via a delegate... Stop the input and then finally close it. This is a very rough example of how this could be acheived - you will need to do some checking and be careful that the Port isn't collected before it's closed and the closed callback has happened or you will freeze your system!

Good luck!

namespace MIDI
{
    public class InputPort
    {
        private NativeMethods.MidiInProc midiInProc;
        private IntPtr handle;

        public InputPort()
        {
            midiInProc = new NativeMethods.MidiInProc(MidiProc);
            handle = IntPtr.Zero;
        }

        public static int InputCount
        {
            get { return NativeMethods.midiInGetNumDevs(); }
        }

        public bool Close()
        {
            bool result = NativeMethods.midiInClose(handle) 
                == NativeMethods.MMSYSERR_NOERROR;
            handle = IntPtr.Zero;
            return result;
        }

        public bool Open(int id)
        {
            return NativeMethods.midiInOpen(
                out handle,
                id,
                midiInProc,
                IntPtr.Zero,
                NativeMethods.CALLBACK_FUNCTION)
                    == NativeMethods.MMSYSERR_NOERROR;
        }

        public bool Start()
        {
            return NativeMethods.midiInStart(handle)
                == NativeMethods.MMSYSERR_NOERROR;
        }

        public bool Stop()
        {
            return NativeMethods.midiInStop(handle)
                == NativeMethods.MMSYSERR_NOERROR;
        }

        private void MidiProc(IntPtr hMidiIn,
            int wMsg,
            IntPtr dwInstance,
            int dwParam1,
            int dwParam2)
        {
            // Receive messages here
        }
    }

    internal static class NativeMethods
    {
        internal const int MMSYSERR_NOERROR = 0;
        internal const int CALLBACK_FUNCTION = 0x00030000;

        internal delegate void MidiInProc(
            IntPtr hMidiIn,
            int wMsg,
            IntPtr dwInstance,
            int dwParam1,
            int dwParam2);

        [DllImport("winmm.dll")]
        internal static extern int midiInGetNumDevs();

        [DllImport("winmm.dll")]
        internal static extern int midiInClose(
            IntPtr hMidiIn);

        [DllImport("winmm.dll")]
        internal static extern int midiInOpen(
            out IntPtr lphMidiIn,
            int uDeviceID,
            MidiInProc dwCallback,
            IntPtr dwCallbackInstance,
            int dwFlags);

        [DllImport("winmm.dll")]
        internal static extern int midiInStart(
            IntPtr hMidiIn);

        [DllImport("winmm.dll")]
        internal static extern int midiInStop(
            IntPtr hMidiIn);
    }
}