tags:

views:

1147

answers:

4

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#.

A: 

I couldnt find much either, but like Josh says Carl has definately done quite a bit of work on MIDI so maybe give him an email... he may even reply but he is typically a VB.Net guy so unless someone has ported his stuff to C#?

For some other references that looked promising...

If you are looking for a pure c# solution... have a look at Alvas.Audio. It does look a bit high end for what you are asking but may be of some help.

Also maybe project midi

Mark Pearl
Thanks Mark, and please see my comment to Josh. I'll ask Carl after convering his project to WPF. Sure I'll have a lot of questions then.
gideonrv
A: 

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);
    }
}
DaveyM69
Davey says: "Receive the messages via a delegate...", and that’s exactly my current problem. Does NativeMethods.MidiInProc solve the problem? So simple, a ready made delegate? Thanks, Davey, I'll check it next weekend.
gideonrv
A: 

Yeah, you pass the delegate instance to the midiInOpen function and ALL input messages are passed to it. This has three interesting parameters: wMsg (tells you what type of message has been received), dwParam1 and dwParam1. It's MIM_DATA (0x3C3) messages you're interested in for wMsg. dwParam1 will contain the message packed in an int32. byte0 of this int contains the status byte, byte1 contains the first data byte (if required) and byte2 contains the second data byte (if required). You don't need to worry about running status for MIDI input. dwParam2 contains the timestamp of the incoming message.

MIM_DATA is called back on a separate thread (IIRC) so be careful updating the UI and use InvokeRequired etc...

http://msdn.microsoft.com/en-us/library/dd757284(VS.85).aspx

DaveyM69
A: 

Use the C# Midi Toolkit to receive the midi information from your keyboard (connected to the midi in-port of you audio card). To create audio (music or tones) you need to generate a continues stream of digital audio data. You could start with the C# Synth Toolkit (same author as the midi toolkit) to write your own synth.

But you could also download a free (open source) sequencer program (audacity for instance) and use VST instrument plugins to do the job for you. There are a lot of free plugins available on the web.

obiwanjacobi