MIDI/Simple.pm under the hood
[email protected] ("Sean M. Burke") Thu, 11 Jul 2002 05:51:05 -0600
| Newsgroups | perl.midi |
|---|---|
| Message-ID | <[email protected]> |
Someone recently asked me some pretty astute questions about MIDI::Simple
internals, and I thought it'd be behoovy to repeat my response, to the list:
--
At 22:20 2002-07-08 -0400, you wrote:
>I have a couple of questions on MIDI::Simple: I didn't see a way to access
>specific time indeces in a non-linear way. From what I saw, the method of
>note addition seems based on pasting a new note/rest onto the end of the
>last thing added,
Not really. The interface and its underlying effects are rather
different. The interface has a notion of the present time, and of order,
and all those state variables like what the current octave is, etc.. But
what that actually makes happen is adding notes (no rests) to @Score. To wit:
% perl -d -e 1
Default die handler restored.
Loading DB routines from perl5db.pl version 1.07
Editor support available.
Enter h or `h h' for help, or `perldoc perldebug' for more help.
main::(-e:1): -1
DB<1> use MIDI::Simple
DB<2> set_tempo 500000; patch_change 1, 8; noop c1, f, o2; n qn,
Cs2; n F; n Ds; n hn, Gs_d1;
DB<3> dump_score()
@notes = ( # 7 notes...
['text_event', 0, '-e at Thu Jul 11 05:13:35 2002'],
['set_tempo', 0, 500000],
['patch_change', 0, 1, 8],
['note', 0, 96, 1, 25, 96],
['note', 96, 96, 1, 29, 96],
['note', 192, 96, 1, 27, 96],
['note', 288, 192, 1, 20, 96],
);
["@notes" should really be "@Score".]
>which is probably what most people what want, and for
>general compositions is the best way to go. But it would be cool to have
>non-linear access to where the events are placed as well, for those of us
>who like to make strange MIDI's :-)
You're quite free to manipulate @Score. It's implemented as an array, but
in fact for most (most!) purposes, you can pretend its ordering doesn't
matter. The time index of each note ($note->[1]) is what determines when
it actually happens.
What's missing is a direct way to say refer to what notes are starting (or
ending, or in progress) at a particular time. I mention that in case
that's what you're looking for.
>This other question I have is about note/rest lenghts. I didn't see a way to
>make non-standard, non-predefined lengths. I't would be cool if the length
>arguments were interchangeable with numeric values.
I think they are. In the docs, I say:
>Here are the kinds of parameters you can use in calls to n/r/noop:
>[...]
>* A numeric duration parameter. This has the form "d" followed by a
>positive (presumably nonzero) integer. Example: "d48", to set Duration to 48.
>* An alphabetic (or in theory, possibly alphanumeric) duration parameter.
>This is a key from the hash %MIDI::Simple::Length. Current legal [...]
I don't think I give many examples of the numeric durations, but let's see
how they work:
DB<1> use MIDI::Simple;
DB<2> n(dhn, F, A); n(d83, D,A); dump_score();
@notes = ( # 5 notes...
['text_event', 0, '-e at Thu Jul 11 05:26:51 2002'],
['note', 0, 288, 0, 65, 64],
['note', 0, 288, 0, 69, 64],
['note', 288, 83, 0, 62, 64],
['note', 288, 83, 0, 69, 64],
);
Mixing them seems to work okay.
In fact, looking in the module source, they seem to basically do the same
thing in the end: setting $Duration:
sub _parse_options { # common parser for n/r/noop options
[...]
foreach my $arg (@args) {
next unless length($arg); # sanity check
if($arg =~ m<^d(\d+)$>s) { # numeric duration spec
${$it->{"Duration"}} = $1;
[...]
} elsif( exists( $MIDI::Simple::Length{$arg} )) { # length spec
${$it->{"Duration"}} =
${$it->{"Tempo"}} * $MIDI::Simple::Length{$arg};
[...]
Incidentally, $Tempo is a basically a magic number that you don't need to
mess with. If you actually want to change the tempo of the piece, use the
set_tempo routine, like so:
set_tempo 500000; # 1 qn => .5 seconds (500,000 microseconds)
As to why the magic number you don't need to mess with is called $Tempo,
it's a long and irrelevent story.
>I guess in summary what I am getting at is to have the same low level access
>that the mod has under the hood. Depending how your actual source code calls
>are implemented of course.
It's basically all accessible. I just don't really document the process of
accessing it, since that would give people the idea that they'd need to
know all this stuff just to make sense of MIDI::Simple (whose docs are
already pretty long).
But if you have more questions, feel free to ask.
(Incidentally, the interface that I give examples of, above, is the
procedural interface. If you're using the OO interface, instead think
@{$score->Notes_r} instead of @notes.)
Hint to anyone reading the MIDI/Simple.pm source: basically every routine
starts out with:
my($am_method, $it) = (ref($_[0]) eq "MIDI::Simple")
? (1, shift @_)
: (0, ($package{ (caller)[0] } ||= &_package_object( (caller)[0] )) );
for sake of your sanity, pretend that all the routines are methods, and
that those three lines are simply this:
my $it = shift(@_);
The three lines you see above are just magic so that the same bit of code
can double as a procedure (like "set_tempo 500000;") and as a method (like
"$score->set_tempo(500000)"). Don't try this at home. (If I had it to do
over again, I think that I would do it a very different way!)