File Coverage

blib/lib/MIDI/Score.pm
Criterion Covered Total %
statement 110 153 71.9
branch 32 62 51.6
condition 8 15 53.3
subroutine 9 11 81.8
pod 8 8 100.0
total 167 249 67.0


line stmt bran cond sub pod time code
1              
2             # Time-stamp: "2013-02-01 22:40:45 conklin"
3             require 5;
4             package MIDI::Score;
5 11     11   63 use strict;
  11         22  
  11         504  
6 11     11   155 use vars qw($Debug $VERSION);
  11         23  
  11         1603  
7 11     11   59 use Carp;
  11         21  
  11         22511  
8              
9             $VERSION = '0.83';
10              
11             =head1 NAME
12              
13             MIDI::Score - MIDI scores
14              
15             =head1 SYNOPSIS
16              
17             # it's a long story; see below
18              
19             =head1 DESCRIPTION
20              
21             This module provides functions to do with MIDI scores.
22             It is used as the basis for all the functions in MIDI::Simple.
23             (Incidentally, MIDI::Opus's draw() method also uses some of the
24             functions in here.)
25              
26             Whereas the events in a MIDI event structure are items whose timing
27             is expressed in delta-times, the timing of items in a score is
28             expressed as an absolute number of ticks from the track's start time.
29             Moreover, pairs of 'note_on' and 'note_off' events in an event structure
30             are abstracted into a single 'note' item in a score structure.
31              
32             'note' takes the following form:
33              
34             ('note_on', I, I, I, I, I)
35              
36             The problem that score structures are meant to solve is that 1)
37             people definitely don't think in delta-times -- they think in absolute
38             times or in structures based on that (like 'time from start of measure');
39             2) people think in notes, not note_on and note_off events.
40              
41             So, given this event structure:
42              
43             ['text_event', 0, 'www.ely.anglican.org/parishes/camgsm/chimes.html'],
44             ['text_event', 0, 'Lord through this hour/ be Thou our guide'],
45             ['text_event', 0, 'so, by Thy power/ no foot shall slide'],
46             ['patch_change', 0, 1, 8],
47             ['note_on', 0, 1, 25, 96],
48             ['note_off', 96, 0, 1, 0],
49             ['note_on', 0, 1, 29, 96],
50             ['note_off', 96, 0, 1, 0],
51             ['note_on', 0, 1, 27, 96],
52             ['note_off', 96, 0, 1, 0],
53             ['note_on', 0, 1, 20, 96],
54             ['note_off', 192, 0, 1, 0],
55             ['note_on', 0, 1, 25, 96],
56             ['note_off', 96, 0, 1, 0],
57             ['note_on', 0, 1, 27, 96],
58             ['note_off', 96, 0, 1, 0],
59             ['note_on', 0, 1, 29, 96],
60             ['note_off', 96, 0, 1, 0],
61             ['note_on', 0, 1, 25, 96],
62             ['note_off', 192, 0, 1, 0],
63             ['note_on', 0, 1, 29, 96],
64             ['note_off', 96, 0, 1, 0],
65             ['note_on', 0, 1, 25, 96],
66             ['note_off', 96, 0, 1, 0],
67             ['note_on', 0, 1, 27, 96],
68             ['note_off', 96, 0, 1, 0],
69             ['note_on', 0, 1, 20, 96],
70             ['note_off', 192, 0, 1, 0],
71             ['note_on', 0, 1, 20, 96],
72             ['note_off', 96, 0, 1, 0],
73             ['note_on', 0, 1, 27, 96],
74             ['note_off', 96, 0, 1, 0],
75             ['note_on', 0, 1, 29, 96],
76             ['note_off', 96, 0, 1, 0],
77             ['note_on', 0, 1, 25, 96],
78             ['note_off', 192, 0, 1, 0],
79              
80             here is the corresponding score structure:
81              
82             ['text_event', 0, 'www.ely.anglican.org/parishes/camgsm/chimes.html'],
83             ['text_event', 0, 'Lord through this hour/ be Thou our guide'],
84             ['text_event', 0, 'so, by Thy power/ no foot shall slide'],
85             ['patch_change', 0, 1, 8],
86             ['note', 0, 96, 1, 25, 96],
87             ['note', 96, 96, 1, 29, 96],
88             ['note', 192, 96, 1, 27, 96],
89             ['note', 288, 192, 1, 20, 96],
90             ['note', 480, 96, 1, 25, 96],
91             ['note', 576, 96, 1, 27, 96],
92             ['note', 672, 96, 1, 29, 96],
93             ['note', 768, 192, 1, 25, 96],
94             ['note', 960, 96, 1, 29, 96],
95             ['note', 1056, 96, 1, 25, 96],
96             ['note', 1152, 96, 1, 27, 96],
97             ['note', 1248, 192, 1, 20, 96],
98             ['note', 1440, 96, 1, 20, 96],
99             ['note', 1536, 96, 1, 27, 96],
100             ['note', 1632, 96, 1, 29, 96],
101             ['note', 1728, 192, 1, 25, 96]
102              
103             Note also that scores aren't crucially ordered. So this:
104              
105             ['note', 768, 192, 1, 25, 96],
106             ['note', 960, 96, 1, 29, 96],
107             ['note', 1056, 96, 1, 25, 96],
108              
109             means the same thing as:
110              
111             ['note', 960, 96, 1, 29, 96],
112             ['note', 768, 192, 1, 25, 96],
113             ['note', 1056, 96, 1, 25, 96],
114              
115             The only exception to this is in the case of things like:
116              
117             ['patch_change', 200, 2, 15],
118             ['note', 200, 96, 2, 25, 96],
119              
120             where two (or more) score items happen I and where one
121             affects the meaning of the other.
122              
123             =head1 WHAT CAN BE IN A SCORE
124              
125             Besides the new score structure item C (covered above),
126             the possible contents of a score structure can be summarized thus:
127             Whatever can appear in an event structure can appear in a score
128             structure, save that its second parameter denotes not a
129             delta-time in ticks, but instead denotes the absolute number of ticks
130             from the start of the track.
131              
132             To avoid the long periphrase "items in a score structure", I will
133             occasionally refer to items in a score structure as "notes", whether or
134             not they are actually C commands. This leaves "event" to
135             unambiguously denote items in an event structure.
136              
137             These, below, are all the items that can appear in a score.
138             This is basically just a repetition of the table in
139             L, with starttime substituting for dtime --
140             so refer to L for an explanation of what the data types
141             (like "velocity" or "pitch_wheel").
142             As far as order, the first items are generally the most important:
143              
144             =over
145              
146             =item ('note', I, I, I, I, I)
147              
148             =item ('key_after_touch', I, I, I, I)
149              
150             =item ('control_change', I, I, I, I)
151              
152             =item ('patch_change', I, I, I)
153              
154             =item ('channel_after_touch', I, I, I)
155              
156             =item ('pitch_wheel_change', I, I, I)
157              
158             =item ('set_sequence_number', I, I)
159              
160             =item ('text_event', I, I)
161              
162             =item ('copyright_text_event', I, I)
163              
164             =item ('track_name', I, I)
165              
166             =item ('instrument_name', I, I)
167              
168             =item ('lyric', I, I)
169              
170             =item ('marker', I, I)
171              
172             =item ('cue_point', I, I)
173              
174             =item ('text_event_08', I, I)
175              
176             =item ('text_event_09', I, I)
177              
178             =item ('text_event_0a', I, I)
179              
180             =item ('text_event_0b', I, I)
181              
182             =item ('text_event_0c', I, I)
183              
184             =item ('text_event_0d', I, I)
185              
186             =item ('text_event_0e', I, I)
187              
188             =item ('text_event_0f', I, I)
189              
190             =item ('end_track', I)
191              
192             =item ('set_tempo', I, I)
193              
194             =item ('smpte_offset', I, I
, I, I, I, I)
195              
196             =item ('time_signature', I, I, I
, I, I)
197              
198             =item ('key_signature', I, I, I)
199              
200             =item ('sequencer_specific', I, I)
201              
202             =item ('raw_meta_event', I, I(0-255), I)
203              
204             =item ('sysex_f0', I, I)
205              
206             =item ('sysex_f7', I, I)
207              
208             =item ('song_position', I)
209              
210             =item ('song_select', I, I)
211              
212             =item ('tune_request', I)
213              
214             =item ('raw_data', I, I)
215              
216             =back
217              
218              
219             =head1 FUNCTIONS
220              
221             This module provides these functions:
222              
223             =over
224              
225             =item $score2_r = MIDI::Score::copy_structure($score_r)
226              
227             This takes a I to a score structure, and returns a
228             I to a copy of it. Example usage:
229              
230             @new_score = @{ MIDI::Score::copy_structure( \@old_score ) };
231              
232             =cut
233              
234             sub copy_structure {
235 0     0 1 0 return &MIDI::Event::copy_structure(@_);
236             # hey, a LoL is an LoL
237             }
238             ##########################################################################
239              
240             =item $events_r = MIDI::Score::score_r_to_events_r( $score_r )
241              
242             =item ($events_r, $ticks) = MIDI::Score::score_r_to_events_r( $score_r )
243              
244             This takes a I to a score structure, and converts it to an
245             event structure, which it returns a I to. In list context,
246             also returns a second value, a count of the number of ticks that
247             structure takes to play (i.e., the end-time of the temporally last
248             item).
249              
250             =cut
251              
252             sub score_r_to_events_r {
253             # list context: Returns the events_r AND the total tick time
254             # scalar context: Returns events_r
255 3     3 1 360 my $score_r = $_[0];
256 3         7 my $time = 0;
257 3         7 my @events = ();
258 3 50       15 croak "MIDI::Score::score_r_to_events_r's first arg must be a listref"
259             unless ref($score_r);
260              
261             # First, turn instances of 'note' into 'note_on' and 'note_off':
262 3         7 foreach my $note_r (@$score_r) {
263 46 50       89 next unless ref $note_r;
264 46 100       90 if($note_r->[0] eq 'note') {
265 38         81 my @note_on = @$note_r;
266             #print "In: ", map("<$_>", @note_on), "\n";
267 38         44 $note_on[0] = 'note_on';
268 38         53 my $duration = splice(@note_on, 2, 1);
269              
270 38         91 my @note_off = @note_on; # /now/ copy it
271 38         43 $note_off[0] = 'note_off';
272 38         42 $note_off[1] += $duration;
273 38         35 $note_off[4] = 0; # set volume to 0
274 38         116 push(@events, \@note_on, \@note_off);
275             #print "on: ", map("<$_>", @note_on), "\n";
276             #print "off: ", map("<$_>", @note_off), "\n";
277             } else {
278 8         24 push(@events, [@$note_r]);
279             }
280             }
281             # warn scalar(@events), " events in $score_r";
282 3         14 $score_r = sort_score_r(\@events);
283             # warn scalar(@$score_r), " events in $score_r";
284              
285             # Now we turn it into an event structure by fiddling the timing
286 3         15 $time = 0;
287 3         11 foreach my $event (@$score_r) {
288 84 50 33     275 next unless ref($event) && @$event;
289 84         96 my $delta = $event->[1] - $time; # Figure out the delta
290 84         83 $time = $event->[1]; # Move it forward
291 84         99 $event->[1] = $delta; # Swap it in
292             }
293 3 100       24 return($score_r, $time) if wantarray;
294 2         7 return $score_r;
295             }
296             ###########################################################################
297              
298             =item $score2_r = MIDI::Score::sort_score_r( $score_r)
299              
300             This takes a I to a score structure, and returns a
301             I to a sorted (by time) copy of it. Example usage:
302              
303             @sorted_score = @{ MIDI::Score::sort_score_r( \@old_score ) };
304              
305             =cut
306              
307             sub sort_score_r {
308             # take a reference to a score LoL, and sort it by note start time,
309             # and return a reference to that sorted LoL. Notes from the same
310             # time must be left in the order they're found!!!! That's why we can't
311             # just use sort { $a->[1] <=> $b->[1] } (@$score_r)
312 3     3 1 8 my $score_r = $_[0];
313 3         13 my %timing = ();
314 3         7 foreach my $note_r (@$score_r) {
315             push(
316 84 50       162 @{$timing{
317 84         291 $note_r->[1]
318             }},
319             $note_r
320             ) if ref($note_r);
321             }
322             # warn scalar(@$score_r), " events in $score_r";
323             #print "sequencing for times: ", map("<$_> ",
324             # sort {$a <=> $b} keys(%timing)
325             # ), "\n";
326              
327             return
328             [
329 51         144 map(@{ $timing{$_} },
  168         163  
330 3         42 sort {$a <=> $b} keys(%timing)
331             )
332             ];
333             }
334             ###########################################################################
335              
336             =item $score_r = MIDI::Score::events_r_to_score_r( $events_r )
337              
338             =item ($score_r, $ticks) = MIDI::Score::events_r_to_score_r( $events_r )
339              
340             This takes a I to an event structure, converts it to a
341             score structure, which it returns a I to. If called in
342             list context, also returns a count of the number of ticks that
343             structure takes to play (i.e., the end-time of the temporally last
344             item).
345              
346             =cut
347              
348             sub events_r_to_score_r {
349             # Returns the score_r AND the total tick time
350 6     6 1 11 my $events_r = $_[0];
351 6 50       14 croak "first argument to MIDI::Score::events_to_score is not a listref!"
352             unless $events_r;
353 6 50       16 my $options_r = ref($_[1]) ? $_[1] : {};
354              
355 6         9 my $time = 0;
356 6 50       15 if( $options_r->{'no_note_abstraction'} ) {
357 0         0 my $score_r = MIDI::Event::copy_structure($events_r);
358 0         0 foreach my $event_r (@$score_r) {
359             # print join(' ', @$event_r), "\n";
360 0 0       0 $event_r->[1] = ($time += $event_r->[1]) if ref($event_r);
361             }
362 0 0       0 return($score_r, $time) if wantarray;
363 0         0 return $score_r;
364             } else {
365 6         10 my %note = ();
366             my @score =
367             map
368             {
369 6 50       16 if(!ref($_)) {
  112         185  
370 0         0 ();
371             } else {
372             # 0.82: the following must be declared local
373 112         317 local $_ = [@$_]; # copy.
374 112 50       270 $_->[1] = ($time += $_->[1]) if ref($_);
375            
376 112 100 66     463 if($_->[0] eq 'note_off'
    100 66        
377             or($_->[0] eq 'note_on' &&
378             $_->[4] == 0) )
379             { # End of a note
380             # print "Note off : @$_\n";
381             # 0.82: handle multiple prior events with same chan/note.
382 42 50 33     46 if ((exists $note{pack 'CC', @{$_}[2,3]}) && (@{$note{pack 'CC', @{$_}[2,3]}})) {
  42         128  
  42         48  
  42         154  
383 42         38 shift(@{$note{pack 'CC', @{$_}[2,3]}})->[2] += $time;
  42         54  
  42         111  
384 42 100       42 unless(@{$note{pack 'CC', @{$_}[2,3]}}) {delete $note{pack 'CC', @{$_}[2,3]};}
  42         66  
  42         108  
  41         42  
  41         90  
385             }
386 42         91 (); # Erase this event.
387             } elsif ($_->[0] eq 'note_on') {
388             # Start of a note
389 42         86 $_ = [@$_];
390            
391 42         55 push(@{$note{ pack 'CC', @{$_}[2,3] }},$_);
  42         46  
  42         133  
392 42         98 splice(@$_, 2, 0, -$time);
393 42         59 $_->[0] = 'note';
394             # ('note', Starttime, Duration, Channel, Note, Veloc)
395 42         75 $_;
396             } else {
397 28         81 $_;
398             }
399             }
400             }
401             @$events_r
402             ;
403              
404             #print "notes remaining on stack: ", scalar(values %note), "\n"
405             # if values %note;
406             # 0.82: clean up pending events gracefully
407 6         23 foreach my $k (keys %note) {
408 0         0 foreach my $one (@{$note{$k}}) {
  0         0  
409 0         0 $one->[2] += $time;
410             }
411             }
412 6 50       18 return(\@score, $time) if wantarray;
413 6         25 return \@score;
414             }
415             }
416             ###########################################################################
417              
418             =item $ticks = MIDI::Score::score_r_time( $score_r )
419              
420             This takes a I to a score structure, and returns
421             a count of the number of ticks that structure takes to play
422             (i.e., the end-time of the temporally last item).
423              
424             =cut
425              
426             sub score_r_time {
427             # returns the duration of the score you pass a reference to
428 2     2 1 12 my $score_r = $_[0];
429 2 50       20 croak "arg 1 of MIDI::Score::score_r_time isn't a ref" unless ref $score_r;
430 2         4 my $track_time = 0;
431 2         5 foreach my $event_r (@$score_r) {
432 31 50       53 next unless @$event_r;
433 31 100       51 my $event_end_time = ($event_r->[0] eq 'note') ?
434             ($event_r->[1] + $event_r->[2]) : $event_r->[1] ;
435             #print "event_end_time: $event_end_time\n";
436 31 100       80 $track_time = $event_end_time if $event_end_time > $track_time;
437             }
438 2         12 return $track_time;
439             }
440             ###########################################################################
441              
442             =item MIDI::Score::dump_score( $score_r )
443              
444             This dumps (via C) a text representation of the contents of
445             the event structure you pass a reference to.
446              
447             =cut
448              
449             sub dump_score {
450 2     2 1 10 my $score_r = $_[0];
451 2         533 print "\@notes = ( # ", scalar(@$score_r), " notes...\n";
452 2         7 foreach my $note_r (@$score_r) {
453 27 50       119 print " [", &MIDI::_dump_quote(@$note_r), "],\n" if @$note_r;
454             }
455 2         220 print ");\n";
456 2         7 return;
457             }
458              
459             ###########################################################################
460              
461             =item MIDI::Score::quantize( $score_r )
462              
463             This takes a I to a score structure, performs a grid
464             quantize on all events, returning a new score reference with new
465             quantized events. Two parameters to the method are: 'grid': the
466             quantization grid, and 'durations': whether or not to also quantize
467             event durations (default off).
468              
469             When durations of note events are quantized, they can get 0 duration.
470             These events are I from the returned score, and it is the
471             responsiblity of the caller to deal with them.
472              
473             =cut
474              
475             # new in 0.82!
476             sub quantize {
477 1     1 1 3 my $score_r = $_[0];
478 1 50       4 my $options_r = ref($_[1]) eq 'HASH' ? $_[1] : {};
479 1         3 my $grid = $options_r->{grid};
480 1 50       4 if ($grid < 1) {carp "bad grid $grid in MIDI::Score::quantize!"; $grid = 1;}
  0         0  
  0         0  
481 1         3 my $qd = $options_r->{durations}; # quantize durations?
482 1         2 my $new_score_r = [];
483 1         1 my $n_event_r;
484 1         2 foreach my $event_r (@{$score_r}) {
  1         2  
485 25         34 my $n_event_r = [];
486 25         24 @{$n_event_r} = @{$event_r};
  25         65  
  25         28  
487 25         55 $n_event_r->[1] = $grid * int(($n_event_r->[1] / $grid) + 0.5);
488 25 100 66     102 if ($qd && $n_event_r->[0] eq 'note') {
489 20         38 $n_event_r->[2] = $grid * int(($n_event_r->[2] / $grid) + 0.5);
490             }
491 25         28 push @{$new_score_r}, $n_event_r;
  25         52  
492             }
493 1         4 $new_score_r;
494             }
495              
496             ###########################################################################
497              
498             =item MIDI::Score::skyline( $score_r )
499              
500             This takes a I to a score structure, performs skyline
501             (create a monophonic track by extracting the event with highest pitch
502             at unique onset times) on the score, returning a new score reference.
503             The parameters to the method is: 'clip': whether durations of events
504             are preserved or possibly clipped and modified.
505              
506             To explain this, consider the following (from Bach 2 part invention
507             no.6 in E major):
508              
509             |------e------|-------ds--------|-------d------|...
510             |****--E-----|-------Fs-------|------Gs-----|...
511              
512             Without duration cliping, the skyline is E, Fs, Gs...
513              
514             With duration clipping, the skyline is E, e, ds, d..., where the
515             duration of E is clipped to just the * portion above
516              
517             =cut
518              
519             # new in 0.83! author DC
520             sub skyline {
521 0     0 1   my $score_r = $_[0];
522 0 0         my $options_r = ref($_[1]) eq 'HASH' ? $_[1] : {};
523 0           my $clip = $options_r->{clip};
524 0           my $new_score_r = [];
525 0           my %events = ();
526 0           my $n_event_r;
527 0           my ($typeidx,$stidx,$duridx,$pitchidx) = (0,1,2,4); # create some nicer event indices
528             # gather all note events into an onset-index hash. push all others directly into the new score.
529 0           foreach my $event_r (@{$score_r}) {
  0            
530 0 0         if ($event_r->[$typeidx] eq "note") {push @{$events{$event_r->[$stidx]}}, $event_r;}
  0            
  0            
  0            
531 0           else {push @{$new_score_r}, $event_r;}
532             }
533 0           my $loff = 0; my $lev = [];
  0            
534             # iterate over increasing onsets
535 0           foreach my $onset (sort {$a<=>$b} (keys %events)) {
  0            
536             # find highest pitch at this onset
537 0           my $ev = (sort {$b->[$pitchidx] <=> $a->[$pitchidx]} (@{$events{$onset}}))[0];
  0            
  0            
538 0 0         if ($onset >= ($lev->[$stidx] + $lev->[$duridx])) {
    0          
539 0           push @{$new_score_r}, $ev;
  0            
540 0           $lev = $ev;
541             }
542             elsif ($clip) {
543 0 0         if ($ev->[$pitchidx] > $lev->[$pitchidx]) {
544 0           $lev->[$duridx] = $ev->[$stidx] - $lev->[$stidx];
545 0           push @{$new_score_r}, $ev;
  0            
546 0           $lev = $ev;
547             }
548             }
549             }
550 0           $new_score_r;
551             }
552              
553             ###########################################################################
554              
555             =back
556              
557             =head1 COPYRIGHT
558              
559             Copyright (c) 1998-2002 Sean M. Burke. All rights reserved.
560              
561             This library is free software; you can redistribute it and/or
562             modify it under the same terms as Perl itself.
563              
564             =head1 AUTHORS
565              
566             Sean M. Burke C (until 2010)
567              
568             Darrell Conklin C (from 2010)
569              
570             =cut
571              
572             1;
573              
574             __END__