How To Play MIDI Instruments

Setup

  • Download the MIDI for .NET v2.0.4 toolkit.
  • -or- DLL library download: MIDI DLL file.
  • Extract the contents of the ZIP file to your !!Visual Studio Projects!! folder, or wherever you keep a repository of your Visual Studio projects.
  • Start a new Visual Studio project or open the existing one to which you wish to add MIDI playback functionality.
  • In the Solution Explorer window, right click on the solution and select Add > Existing Project.
  • Browse to the location you unzipped the MIDI toolkit and select the Toub.Sound.Midi.csproj file. Click OPEN. The MIDI toolkit project is added to your solution.
  • Right click on the References item for your project in the Solution Explorer and choose Add Reference.... You will be brought to a dialog box.
  • Click on the Projects tab and select Toub.Sound.Midi from the list. Click the Select button to add it to the list below, and then click OK.

Examples

The toolkit comes with several example projects that demonstrate how to use some of the basic and advanced features of the toolkit. You may switch demos by right-clicking on the demo project in the Solution Explorer and choosing Set as Startup Project.

Initialization/Destruction

The MidiPlayer class is a static object that functions as the main interface between the you and the MIDI device.

  • When your program starts up, you must call MidiPlayer.OpenMidi(); to initialize the device.
  • When your program shuts down, you must call MidiPlayer.CloseMidi(); to free the device resources.

Playing a MIDI File

The MidiPlayer class has the function Play. One override takes a string argument which is used to specify the path of the MIDI file to open and immediately begin playing. For example:

MidiPlayer.Play("mymusic.mid");

Playing Sounds in Real Time

The MidiPlayer class has the function Play which takes an argument of type MidiEvent. MidiEvent is a subclass for several types of events that can be sent to the MIDI Device - of particular interest is the NoteOn event, which turns a particular note on, thus producing a sound.

Melodic Tracks

The MIDI device has 15 channels with which to output sound. Other than Channel 10, all channels are used to output melodic sound. Each channel is assigned an Instrument Preset, which allows you to choose from 127 instruments, including Pianos, Strings, Woodwinds, Bass, Electronic, and Sound Effects. MIDI files consist of one or more tracks that map to a channel. Each track can contain any number of notes to be played. These notes aren't restricted to having a single one playing at a time - notes in the same track can overlap to create chords. More than one track can be mapped to the same channel, however this results in both tracks having the same instrument preset. This is usually only done to keep two counterpoints logically separated - for instance, the notes for two harmonizing trumpets.

The default instrument for any track is instrument preset 0, which corresponds to the Grand Piano. If we play a melodic note without setting the instrument preset, we will get a note played on the Grand Piano. To play a note to a given channel, use the following code:

MidiPlayer.Play( new NoteOn( 0, 1, "C4", 127 ) );

Keep in mind this turns a note on for an indefinite amount of time! You won't notice this with a piano as it has a decay that eventually makes the sound fade out, however for sustained instruments such as strings, you'll just have a continuous drone. To turn notes back off again, use the following code:

MidiPlayer.Play( new NoteOff( 0, 1, "C4", 127 ) );

The constructor for this object takes exactly the same parameters as the NoteOn object. As long as the channel and note match something that is currently playing, the note will be switched off again.

Now let's take a look at the parameters:

  1. Delta Time parameter - Suffice it to say that this parameter will remain at 0 for now.
  2. Channel parameter - can range from 0 to 15. (Oddly enough channel 10 still produces a melodic note, rather than being percussion as it should.)
  3. Note parameter - is a string to represent a note in traditional musical notation. Values can range from "C0" to "G10", where "C3" is considered middle-C (basically an anchor point for the most commonly used range of 8 notes). Enumerations go as follows (where n represents a number): C0, ... Cn, C#n, Dn, D#n, En, Fn, F#n, Gn, G#n, An+1, A#n+1, Bn+1, B#n+1, Cn+1, ..., G10. As you can see, the note lettering iterates from A to G, with #s (sharps) between. E does not have a # version, but don't ask me why, that's just how it is in music.
  4. Velocity parameter - is an indication of how hard the note is played, where 0 is completely silent, and 127 is the hardest possible. Usually 100 is a reasonable value to be heard without using excessive force.

The Percussion Track

MIDI contains a single track known as the "percussion track". It is an instrument where different note pitches map to a whole array of percussion instruments. MIDI Channel 10 is reserved for percussion, however it is possible to have multiple percussion presets much in the same way that one has instrument presets on any other channel. This toolkit has an enumeration called GeneralMidiPercussion that lists all the standard percussion instruments. To play a sound on the percussion track, use the following code:

MidiPlayer.Play( new NoteOn( 0, GeneralMidiPercussion.AcousticSnare, 127 ) );

The parameters are similar to before:

  1. Delta Time - again, leave it as 0 for now.
  2. Percussion Preset - use the GeneralMidiPercussion enumeration to select the percussion instrument to play.
  3. Velocity - the force of the instrument, from 0 to 127.

Setting the Instrument Preset

There are 128 different instruments to choose from. Luckily these instruments are listed in the GeneralMidiInstruments enumeration.

The Instrument Preset is also referred to as a Channel's Program, thus we use a ProgramChange object to set the instrument preset for a particular channel. For instance, to set Channel 1 to the StringEnsemble1 instrument, we use the following code:

MidiPlayer.Play( new ProgramChange(0, 1, GeneralMidiInstruments.StringEnsemble1) );

All new NoteOn events to Channel 1 will now sound like Strings instead of piano. You can play around for hours trying out all the different instrument presets now!

Reference Links