File Coverage

blib/lib/MIDI/XML/MidiFile.pm
Criterion Covered Total %
statement 11 139 7.9
branch 0 40 0.0
condition 0 9 0.0
subroutine 4 17 23.5
pod 13 13 100.0
total 28 218 12.8


line stmt bran cond sub pod time code
1             package MIDI::XML::MidiFile;
2              
3 1     1   4112 use 5.006;
  1         3  
4 1     1   3 use strict;
  1         1  
  1         15  
5 1     1   3 use warnings;
  1         8  
  1         22  
6 1     1   436 use MIDI::Opus;
  1         2291  
  1         983  
7              
8             our @ISA = qw();
9              
10             our $VERSION = 0.02;
11              
12             =head1 NAME
13              
14             MIDI::XML::MIDI - MIDI file/stream data.
15              
16             =head1 SYNOPSIS
17              
18             use strict;
19             use MIDI::XML::MidiFile;
20             use MIDI::XML::Track;
21             use MIDI::XML::Parser;
22             use XML::Parser;
23              
24             use MIDI::Opus;
25             use MIDI::Track;
26             use MIDI::Event;
27              
28             unless (@ARGV) {
29             die "Usage: perl test.pl filename\n";
30             }
31              
32             my $file = shift @ARGV;
33              
34             my $opus = MIDI::Opus->new({ 'from_file' => "$file.mid"});
35             my $midi=MIDI::XML::MidiFile->new({'from_opus' => $opus});
36             my $measures = $midi->measures();
37             open XML,">","$file.xml";
38             print XML "\n";
39             print XML join("\n",$midi->as_MidiXML());
40             close XML;
41             my $MidiFile = MIDI::XML::Parser->parse_MidiXML("$file.xml");
42              
43             =head1 DESCRIPTION
44              
45             MIDI::XML::MidiFile is a class for representing a MIDI file. There are
46             methods for the file attribute and a collection containing MIDI::XML::Track
47             objects.
48              
49             =head2 EXPORT
50              
51             None.
52              
53             =cut
54              
55             #==========================================================================
56              
57             =head1 METHODS AND ATTRIBUTES
58              
59             =over 4
60              
61             =item $obj = MIDI::XML::MidiFile->new()
62              
63             This creates a new MIDI::XML::MidiFile object.
64              
65             =cut
66              
67             sub new {
68 0     0 1   my $class = shift;
69 0   0       $class = ref($class) || $class;
70              
71 0 0 0       my $options_r = (defined($_[0]) and ref($_[0]) eq 'HASH') ? $_[0] : {};
72              
73 0           my $self = {
74             '_Format'=> undef,
75             '_TrackCount'=> undef,
76             '_TicksPerBeat'=> undef,
77             '_FrameRate'=> undef,
78             '_TicksPerFrame'=> undef,
79             '_TimestampType'=> undef,
80             '_Tracks'=> [],
81             '_Measures'=> undef,
82             };
83              
84 0           bless($self,$class);
85 0 0 0       if( exists( $options_r->{'from_opus'} )
86             && defined( $options_r->{'from_opus'} ) )
87             {
88 0           $self->from_opus( $options_r->{'from_opus'}, $options_r );
89             }
90              
91 0           return $self;
92             }
93              
94             #==========================================================================
95              
96             =item $format = $MidiFile->format() or $MidiFile->format($format)
97              
98             Returns or optionally sets the format indicator for the MidiFile object. Valid
99             values are 0, 1, and 2.
100              
101             =cut
102              
103             sub format {
104 0     0 1   my $self = shift;
105 0 0         if (@_) {
106 0           $self->{'_Format'} = shift;
107             }
108 0           return $self->{'_Format'};
109             }
110              
111             #==========================================================================
112              
113             =item $track_count = $MidiFile->track_count() or $MidiFile->track_count($track_count)
114              
115             Returns or optionally sets the track count for the MidiFile object. This does not
116             necessarilly indicate the number of Track objects contained by the tracks array.
117              
118             =cut
119              
120             sub track_count {
121 0     0 1   my $self = shift;
122 0 0         if (@_) {
123 0           $self->{'_TrackCount'} = shift;
124             }
125 0           return $self->{'_TrackCount'};
126             }
127              
128             #==========================================================================
129              
130             =item $ticks_per_beat = $MidiFile->ticks_per_beat() or $MidiFile->ticks_per_beat($ticks_per_beat)
131              
132             Returns or optionally sets the ticks per beat for the MidiFile object. To avoid
133             contradictory values, frame_rate and ticks_per_frame are set to undef when
134             ticks_per_beat is set.
135              
136             =cut
137              
138             sub ticks_per_beat {
139 0     0 1   my $self = shift;
140 0 0         if (@_) {
141 0           $self->{'_TicksPerBeat'} = shift;
142 0           $self->{'_FrameRate'} = undef;
143 0           $self->{'_TicksPerFrame'} = undef;
144             }
145 0           return $self->{'_TicksPerBeat'};
146             }
147              
148             #==========================================================================
149              
150             =item $frame_rate = $MidiFile->frame_rate() or $MidiFile->frame_rate($frame_rate)
151              
152             Returns or optionally sets the frame rate for the MidiFile object. To avoid
153             contradictory values, ticks_per_beat is set to undef when
154             frame_rate is set.
155              
156             =cut
157              
158             sub frame_rate {
159 0     0 1   my $self = shift;
160 0 0         if (@_) {
161 0           $self->{'_FrameRate'} = shift;
162 0           $self->{'_TicksPerBeat'} = undef;
163             }
164 0           return $self->{'_FrameRate'};
165             }
166              
167             #==========================================================================
168              
169             =item $ticks_per_frame = $MidiFile->ticks_per_frame() or $MidiFile->ticks_per_frame($ticks_per_frame)
170              
171             Returns or optionally sets the ticks per frame for the MidiFile object. To avoid
172             contradictory values, ticks_per_beat is set to undef when
173             ticks_per_frame is set.
174              
175             =cut
176              
177             sub ticks_per_frame {
178 0     0 1   my $self = shift;
179 0 0         if (@_) {
180 0           $self->{'_TicksPerFrame'} = shift;
181 0           $self->{'_TicksPerBeat'} = undef;
182             }
183 0           return $self->{'_TicksPerFrame'};
184             }
185              
186             #==========================================================================
187              
188             =item $timestamp_type = $MidiFile->timestamp_type() or $MidiFile->timestamp_type($timestamp_type)
189              
190             Returns or optionally sets the format indicator for the MidiFile object. Valid
191             values are Delta and Absolute. This value is only used for the production of
192             MidiXML.
193              
194             =cut
195              
196             sub timestamp_type {
197 0     0 1   my $self = shift;
198 0 0         if (@_) {
199 0           $self->{'_TimestampType'} = shift;
200             }
201 0           return $self->{'_TimestampType'};
202             }
203              
204             #==========================================================================
205              
206             =item $tracks = $MidiFile->tracks()
207              
208             Returns an array reference to the tracks contained in the MidiFile object.
209              
210             =cut
211              
212             sub tracks {
213 0     0 1   my $self = shift;
214              
215 0           return $self->{'_Tracks'};
216             }
217              
218             #==========================================================================
219              
220             =item $MidiFile->append($Track)
221              
222             Appends a track to the track array in this MIDI::Xml:MidiFile object.
223              
224             =cut
225              
226             sub append {
227 0     0 1   my $self = shift;
228 0           my $Track = shift;
229              
230 0 0         push @{$self->{'_Tracks'}},$Track if (defined($Track));
  0            
231             }
232              
233             #==========================================================================
234              
235             =item $MidiFile->from_opus($Opus)
236              
237             Appends the events in a MIDI::Opus object to the event array in this
238             MIDI::Xml:MidiFile object.
239              
240             =cut
241              
242             sub from_opus {
243 0     0 1   my $self = shift;
244 0           my $opus = shift;
245              
246 0           my $tracks = $opus->tracks_r();
247 0           $self->{'_Format'} = $opus->format();
248 0           $self->{'_TicksPerBeat'} = $opus->ticks();
249 0           $self->{'_FrameRate'} = undef;
250 0           $self->{'_TicksPerFrame'} = undef;
251 0           $self->{'_TimestampType'} = 'Delta';
252 0           $self->{'_Tracks'} = [];
253              
254 0           foreach my $trk (@{$tracks}) {
  0            
255 0           push @{$self->{'_Tracks'}}, MIDI::XML::Track->new({from_track => $trk});
  0            
256 0           $self->{'_Tracks'}->[-1]->number($#{$self->{'_Tracks'}});
  0            
257             }
258 0           $self->{'_TrackCount'} = $#{$self->{'_Tracks'}}+1;
  0            
259             }
260              
261             #==========================================================================
262              
263             =item $Midi_opus = $MidiFile->as_midi_opus();
264              
265             Returns a MIDI:Opus object constructed from this MidiFile object.
266              
267             =cut
268              
269             sub as_midi_opus {
270 0     0 1   my $self = shift;
271              
272 0           my $Midi_opus = MIDI::Opus->new();
273 0           foreach my $trk (@{$self->{'_Track'}}) {
  0            
274 0           push @{$Midi_opus->tracks_r}, $trk->as_midi_track();
  0            
275             }
276 0           $Midi_opus->format($self->{'_Format'});
277 0           $Midi_opus->ticks($self->{'_TicksPerBeat'});
278 0           return $Midi_opus;
279             }
280              
281             #==========================================================================
282              
283             =item $array_ref = $MidiFile->measures() or $MidiFile->measures('refresh');
284              
285             Returns a reference to an array of measures. If called with
286             any parameter the array is refreshed before the reference is returned.
287              
288             =cut
289              
290             sub measures {
291 0     0 1   my $self = shift;
292 0           my @measures;
293             my @timesig;
294 0           my $end = 0;
295              
296 0 0         if (@_) {
297 0           $self->{'_Measures'} = undef;
298             }
299 0 0         if (defined($self->{'_Measures'})) {
300 0           return $self->{'_Measures'};
301             }
302 0           foreach my $track (@{$self->{'_Tracks'}}) {
  0            
303 0           $track->make_times_absolute();
304 0           my $e = $track->end();
305 0 0         $end = $e if ($e > $end);
306             }
307 0 0         if (@_) {
308             } else {
309 0           my $tsigs = $self->{'_Tracks'}->[0]->time_signatures();
310 0           foreach my $tsig (@{$tsigs}) {
  0            
311 0           my $abs = $tsig->absolute();
312 0           my $num = $tsig->numerator();
313 0           my $log = $tsig->logDenominator();
314 0           my $den = 2 ** $log;
315 0           push @timesig, [$abs,$num,$den];
316             }
317             }
318              
319 0           push @timesig, [$end,1,1];
320 0           my $meas = 1;
321 0           my $time=0;
322 0           my $denom_ticks;
323 0           for (my $i=0; $i<$#timesig; $i++) {
324 0           my $lim = $timesig[$i+1]->[0];
325 0           $denom_ticks = $self->{'_TicksPerBeat'} * 4 / $timesig[$i]->[2];
326 0           my $divs = $denom_ticks * $timesig[$i]->[1];
327 0           while ($time < $lim) {
328 0           push @{$self->{'_Measures'}},[$time,$denom_ticks];
  0            
329 0           $meas++;
330 0           $time += $divs;
331             }
332             }
333 0           push @{$self->{'_Measures'}},[$time,$denom_ticks];
  0            
334              
335 0           return $self->{'_Measures'};
336             }
337              
338             #==========================================================================
339              
340             =item @xml = $File->as_MidiXML();
341              
342             Returns an array of elements formatted according to the MidiXML DTD. These
343             elements may be assembled by track into entire documents with the following
344             suggested DOCTYPE declaration:
345              
346            
347             "-//Recordare//DTD MusicXML 0.7 MIDI//EN"
348             "http://www.musicxml.org/dtds/midixml.dtd">
349              
350             =back
351              
352             =cut
353              
354             sub as_MidiXML {
355 0     0 1   my $self = shift;
356 0           my @xml;
357              
358 0           push @xml, '';
359 0 0         if ( defined($self->{'_Format'})) {
360 0           push @xml, "$self->{'_Format'}";
361             }
362 0           $self->{'_TrackCount'} = $#{$self->{'_Tracks'}}+1;
  0            
363 0 0         if ( defined($self->{'_TrackCount'})) {
364 0           push @xml, "$self->{'_TrackCount'}";
365             }
366 0 0         if ( defined($self->{'_TicksPerBeat'})) {
367 0           push @xml, "$self->{'_TicksPerBeat'}";
368             }
369 0 0         if ( defined($self->{'_FrameRate'})) {
370 0           push @xml, "$self->{'_FrameRate'}";
371             }
372 0 0         if ( defined($self->{'_TicksPerFrame'})) {
373 0           push @xml, "$self->{'_TicksPerFrame'}";
374             }
375 0 0         if ( defined($self->{'_TimestampType'})) {
376 0           push @xml, "$self->{'_TimestampType'}";
377             }
378 0 0         if ( defined($self->{'_Tracks'})) {
379 0           foreach my $trk (@{$self->{'_Tracks'}}) {
  0            
380 0           push @xml, $trk->as_MidiXML();
381             }
382             }
383 0           push @xml, '';
384 0           return @xml;
385             }
386              
387             return 1;
388             __END__