File Coverage

blib/lib/Audio/Wav/Read.pm
Criterion Covered Total %
statement 246 316 77.8
branch 58 110 52.7
condition 8 24 33.3
subroutine 21 31 67.7
pod 14 15 93.3
total 347 496 69.9


line stmt bran cond sub pod time code
1             package Audio::Wav::Read;
2              
3 1     1   5 use strict;
  1         1  
  1         55  
4             eval { require warnings; }; #it's ok if we can't load warnings
5              
6 1     1   5 use FileHandle;
  1         1  
  1         11  
7              
8 1     1   493 use vars qw( $VERSION );
  1         3  
  1         3615  
9             $VERSION = '0.14';
10              
11             =head1 NAME
12              
13             Audio::Wav::Read - Module for reading Microsoft WAV files.
14              
15             =head1 SYNOPSIS
16              
17             use Audio::Wav;
18              
19             my $wav = new Audio::Wav;
20             my $read = $wav -> read( 'filename.wav' );
21             #OR
22             my $read = Audio::Wav -> read( 'filename.wav' );
23              
24             my $details = $read -> details();
25              
26             =head1 DESCRIPTION
27              
28             Reads Microsoft Wav files.
29              
30             =head1 SEE ALSO
31              
32             L
33              
34             L
35              
36             =head1 NOTES
37              
38             This module shouldn't be used directly, a blessed object can be returned from L.
39              
40             =head1 METHODS
41              
42             =cut
43              
44             sub new {
45 2     2 0 6 my $class = shift;
46 2         13 my $file = shift;
47 2         4 my $tools = shift;
48 2         7 $file =~ s#//#/#g;
49 2         42 my $size = -s $file;
50              
51 2 50       38 my $handle = (ref $file eq 'GLOB') ? $file : new FileHandle "<$file";
52              
53 2         199 my $self = {
54             'real_size' => $size,
55             'file' => $file,
56             'handle' => $handle,
57             'tools' => $tools,
58             };
59              
60 2         7 bless $self, $class;
61              
62 2 50       7 unless ( defined $handle ) {
63 0         0 $self -> _error( "unable to open file ($!)" );
64 0         0 return $self;
65             }
66              
67 2         14 binmode $handle;
68              
69 2 50       5 if( $Audio::Wav::_has_inline ) {
70 0         0 local $/ = undef;
71 0         0 my $c_string = ;
72 0         0 Inline->import(C => $c_string);
73             } else {
74             #TODO: do we have a reference to $tools here if using shortcuts?
75 2 50 33     15 if( $tools && $tools -> is_debug() ) {
76 0         0 warn "can't load Inline, using slow pure perl reads\n";
77             }
78             }
79              
80 2         14 $self -> {data} = $self -> _read_file();
81 2         7 my $details = $self -> details();
82 2         7 $self -> _init_read_sub();
83 2         13 $self -> {pos} = $details -> {data_start};
84 2         6 $self -> move_to();
85 2         7 return $self;
86             }
87              
88             # just in case there are any memory leaks
89             sub DESTROY {
90 0     0   0 my $self = shift;
91 0 0       0 return unless $self;
92 0 0 0     0 if ( exists $self->{handle} && defined $self->{handle} ) {
93 0         0 $self->{handle}->close();
94             }
95 0 0       0 if ( exists $self->{tools} ) {
96 0         0 delete $self->{tools};
97             }
98             }
99              
100             =head2 file_name
101              
102             Returns the file name.
103              
104             my $file = $read -> file_name();
105              
106             =cut
107              
108             sub file_name {
109 0     0 1 0 my $self = shift;
110 0         0 return $self -> {file};
111             }
112              
113             =head2 get_info
114              
115             Returns information contained within the wav file.
116              
117             my $info = $read -> get_info();
118              
119             Returns a reference to a hash containing;
120             (for example, a file marked up for use in Audio::Mix)
121              
122             {
123             'keywords' => 'bpm:126 key:a',
124             'name' => 'Mission Venice',
125             'artist' => 'Nightmares on Wax'
126             };
127              
128             =cut
129              
130             sub get_info {
131 0     0 1 0 my $self = shift;
132 0 0       0 return unless exists $self -> {data} -> {info};
133 0         0 return $self -> {data} -> {info};
134             }
135              
136             =head2 get_cues
137              
138             Returns the cuepoints marked within the wav file.
139              
140             my $cues = $read -> get_cues();
141              
142             Returns a reference to a hash containing;
143             (for example, a file marked up for use in Audio::Mix)
144             (position is sample position)
145              
146             {
147             1 => {
148             label => 'sig',
149             position => 764343,
150             note => 'first',
151             },
152             2 => {
153             label => 'fade_in',
154             position => 1661774,
155             note => 'trig',
156             },
157             3 => {
158             label => 'sig',
159             position => 18033735,
160             note => 'last',
161             },
162             4 => {
163             label => 'fade_out',
164             position => 17145150,
165             note => 'trig',
166             },
167             5 => {
168             label => 'end',
169             position => 18271676,
170             }
171             }
172              
173             =cut
174              
175             sub get_cues {
176 2     2 1 35 my $self = shift;
177 2 50       7 return unless exists $self -> {data} -> {cue};
178 2         3 my $data = $self -> {data};
179 2         4 my $cues = $data -> {cue};
180 2         4 my $output = {};
181 2         4 foreach my $id ( keys %{$cues} ) {
  2         4  
182 4         7 my $pos = $cues -> {$id} -> {position};
183 4         6 my $record = { 'position' => $pos };
184 4 50       14 $record -> {label} = $data -> {labl} -> {$id} if ( exists $data -> {labl} -> {$id} );
185 4 50       13 $record -> {note} = $data -> {note} -> {$id} if ( exists $data -> {note} -> {$id} );
186 4         9 $output -> {$id} = $record;
187             }
188 2         5 return $output;
189             }
190              
191             =head2 read_raw
192              
193             Reads raw packed bytes from the current audio data position in the file.
194              
195             my $data = $self -> read_raw( $byte_length );
196              
197             =cut
198              
199             sub read_raw {
200 44     44 1 261 my $self = shift;
201 44         52 my $len = shift;
202 44         82 my $data_finish = $self -> {data} -> {data_finish};
203 44 50       266 if ( $self -> {pos} + $len > $data_finish ) {
204 0         0 $len = $data_finish - $self -> {pos};
205             }
206 44         93 return $self -> _read_raw( $len );
207             }
208              
209             =head2 read_raw_samples
210              
211             Reads raw packed samples from the current audio data position in the file.
212              
213             my $data = $self -> read_raw_samples( $samples );
214              
215             =cut
216              
217             sub read_raw_samples {
218 0     0 1 0 my $self = shift;
219 0         0 my $len = shift;
220 0         0 $len *= $self -> {data} -> {block_align};
221 0         0 return $self -> read_raw( $len );
222             }
223              
224             sub _read_raw {
225 169     169   211 my $self = shift;
226 169         177 my $len = shift;
227 169         160 my $data;
228 169 100 66     601 return unless $len && $len > 0;
229 168         454 $self -> {pos} += read $self -> {handle}, $data, $len;
230 168         385 return $data;
231             }
232              
233             =head2 read
234              
235             Returns the current audio data position sample across all channels.
236              
237             my @channels = $self -> read();
238              
239             Returns an array of unpacked samples.
240             Each element is a channel i.e ( left, right ).
241             The numbers will be in the range;
242              
243             where $samp_max = ( 2 ** bits_per_sample ) / 2
244             -$samp_max to +$samp_max
245              
246             =cut
247              
248             # read is generated by _init_read_sub
249             sub read { die "ERROR: can't call read without first calling _init_read_sub"; };
250              
251             sub _init_read_sub {
252 2     2   3 my $self = shift;
253 2         4 my $handle = $self -> {handle};
254 2         8 my $details = $self -> {data};
255 2         6 my $channels = $details -> {channels};
256 2         4 my $block = $details -> {block_align};
257 2         3 my $read_op;
258              
259             #TODO: we try to do something if we have bits_per_sample != multiple of 8?
260 2 50       8 if ( $details -> {bits_sample} <= 8 ) {
    0          
    0          
261             # Data in .wav-files with <= 8 bits is unsigned. >8 bits is signed
262 2         5 my $offset = 2 ** ($details -> {bits_sample}-1);
263 2         28 $read_op = q[ return map $_ - ] . $offset .
264             q[, unpack( 'C'.$channels, $val ); ];
265             } elsif ( $details -> {bits_sample} == 16 ) {
266             # 16 bits could be handled by general case below, but this is faster
267 0 0       0 if ( $self -> {tools} -> is_big_endian() ) {
268 0         0 $read_op = q[ return
269             unpack 's' . $channels, # 3. unpack native as signed short
270             pack 'S' . $channels, # 2. pack native unsigned short
271             unpack 'v' . $channels, $val; # 1. unpack little-endian unsigned short
272             ];
273             } else {
274 0         0 $read_op = q[ return unpack( 's' . $channels, $val ); ];
275             }
276             } elsif ( $details -> {bits_sample} <= 32 ) {
277 0         0 my $bytes = $details -> {block_align} / $channels;
278 0         0 my $fill = 4 - $bytes;
279 0         0 my $limit = 2 ** ($details -> {bits_sample}-1);
280 0         0 my $offset = 2 ** $details -> {bits_sample};
281             #warn "b: $bytes, f: $fill";
282 0         0 $read_op = q[ return
283             map {$_ & ] . $limit . q[ ? # 4. If sign bit is set
284             $_ - ] . $offset . q[ : $_} # convert to negative number
285             unpack 'V*', # 3. unpack as little-endian unsigned long
286             pack "(a] . $bytes.'x'.$fill . q[)*", # 2. fill with \0 to 4-byte-blocks and repack
287             unpack "(a] . $bytes . q[)*", $val; # 1. unpack to elements sized "$bytes"-bytes
288             ];
289             # $sub = sub
290             # { return map {$_ & $limit ? # 4. If sign bit is set
291             # $_ - $offset : $_} # convert to negative number
292             # unpack 'V*', # 3. unpack as little-endian unsigned long
293             # pack "(a${bytes}x${fill})*", # 2. fill with \0 to 4-byte-blocks and repack
294             # unpack "(a$bytes)*", shift() # 1. unpack to elements sized "$bytes"-bytes
295             # };
296             } else {
297 0         0 $self->_error("Unpacking elements with more than 32 ($details->{bits_sample}) bits per sample not supported!");
298             }
299              
300 2         11 $self -> {read_sub_string} = q[
301             sub {
302             my $val;
303             $self -> {pos} += read( $handle, $val, $block );
304             return unless defined $val;
305             ] . $read_op . q[
306             };
307             ];
308 2 50       5 if( $Audio::Wav::_has_inline ) {
309 0 0       0 init( $handle, $details->{bits_sample}/8, $channels,
310             $self -> {tools} -> is_big_endian() ? 1 : 0);
311 0         0 *read = \&read_c;
312             } else {
313 2 50   1   323 my $read_sub = eval $self -> {read_sub_string};
  1         29  
  1         9  
  1         3  
  1         7  
314 2 50       7 die "eval of read_sub failed: $@\n" if($@);
315 2         6 $self -> {read_sub} = $read_sub; #in case any legacy code peaked at that
316 2         12 *read = \&$read_sub;
317             }
318             #warn $self -> {read_sub_string};
319             }
320              
321             =head2 position
322              
323             Returns the current audio data position (as byte offset).
324              
325             my $byte_offset = $read -> position();
326              
327             =cut
328              
329             sub position {
330 0     0 1 0 my $self = shift;
331 0         0 return $self -> {pos} - $self -> {data} -> {data_start};
332             }
333              
334             =head2 position_samples
335              
336             Returns the current audio data position (in samples).
337              
338             my $samples = $read -> position_samples();
339              
340             =cut
341              
342             sub position_samples {
343 0     0 1 0 my $self = shift;
344 0         0 return ( $self -> {pos} - $self -> {data} -> {data_start} ) / $self -> {data} -> {block_align};
345             }
346              
347             =head2 move_to
348              
349             Moves the current audio data position to byte offset.
350              
351             $read -> move_to( $byte_offset );
352              
353             =cut
354              
355             sub move_to {
356 3     3 1 9 my $self = shift;
357 3         5 my $pos = shift;
358 3         5 my $data_start = $self -> {data} -> {data_start};
359 3 100       5 if ( $pos ) {
360 1 50       4 $pos = 0 if $pos < 0;
361             } else {
362 2         3 $pos = 0;
363             }
364 3         3 $pos += $data_start;
365 3 100       8 if ( $pos > $self -> {pos} ) {
366 1         3 my $max_pos = $self -> reread_length() + $data_start;
367 1 50       5 $pos = $max_pos if $pos > $max_pos;
368             }
369 3 50       19 if ( seek $self -> {handle}, $pos, 0 ) {
370 3         5 $self -> {pos} = $pos;
371 3         6 return 1;
372             } else {
373 0         0 return $self -> _error( "can't move to position '$pos'" );
374             }
375             }
376              
377             =head2 move_to_sample
378              
379             Moves the current audio data position to sample offset.
380              
381             $read -> move_to_sample( $sample_offset );
382              
383             =cut
384              
385             sub move_to_sample {
386 0     0 1 0 my $self = shift;
387 0         0 my $pos = shift;
388 0 0       0 return $self -> move_to() unless defined $pos ;
389 0         0 return $self -> move_to( $pos * $self -> {data} -> {block_align} );
390             }
391              
392             =head2 length
393              
394             Returns the number of bytes of audio data in the file.
395              
396             my $audio_bytes = $read -> length();
397              
398             =cut
399              
400             sub length {
401 1     1 1 164 my $self = shift;
402 1         4 return $self -> {data} -> {data_length};
403             }
404              
405             =head2 length_samples
406              
407             Returns the number of samples of audio data in the file.
408              
409             my $audio_samples = $read -> length_samples();
410              
411             =cut
412              
413             sub length_samples {
414 0     0 1 0 my $self = shift;
415 0         0 my $data = $self -> {data};
416 0         0 return $data -> {data_length} / $data -> {block_align};
417             }
418              
419             =head2 length_seconds
420              
421             Returns the number of seconds of audio data in the file.
422              
423             my $audio_seconds = $read -> length_seconds();
424              
425             =cut
426              
427             sub length_seconds {
428 0     0 1 0 my $self = shift;
429 0         0 my $data = $self -> {data};
430 0         0 return $data -> {data_length} / $data -> {bytes_sec};
431             }
432              
433             =head2 details
434              
435             Returns a reference to a hash of lots of details about the file.
436             Too many to list here, try it with Data::Dumper.....
437              
438             use Data::Dumper;
439             my $details = $read -> details();
440             print Data::Dumper->Dump([ $details ]);
441              
442             =cut
443              
444             sub details {
445 4     4 1 9 my $self = shift;
446 4         12 return $self -> {data};
447             }
448              
449             =head2 reread_length
450              
451             Rereads the length of the file in case it is being written to
452             as we are reading it.
453              
454             my $new_data_length = $read -> reread_length();
455              
456             =cut
457              
458             sub reread_length {
459 1     1 1 1 my $self = shift;
460 1         2 my $handle = $self -> {handle};
461 1         2 my $old_pos = $self -> {pos};
462 1         4 my $data = $self -> {data};
463 1         3 my $data_start = $data -> {data_start};
464 1         5 seek $handle, $data_start - 4, 0;
465 1         4 my $new_length = $self -> _read_long();
466 1         9 seek $handle, $old_pos, 0;
467 1         2 $data -> {data_length} = $new_length;
468 1         2 return $new_length;
469             }
470              
471             #########
472              
473             sub _read_file {
474 2     2   4 my $self = shift;
475 2         11 my $handle = $self -> {handle};
476 2         4 my %details;
477 2         5 my $type = $self -> _read_raw( 4 );
478 2         16 my $length = $self -> _read_long( );
479 2         6 my $subtype = $self -> _read_raw( 4 );
480 2         4 my $tools = $self -> {tools};
481 2         10 my $old_cooledit = $tools -> is_oldcooledithack();
482 2         7 my $debug = $tools -> is_debug();
483              
484 2         5 $details{total_length} = $length;
485              
486 2 50 33     16 unless ( $type eq 'RIFF' && $subtype eq 'WAVE' ) {
487 0         0 return $self -> _error( "doesn't seem to be a wav file" );
488             }
489              
490 2         3 my $walkover; # for fixing cooledit 96 data-chunk bug
491              
492 2   66     27 while ( ! eof $handle && $self -> {pos} < $length ) {
493 11         13 my $head;
494 11 50       15 if ( $walkover ) {
495             # rectify cooledit 96 data-chunk bug
496 0         0 $head = $walkover . $self -> _read_raw( 3 );
497 0         0 $walkover = undef;
498 0 0       0 print "debug: CoolEdit 96 data-chunk bug detected!\n" if $debug;
499             } else {
500 11         22 $head = $self -> _read_raw( 4 );
501             }
502 11         20 my $chunk_len = $self -> _read_long();
503 11 50       23 printf "debug: head: '$head' at %6d (%6d bytes)\n", $self->{pos}, $chunk_len if $debug;
504 11 100       37 if ( $head eq 'fmt ' ) {
    100          
    100          
    100          
    100          
    50          
505 2         7 my $format = $self -> _read_fmt( $chunk_len );
506 2         7 my $comp = delete $format -> {format};
507 2 50       8 if ( $comp == 65534 ) {
    50          
508 0         0 $format -> {'wave-ex'} = 1;
509             } elsif ( $comp != 1 ) {
510 0         0 return $self -> _error( "seems to be compressed, I can't handle anything other than uncompressed PCM" );
511             } else {
512 2         5 $format -> {'wave-ex'} = 0;
513             }
514 2         5 %details = ( %details, %{$format} );
  2         11  
515 2         16 next;
516             } elsif ( $head eq 'cue ' ) {
517 2         7 $details{cue} = $self -> _read_cue( $chunk_len, \%details );
518 2         11 next;
519             } elsif ( $head eq 'smpl' ) {
520 1         3 $details{sampler} = $self -> _read_sampler( $chunk_len );
521 1         10 next;
522             } elsif ( $head eq 'LIST' ) {
523 3         10 my $list = $self -> _read_list( $chunk_len, \%details );
524 3         23 next;
525             } elsif ( $head eq 'DISP' ) {
526 1         3 $details{display} = $self -> _read_disp( $chunk_len );
527 1         5 next;
528             } elsif ( $head eq 'data' ) {
529 2         7 $details{data_start} = $self -> {pos};
530 2         5 $details{data_length} = $chunk_len;
531             } else {
532 0         0 $head =~ s/[^\w]+//g;
533 0         0 $self -> _error( "ignored unknown block type: $head at $self->{pos} for $chunk_len", 'warn' );
534             }
535              
536 2         17 seek $handle, $chunk_len, 1;
537 2         4 $self -> {pos} += $chunk_len;
538              
539             # read padding
540 2 50       24 if ($chunk_len % 2) {
541 0         0 my $pad = $self->_read_raw(1);
542 0 0 0     0 if ( ($pad =~ /\w/) && $old_cooledit && ($head eq 'data') ) {
      0        
543             # Oh no, this file was written by cooledit 96...
544             # This is not a pad byte but the first letter of the next head.
545 0         0 $walkover = $pad;
546             }
547             }
548              
549             #unless ( $old_cooledit ) {
550             # $chunk_len += 1 if $chunk_len % 2; # padding
551             #}
552             #seek $handle, $chunk_len, 1;
553             #$self -> {pos} += $chunk_len;
554              
555             }
556              
557 2 50       5 if ( exists $details{data_start} ) {
558 2         15 $details{length} = $details{data_length} / $details{bytes_sec};
559 2         5 $details{data_finish} = $details{data_start} + $details{data_length};
560             } else {
561 0         0 $details{data_start} = 0;
562 0         0 $details{data_length} = 0;
563 0         0 $details{length} = 0;
564 0         0 $details{data_finish} = 0;
565             }
566 2         8 return \%details;
567             }
568              
569              
570             sub _read_list {
571 3     3   5 my $self = shift;
572 3         4 my $length = shift;
573 3         4 my $details = shift;
574 3         6 my $note = $self -> _read_raw( 4 );
575 3         6 my $pos = 4;
576              
577 3 100       9 if ( $note eq 'adtl' ) {
    50          
578 2         4 my %allowed = map { $_ => 1 } qw( ltxt note labl );
  6         26  
579 2         7 while ( $pos < $length ) {
580 9         18 my $head = $self -> _read_raw( 4 );
581 9         10 $pos += 4;
582 9 100       15 if ( $head eq 'ltxt' ) {
583 1         5 my $record = $self -> _decode_block( [ 1 .. 6 ] );
584 1         5 $pos += 24;
585             } else {
586 8         16 my $bits = $self -> _read_long();
587 8         9 $pos += $bits + 4;
588              
589 8 50 66     26 if ( $head eq 'labl' || $head eq 'note' ) {
590 8         17 my $id = $self -> _read_long();
591 8         16 my $text = $self -> _read_raw( $bits - 4 );
592 8         30 $text =~ s/\0+$//;
593 8         19 $details -> {$head} -> {$id} = $text;
594             } else {
595 0         0 my $unknown = $self -> _read_raw ( $bits ); # skip unknown chunk
596             }
597 8 100       21 if ($bits % 2) { # eat padding
598 4         8 my $padding = $self -> _read_raw(1);
599 4         11 $pos++;
600             }
601             }
602             }
603             # if it's a broken file and we've read too much then go back
604 2 50       10 if ( $pos > $length ) {
605 0         0 seek $self->{handle}, $length-$pos, 1;
606             }
607             }
608             elsif ( $note eq 'INFO' ) {
609 1         4 my %allowed = $self -> {tools} -> get_info_fields();
610 1         5 while ( $pos < $length ) {
611 1         2 my $head = $self -> _read_raw( 4 );
612 1         2 $pos += 4;
613 1         2 my $bits = $self -> _read_long();
614 1         1 $pos += $bits + 4;
615 1         2 my $text = $self -> _read_raw( $bits );
616 1 50       4 if ( $allowed{$head} ) {
617 1         7 $text =~ s/\0+$//;
618 1         3 $details -> {info} -> { $allowed{$head} } = $text;
619             }
620 1 50       2 if ($bits % 2) { # eat padding
621 1         3 my $padding = $self -> _read_raw(1);
622 1         5 $pos++;
623             }
624             }
625             } else {
626 0         0 my $data = $self -> _read_raw( $length - 4 );
627             }
628             }
629              
630             sub _read_cue {
631 2     2   2 my $self = shift;
632 2         3 my $length = shift;
633 2         4 my $details = shift;
634 2         4 my $cues = $self -> _read_long();
635 2         6 my @fields = qw( id position chunk cstart bstart offset );
636 2         2 my @plain = qw( chunk );
637 2         10 my $output;
638 2         5 for ( 1 .. $cues ) {
639 4         12 my $record = $self -> _decode_block( \@fields, \@plain );
640 4         9 my $id = delete $record -> {id};
641 4         10 $output -> {$id} = $record;
642             }
643 2         8 return $output;
644             }
645              
646             sub _read_disp {
647 1     1   3 my $self = shift;
648 1         8 my $length = shift;
649 1         10 my $type = $self -> _read_long();
650 1         3 my $data = $self -> _read_raw( $length - 4 + ($length%2) );
651 1         4 $data =~ s/\0+$//;
652 1         3 return [ $type, $data ];
653             }
654              
655             sub _read_sampler {
656 1     1   1 my $self = shift;
657 1         2 my $length = shift;
658 1         4 my %sampler_fields = $self -> {tools} -> get_sampler_fields();
659              
660 1         3 my $record = $self -> _decode_block( $sampler_fields{fields} );
661              
662 1         2 for my $id ( 1 .. $record -> {sample_loops} ) {
663 1         2 push @{ $record -> {loop} }, $self -> _decode_block( $sampler_fields{loop} );
  1         9  
664             }
665              
666 1         4 $record -> {sample_specific_data} = _read_raw( $record -> {sample_data} );
667              
668 1         3 my $read_bytes =
669             9 * 4 # sampler info
670             + 6 * 4 * $record -> {sample_loops} # loops
671             + $record -> {sample_data}; # specific data
672              
673              
674             # read any junk
675 1 50       3 if ($read_bytes < $length ) {
676 0         0 my $junk = $self->_read_raw( $length - $read_bytes );
677             }
678              
679 1 50       4 if ( $length % 2 ) {
680 0         0 my $pad = $self -> _read_raw( 1 );
681             }
682              
683             # temporary nasty hack to gooble the last bogus 12 bytes
684             #my $extra = $self -> _decode_block( $sampler_fields{extra} );
685              
686 1         4 return $record;
687             }
688              
689              
690             sub _decode_block {
691 7     7   9 my $self = shift;
692 7         8 my $fields = shift;
693 7         9 my $plain = shift;
694 7         9 my %plain;
695 7 100       13 if ( $plain ) {
696 4         5 foreach my $field ( @{$plain} ) {
  4         7  
697 4         5 for my $id ( 0 .. $#{$fields} ) {
  4         8  
698 24 100       55 next unless $fields -> [$id] eq $field;
699 4         10 $plain{$id} = 1;
700             }
701             }
702             }
703 7         9 my $no_fields = scalar @{$fields};
  7         10  
704 7         6 my %record;
705 7         9 for my $id ( 0 .. $#{$fields} ) {
  7         12  
706 45 100       74 if ( exists $plain{$id} ) {
707 4         17 $record{ $fields -> [$id] } = $self -> _read_raw( 4 );
708             } else {
709 41         72 $record{ $fields -> [$id] } = $self -> _read_long();
710             }
711             }
712 7         19 return \%record;
713             }
714              
715             sub _read_fmt {
716 2     2   3 my $self = shift;
717 2         4 my $length = shift;
718 2         4 my $data = $self -> _read_raw( $length );
719 2         10 my $types = $self -> {tools} -> get_wav_pack();
720 2         4 my $pack_str = '';
721 2         5 my $fields = $types -> {order};
722 2         3 foreach my $type ( @{$fields} ) {
  2         4  
723 12         21 $pack_str .= $types -> {types} -> {$type};
724             }
725 2         15 my @data = unpack $pack_str, $data;
726 2         3 my %record;
727 2         4 for my $id ( 0 .. $#{$fields} ) {
  2         6  
728 12         28 $record{ $fields -> [$id] } = $data[$id];
729             }
730 2         97 return { %record };
731             }
732              
733             sub _read_long {
734 75     75   83 my $self = shift;
735 75         111 my $data = $self -> _read_raw( 4 );
736 75         194 return unpack 'V', $data;
737             }
738              
739             sub _error {
740 0     0     my ($self, @args) = @_;
741 0           return $self -> {tools} -> error( $self -> {file}, @args );
742             }
743              
744             =head1 AUTHORS
745              
746             Nick Peskett (see http://www.peskett.co.uk/ for contact details).
747             Brian Szymanski (0.07-0.14)
748             Wolfram humann (pureperl 24 and 32 bit read support in 0.09)
749             Kurt George Gjerde . (0.02-0.03)
750              
751             =cut
752              
753             1;
754              
755             __DATA__