MIDI-Perl
view release on metacpan or search on metacpan
lib/MIDI/Simple.pm view on Meta::CPAN
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.
=head2 Routine/Method/Procedure
MIDI::Simple provides some pure functions (i.e., things that take
input, and give a return value, and that's all they do), but what
you're mostly interested in its routines. By "routine" I mean a
subroutine that you call, whether as a procedure or as a method, and
that affects data structures other than the return value.
Here I'm using "procedure" to mean a routine you call like this:
name(parameters...);
# or, just maybe:
name;
(In technical terms, I mean a non-method subroutine that can have side
effects, and which may not even provide a useful return value.) 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
like one, or the other, or both.
=head2 MAIN ROUTINES
These are the most important routines:
=over
=item new_score() or $obj = MIDI::Simple->new_score()
As a procedure, this initializes 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.
=cut
=item 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 -- or
several notes to the Score, if Notes has more than one element in it
-- or no notes at all, if Notes is empty list.
Then it moves Time ahead as appropriate. See the section "Parameters
For n/r/noop", below.
=cut
sub n { # a note
my($am_method, $it) = (ref($_[0]) eq "MIDI::Simple")
? (1, shift @_)
: (0, ($package{ (caller)[0] } ||= &_package_object( (caller)[0] )) );
&MIDI::Simple::_parse_options($it, @_);
foreach my $note_val (@{$it->{"Notes"}}) {
# which should presumably not be a null list
unless($note_val =~ /^\d+$/) {
carp "note value \"$note_val\" from Notes is non-numeric! Skipping.";
next;
}
push @{$it->{"Score"}},
['note',
int(${$it->{"Time"}}),
int(${$it->{"Duration"}}),
int(${$it->{"Channel"}}),
int($note_val),
int(${$it->{"Volume"}}),
];
}
${$it->{"Time"}} += ${$it->{"Duration"}};
return;
}
###########################################################################
=item r(...parameters...) or $obj->r(...parameters...)
This is exactly like C<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.)
=cut
sub r { # a rest
my($am_method, $it) = (ref($_[0]) eq "MIDI::Simple")
? (1, shift @_)
: (0, ($package{ (caller)[0] } ||= &_package_object( (caller)[0] )) );
&MIDI::Simple::_parse_options($it, @_);
${$it->{"Time"}} += ${$it->{"Duration"}};
return;
}
###########################################################################
=item noop(...parameters...) or $obj->noop(...parameters...)
This is exactly like C<n> and C<r>, except it never alters Score,
I<and> never changes Time. It is meant to be used for setting the
other state variables, i.e.: Channel, Duration, Octave, Volume, Notes.
=cut
sub noop { # no operation
my($am_method, $it) = (ref($_[0]) eq "MIDI::Simple")
? (1, shift @_)
: (0, ($package{ (caller)[0] } ||= &_package_object( (caller)[0] )) );
&MIDI::Simple::_parse_options($it, @_);
return;
}
#--------------------------------------------------------------------------
=back
=cut
=head2 Parameters for n/r/noop
A parameter in an C<n>, C<r>, or C<noop> call is meant to change an
attribute (AKA state variable), namely Channel, Duration, Octave,
Volume, or Notes.
Here are the kinds of parameters you can use in calls to n/r/noop:
* A numeric B<volume> parameter. This has the form "V" followed by a
positive integer in the range 0 (completely inaudible?) to 127 (AS
LOUD AS POSSIBLE). Example: "V90" sets Volume to 90.
* An alphanumeric B<volume> parameter. This is a key from the hash
C<%MIDI::Simple::Volume>. Current legal values are "ppp", "pp", "p",
"mp", "mezzo" (or "m"), "mf", "f", "ff", and "fff". Example: "ff"
sets Volume to 112. (Note that "m" isn't a good bareword, so use
"mezzo" instead, or just always remember to use quotes around "m".)
* A numeric B<channel> parameter. This has the form "c" followed by a
positive integer 0 to 15. Example: "c2", to set Channel to 2.
* A numeric B<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) B<duration>
parameter. This is a key from the hash C<%MIDI::Simple::Length>.
Current legal values start with "wn", "hn", "qn", "en", "sn" for
whole, half, quarter, eighth, or sixteenth notes. Add "d" to the
beginning of any of these to get "dotted..." (e.g., "dqn" for a dotted
quarter note). Add "dd" to the beginning of any of that first list to
get "double-dotted..." (e.g., "ddqn" for a double-dotted quarter
note). Add "t" to the beginning of any of that first list to get
"triplet..." (e.g., "tsn" for a triplet sixteenth note -- i.e. a note
such that 3 of them add up to something as long as one eighth note).
You may add to the contents of C<%MIDI::Simple::Length> to support
whatever abbreviations you want, as long as the parser can't mistake
them for any other kind of n/r/noop parameter.
* A numeric, absolute B<octave> specification. This has the form: an
"o" (lowercase oh), and then an integer in the range 0 to 10,
representing an octave 0 to 10. The Octave attribute is used only in
resolving relative note specifications, as explained further below in
this section. (All absolute note specifications also set Octave to
whatever octave they occur in.)
* A numeric, relative B<octave> specification. This has the form:
"o_d" ("d" for down) or "o_u" ("u" for down), and then an integer.
This increments, or decrements, Octave. E.g., if Octave is 6, "o_d2"
will decrement Octave by 2, making it 4. If this moves Octave below
lib/MIDI/Simple.pm view on Meta::CPAN
$octave += $1;
$octave = 10 if $octave > 10;
} else {
die "Unexpected error 5176123";
}
my $note_value = int($note + $octave * 12);
# Enforce sanity...
while($note_value < 0) { $note_value += 12 } # bump up an octave
while($note_value > 127) { $note_value -= 12 } # drop down an octave
push @new_notes, $note_value;
# 12 = number of MIDI notes in an octive
} else {
croak "Unknown note/rest option: \"$arg\"" if length($arg);
}
}
@{$it->{"Notes"}} = @new_notes if @new_notes; # otherwise inherit last list
return;
}
# Internal-use proc: create a package object for the package named.
sub _package_object {
my $package = $_[0] || die "no package!!!";
no strict;
print "Linking to package $package\n" if $Debug;
$package{$package} = bless {
# note that these are all refs, not values
"Score" => \@{"$package\::Score"},
"Time" => \${"$package\::Time"},
"Duration" => \${"$package\::Duration"},
"Channel" => \${"$package\::Channel"},
"Octave" => \${"$package\::Octave"},
"Tempo" => \${"$package\::Tempo"},
"Notes" => \@{"$package\::Notes"},
"Volume" => \${"$package\::Volume"},
"Cookies" => \%{"$package\::Cookies"},
};
&_init_score($package{$package});
return $package{$package};
}
###########################################################################
sub new_score {
my $p1 = $_[0];
my $it;
if(
defined($p1) &&
($p1 eq 'MIDI::Simple' or ref($p1) eq 'MIDI::Simple')
) { # I'm a method!
print "~ new_score as a MIDI::Simple constructor\n" if $Debug;
$it = bless {};
&_init_score($it);
} else { # I'm a proc!
my $cpackage = (caller)[0];
print "~ new_score as a proc for package $cpackage\n" if $Debug;
if( ref($package{ $cpackage }) ) { # Already exists in %package
print "~ reinitting pobj $cpackage\n" if $Debug;
&_init_score( $it = $package{ $cpackage } );
# no need to call _package_object
} else { # Doesn't exist in %package
print "~ new pobj $cpackage\n" if $Debug;
$package{ $cpackage } = $it = &_package_object( $cpackage );
# no need to call _init_score
}
}
return $it; # for object use, we'll be capturing this
}
sub _init_score { # Set some default initial values for the object
my $it = $_[0];
print "Initting score $it\n" if $Debug;
@{$it->{"Score"}} = (['text_event', 0, "$0 at " . scalar(localtime) ]);
${$it->{"Time"}} = 0;
${$it->{"Duration"}} = 96; # a whole note
${$it->{"Channel"}} = 0;
${$it->{"Octave"}} = 5;
${$it->{"Tempo"}} = 96; # ticks per qn
@{$it->{"Notes"}} = (60); # middle C. why not.
${$it->{"Volume"}} = 64; # normal
%{$it->{"Cookies"}} = (); # empty
return;
}
###########################################################################
###########################################################################
=head2 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?
Like all methods in this class, all the above-named attribute methods
double as procedures that act on the default object -- in other words,
you can say:
Volume 10; # same as: $Volume = 10;
@score_copy = Score; # same as: @score_copy = @Score
Score @new_score; # same as: @Score = @new_score;
$score_ref = Score_r; # same as: $score_ref = \@Score
Volume(Volume + 10) # same as: $Volume += 10
But, stylistically, I suggest not using these procedures -- just
directly access the variables instead.
=cut
#--------------------------------------------------------------------------
# read-or-write methods
sub Score (;\@) { # yes, a prototype!
my($am_method, $it) = (ref($_[0]) eq "MIDI::Simple")
? (1, shift @_)
: (0, ($package{ (caller)[0] } ||= &_package_object( (caller)[0] )) );
if(@_) {
if($am_method){
@{$it->{'Score'}} = @_;
} else {
@{$it->{'Score'}} = @{$_[0]}; # sneaky, huh!
}
return; # special case -- return nothing if this is a PUT
} else {
return @{$it->{'Score'}}; # you asked for it
}
}
sub Cookies {
my($it) = (ref($_[0]) eq "MIDI::Simple") ? (shift @_)
: ($package{ (caller)[0] } ||= &_package_object( (caller)[0] ));
%{$it->{'Cookies'}} = @_ if @_; # Better have an even number of elements!
return %{$it->{'Cookies'}};
}
sub Time {
my($it) = (ref($_[0]) eq "MIDI::Simple") ? (shift @_)
: ($package{ (caller)[0] } ||= &_package_object( (caller)[0] ));
${$it->{'Time'}} = $_[0] if @_;
return ${$it->{'Time'}};
}
sub Duration {
my($it) = (ref($_[0]) eq "MIDI::Simple") ? (shift @_)
: ($package{ (caller)[0] } ||= &_package_object( (caller)[0] ));
${$it->{'Duration'}} = $_[0] if @_;
return ${$it->{'Duration'}};
}
sub Channel {
my($it) = (ref($_[0]) eq "MIDI::Simple") ? (shift @_)
: ($package{ (caller)[0] } ||= &_package_object( (caller)[0] ));
${$it->{'Channel'}} = $_[0] if @_;
return ${$it->{'Channel'}};
}
sub Octave {
my($it) = (ref($_[0]) eq "MIDI::Simple") ? (shift @_)
: ($package{ (caller)[0] } ||= &_package_object( (caller)[0] ));
${$it->{'Octave'}} = $_[0] if @_;
return ${$it->{'Octave'}};
}
sub Tempo {
my($it) = (ref($_[0]) eq "MIDI::Simple") ? (shift @_)
: ($package{ (caller)[0] } ||= &_package_object( (caller)[0] ));
${$it->{'Tempo'}} = $_[0] if @_;
return ${$it->{'Tempo'}};
}
sub Notes {
my($it) = (ref($_[0]) eq "MIDI::Simple") ? (shift @_)
: ($package{ (caller)[0] } ||= &_package_object( (caller)[0] ));
@{$it->{'Notes'}} = @_ if @_;
return @{$it->{'Notes'}};
}
sub Volume {
my($it) = (ref($_[0]) eq "MIDI::Simple") ? (shift @_)
: ($package{ (caller)[0] } ||= &_package_object( (caller)[0] ));
${$it->{'Volume'}} = $_[0] if @_;
return ${$it->{'Volume'}};
}
#-#-#-#-#-#-#-#-##-#-#-#-#-#-#-#-#-#-#-#-##-#-#-#-#-#-#-#-##-#-#-#-#-#-#-#-
# read-only methods that return references
sub Score_r {
my($it) = (ref($_[0]) eq "MIDI::Simple") ? (shift @_)
: ($package{ (caller)[0] } ||= &_package_object( (caller)[0] ));
return $it->{'Score'};
}
sub Time_r {
my($it) = (ref($_[0]) eq "MIDI::Simple") ? (shift @_)
: ($package{ (caller)[0] } ||= &_package_object( (caller)[0] ));
return $it->{'Time'};
}
sub Duration_r {
my($it) = (ref($_[0]) eq "MIDI::Simple") ? (shift @_)
: ($package{ (caller)[0] } ||= &_package_object( (caller)[0] ));
return $it->{'Duration'};
}
sub Channel_r {
my($it) = (ref($_[0]) eq "MIDI::Simple") ? (shift @_)
: ($package{ (caller)[0] } ||= &_package_object( (caller)[0] ));
return $it->{'Channel'};
}
sub Octave_r {
my($it) = (ref($_[0]) eq "MIDI::Simple") ? (shift @_)
: ($package{ (caller)[0] } ||= &_package_object( (caller)[0] ));
return $it->{'Octave'};
}
sub Tempo_r {
my($it) = (ref($_[0]) eq "MIDI::Simple") ? (shift @_)
: ($package{ (caller)[0] } ||= &_package_object( (caller)[0] ));
return $it->{'Tempo'};
}
sub Notes_r {
my($it) = (ref($_[0]) eq "MIDI::Simple") ? (shift @_)
: ($package{ (caller)[0] } ||= &_package_object( (caller)[0] ));
return $it->{'Notes'};
}
sub Volume_r {
my($it) = (ref($_[0]) eq "MIDI::Simple") ? (shift @_)
: ($package{ (caller)[0] } ||= &_package_object( (caller)[0] ));
return $it->{'Volume'};
}
sub Cookies_r {
my($it) = (ref($_[0]) eq "MIDI::Simple") ? (shift @_)
: ($package{ (caller)[0] } ||= &_package_object( (caller)[0] ));
return $it->{'Cookies'};
}
###########################################################################
###########################################################################
=head2 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
L<MIDI::Event> for an explanation of what the data types (like
"velocity" or "pitch_wheel") mean. I've reordered this list so that
what I guess are the most important ones are toward the top:
=over
=item patch_change I<channel>, I<patch>;
=item key_after_touch I<channel>, I<note>, I<velocity>;
=item channel_after_touch I<channel>, I<velocity>;
=item control_change I<channel>, I<controller(0-127)>, I<value(0-127)>;
=item pitch_wheel_change I<channel>, I<pitch_wheel>;
=item set_tempo I<tempo>; (See the section on tempo, below.)
=item smpte_offset I<hr>, I<mn>, I<se>, I<fr>, I<ff>;
=item time_signature I<nn>, I<dd>, I<cc>, I<bb>;
=item key_signature I<sf>, I<mi>;
=item text_event I<text>;
=item copyright_text_event I<text>;
=item track_name I<text>;
=item instrument_name I<text>;
=item lyric I<text>;
=item set_sequence_number I<sequence>;
=item marker I<text>;
=item cue_point I<text>;
=item sequencer_specific I<raw>;
=item sysex_f0 I<raw>;
lib/MIDI/Simple.pm view on Meta::CPAN
In other words, this says to make each quarter note take up 500,000
microseconds, namely .5 seconds. And there's 120 of those
half-seconds to the minute; so, 120 quarter notes to the minute.
If you see a "[quarter note symbol] = 160" in a piece of sheet music,
and you want to figure out what number you need for the C<set_tempo>,
do:
60_000_000 / 160 ... and you get: 375_000
Therefore, you should call:
set_tempo 375_000;
So in other words, this general formula:
set_tempo int(60_000_000 / $quarter_notes_per_minute);
should do you fine.
As to the Tempo/Duration parameter, leave it alone and just assume
that 96 ticks-per-quarter-note is a universal constant, and you'll be
happy.
(You may wonder: Why 96? As far as I've worked out, all permutations
of the normal note lengths (whole, half, quarter, eighth, sixteenth,
and even thirty-second notes) and tripletting, dotting, or
double-dotting, times 96, all produce integers. For example, if a
quarter note is 96 ticks, then a double-dotted thirty-second note is
21 ticks (i.e., 1.75 * 1/8 * 96). But that'd be a messy 10.5 if there
were only 48 ticks to a quarter note. Now, if you wanted a quintuplet
anywhere, you'd be out of luck, since 96 isn't a factor of five. It's
actually 3 * (2 ** 5), i.e., three times two to the fifth. If you
really need quintuplets, then you have my very special permission to
mess with the Tempo attribute -- I suggest multiples of 96, e.g., 5 *
96.)
(You may also have read in L<MIDI::Filespec> that C<time_signature>
allows you to define an arbitrary mapping of your concept of quarter
note, to MIDI's concept of quarter note. For your sanity and mine,
leave them the same, at a 1:1 mapping -- i.e., with an '8' for
C<time_signature>'s last parameter, for "eight notated 32nd-notes per
MIDI quarter note". And this is relevant only if you're calling
C<time_signature> anyway, which is not necessarily a given.)
=cut
###########################################################################
###########################################################################
=head2 MORE ROUTINES
=over
=cut
sub _test_proc {
my($am_method, $it) = (ref($_[0]) eq "MIDI::Simple")
? (1, shift @_)
: (0, ($package{ (caller)[0] } ||= &_package_object( (caller)[0] )) );
print " am method: $am_method\n it: $it\n params: <", join(',',@_), ">\n";
}
###########################################################################
=item $opus = write_score I<filespec>
=item $opus = $obj->write_score(I<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 C<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: C<write_score *STDOUT{IO}>.)
=cut
sub write_score {
my($am_method, $it) = (ref($_[0]) eq "MIDI::Simple")
? (1, shift @_)
: (0, ($package{ (caller)[0] } ||= &_package_object( (caller)[0] )) );
my($out, $ticks, $score_r) =
( $_[0], (${$it->{'Tempo'}} || 96), $it->{'Score'} );
croak "First parameter to MIDI::Simple::write_score can't be null\n"
unless( ref($out) || length($out) );
croak "Ticks can't be 0" unless $ticks;
carp "Writing a score with no notes!" unless @$score_r;
my $opus = $it->make_opus;
# $opus->dump( { 'dump_tracks' => 1 } );
if(ref($out)) {
$opus->write_to_handle($out);
} else {
$opus->write_to_file($out);
}
return $opus; # capture it if you want it.
}
###########################################################################
=item read_score I<filespec>
=item $obj = MIDI::Simple->read_score('foo.mid'))
In the first case (a procedure call), does C<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, C<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: I<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: C<read_score *STDIN{IO}>.)
If ever you have to make a Score out of a single track from a
I<multitrack> file, read the file into an $opus, and then consider
something like:
new_score;
$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;
=cut
sub read_score {
my $am_cons = ($_[0] eq "MIDI::Simple");
shift @_ if $am_cons;
my $in = $_[0];
my($track, @eventful_tracks);
croak "First parameter to MIDI::Simple::read_score can't be null\n"
unless( ref($in) || length($in) );
my $in_switch = ref($in) ? 'from_handle' : 'from_file';
my $opus = MIDI::Opus->new({ $in_switch => $in });
@eventful_tracks = grep( scalar(@{$_->events_r}), $opus->tracks );
if(@eventful_tracks == 0) {
croak "Opus from $in has NO eventful tracks to consider as a score!\n";
} elsif (@eventful_tracks > 1) {
croak
"Opus from $in has too many (" .
scalar(@eventful_tracks) . ") tracks to be a score.\n";
} # else OK...
$track = $eventful_tracks[0];
#print scalar($track->events), " events in track\n";
# If ever you want just a single track as a score, here's how:
#my $score_r = ( MIDI::Score::events_r_to_score_r($track->events_r) )[0];
my( $score_r, $time) = MIDI::Score::events_r_to_score_r($track->events_r);
#print scalar(@$score_r), " notes in score\n";
my $it;
if($am_cons) { # just make a new object and return it.
$it = MIDI::Simple->new_score;
$it->{'Score'} = $score_r;
} else { # need to fudge it back into the pobj
my $cpackage = (caller)[0];
#print "~ read_score as a proc for package $cpackage\n";
if( ref($package{ $cpackage }) ) { # Already exists in %package
print "~ reinitting pobj $cpackage\n" if $Debug;
&_init_score( $it = $package{ $cpackage } );
# no need to call _package_object
} else { # Doesn't exist in %package
print "~ new pobj $cpackage\n" if $Debug;
$package{ $cpackage } = $it = &_package_object( $cpackage );
# no need to call _init_score
}
@{$it->{'Score'}} = @$score_r;
}
${$it->{'Tempo'}} = $opus->ticks;
${$it->{'Time'}} = $time;
return $it;
}
###########################################################################
=item synch( LIST of coderefs )
=item $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 C<(\&foo, \&bar, \&baz)>, or a mixture of
both) that C<synch> calls in order to add to the given object -- which
in the first form is the package's default object, and which in the
second case is C<$obj>. What C<synch> does is:
* remember the initial value of 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 the value of Time is, after each call;
* then, after having called all of the routines, set Time to whatever
was the greatest (equals latest) value of Time that resulted from any
of the calls to the routines.
The coderefs are all called with one argument in C<@_> -- the object
they are supposed to affect. All these routines should/must therefore
use method calls instead of procedure calls. 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
# not just "r('wn')" -- we want a method, not a procedure!
++$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); }
}
=cut
sub synch {
my($am_method, $it) = (ref($_[0]) eq "MIDI::Simple")
? (1, shift @_)
: (0, ($package{ (caller)[0] } ||= &_package_object( (caller)[0] )) );
my @subs = grep(ref($_) eq 'CODE', @_);
print " My subs: ", map("<$_> ", @subs), ".\n"
if $Debug;
return unless @subs;
# my @end_times = (); # I am the Lone Array of the Apocalypse!
my $orig_time = ${$it->{'Time'}};
my $max_time = $orig_time;
foreach my $sub (@subs) {
printf " Before %s\: Entry time: %s Score items: %s\n",
$sub, $orig_time, scalar(@{$it->{'Score'}}) if $Debug;
${$it->{'Time'}} = $orig_time; # reset Time
&{$sub}($it); # now call it
printf " %s items ending at %s\n",
scalar( @{$it->{'Score'}} ), ${$it->{'Time'}} if $Debug;
$max_time = ${$it->{'Time'}} if ${$it->{'Time'}} > $max_time;
}
print " max end-time of subs: $max_time\n" if $Debug;
# now update and get out
${$it->{'Time'}} = $max_time;
}
###########################################################################
=item $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.
=cut
sub make_opus {
# Make a format-0 one-track MIDI out of this score.
my($am_method, $it) = (ref($_[0]) eq "MIDI::Simple")
? (1, shift @_)
: (0, ($package{ (caller)[0] } ||= &_package_object( (caller)[0] )) );
my($ticks, $score_r) = (${$it->{'Tempo'}}, $it->{'Score'});
carp "Encoding a score with no notes!" unless @$score_r;
my $events_r = ( MIDI::Score::score_r_to_events_r($score_r) )[0];
carp "Creating a track with no events!" unless @$events_r;
my $opus =
MIDI::Opus->new({ 'ticks' => $ticks,
'format' => 0,
'tracks' => [ MIDI::Track->new({
'events' => $events_r
}) ]
});
return $opus;
}
###########################################################################
=item dump_score or $obj->dump_score
Dumps Score's contents, via C<print> (so you can C<select()> an output
handle for it). Currently this is in this somewhat uninspiring format:
['note', 0, 96, 1, 25, 96],
['note', 96, 96, 1, 29, 96],
as it is (currently) just a call to C<&MIDI::Score::dump_score>; but in
the future I may (should?) make it output in C<n>/C<r> notation. In
the meantime I assume you'll use this, if at all, only for debugging
purposes.
=cut
sub dump_score {
my($am_method, $it) = (ref($_[0]) eq "MIDI::Simple")
? (1, shift @_)
: (0, ($package{ (caller)[0] } ||= &_package_object( (caller)[0] )) );
return &MIDI::Score::dump_score( $it->{'Score'} );
}
###########################################################################
###########################################################################
=back
=head2 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.
=over
=item 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<(C,E,G,Bf3,D4,F4)>.
Items in LIST which aren't note specifications are passed thru
unaltered.
=cut
sub interval { # apply an interval to a list of notes.
my(@out);
my($am_method, $it) = (ref($_[0]) eq "MIDI::Simple")
? (1, shift @_)
: (0, ($package{ (caller)[0] } ||= &_package_object( (caller)[0] )) );
my($interval_r, @notes) = @_;
croak "first argument to &MIDI::Simple::interval must be a listref\n"
unless ref($interval_r);
# or a valid key into a hash %Interval?
foreach my $note (@notes) {
my(@them, @status, $a_flag, $note_number);
@status = &is_note_spec($note);
unless(@status) { # not a note spec
push @out, $note;
}
($a_flag, $note_number) = @status;
@them = map { $note_number + $_ } @$interval_r;
if($a_flag) { # If based on an absolute note spec.
if($note =~ m<^\d+$>s) { # "12"
# no-op -- leave as is
} elsif ($note =~ m<^n\d+$>s) { # "n12"
@them = map("n$_", @them);
} else { # "C4"
@them = map(&number_to_absolute($_), @them);
}
} else { # If based on a relative note spec.
@them = map(&number_to_relative($_), @them);
}
push @out, @them;
}
return @out;
}
#--------------------------------------------------------------------------
=item note_map { BLOCK } LIST
This is pretty much based on (or at least inspired by) the normal Perl
C<map> function, altho the syntax is a bit more restrictive (i.e.,
C<map> can take the form C<map {BLOCK} LIST> or C<map(EXPR,LIST)> --
the latter won't work with C<note_map>).
C<note_map {BLOCK} (LIST)> evaluates the BLOCK for each element of
LIST (locally setting $_ to each element's note-number value) and
returns the list value composed of the results of each such
evaluation. Evaluates BLOCK in a list context, so each element of
LIST may produce zero, one, or more elements in the returned value.
Moreover, besides setting $_, C<note_map> feeds BLOCK (which it sees
as an anonymous subroutine) three parameters, which BLOCK can access
in @_ :
$_[0] : Same as $_. I.e., The current note-specification,
as a note number.
This is the result of having fed the original note spec
(which you can see in $_[2]) to is_note_spec.
$_[1] : The absoluteness flag for this note, from the
above-mentioned call to is_note_spec.
0 = it was relative (like 'C')
1 = it was absolute (whether as 'C4' or 'n41' or '41')
$_[2] : the actual note specification from LIST, if you want
lib/MIDI/Simple.pm view on Meta::CPAN
}
unless($note_number == -1) {
@ret = ( $note_number + $octave_number * 12 );
}
return @ret;
}
=item is_absolute_note_spec STRING
Just like C<is_relative_note_spec>, but for absolute note
specifications instead of relative ones.
=cut
sub is_absolute_note_spec ($) {
# if false, return()
# if true, return($note_number)
my($note_number, $in, @ret) = (-1, $_[0]);
return() unless length $in;
if( $in =~ /^n?(\d+)$/s ) { # E.g., "29", "n38"
$note_number = 0 + $1;
} elsif( $in =~ /^([A-Za-z]+)(\d+)/s ) { # E.g., "C3", "As4"
$note_number = $MIDI::Simple::Note{$1} + $2 * 12
if exists($MIDI::Simple::Note{$1});
}
@ret = ($note_number) if( $note_number >= 0 and $note_number < 128);
return @ret;
}
#--------------------------------------------------------------------------
=item Self() or $obj->Self();
Presumably the second syntax is useless -- it just returns $obj. But
the first syntax returns the current package's default object.
Suppose you write a routine, C<funkify>, that does something-or-other
to a given MIDI::Simple object. You could write it so that acts on
the current package's default object, which is fine -- but, among
other things, that means you can't call C<funkify> from a sub you have
C<synch> call, since such routines should/must use only method calls.
So let's say that, instead, you write C<funkify> so that the first
argument to it is the object to act on. If the MIDI::Simple object
you want it to act on is it C<$sonata>, you just say
funkify($sonata)
However, if you want it to act on the current package's default
MIDI::Simple object, what to say? Simply,
$package_opus = Self;
funkify($package_opus);
=cut
sub Self { # pointless as a method -- but as a sub, useful if
# you want to access your current package's object.
# Juuuuuust in case you need it.
my($am_method, $it) = (ref($_[0]) eq "MIDI::Simple")
? (1, shift @_)
: (0, ($package{ (caller)[0] } ||= &_package_object( (caller)[0] )) );
return $it;
}
=back
=cut
###########################################################################
=head1 COPYRIGHT
Copyright (c) 1998-2005 Sean M. Burke. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=head1 AUTHOR
Sean M. Burke C<sburke@cpan.org>
=cut
1;
__END__
( run in 2.516 seconds using v1.01-cache-2.11-cpan-cdf2f3d4e48 )