NAME

MIDI::Simple - procedural/OOP interface for MIDI composition

SYNOPSIS

use MIDI::Simple;  # uses MIDI, which uses everything it needs.
new_score;
push @Score,
 ['text_event', 0, 'www.ely.anglican.org/parishes/camgsm/chimes.html'],
 ['text_event', 0, 'Lord through this hour/ be Thou our guide'],
 ['text_event', 0, 'so, by Thy power/ no foot shall slide'],
 ['patch_change', 0, 1, 8]; # Patch 8 = Celesta
n c1, f, qn, Cs2; n F; n Ds; n hn, Gs_d1;
n qn, Cs; n Ds; n F; n hn, Cs;
n qn, F; n Cs; n Ds; n hn, Gs_d1;
n qn, Gs_d1; n Ds; n F; n hn, Cs;
write_score 'chimes3y.mid';

DESCRIPTION

This module is in beta -- a sloooowly maturing meta. Let me know if you run into any problems, and feel free to suggest features.

This module is somewhat incompatible with the MIDI::Simple versions before .700.

I think I've settled on (i.e., basically frozen) the basic interface for this module, and will now hopefully only add functionality.

This module sits on top of all the MIDI modules -- notably MIDI::Score (so you should skim MIDI::Score) -- and is meant to serve as a basic interface to them. By composition, I mean composing anew; you can use this module to add to or modify existing MIDI files, but that functionality is to be considered expermental.

This module provides two related but distinct bits of functionality: 1) a mini-language (implemented as procedures that can double as methods) for composing by adding notes to a score structure; and 2) simple functions for reading and writing scores, specifically the scores you make with the composition language.

The fact that this module's interface is both procedural and object-oriented makes it a definite two-headed beast. The guts of the source code is not for the faint of heart.

OBJECT STRUCTURE

A MIDI::Simple object is a data structure with the following attributes:

Score

This is a list of all the notes (each a listref) that constitute this one-track musical piece. Scores are explained in MIDI::Score. You probably don't need to access the Score attribute directly, but be aware that this is where all the notes you make with n events go.

Time

This is a non-negative integer expressing the time, in ticks from the start-time of the MIDI piece, that the next note pushed to the Score will have.

Channel

This is a number in the range [0-15] that specifies the current default channel for note events.

Duration

This is a non-negative (presumably nonzero) number expressing, in ticks, the current default length of note events, or rests.

Octave

This is a number in the range [0-10], expressing what the current default octave number is. This is used for figuring out exactly what note-pitch is meant by a relative note-pitch specification like "A".

Notes

This is a list (presumably non-empty) of note-pitch specifications, as note numbers in the range [0-127].

Volume

This is an integer in the range [0-127] expressing the current default volume for note events.

Tempo

This is an integer expressing the number of ticks a quarter note occupies. It's currently 96, and you shouldn't alter it unless you really know what you're doing. If you want to control the tempo of a piece, use the set_tempo routine, instead.

Cookies

This is a hash that can be used by user-defined object-methods for storing whatever they want.

Each package that you call the procedure new_score from, has a default MIDI::Simple object associated with it, and all the above attributes are accessible as:

@Score $Time $Channel $Duration $Octave
@Notes $Volume $Tempo %Cookies

(Although I doubt you'll use these from any package other than "main".) If you don't know what a package is, don't worry about it. Just consider these attributes synonymous with the above-listed variables. Just start your programs with

use MIDI::Simple;
new_score;

and you'll be fine.

ROUTINE/METHOD/PROCEDURE

MIDI::Simple provides some pure functions (i.e., things that take input, and return output, and that's all they do), but what you're mostly interested in its routines. By "routine" I mean a bit of code that you call, whether as a procedure or as a method.

Here I'm using "procedure" to mean a routine you call like this:

name(parameters...);
# or, just maybe:
&name(parameters);

(In technical terms, I mean a non-method subroutine that can have side effects.) And I'm using "method" to mean a routine you call like this:

$object->name(parameters)

So bear these terms in mind when you see routines below that act one one, or the other, or both.

MAIN ROUTINES

These are the most important routines:

new_score() or $obj = MIDI::Simple->new_score()

As a procedure, this intializes the package's default object (Score, etc.). As a method, this is a constructor, returning a new MIDI::Simple object. Neither form takes any parameters.

n(...parameters...) or $obj->n(...parameters...)

This uses the parameters given (and/or the state variables like Volume, Channel, Notes, etc) to add a new note to the Score. Then it moves Time ahead as appropriate. These parameters may affect the other state variables. See the section "Parameters For n/r/noop", below.

r(...parameters...) or $obj->r(...parameters...)

This is exactly like n, except it never pushes anything to Score, but moves ahead Time. (In other words, there is no such thing as a rest-event; it's just a item during which there are no note-events playing.)

noop(...parameters...) or $obj->noop(...parameters...)

This is exactly like n and r, except it never pushes anything to Score, and never changes Time. It is meant to be used for setting the other state variables, i.e.: Channel, Duration, Octave, Volume, Notes.

ATTRIBUTE METHODS

The object attributes discussed above are readable and writeable with object methods. For each attribute there is a read/write method, and a read-only method that returns a reference to the attribute's value:

Attribute ||  R/W-Method ||   RO-R-Method
----------++-------------++--------------------------------------
Score     ||  Score      ||   Score_r      (returns a listref)
Notes     ||  Notes      ||   Notes_r      (returns a listref)
Time      ||  Time       ||   Time_r       (returns a scalar ref)
Duration  ||  Duration   ||   Duration_r   (returns a scalar ref)
Channel   ||  Channel    ||   Channel_r    (returns a scalar ref)
Octave    ||  Octave     ||   Octave_r     (returns a scalar ref)
Volume    ||  Volume     ||   Volume_r     (returns a scalar ref)
Tempo     ||  Tempo      ||   Tempo_r      (returns a scalar ref)
Cookies   ||  Cookies    ||   Cookies_r    (returns a hashref)

To read any of the above via a R/W-method, call with no parameters, e.g.:

$notes = $obj->Notes;  # same as $obj->Notes()

The above is the read-attribute ("get") form.

To set the value, call with parameters:

$obj->Notes(13,17,22);

The above is the write-attribute ("put") form. Incidentally, when used in write-attribute form, the return value is the same as the parameters, except for Score or Cookies. (In those two cases, I've suppressed it for efficiency's sake.)

Alternately (and much more efficiently), you can use the read-only reference methods to read or alter the above values;

$notes_r = $obj->Notes_r;
# to read:
@old_notes = @$notes_r;
# to write:
@$notes_r = (13,17,22);

And this is the only way to set Cookies, Notes, or Score to a (), like so:

$notes_r = $obj->Notes_r;
@$notes_r = ();

Since this:

$obj->Notes;

is just the read-format call, remember?

MIDI EVENT ROUTINES

These routines, below, add a MIDI event to the Score, with a start-time of Time. Example:

text_event "And now the bongos!";  # procedure use

$obj->text_event "And now the bongos!";  # method use

These are named after the MIDI events they add to the score, so see MIDI::Event for an explanation of what the data types (like "velocity" or "pitch_wheel"). I've reordered this list so that what I guess are the most important ones are toward the top:

patch_change channel, patch;
key_after_touch channel, note, velocity;
channel_after_touch channel, velocity;
control_change channel, controller(0-127), value(0-127);
pitch_wheel_change channel, pitch_wheel;
set_tempo tempo;
smpte_offset hr, mn, se, fr, ff;
time_signature nn, dd, cc, bb;
key_signature sf, mi;
text_event text;
track_name text;
instrument_name text;
lyric text;
set_sequence_number sequence;
marker text;
cue_point text;
sequencer_specific raw;
sysex_f0 raw;
sysex_f7 raw;

And here's the ones I'll be surprised if anyone ever uses:

text_event_08 text;
text_event_09 text;
text_event_0a text;
text_event_0b text;
text_event_0c text;
text_event_0d text;
text_event_0e text;
text_event_0f text;
raw_meta_event command(0-255), raw;
song_position starttime;
song_select thing;
tune_request starttime;
raw_data raw;
end_track starttime;
note duration, channel, note, velocity;

MORE ROUTINES

$opus = write_score filespec
$opus = $obj->write_score(filespec)

Writes the score to the filespec (e.g, "../../samples/funk2.midi", or a variable containing that value), with the score's Ticks as its tick parameters (AKA "divisions"). Among other things, this function calls the function make_opus, below, and if you capture the output of write_score, you'll get the opus created, if you want it for anything. (Also: you can also use a filehandle-reference instead of the filespec: write_score *STDOUT{IO}.)

read_score filespec
$obj = MIDI::Simple->read_score('foo.mid'))

In the first case (a procedure call), does new_score to erase and initialize the object attributes (Score, Octave, etc), then reads from the file named. The file named has to be a MIDI file with exactly one eventful track, or Perl dies. And in the second case, read_score acts as a constructor method, returning a new object read from the file.

Score, Ticks, and Time are all affected:

Score is the event form of all the MIDI events in the MIDI file. (Note: Seriously deformed MIDI files may confuse the routine that turns MIDI events into a Score.)

Ticks is set from the ticks setting (AKA "divisions") of the file.

Time is set to the end time of the latest event in the file.

(Also: you can also use a filehandle-reference instead of the filespec: read_score *STDIN{IO}.)

If ever you have to make a Score out of a single track from a multitrack file, read the file into an $opus, consider something like:

$opus = MIDI::Opus->new({ 'from_file' => "foo2.mid" });
$track = ($opus->tracks)[2]; # get the third track

$score_r, $end_time =
  MIDI::Score::events_r_to_score_r($track->events_r);

$Ticks = $opus->ticks;
@Score =  @$score_r;
$Time = $end_time;
synch( LIST of coderefs )
$obj->synch( LIST of coderefs )

LIST is a list of coderefs (whether as a series of anonymous subs, or as a list of items like (\&foo, \&bar, \&baz), or a mixture of both) that synch calls in order to add to the given object -- which in the first form is the package object, and which in the second case is $obj. What synch does is:

* remember Time before calling any of the routines;

* for each routine given, reset Time to what it was initially, call the routine, and then note what Time is then;

* then, after having called all of the routines, set Time to whatever the greatest (latest) value of it resulting from any of the routines.

The coderefs are all called with one argument in @_ -- the object they are supposed to affect. All these routines should therefore use methods instead of procedures. Here's an example usage of synch:

my $measure = 0;
my @phrases =(
  [ Cs, F,  Ds, Gs_d1 ], [Cs,    Ds, F, Cs],
  [ F,  Cs, Ds, Gs_d1 ], [Gs_d1, Ds, F, Cs]
);

for(1 .. 20) { synch(\&count, \&lalala); }

sub count {
  my $it = $_[0];  $it->r(wn); # whole rest
  ++$measure;
}

sub lalala {
  my $it = $_[0];
  $it->noop(c1,mf,o3,qn); # setup
  my $phrase_number = ($measure + -1) % 4;
  my @phrase = @{$phrases[$phrase_number]};
  foreach my $note (@phrase) { $it->n($note); }
}
$opus = make_opus or $opus = $obj->make_opus

Makes an opus (a MIDI::Opus object) out of Score, setting the opus's tick parameter (AKA "divisions") to $ticks. The opus is, incidentally, format 0, with one track.

dump_score or $obj->dump_score

Dumps Score's contents, via print (so you can select an output handle for it). Currently this is in this kind of uninspiring format:

['note', 0, 96, 1, 25, 96],
['note', 96, 96, 1, 29, 96],

as it is (currently) just a call to &MIDI::Score::dump_score; but in the future I may (should?) make it output in n/r notation. In the meantime I assume you'll use this, if at all, only for debugging purposes.

FUNCTIONS

These are subroutines that aren't methods and don't affect anything (i.e., don't have "side effects") -- they just take input and/or give output.

interval LISTREF, LIST

This takes a reference to a list of integers, and a list of note-pitch specifications (whether relative or absolute), and returns a list consisting of the given note specifications transposed by that many half-steps. E.g.,

@majors = interval [0,4,7], C, Bflat3;

which returns the list (C,E,G,Bf3,D4,F4).

note_map { BLOCK } ( ... LIST ... )

This is pretty much based on the normal map() function, altho the syntax is a bit more restrictive.

BLOCK is a segment of code to apply LIST is a list of note-pitch specifications

number_to_relative
number_to_absolute
is_note_spec(STRING)
is_relative_note_spec(STRING)
is_absolute_note_spec(STRING)
Self() or $obj->Self();

AUTHOR

Sean M. Burke sburke@netadventure.net