Scripting with MIDI

By Sean D. Spencer

"MIDI files are stored in a compact binary form intended to optimise speed ... While well-suited to its intended purposes, MIDI files are somewhat difficult to read and write without a special-purpose library, particularly in commonly used text processing languages such as Perl and Python which are otherwise ideally suited to the kinds of transformations one often wishes to apply to MIDI-encoded music."
~ John Walker ~
  1. Introduction
  2. The Circle of Fifths
  3. Intermediary MIDI File Formats
    1. MIDI CSV Format
    2. MIDI XML Format
    3. Parsing MIDI directly
      1. Python MIDI Tools
      2. Perl MIDI Tools
      3. MIDI Format Docs & Specs
  4. Playing MIDI notes
    1. Software Notes
      1. Linux ALSA & Timidity++
        1. Extending Python for ALSA
      2. PySonic
      3. Windows Software Sequencer
        1. Extending Python for Windows
      4. How to Convert Midi To Wav
    2. Hardware Notes
      1. UNIX - /dev/midi or /dev/sequencer
      2. Windows Support for Hardware

Introduction

This is intended to be resource guide for anyone interesting in manipulating midi files, especially with scripting languages.

The Circle of Fifths

Before we begin experimenting with music files, allow me to mention some basics I've had to learn about music theory, namely, let me mention the Circle of Fifths.

There are a great many kinds of scales in the world. Some examples include lydian, mixolydian, ionian, dorian, aeolian, phrygian, locrian, and of course, major and minor.

My point? Trying to support all these different scales in a computer program would be a daunting task...

But try to do it, and you'll find that many of these patterns are actually one and the same as another. For example, G mixolydian has the same pattern as D dorian, and C major has the same pattern as A minor.

These similarities can be summarized in what's called the Circle of Fifths , which is a much more concise overview of the most common scales.

Various music formats, including MIDI, will often indicate a song's "fifth" number in the header of the file.

The "Circle of Fifths":

-5 -4 -3 -2 -1 0 1 2 3 4 5 6
Db Ab Eb Bb F C G D A E B F#

Contrast the two files here: scales.zip

Intermediary MIDI File Formats

Before attempting to process data from a midi file, it can be quite useful to first convert the midi file into a more tangible format. The following describes a few such formats.

MIDI CSV Format

The MIDI-CSV format is a format arranged by John Walker which represents MIDI files and their events in a comma separated value format ideal for use with scripting languages. The midicsv program was written in C and can convert MIDI to and from CSV.

Example MIDI CSV snippet:

 0, 0, Header, 0, 1, 480
 1, 0, Start_track
 1, 0, Tempo, 500000
 1, 0, Key_signature, 2, "major"
 1, 0, Time_signature, 9, 3, 27, 8
 1, 0, Title_t, "Another Jig Will Do"
 1, 0, Text_t, "Z:id:dc-slipjig-1"
 1, 0, Note_on_c, 0, 69, 105
 1, 239, Note_off_c, 0, 69, 0
 1, 240, Note_on_c, 0, 71, 80
 ...
 ...
 1, 34560, End_track
 0, 0, End_of_file

MIDI XML Format

This is a MIDI XML definition promoted by Recordare which represents MIDI files and their events in a E(X)tensible (M)ark-up (L)anguage ideal for sharing midi over the web, for use as an intermediary format when converting MIDI to another format, and for web-based scripting languages such as PHP.

There is a PHP MIDI class supplied by Valentin Schmidt which can convert MIDI to MIDI XML format.

Because PHP can be used, not only as a web language, but also as a client-side scripting language, it's fairly easy to arrange a client-side version of the midi2xml webpage, for example: mid2xml-client.zip.

Example MIDI XML snippet:

 <?xml version="1.0" encoding="ISO-8859-1"?>
 <!DOCTYPE MIDIFile PUBLIC
  "-//Recordare//DTD MusicXML 0.9 MIDI//EN"
  "http://www.musicxml.org/dtds/midixml.dtd">
 <MIDIFile>
 <Format>0</Format>
 <TrackCount>1</TrackCount>
 <TicksPerBeat>480</TicksPerBeat>
 <TimestampType>Delta</TimestampType>
 <Track Number="0">
  <Event>
    <Delta>0</Delta>
    <SetTempo Value="500000"/>
  </Event>
  <Event>
    <Delta>0</Delta>
    <KeySignature Fifths="1" Mode="1"/>
  </Event>
  <Event>
    <Delta>0</Delta>
    <TimeSignature 
     Numerator="9" 
     LogDenominator="3" 
     MIDIClocksPerMetronomeClick="27" 
     ThirtySecondsPer24Clocks="8"/>
  </Event>
  <Event>
    <Delta>0</Delta>
    <TrackName>Butterfly</TrackName>
  </Event>
  <Event>
    <Delta>0</Delta>
    <TextEvent> This is a slipjig</TextEvent>
  </Event>
  <Event>
    <Delta>0</Delta>
    <NoteOn Channel="1" Note="71" Velocity="105"/>
  </Event>
  ...

Parsing MIDI directly

Python MIDI Tools

There is this script: midi.py by Will Ware, which is the closest thing I've been able to find to a complete MIDI parser for Python.

I've written my own mostly complete (though not entirely complete) MIDI parser library for Python available here: midiparser.zip. The code is loosely based on midi.py and the code from John Walker's midicsv. Unlike midi.py, it is intended to have a structured class interface.

Perl MIDI Tools

There is MIDI-Perl by Sean M. Burke, which contains documentation and tutorials of its usage. It's also available at CPAN.

Midi Format Docs & Specs

Many specs for the MIDI file format can be found at the WOTSIT website. Many more complete specs include the following:

Playing MIDI Notes

Software Notes

Linux ALSA & Timidity++

The Linux operating system has a midi player called Timidity++. In order for it to work it needs a set of software instruments. A set of instruments, and example timidity.cfg, files can be found at www.linuxpackages.net.

Timidity has it's own built-in "trace" option, which can show a graphical representation of a midi file's events as timidity parses it. This functionality is best represented in its XAW interface (see original screenshot).
Timidity XAW Interface

Timidity can also be used as a software sequencer daemon in conjunction with ALSA drivers. Here is a good tutorial: ALSA Related Stuff.

In order to program with the timidity alsa interface, you'll need to get familiar with the ALSA sequencer API. The best tutorial for this API that I've found, is Takashi Iwai's Virtual Keyboard.
Virtual Keyboard Screenshot

Extending Python for ALSA

It's possible to extend Python so that it can access the ALSA Sequencer API.

I've arranged my own wrapper for Python, which wraps just a few of the basic ASLA Sequencer functions like turning notes on and off and changing instruments. It's available here: alsawrap.zip

PySonic

Another way to output notes without a hardware sequencer is via PySonic, which is a Python wrapper to the FMOD Sound Library, a cross platform audio output API.

It's primarily distributed for Windows, but with enough fiddling I've been able to get it to run on some versions of Linux. In order to compile, you'll need Pyrex and a non-"Ex" edition of FMOD.

Windows Software Sequencer

PyMidi is one Python library I've found for Windows.

Extending Python for Windows

It should also be possible to extend Python so that it can access the Windows MIDI API.

Here's one tutorial on the Windows MIDI API...

Basic MIDI Input/Output Programming

How to Convert Midi To Wav

If you decide to use a non-MIDI audio output API, the forumla for converting notes to hertz is fairly simple.

Let's assume you have a sample (that is, a wav file) that represents middle-C. Normally middle-C is approximately 261.6 Hz. But since we're using a wav file, we're going to replace that with 44100 Hz (the typical sampling rate of a high quality wav file.)

 let freq = frequency (in hertz)
 let note = the note, where 0 is middle C

 freq = 44100 * 2^(note/12)
 note = (log2(freq / 44100)) / 12

So, in Python, the syntax would be...

sample.Frequency = sample.original_frequency * \
    pow(2, float(note_number + (12 * octave)) / 12)

Of course, the octave numbers may vary depending on what octave was used to record the sample for middle-C.

I've arranged a simple PySonic example that uses this formula here: audio.zip

Hardware Notes

UNIX - /dev/midi or /dev/sequencer

If you're fortunate enough to own a soundcard that has a real hardware sequencer, there are plenty of good programming tutorials. For example, Craig Stuart Sapp has arranged his...

Introduction to MIDI programming in Linux.

Windows Support for Hardware

In Windows XP, one can choose from any available so-called "wave tables" (be it hardware or software) by going to Control Panel -> Audio Tab -> Midi Music Playback section. The Windows MIDI API should in-turn use the selected wave table.

Windows XP Sound Tab in Control Panel

© Copyright Sean D. Spencer (Last Updated: 4/22/2007) sean DOT spencer AT att DOT net