File Coverage

blib/lib/MIDI/Track.pm
Criterion Covered Total %
statement 49 86 56.9
branch 23 54 42.5
condition 9 21 42.8
subroutine 10 15 66.6
pod 9 11 81.8
total 100 187 53.4


line stmt bran cond sub pod time code
1              
2             # Time-stamp: "2013-02-01 22:40:38 conklin"
3             require 5;
4             package MIDI::Track;
5 11     11   237 use strict;
  11         22  
  11         863  
6 11     11   59 use vars qw($Debug $VERSION);
  11         38  
  11         611  
7 11     11   57 use Carp;
  11         21  
  11         18117  
8              
9             $Debug = 0;
10             $VERSION = '0.83';
11              
12             =head1 NAME
13              
14             MIDI::Track -- functions and methods for MIDI tracks
15              
16             =head1 SYNOPSIS
17              
18             use MIDI; # ...which "use"s MIDI::Track et al
19             $taco_track = MIDI::Track->new;
20             $taco_track->events(
21             ['text_event', 0, "I like tacos!"],
22             ['note_on', 0, 4, 50, 96 ],
23             ['note_off', 300, 4, 50, 96 ],
24             );
25             $opus = MIDI::Opus->new(
26             { 'format' => 0, 'ticks' => 240, 'tracks' => [ $taco_track ] }
27             );
28             ...etc...
29              
30             =head1 DESCRIPTION
31              
32             MIDI::Track provides a constructor and methods for objects
33             representing a MIDI track. It is part of the MIDI suite.
34              
35             MIDI tracks have, currently, three attributes: a type, events, and
36             data. Almost all tracks you'll ever deal with are of type "MTrk", and
37             so this is the type by default. Events are what make up an MTrk
38             track. If a track is not of type MTrk, or is an unparsed MTrk, then
39             it has (or better have!) data.
40              
41             When an MTrk track is encoded, if there is data defined for it, that's
42             what's encoded (and "encoding data" means just passing it thru
43             untouched). Note that this happens even if the data defined is ""
44             (but it won't happen if the data is undef). However, if there's no
45             data defined for the MTrk track (as is the general case), then the
46             track's events are encoded, via a call to C.
47              
48             (If neither events not data are defined, it acts as a zero-length
49             track.)
50              
51             If a non-MTrk track is encoded, its data is encoded. If there's no
52             data for it, it acts as a zero-length track.
53              
54             In other words, 1) events are meaningful only in an MTrk track, 2) you
55             probably don't want both data and events defined, and 3) 99.999% of
56             the time, just worry about events in MTrk tracks, because that's all
57             you ever want to deal with anyway.
58              
59             =head1 CONSTRUCTOR AND METHODS
60              
61             MIDI::Track provides...
62              
63             =over
64              
65             =cut
66              
67             ###########################################################################
68              
69             =item the constructor MIDI::Track->new({ ...options... })
70              
71             This returns a new track object. By default, the track is of type
72             MTrk, which is probably what you want. The options, which are
73             optional, is an anonymous hash. There are four recognized options:
74             C, which sets the data of the new track to the string provided;
75             C, which sets the type of the new track to the string provided;
76             C, which sets the events of the new track to the contents of
77             the list-reference provided (i.e., a reference to a LoL -- see
78             L for the skinny on LoLs); and C, which is an exact
79             synonym of C.
80              
81             =cut
82              
83             sub new {
84             # make a new track.
85 18     18 1 1165 my $class = shift;
86 18         64 my $this = bless( {}, $class );
87 18 50       79 print "New object in class $class\n" if $Debug;
88 18         87 $this->_init( @_ );
89 18         56 return $this;
90             }
91              
92             sub _init {
93             # You can specify options:
94             # 'event' => [a list of events], AKA 'event_r'
95             # 'type' => 'Whut', # default is 'MTrk'
96             # 'data' => 'scads of binary data as you like it'
97 18     18   30 my $this = shift;
98 18 100       72 my $options_r = ref($_[0]) eq 'HASH' ? $_[0] : {};
99 18 50       73 print "_init called against $this\n" if $Debug;
100 18 50       60 if($Debug) {
101 0 0       0 if(%$options_r) {
102 0         0 print "Parameters: ", map("<$_>", %$options_r), "\n";
103             } else {
104 0         0 print "Null parameters for opus init\n";
105             }
106             }
107              
108 18 50       118 $this->{'type'} =
109             defined($options_r->{'type'}) ? $options_r->{'type'} : 'MTrk';
110 18 50       59 $this->{'data'} = $options_r->{'data'}
111             if defined($options_r->{'data'});
112              
113 18 100 66     127 $options_r->{'events'} = $options_r->{'events_r'}
114             if( exists( $options_r->{'events_r'} ) and not
115             exists( $options_r->{'events'} )
116             );
117             # so events_r => [ @events ] is a synonym for
118             # events => [ @events ]
119             # as on option for new()
120              
121 18 100 66     145 $this->{'events'} =
122             ( defined($options_r->{'events'})
123             and ref($options_r->{'events'}) eq 'ARRAY' )
124             ? $options_r->{'events'} : []
125             ;
126 18         53 return;
127             }
128              
129             =item the method $new_track = $track->copy
130              
131             This duplicates the contents of the given track, and returns
132             the duplicate. If you are unclear on why you may need this function,
133             consider:
134              
135             $funk = MIDI::Opus->new({'from_file' => 'funk1.mid'});
136             $samba = MIDI::Opus->new({'from_file' => 'samba1.mid'});
137            
138             $bass_track = ( $funk->tracks )[-1]; # last track
139             push(@{ $samba->tracks_r }, $bass_track );
140             # make it the last track
141            
142             &funk_it_up( ( $funk->tracks )[-1] );
143             # modifies the last track of $funk
144             &turn_it_out( ( $samba->tracks )[-1] );
145             # modifies the last track of $samba
146            
147             $funk->write_to_file('funk2.mid');
148             $samba->write_to_file('samba2.mid');
149             exit;
150              
151             So you have your routines funk_it_up and turn_it_out, and they each
152             modify the track they're applied to in some way. But the problem is that
153             the above code probably does not do what you want -- because the last
154             track-object of $funk and the last track-object of $samba are the
155             I. An object, you may be surprised to learn, can be in
156             different opuses at the same time -- which is fine, except in cases like
157             the above code. That's where you need to do copy the object. Change
158             the above code to read:
159              
160             push(@{ $samba->tracks_r }, $bass_track->copy );
161              
162             and what you want to happen, will.
163              
164             Incidentally, this potential need to copy also occurs with opuses (and
165             in fact any reference-based data structure, altho opuses and tracks
166             should cover almost all cases with MIDI stuff), which is why there's
167             $opus->copy, for copying entire opuses.
168              
169             (If you happen to need to copy a single event, it's just $new = [@$old] ;
170             and if you happen to need to copy an event structure (LoL) outside of a
171             track for some reason, use MIDI::Event::copy_structure.)
172              
173             =cut
174              
175             sub copy {
176             # Duplicate a given track. Even dupes the events.
177             # Call as $new_one = $track->copy
178 0     0 1 0 my $track = shift;
179              
180 0         0 my $new = bless( { %{$track} }, ref $track );
  0         0  
181             # a first crude dupe
182 0 0       0 $new->{'events'} = &MIDI::Event::copy_structure( $new->{'events'} )
183             if $new->{'events'};
184 0         0 return $new;
185             }
186              
187             ###########################################################################
188              
189             =item track->skyline({ ...options... })
190              
191             skylines the entire track. Modifies the track. See MIDI::Score for
192             documentation on skyline
193              
194             =cut
195              
196             sub skyline {
197 0     0 1 0 my $track = shift;
198 0 0       0 my $options_r = ref($_[1]) eq 'HASH' ? $_[1] : {};
199 0         0 my $score_r = MIDI::Score::events_r_to_score_r($track->events_r);
200 0         0 my $new_score_r = MIDI::Score::skyline($score_r,$options_r);
201 0         0 my $events_r = MIDI::Score::score_r_to_events_r($new_score_r);
202 0         0 $track->events_r($events_r);
203             }
204              
205             ###########################################################################
206             # These three modify all the possible attributes of a track
207              
208             =item the method $track->events( @events )
209              
210             Returns the list of events in the track, possibly after having set it
211             to @events, if specified and not empty. (If you happen to want to set
212             the list of events to an empty list, for whatever reason, you have to use
213             "$track->events_r([])".)
214              
215             In other words: $track->events(@events) is how to set the list of events
216             (assuming @events is not empty), and @events = $track->events is how to
217             read the list of events.
218              
219             =cut
220              
221             sub events { # list or set events in this object
222 9     9 1 29 my $this = shift;
223 9 50       31 $this->{'events'} = [ @_ ] if @_;
224 9         17 return @{ $this->{'events'} };
  9         399  
225             }
226              
227             =item the method $track->events_r( $event_r )
228              
229             Returns a reference to the list of events in the track, possibly after
230             having set it to $events_r, if specified. Actually, "$events_r" can be
231             any listref to a LoL, whether it comes from a scalar as in
232             C<$some_events_r>, or from something like C<[@events]>, or just plain
233             old C<\@events>
234              
235             Originally $track->events was the only way to deal with events, but I
236             added $track->events_r to make possible 1) setting the list of events
237             to (), for whatever that's worth, and 2) so you can directly
238             manipulate the track's events, without having to I the list of
239             events (which might be tens of thousands of elements long) back
240             and forth. This way, you can say:
241              
242             $events_r = $track->events_r();
243             @some_stuff = splice(@$events_r, 4, 6);
244              
245             But if you don't know how to deal with listrefs outside of LoLs,
246             that's OK, just use $track->events.
247              
248             =cut
249              
250             sub events_r {
251             # return (maybe set) a list-reference to the event-structure for this track
252 42     42 1 285 my $this = shift;
253 42 50       110 if(@_) {
254 0 0       0 croak "parameter for MIDI::Track::events_r must be an array-ref"
255             unless ref($_[0]);
256 0         0 $this->{'events'} = $_[0];
257             }
258 42         212 return $this->{'events'};
259             }
260              
261             =item the method $track->type( 'MFoo' )
262              
263             Returns the type of $track, after having set it to 'MFoo', if provided.
264             You probably won't ever need to use this method, other than in
265             a context like:
266              
267             if( $track->type eq 'MTrk' ) { # The usual case
268             give_up_the_funk($track);
269             } # Else just keep on walkin'!
270              
271             Track types must be 4 bytes long; see L for details.
272              
273             =cut
274              
275             sub type {
276 13     13 1 2462 my $this = shift;
277 13 100       53 $this->{'type'} = $_[0] if @_; # if you're setting it
278 13         67 return $this->{'type'};
279             }
280              
281             =item the method $track->data( $kooky_binary_data )
282              
283             Returns the data from $track, after having set it to
284             $kooky_binary_data, if provided -- even if it's zero-length! You
285             probably won't ever need to use this method. For your information,
286             $track->data(undef) is how to undefine the data for a track.
287              
288             =cut
289              
290             sub data {
291             # meant for reading/setting generally non-MTrk track data
292 0     0 1 0 my $this = shift;
293 0 0       0 $this->{'data'} = $_[0] if @_;
294 0         0 return $this->{'data'};
295             }
296              
297             ###########################################################################
298              
299             =item the method $track->new_event('event', ...parameters... )
300              
301             This adds the event ('event', ...parameters...) to the end of the
302             event list for $track. It's just sugar for:
303              
304             push( @{$this_track->events_r}, [ 'event', ...params... ] )
305              
306             If you want anything other than the equivalent of that, like some
307             kinda splice(), then do it yourself with $track->events_r or
308             $track->events.
309              
310             =cut
311              
312             sub new_event {
313             # Usage:
314             # $this_track->new_event('text_event', 0, 'Lesbia cum Prono');
315              
316 0     0 1 0 my $track = shift;
317 0         0 push( @{$track->{'events'}}, [ @_ ] );
  0         0  
318             # this returns the new number of events in that event list, if that
319             # interests you.
320             }
321              
322             ###########################################################################
323              
324             =item the method $track->dump({ ...options... })
325              
326             This dumps the track's contents for your inspection. The dump format
327             is code that looks like Perl code that you'd use to recreate that track.
328             This routine outputs with just C, so you can use C
329             change where that'll go. I intended this to be just an internal
330             routine for use only by the method MIDI::Opus::dump, but I figure it
331             might be useful to you, if you need to dump the code for just a given
332             track.
333             Read the source if you really need to know how this works.
334              
335             =cut
336              
337             sub dump { # dump a track's contents
338 0     0 1 0 my $this = $_[0];
339 0 0       0 my $options_r = ref($_[1]) eq 'HASH' ? $_[1] : {};
340 0         0 my $type = $this->type;
341              
342 0         0 my $indent = ' ';
343 0         0 my @events = $this->events;
344 0 0       0 print(
345             $indent, "MIDI::Track->new({\n",
346             $indent, " 'type' => ", &MIDI::_dump_quote($type), ",\n",
347             defined($this->{'data'}) ?
348             ( $indent, " 'data' => ",
349             &MIDI::_dump_quote($this->{'data'}), ",\n" )
350             : (),
351             $indent, " 'events' => [ # ", scalar(@events), " events.\n",
352             );
353 0         0 foreach my $event (@events) {
354 0         0 &MIDI::Event::dump(@$event);
355             # was: print( $indent, " [", &MIDI::_dump_quote(@$event), "],\n" );
356             }
357 0         0 print( "$indent ]\n$indent}),\n$indent\n" );
358 0         0 return;
359             }
360              
361             ###########################################################################
362              
363             # CURRENTLY UNDOCUMENTED -- no end-user ever needs to call this as such
364             #
365             sub encode { # encode a track object into track data (not a chunk)
366             # Calling format:
367             # $data_r = $track->encode( { .. options .. } )
368             # The (optional) argument is an anonymous hash of options.
369             # Returns a REFERENCE to track data.
370             #
371 2     2 0 6 my $track = $_[0];
372 2 50       9 croak "$track is not a track object!" unless ref($track);
373 2 50       21 my $options_r = ref($_[1]) eq 'HASH' ? $_[1] : {};
374              
375 2         4 my $data = '';
376              
377 2 50 33     13 if( exists( $track->{'data'} ) and defined( $track->{'data'} ) ) {
378             # It might be 0-length, by the way. Might this be problematic?
379 0         0 $data = $track->{'data'};
380             # warn "Encoding 0-length track data!" unless length $data;
381             } else { # Data is not defined for this track. Parse the events
382 2 50 33     50 if( ($track->{'type'} eq 'MTrk' or length($track->{'type'}) == 0)
      33        
      33        
383             and defined($track->{'events'})
384             # not just exists -- but DEFINED!
385             and ref($track->{'events'})
386             ) {
387 2 50       8 print "Encoding ", $track->{'events'}, "\n" if $Debug;
388             return
389 2         13 &MIDI::Event::encode($track->{'events'}, $options_r );
390             } else {
391 0         0 $data = ''; # what else to do?
392 0 0       0 warn "Spork 8851\n" if $Debug;
393             }
394             }
395 0         0 return \$data;
396             }
397             ###########################################################################
398              
399             # CURRENTLY UNDOCUMENTED -- no end-user ever needs to call this as such
400             #
401             sub decode { # returns a new object, but doesn't accept constructor syntax
402             # decode track data (not a chunk) into a new track object
403             # Calling format:
404             # $new_track =
405             # MIDI::Track::decode($type, \$track_data, { .. options .. })
406             # Returns a new track_object.
407             # The anonymous hash of options is, well, optional
408              
409 15     15 0 88 my $track = MIDI::Track->new();
410              
411 15         51 my ($type, $data_r, $options_r) = @_[0,1,2];
412 15 50       77 $options_r = {} unless ref($options_r) eq 'HASH';
413              
414 15 50       54 die "\$_[0] \"$_[0]\" is not a data reference!"
415             unless ref($_[1]) eq 'SCALAR';
416              
417 15         133 $track->{'type'} = $type;
418 15 50 33     102 if($type eq 'MTrk' and not $options_r->{'no_parse'}) {
419 15         83 $track->{'events'} =
420             &MIDI::Event::decode($data_r, $options_r);
421             # And that's where all the work happens
422             } else {
423 0         0 $track->{'data'} = $$data_r;
424             }
425 15         268 return $track;
426             }
427              
428             ###########################################################################
429              
430             =back
431              
432             =head1 COPYRIGHT
433              
434             Copyright (c) 1998-2002 Sean M. Burke. All rights reserved.
435              
436             This library is free software; you can redistribute it and/or
437             modify it under the same terms as Perl itself.
438              
439             =head1 AUTHOR
440              
441             Sean M. Burke C (until 2010)
442              
443             Darrell Conklin C (from 2010)
444              
445             =cut
446              
447             1;
448              
449             __END__