Thursday 21 November 2013

MidiOverIP

I've started to play around a bit with Linux as a music studio of late.

Some stuff is up to scratch, some stuff is better and some stuff is way behind.  Its the commercial stuff that is behind, or rather way ahead on Windows but in the case of music making this means VST plugins are a pain on Linux.  No VST mean no instruments which makes sound quite dull.

I recently purchased a midi keyboard  which comes with a bunch of analog soft-synths in VST format which I quite fancy using. The plugin failed to load in the VSTi plugin in LMMS. There was no claim from the manufacturer that they might work. With that fail I needed to hook up my LMMS / Hydrogen / Jack setup up running on my main PC to a Vista box I have that can run the VST plugins.

I started by spending a day shopping for midi hardware before coming up with nothing under 100€ and needing two of them.  This struck me as too much for an ameture tekno hack like myself. On the way back from the shops I resolved to write some midi over IP software since it was not going to be hard, I figured. It is not a tricky job midi over IP you need to sent 4 bytes of digital data across the wire and Ethernet speeds nowadays are easily fast enough.

Before I got started I checked what was out there on the net to see if there was anything that would just work out of the box. It seems there is progress.

Mac has midi over IP built in to the operating system which is handy for mac users.

Linux has a couple of options that work fine, multimidicast is nice and simple and does exactly what I wanted it too.  Trouble is, the Windows side is a pay tool to be found at http://nerds.de, not free software.

There are plenty of options if you fancy paying up real money but we are talking about 4 bytes of un-modified data over the wire so I thought Id rectify the Lin-Win interop issues myself. multimidicast is designed to be compatible with ipMidi from nerds.de so that was my starting point.

I thought I'd try to just port multimidicast code to Windows, but in the end that was not going to happen. The ALSA part (the midi side of things) is obvioulsy Linux specific thats half the code anyway. The network side should be posix compatible but writing C in for Windows you always end up getting sucked in to proprietory stuff there is really no point trying to write portable code IMHO. It was only going to be 1000 lines of code anyway, so I started with a blank page.  A blank page has the advantage of meaning I can apply GPLv3 to the code I write.

Windows MultiMedia has some failrly straight-forward midi APIs with some really shitty documentation, as usual and no example code. Once you get it working it all looks pretty simple. In the world of open source you get used to the fact that you have top quality example code for everything you might like to do. Not so with Windows C, its a dark art. It took me too long to find out that the header I needed was called mmsystem.h and the DLL is called winmm.dll and the linking should be done with -lwinmm.
For winsock I have to include winsock2.h Ws2tcpip.h mswsoc.h the dll is WINSOCK.DLL and you link with -lwsock32.
Perhaps there is some logic to Microsoft naming conventions, but it seems there just is no convention at all. Case is significant in C code and MS libraries are all over the shop so it is impossible to take educated guesses at where to start googling.

After spending a while day with firefox open and textpad to write the C I found a few good articles, notably, nothing hosted at MSDN. With some example code I had enough to search MSDN and from there could bash out the first few lines of code.  MSDN docs there are perhaps sufficient if you have plenty of C on Windows experience, but I dont. After about half a days scribbling I had something up and running that would read packets from the LAN and send them to midi. I wasted a lot of time on the fact that Linux APIs return error codes and Windows return random numbers, again no conventions, and WSAGetLastError() is where the real error is to be found.  Turns out I was listening to multicast about 2 hours before I new I was, since the return code was not as I expected it to be. Winsock is based on Berkley sockets, but that base was taken a long time ago and the similarites are now lost and just confuse more than they help.

Next step was to hook up the midi to a "real" soft-synth, and here we enter frustrating terriroy. It turns out to talk to the instrument a piece of the puzzle is missing, you need a virtual midi device and this is a software driver.  Presumably one that does very little, but a driver none the less and that means kernel code. I've been down this route before and its a pain in the arse, essentially Microsoft reserve the right to tell you what you are doing is not allowed and to get your driver work in you have to have it signed, that costs money.  You can not write you own code on your own PC in the world of Microsoft they still own your hardware.  Slightly scared I would hit a brick wall again I scoured the net for virtual midi software and came up with, thankfully, a few options.  The rtsMidi tool that is a mac compatible midi over IP thing works in Windows and has a virtual midi driver you can download for free. Nerds.de also have a virtual midi device that you can download for free and the basic version does not have a 60 minutes usage limit. This enabled me to skip the driver writing and plug in my VST instruments onto the network. A bit more fiddling and another 100 lines of frustrating code and LMMS can trigger my VST soft synths, nice.

Since I'd got this far, I figured it made sense to get the app to work in the other direction, taking midi from a real or virtual device and broadcasting it onto the network. This was as frustrating a job too.  Mostly because to read midi you can only have ONE line of code in you APP that opens the device, not two. This is wierd since it is farily mormal to have multiple sources and sinks in the world of middi. Having one APP with a main loop listenting for midi events and another for listening for network events meant two threads.  In Linux since ALSA uses the same APIs that the network does you only need one. Threading is C is a dark art too it seems. There are APIs that just dont work. You find this out by googling, MSDN makes no mention of the fact that the seemingly handly looking method called CreateThread will break your C app if you use it. The much clearer named _beginthreadex is the method you want. This was the first function I have found all day that starts with an underscore; wins points for originality I suppose.
The Windos midi APIs are split down the middle with IN and OUT very seprate so despite having two threads there was nothing shared and no need to synchronize.  This is good since I don't fancy writing thread safe code with guess work. To write thread safe code you need to know that what you are doing is correct. You can't test it, it could run for days before blowing up just by chance.

At the end of the day I have got somewhere, I now have Linux and Windows talking happily over midi. I can plug the hardware device in either PC and bang the keys and make that trigger whatever I like. Latency in Linux is exceptionally low, not so in Windows. Tomorrow's job is to get Windows midi to play the note when I tell it to not 100ms after.

Code is here, there is an exe there too with a README.  The code is fairly ledgibubble if you feel like modding it.


No comments:

Post a Comment