Overview

The average user only has one MIDI playback device on their system, the internal software synthesizer 'Microsoft GS Wavetable Synth', and any MIDI files are played back via that - no problem. However, many users (including me and probably you as you're reading this) have superior or multiple devices, either software or hardware based, so we want/need to be able to select the playback device that the system uses.

Pre Vista, you could select the playback device via Sounds and Audio Devices|Audio in the Control Panel. For some reason, Microsoft decided to remove this option in Vista, and it hasn't reappeared in any service pack to date or in Windows 7 (Beta) either, even though there have been many requests for its inclusion.

In February 2008, I made a tiny application to address this which worked fine on my Vista systems and I made publicly available elsewhere. This didn't work for everybody though. Some people didn't get any devices listed, others only a selection of the ones available. I'm currently in the process of writing a comprehensive managed wrapper around all things MIDI, and it occurred to me that I should revisit the problem and solve it conclusively as part of that project. This article is the result.

The problem with my previous solution was that it used the Registry to retrieve information about installed devices. Not all devices create these Registry entries, hence they weren't all shown by the application.

The solution

Getting the devices

To retrieve all the MIDI out devices turns out to be trivial - although it requires the use of a little PInvoke, calling a function in winmm.dll: midiOutGetNumDevs. This is a very simple function. It takes no parameters, and simply returns the number of output devices.

[DllImport("winmm.dll")]
staticexternUInt32 midiOutGetNumDevs();

We could then simply use any device ID, from 0 to the result of this function -1 (it's zero based) and save this, but that's not very user friendly. Nobody knows the ID number of their devices, only the name, so we need another function to get information about each device including the name, midiOutGetDevCaps. This function is slightly more complicated.

The first parameter is the ID of the device we want to query. Device IDs are sequential and zero based, so we can simply use a foreach loop based on the result we retrieved above and recursively call this function.

The second parameter is a structure MIDIOUTCAPS where the function will store the device information.

It's only the szPname that we're interested in. The constant MAXPNAMELEN is from the MMSystem.h file (I've not included it in the project as most of it is not needed), and has a value of 32.

The third parameter is the size of the MIDIOUTCAPS structure. The function returns a value that indicates the error (if any) that occurred when the function was called. We're only interested if there was no error (when it returns MMSYSERR_NOERROR, this constant's value is 0).

Output class

To call this function for each ID from the managed world, we need a class Output that takes a MIDIOUTCAPS in the constructor.

Saving the ID

Windows uses a Registry setting that contains the ID of the default MIDI out device. The key is: HKEY_CURRENT_USER\Software\Microsoft\ActiveMovie\devenum\{4EFE2452-168A-11D1-BC76-00C04FB9453B}\Default MidiOut Device, and the value that needs changing is a DWORD called MidiOutId.

All we need to do is change this value to the ID of our preferred device and we're done.

Conclusion

Unless Microsoft decides to make breaking changes in later OSes to this part of the Registry or to winmm.dll, this should work under all future versions too. I've tested it on XP (not needed, but it works anyway!), Vista, and Windows 7 Beta (build 7000).

I've included the binary (as well as the source) for anyone that just needs the application to take back control of their MIDI system.

Comments and Discussions

First of all thanks for a rather useful little programme. It works flawlessly with things like the Windows Media Player. I am using Win 8.1 64bit. And your little goodie is the only programme that I found to work under Windows 8.1 64bit! Looks like things are not that simple after all .

However, I am also trying to get old pre-DirectX games to use my hardware midi stuff and not the software midi provided with Windows. I have tried out the Windows version of Lemmings and what is known here in Germany as Battle Isle 3. In both cases, regardless of what you select in Midi Out Setter, only the internal software midi stuff is used and not my hardware midi device (Roland SC-55 on a Roland USB to Midi adapter).

Where do I have to look (and if necessary tweak) inside the Windows Registry so that old pre-DirectX Windows games also find my hardware midi stuff?

(Things like DOSBox or other programmes where you can select midi devices of course work fine, anyway.)

One thing I noticed is that the Roland USB to Midi adapter only appears under 32bit in the Windows Registry. It is not included under 64bit.

I've just had a look in the registry on a 64 bit machine and there is a separate directory devenum 64-bit in there (all the other bits are the same). Perhaps the DWORD value needs to be stored there too? It's worth a try - just download the amend the code and recompile.

As these are older games, it wouldn't surprise me if they aren't using their own config or ini files to store the hardware information, so perhaps try searching their installation folders for anything that looks like a config and examine the contents...

I'm currently migrating a large MIDI project from XP to Vista Ultimate 64bit, and was looking for a new way to link MIDI input devices to output devices. Used to use MIDI Yoke, but that no longer works under 64bit Vista (and there's no replacement in sight).But winmm.dll seems to have input device functions corresponding to the output device functions you used... Lets hope it works!

midiConnect and midiDisconnect will handle low level coupling of In to Out/Thru & Thru to Out.

Be careful of some of the data types in winmm, especially in a 64bit environment (I've only used MIDI in 32 bit).

By the way, one thing I didn't mention in the article that may be worth bearing in mind is, the ID should technically be an unsigned int, but MIDIMapper is assigned -1 in MMSystem.h which is why I went for a signed int.

I'll post back here when I've completed my full managed wrapper with a link, it might be useful. I've just got a couple of problems left to solve

DaveBTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)Visual Basic is not used by normal people so we're not covering it here. (Uncyclopedia)Why are you using VB6? Do you hate yourself? (Christian Graus)

As far as I can tell, both my Output and Input Devices are all now working correctly under Vista Ultimate 64bit using winmm. I just added equivalent code for input devices to your output device code, and interfaced to Leslie Sanford's C# MIDI Toolkit (which I was already using) using the device numbers.

Its a great compliment to Leslie's programming that his library still works under 64bit, but he seems to have moved on, so I'm keeping an eye out for alternatives. I'll be very interested in seeing your wrapper when its ready!

I had a look a Leslie's toolkit before I approached the wrapper myself. It's good and works well, but I found a couple of bits in it I wasn't totally happy with and rather than ammend his code - I figured I'd learn more and it'd be more rewarding by doing it myself from scratch.

I'm almost there with the wrapper now - a few stream issues to sort out but otherwise ...

The point I made about the signed/unsigned bit for the deviceID - it turns out the MidiMapper has a value of (on 32bit anyway) 0xffffffff - the C++ boys used UINT-1 to cheat to get the equivalent of C#'s UInt32.MaxValue, so it really is unsigned. (-1 in binary is 0xffffffff).

DaveBTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)Visual Basic is not used by normal people so we're not covering it here. (Uncyclopedia)Why are you using VB6? Do you hate yourself? (Christian Graus)