I have been scouring the net looking for updated information on interfacing a C# applilcation to a MIDI control interface (M-Audio Xponent). The best I could come up with is PureMidi, Midi-dot-net, and MIDI out setter here on codeplex. Unfortunately, I need the raw information, and need to strip out hundreds of lines of code to make it work. I can get the input and output devices fine, but I need to send simple MidiOutshortmsg and MidiOutLongMsg (sysex), and recieve MidiInshortMsgs. None of the examples really touch on long messages, and all color the short into actual deciphered note info that I don't want. The examples are much too complex for my needs. I need something very simple without 10 support files full of nonsence to sift through and strip down. Most everything is also for vc2005 or before, and are in dreadful need up update to VC2010/2012. The Midi toolkit is useless to me. It won't compile,and I can't determine why. I've spent the last week looking, and the 3 examples provided are the best usefull examples I've found to date, but are much too complex as stated. Please help, I'm beyond frustrated. All I need are simple examples to send and recieve raw hex data. I can parse it from there.
Thanks for any assistance you can provide.
midiOutShortMsg is quite simple, midiOutLongMsg is more complex due to the way that the MIDIHDR structure is used.
Receiving SysEx can be tricky as you will need to add and control the receive buffers.
I can help you with this but I don't think I'll have time until around this time tomorrow unfortunately. If you haven't marked this as solved by then I'll dig out some code to assist.
In the meantime, for output you're going to need these functions:
midiOutProc (this will be a delegate in C#)
and these structs
You will find all the definitions for these on MSDN. They are shown in C++. You will need to create PInvoke versions (to call into Winmm.dll) of these in C#. Search your system for MMSystem.h as there are many things in there (such as the values for constants used etc) that will be handy.
Edit: 20 hrs and no other answers, give me 8 to get some sleep (UK) and I'll post some working code and pointers. Do you have a link to the MIDI implementation chart for the device you're trying to communicate with?
Dave, Thanks! Yes! I do have the MIDI command chart for the Xponent, but I will be making this mostly generic. I don't actually have to recieve sysex, but that would be an added benefit.
I only really need to send a single sysex message to put the unit into advanced mode, and another message to put it back to normal.
I've also been looking at an autohotkey example of Midiin and Midiout that supposedly has sysex, but I have yet to see anything in it refering to MidiOutLongMsg. I appriciate your assistance and willingness to assist. Thanks again!
With MIDI In, the procedure is similar. The problem is you have to prepare a MIDIHDR buffer of x size (x is up to you), add it using midiInAddBuffer and that will be returned to you through a MIM_LONGDATA message in a MidiInProc callback - dwParam1 (you will need to cast the IntPtr to int) contains the MIDIHDR (dwParam2 contains the timestamp which you probably don't need). To get a callback, you will need to set CALLBACK_FUNCTION in the dwFlags when using midiInOpen and also call midiInStart.
Checking the dwBytesRecorded (you will need to add a property to expose it) will tell you how much data there actually is in the buffer. The real data can be copied out of the buffer using Marshal.Copy. Don't forget to unprepare the buffer, free the GCHandle and the data pointer! If it is full and the last byte isn't EOX, you will need to quickly add another buffer to get the rest of the sysex data (you can add more than one buffer)!
Once you have all the data and freed everything you can stop and close the input when you're done - one issue though. You may have prepared and added a buffer(s) but received no sysex into it so it's just sitting there. To deal with these you will need to call midiInReset, get the buffers via the callback, unprepare them (and free the GCHandle and data pointer) before calling midiInClose. There is a midiInStop function, this doesn't return unused buffers. I personally call midiInReset, get the buffers in the callback (freeing and unpreparing them) and then call midiInStop to ensure compatibility with all device drivers just in case.
If you get stuck I will happily knock you up some smple code. Based on what I've already given you and this[^] you should be OK though.
Short messages are a doddle. Open, Start, receive MIM_DATA in the callback and extract the bytes from dwParam1, and once you're done Stop (or Reset then Stop) and Close.
Edit: Just checked, calling midiInClose on a MIDI In that still has buffers will not close the device. Instead the function result will be MIDIERR_STILLPLAYING.
I'm not sure I'm that smart, but with a bit more research on the web, I may figure it out. TBO, I need to re-learn C# programming every time I have a project like this to work on. When complete, I'll probably not touch programming in C# until I get another project I want to tackle maby 6-12 months from now. I tend to do most of my programming in AutoHotkey, unless I want something a bit beefier. It's definatly easier to step through studio code than ahk code, as you can't set breakpoins and step into AHK code.
Haven't forgotten this Jeff! I figured that this was worth making into a full blown article so I'm putting in more effort than I would otherwise - fun stuff though
I'll keep you updated - hopefully it'll finish up with a library that will make it a doddle to use all the low level MIDI API and Stream API directly from C# with no effort. It shouldn't take long now, it's nearly finished.
That would be so awesome Dave! There needs to be something better and more updated available like that for the population. I'm sure someone besides myself will benefit from your efforts! Thanks for doing so, and I look forward to the article.
Well work has put a delay on this. Got a huge project on with a deadline of only two weeks so throwing in 12 hour plus days I'm not going to get time to do anything with this until that's over unfortunately.
In the meantime, this is what I have completed so far. All the MIDI In and MIDI Out is working including SysEx
Not everything is commented and handle locking needs some more attention to ensure thread safety. There is no inclusion of the MIDI Stream API yet.
Check out the GeneralInstructions.txt file in the root folder. That should be enough to get you underway.
I managed to get this somewhat working Dave. I'm having issues however with the SystemExclusiveMessageRecieved however. It reports back as Midi.Net.Messages.SystemCommon.Sysex.SystemExclusive, and then hangs the app.
The ShortMessageRecieved works ok, although I would have preffered to have raw data reported rather than transformed. I do believe I can work around that however. Again, Thanks for your effort.
I've reworked alot of the code and thoroughly tested it too so I know it works just fine. The handle locking is improved so there should be no deadlocks. I've still not got to the Stream API stuff but that can wait.
For the short message, in the MidiInput class, the Callback method receives the data as an IntPtr in dwParam1. To get the raw data, cast this to an int. The int is packed by Windows like this:
bits 0-7: Status byte - will be 0x80-0xFF
bits 8-15: First data byte (if applicable) - will be 0x00-0x7F
bits 16-23: second data byte (if applicable) - will be 0x00-0x7F
bits 24-31: unused - should be 0x00
The new version handles this better too with interfaces to give more suitable access to the data if required. Implementing a ToByteArray() in the base interface would be no big deal. I'll have a look before uploading
I'm not sure why your app is hanging on SystemExclusiveMessageRecieved, it's probably fixed in the new code anyway so let me know.
I'll upload the revised code later and drop you a message to let you know.
TBO, I don't need any streaming stuff, but it should still be usefull in your article for others. My needs are stritly for communication to and from a hardware controller device to create a map for another software I use. I look forward to your revised code, and again, thanks for your efforts.
The code has been updated, just use the link from before
There are still numerous XML comments missing (a minor but time consuming thing to do) and as I mentionsed, the Stream API (which at the moment you don't require), but otherwise it's fairly complete and much 'tighter' than before.
As you will see, I've changed the way some things work quite drastically:
There is one common event for receiving both short and long messages, check out the Console Test app to see examples.
Short and long messages are both sent via the same method (as an IMidiMessage).
Combined messages such as 14bit controllers, RPNs etc implement IEnumerable<IMidiMessage>, and can sent by simply calling the Send method overload.
The raw bytes received are available in e.MidiMessage.ToByteArray() as requested.
Program changes, controllers etc have the standard names defined in enums - or you can use a numeric value if needed.
Many classes have static readonly instances of commonly required things or static methods that with the required parameters will create commonly required instances.
There are many other things too, but as you can see, this is now nearing a complet project!
If you have any issues, let me know and I'll happily look into them.
Just the stream stuff to do now I think, and MIDI file/sysex dump file handling if I decide to add it (I may add some more Universal System Exclusive static stuff too). Then, an article to write - should be fun
Just an FYI... When closing the app, it tends to hang on the "InternalReset()" when comparing "NativeMethods.midiInReset(handle)". It never exits unless I bypass the if statement completely. This is the first I've had time to play with the latest code.