About me:

IT Consultant
Software Developer
more about me...


My Translation & Language resources website


Chess Variants :my contributions and the Main Site
Other sites I have something to do with:
- Drora's Music Chamber
- Fibersiv - Mirjam Bruck-Cohen's fiberart site
- Different Art - Mirjam Bruck-Cohen's art blog
- not the Last Word - A blog - mainly about language and translation
- Hebrew translation of the Perl Maven perl tutorials

If Music be the Food of Love - Try Playing With it Perl


2017-10-07
An introduction to MIDI and a few modules to handle MIDI using the Perl Programming Language
MIDI -Musical Instrument Digital interface is a communications protocol between music
instrument, music controllers, sound synthesizers, and computers. It primarily carries music
performance data, and the data can also stored in files.

What does it mean it to store performance data?

Unlike mp3 or wav, MIDI doesn't record sound. It records the actions performed by the player to
play the notes. A good analogy would be player pianos. Player pianos were popular during the late
19 th cent and early 20 th century. They used piano rolls, long rolls of paper, to control a mechanism
that would play the notes on the piano. Thus, a performance of a specific song could be recorded
onto paper, and replayed on a different piano. The same notes would be played, in the same order,
for the same duration, however the sound would be generated by the piano on which the piano-roll
is played, and may be different than the sound of the piano on which the piano roll was originally
created. If you did not have your player piano regularly tuned, the songs would sound out of tune.
Similarly, MIDI records the notes and several parameters about each note. The sound itself is
generated by a synthesizer. This can be the synthesizer from your computer's internal sound card, an
external sound card, a dedicated synth module, a software synth, or even your smart phone's
internal synthesizer. Dedicated synthesizer modules and high-end software synths produce higher
quality sound, more suitable for music production.

MIDI stores data about a note's pitch and volume (called 'velocity' in MIDI), it has commands for
starting and for ending a note and additional control commands for channel volume, vibrato, pitch
shift, etc. The most common controllers for MIDI are keyboard controllers.

The General MIDI specification defines a standard for musical instruments that includes a standard
set of instrument names, and a reserved channel for percussion.

One of the commands MIDI can send to a channel is called a Program Change. A program change
includes a program number. General MIDI assigns each program number to an instrument.

e.g.

1 - Acoustic Grand Piano
2 - Bright Acoustic Piano
3 - Electric Grand Piano
etc.
25-32 are various guitars
41-38 are various string instruments.
Etc.

That way, you know that sending a Program Change with 1 as a parameter, will tell the instrument
to use a piano sound. Since each synthesizer brand may have its own version of a piano, you don't
know exactly what the piano will sound like, but you know it's a piano.

MIDI commands are usually called events. This includes all commands: programs changes, tempo
changes, note commands, etc.

MIDI commands can be generated in real time by programs, and MIDI files can be manipulated by
programs. In the Perl Programming Language the MIDI module is commonly used to generate, read, and manipulate MIDI
files. It uses, in turn, several other modules to abstract various constructs of the MIDI protocol. A
MIDI file can store information about multiple tracks, just as music is often recorded on multiple
tracks.

We'll start with a short demo that takes a filename and extract information about the file.#!/usr/bin/perl

use strict;
use warnings;
use MIDI;
my $file = shift @ARGV;
die "no file specified" unless $file;
my $opus = MIDI::Opus->new ({'from_file' => $file});
print "ticks = " . $opus->ticks() . "\n";
my @tracks = $opus->tracks();
for(my $i = 0; $i < @tracks; ++$i) {
foreach my $e ($tracks[$i]->events) {
if($e->[0] eq 'patch_change') {
printf "track %02d channel %02d uses instrument %03d (%s)\n",
$i, $e->[2],
$e->[3], $MIDI::number2patch{$e->[3]} || '?'
}
}
}

MIDI::Opus is a module that deals with a single midi file, or a single piece of music. In MIDI
terminology a single piece of music is usually referred to as a song. Here the constructor creates a
new MIDI::Opus object from an existing file.
It retrieves the tempo, the metronome setting using the ticks method.
Then it retrieves the tracks data using the tracks method.
Then it loops over the tracks, and in each track it loops over all events looking for 'patch_change' (ie
- program change) events and prints out the program change data.
The following output was generated when this code was run on a midi file of the Promenade from
Mussorgsky's 'Pictures in an Exhibition':

ticks = 480
track 01 channel 00 uses instrument 056 (Trumpet)
track 02 channel 01 uses instrument 060 (French Horn)
track 02 channel 01 uses instrument 060 (French Horn)
track 03 channel 02 uses instrument 057 (Trombone)
track 03 channel 02 uses instrument 057 (Trombone)
track 04 channel 03 uses instrument 058 (Tuba)
track 04 channel 03 uses instrument 058 (Tuba)
track 05 channel 04 uses instrument 048 (String Ensemble 1)
track 05 channel 04 uses instrument 048 (String Ensemble 1)
track 06 channel 05 uses instrument 045 (Pizzicato Strings)
track 06 channel 05 uses instrument 045 (Pizzicato Strings)
track 07 channel 06 uses instrument 068 (Oboe)
track 07 channel 06 uses instrument 068 (Oboe)track 08 channel 07 uses instrument 071 (Clarinet)
track 08 channel 07 uses instrument 071 (Clarinet)
track 09 channel 08 uses instrument 070 (Bassoon)
track 09 channel 08 uses instrument 070 (Bassoon)
track 10 channel 10 uses instrument 073 (Flute)
track 10 channel 10 uses instrument 073 (Flute)
track 11 channel 11 uses instrument 048 (String Ensemble 1)
track 11 channel 11 uses instrument 048 (String Ensemble 1)
track 12 channel 12 uses instrument 072 (Piccolo)
track 12 channel 12 uses instrument 072 (Piccolo)

If we like, we can also see the actual note data.
The following code prints out all the note-on/note-off events:

#!/usr/bin/perl
use strict;
use warnings;
use MIDI;
my $file = shift @ARGV;
die "no file specified" unless $file;
my $opus = MIDI::Opus->new ({'from_file' => $file});
MIDI::Opus->new( {
"from_file" => $ARGV[0],
} );
my @tracks = $opus->tracks();
for(my $i = 0; $i < @tracks; ++$i) {
foreach my $e ($tracks[$i]->events) {
next unless $e->[0] =~ /note/;
print join "\t" , @{$e} , "\n";
}
}

Typically there are a lot of notes, and therefore a lot of note events in any piece of music, so I'll only
show the first few lines of the output:

note_on 0 0 67 110
note_off 480 0 67 0
note_on 0 0 65 110
note_off 480 0 65 0
note_on 0 0 70 110
note_off 480 0 70 0
note_on 0 0 72 110
note_off 240 0 72 0
note_on 0 0 77 11
note_off 240 0 77 0
note_on 0 0 74 110
note_off 480 0 74 0
note_on 0 0 72 110
note_off 240 0 72 0
note_on 0 0 74 110
note_off 480 0 74 0
note_on 0 0 70 110
note_off 480 0 70 0
note_on 0 0 70 110
note_off 480 0 70 0
note_on 0 0 72 110
note_off 480 0 72 0
note_on 0 0 67 110
note_off 480 0 67 0
note_on 0 0 65 110
note_off 480 0 65 0
note_on 0 0 58 110
note_on 0 0 67 110
note_on 0 0 62 110

The columns are:
event delta channel note velocity
Only note_on and note_off events are shown here. It should be noted that many a note_on event
with a velocity of 0 is the same as a note_off event. It's not seen in this output, however, many
instruments generate it that way.

Even - the MIDI event

delta - The time since the previous event, measured in internal clock-ticks (not the ticks parameter

in the code)
note - the note number. The middle C on the piano is note number 60. Each key/semi-tone is

assigned a sequential number.
Velocity – as noted above, in MIDI this means the volume of the specific note.
The Promenade starts with a solo instrument, thus each note_on is followed by a note_off. The next
note note_on follwed the note_off immediately (delta=0). Then we see three note_on events with a
0 value for delta. These notes are played simultaneously.

The MIDI modules can also be used to generate MIDI files.
The following code sample generates a file with the La Folia bass part:

#!/usr/bin/perl
use MIDI;
my $bass_track = MIDI::Track->new;
#delta, channel, note, velocity
$bass_track->events(['text_event', 0 , 'la folia bass'],
['note_on', 0, 4 , 55, 100],
['note_on', 240, 4, 55, 0],
['note_on', 0, 4 , 50, 100],
['note_on', 240, 4, 50, 0],
['note_on', 0, 4 , 55, 100],
['note_on', 240, 4, 55, 0],
['note_on', 0, 4 , 53, 100],
['note_on', 240, 4, 53, 0],
['note_on', 0, 4 , 59, 100],
['note_on', 240, 4, 59, 0],
['note_on', 0, 4 , 53, 100],
['note_on', 240, 4, 53, 0],
['note_on', 0, 4 , 55, 100],
['note_on', 240, 4, 55, 0],
['note_on', 0, 4 , 50, 100],
['note_on', 240, 4, 50, 0],
['note_on', 0, 4 , 55, 100],
['note_on', 240, 4, 55, 0],
['note_on', 0, 4 , 50, 100],
['note_on', 240, 4, 50, 0],
['note_on', 0, 4 , 55, 100],
['note_on', 240, 4, 55, 0],
['note_on', 0, 4 , 53, 100],
['note_on', 240, 4, 53, 0],
['note_on', 0, 4 , 59, 100],
['note_on', 240, 4, 59, 0],
['note_on', 0, 4 , 53, 100],
['note_on', 240, 4, 53, 0],
['note_on', 0, 4 , 55, 100],
['note_on', 80, 4, 55, 0],
['note_on', 0, 4 , 50, 100],
['note_on', 160, 4, 50, 0],
['note_on', 0, 4 , 43, 100],
['note_on', 240, 4, 43, 0],
);
my $opus = MIDI::Opus -> new ({'format' => 0, 'ticks' => 80, 'tracks' => [$bass_track]});
$opus -> write_to_file('folia_bass.mid');

First it instantiates a MIDI::Track object. The first even is a text event naming the track. It's
followed by note events for the track. This is a single solo track, so there are no simultaneous notes.
Note_on events with non-zero velocity are used to start playing a note. note_on events with zero
velocity are used to end a note.

Finally, a new MIDI::Opus object is created, the track we just created is assigned to 'tracks' in its
constructor, and the write_to_file method is used to generate the midi file.

Now, suppose you're a music composition instructor, and you're giving your students an assignmentto compose La Folia variations. You specify that the variations must be playable with the bass line
provided. You further specify that they must submit only the melody track.

Then you can use the MIDI module to put the melody track together with your bass track, and then
when you listen to them, or when you read the notes, you can see that they really match up.
The following code reads two MIDI files and generates a new MIDI file combining the two:

#!/usr/bin/perl
use MIDI;
my $bass = MIDI::Opus->new({'from_file' => 'folia_bass.mid'});
my $theme = MIDI::Opus->new({'from_file' => 'folia_theme.mid'});
my $folia = MIDI::Opus->new({'format' => 0, 'ticks' => 80, 'tracks' => [ ($bass->tracks)[0] ,
($theme->tracks)[0]]});
$folia -> write_to_file('folia.mid');

We've seen a few code sample showing how it only takes a few lines of code to read, manipulate
and generate MIDI files in Perl. This can be both fun and useful.

comments powered by Disqus