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