diff options
Diffstat (limited to 'tools/vorbiscomm.pm')
-rw-r--r-- | tools/vorbiscomm.pm | 682 |
1 files changed, 682 insertions, 0 deletions
diff --git a/tools/vorbiscomm.pm b/tools/vorbiscomm.pm new file mode 100644 index 0000000000..4577a700a5 --- /dev/null +++ b/tools/vorbiscomm.pm | |||
@@ -0,0 +1,682 @@ | |||
1 | ############################################################################# | ||
2 | # This is | ||
3 | # http://search.cpan.org/~amolloy/Ogg-Vorbis-Header-PurePerl-0.07/PurePerl.pm | ||
4 | # written by Andrew Molloy | ||
5 | # Code under GNU GENERAL PUBLIC LICENCE v2 | ||
6 | # $Id$ | ||
7 | ############################################################################# | ||
8 | |||
9 | package vorbiscomm; | ||
10 | |||
11 | use 5.005; | ||
12 | use strict; | ||
13 | use warnings; | ||
14 | |||
15 | use Fcntl qw/SEEK_END/; | ||
16 | |||
17 | our $VERSION = '0.07'; | ||
18 | |||
19 | sub new | ||
20 | { | ||
21 | my $class = shift; | ||
22 | my $file = shift; | ||
23 | |||
24 | return load($class, $file); | ||
25 | } | ||
26 | |||
27 | sub load | ||
28 | { | ||
29 | my $class = shift; | ||
30 | my $file = shift; | ||
31 | my $from_new = shift; | ||
32 | my %data; | ||
33 | my $self; | ||
34 | |||
35 | # there must be a better way... | ||
36 | if ($class eq 'vorbiscomm') | ||
37 | { | ||
38 | $self = bless \%data, $class; | ||
39 | } | ||
40 | else | ||
41 | { | ||
42 | $self = $class; | ||
43 | } | ||
44 | |||
45 | if ($self->{'FILE_LOADED'}) | ||
46 | { | ||
47 | return $self; | ||
48 | } | ||
49 | |||
50 | $self->{'FILE_LOADED'} = 1; | ||
51 | |||
52 | # check that the file exists and is readable | ||
53 | unless ( -e $file && -r _ ) | ||
54 | { | ||
55 | warn "File does not exist or cannot be read."; | ||
56 | # file does not exist, can't do anything | ||
57 | return undef; | ||
58 | } | ||
59 | # open up the file | ||
60 | open FILE, $file; | ||
61 | # make sure dos-type systems can handle it... | ||
62 | binmode FILE; | ||
63 | |||
64 | $data{'filename'} = $file; | ||
65 | $data{'fileHandle'} = \*FILE; | ||
66 | |||
67 | _init(\%data); | ||
68 | _loadInfo(\%data); | ||
69 | _loadComments(\%data); | ||
70 | _calculateTrackLength(\%data); | ||
71 | |||
72 | close FILE; | ||
73 | |||
74 | return $self; | ||
75 | } | ||
76 | |||
77 | sub info | ||
78 | { | ||
79 | my $self = shift; | ||
80 | my $key = shift; | ||
81 | |||
82 | # if the user did not supply a key, return the entire hash | ||
83 | unless ($key) | ||
84 | { | ||
85 | return $self->{'INFO'}; | ||
86 | } | ||
87 | |||
88 | # otherwise, return the value for the given key | ||
89 | return $self->{'INFO'}{lc $key}; | ||
90 | } | ||
91 | |||
92 | sub comment_tags | ||
93 | { | ||
94 | my $self = shift; | ||
95 | |||
96 | return @{$self->{'COMMENT_KEYS'}}; | ||
97 | } | ||
98 | |||
99 | sub comment | ||
100 | { | ||
101 | my $self = shift; | ||
102 | my $key = shift; | ||
103 | |||
104 | # if the user supplied key does not exist, return undef | ||
105 | unless($self->{'COMMENTS'}{lc $key}) | ||
106 | { | ||
107 | return undef; | ||
108 | } | ||
109 | |||
110 | return @{$self->{'COMMENTS'}{lc $key}}; | ||
111 | } | ||
112 | |||
113 | sub add_comments | ||
114 | { | ||
115 | warn "Ogg::Vorbis::Header::PurePerl add_comments() unimplemented."; | ||
116 | } | ||
117 | |||
118 | sub edit_comment | ||
119 | { | ||
120 | warn "Ogg::Vorbis::Header::PurePerl edit_comment() unimplemented."; | ||
121 | } | ||
122 | |||
123 | sub delete_comment | ||
124 | { | ||
125 | warn "Ogg::Vorbis::Header::PurePerl delete_comment() unimplemented."; | ||
126 | } | ||
127 | |||
128 | sub clear_comments | ||
129 | { | ||
130 | warn "Ogg::Vorbis::Header::PurePerl clear_comments() unimplemented."; | ||
131 | } | ||
132 | |||
133 | sub path | ||
134 | { | ||
135 | my $self = shift; | ||
136 | |||
137 | return $self->{'fileName'}; | ||
138 | } | ||
139 | |||
140 | sub write_vorbis | ||
141 | { | ||
142 | warn "Ogg::Vorbis::Header::PurePerl write_vorbis unimplemented."; | ||
143 | } | ||
144 | |||
145 | # "private" methods | ||
146 | |||
147 | sub _init | ||
148 | { | ||
149 | my $data = shift; | ||
150 | my $fh = $data->{'fileHandle'}; | ||
151 | my $byteCount = 0; | ||
152 | |||
153 | # check the header to make sure this is actually an Ogg-Vorbis file | ||
154 | $byteCount = _checkHeader($data); | ||
155 | |||
156 | unless($byteCount) | ||
157 | { | ||
158 | # if it's not, we can't do anything | ||
159 | return undef; | ||
160 | } | ||
161 | |||
162 | $data->{'startInfoHeader'} = $byteCount; | ||
163 | } | ||
164 | |||
165 | sub _checkHeader | ||
166 | { | ||
167 | my $data = shift; | ||
168 | my $fh = $data->{'fileHandle'}; | ||
169 | my $buffer; | ||
170 | my $pageSegCount; | ||
171 | my $byteCount = 0; # stores how far into the file we've read, | ||
172 | # so later reads into the file can skip right | ||
173 | # past all of the header stuff | ||
174 | |||
175 | # check that the first four bytes are 'OggS' | ||
176 | read($fh, $buffer, 4); | ||
177 | if ($buffer ne 'OggS') | ||
178 | { | ||
179 | warn "This is not an Ogg bitstream (no OggS header)."; | ||
180 | return undef; | ||
181 | } | ||
182 | $byteCount += 4; | ||
183 | |||
184 | # check the stream structure version (1 byte, should be 0x00) | ||
185 | read($fh, $buffer, 1); | ||
186 | if (ord($buffer) != 0x00) | ||
187 | { | ||
188 | warn "This is not an Ogg bitstream (invalid structure version)."; | ||
189 | return undef; | ||
190 | } | ||
191 | $byteCount += 1; | ||
192 | |||
193 | # check the header type flag | ||
194 | # This is a bitfield, so technically we should check all of the bits | ||
195 | # that could potentially be set. However, the only value this should | ||
196 | # possibly have at the beginning of a proper Ogg-Vorbis file is 0x02, | ||
197 | # so we just check for that. If it's not that, we go on anyway, but | ||
198 | # give a warning (this behavior may (should?) be modified in the future. | ||
199 | read($fh, $buffer, 1); | ||
200 | if (ord($buffer) != 0x02) | ||
201 | { | ||
202 | warn "Invalid header type flag (trying to go ahead anyway)."; | ||
203 | } | ||
204 | $byteCount += 1; | ||
205 | |||
206 | # skip to the page_segments count | ||
207 | read($fh, $buffer, 20); | ||
208 | $byteCount += 20; | ||
209 | # we do nothing with this data | ||
210 | |||
211 | # read the number of page segments | ||
212 | read($fh, $buffer, 1); | ||
213 | $pageSegCount = ord($buffer); | ||
214 | $byteCount += 1; | ||
215 | |||
216 | # read $pageSegCount bytes, then throw 'em out | ||
217 | read($fh, $buffer, $pageSegCount); | ||
218 | $byteCount += $pageSegCount; | ||
219 | |||
220 | # check packet type. Should be 0x01 (for indentification header) | ||
221 | read($fh, $buffer, 1); | ||
222 | if (ord($buffer) != 0x01) | ||
223 | { | ||
224 | warn "Wrong vorbis header type, giving up."; | ||
225 | return undef; | ||
226 | } | ||
227 | $byteCount += 1; | ||
228 | |||
229 | # check that the packet identifies itself as 'vorbis' | ||
230 | read($fh, $buffer, 6); | ||
231 | if ($buffer ne 'vorbis') | ||
232 | { | ||
233 | warn "This does not appear to be a vorbis stream, giving up."; | ||
234 | return undef; | ||
235 | } | ||
236 | $byteCount += 6; | ||
237 | |||
238 | # at this point, we assume the bitstream is valid | ||
239 | return $byteCount; | ||
240 | } | ||
241 | |||
242 | sub _loadInfo | ||
243 | { | ||
244 | my $data = shift; | ||
245 | my $start = $data->{'startInfoHeader'}; | ||
246 | my $fh = $data->{'fileHandle'}; | ||
247 | my $buffer; | ||
248 | my $byteCount = $start; | ||
249 | my %info; | ||
250 | |||
251 | seek $fh, $start, 0; | ||
252 | |||
253 | # read the vorbis version | ||
254 | read($fh, $buffer, 4); | ||
255 | $info{'version'} = _decodeInt($buffer); | ||
256 | $byteCount += 4; | ||
257 | |||
258 | # read the number of audio channels | ||
259 | read($fh, $buffer, 1); | ||
260 | $info{'channels'} = ord($buffer); | ||
261 | $byteCount += 1; | ||
262 | |||
263 | # read the sample rate | ||
264 | read($fh, $buffer, 4); | ||
265 | $info{'rate'} = _decodeInt($buffer); | ||
266 | $byteCount += 4; | ||
267 | |||
268 | # read the bitrate maximum | ||
269 | read($fh, $buffer, 4); | ||
270 | $info{'bitrate_upper'} = _decodeInt($buffer); | ||
271 | $byteCount += 4; | ||
272 | |||
273 | # read the bitrate nominal | ||
274 | read($fh, $buffer, 4); | ||
275 | $info{'bitrate_nominal'} = _decodeInt($buffer); | ||
276 | $byteCount += 4; | ||
277 | |||
278 | # read the bitrate minimal | ||
279 | read($fh, $buffer, 4); | ||
280 | $info{'bitrate_lower'} = _decodeInt($buffer); | ||
281 | $byteCount += 4; | ||
282 | |||
283 | # read the blocksize_0 and blocksize_1 | ||
284 | read($fh, $buffer, 1); | ||
285 | # these are each 4 bit fields, whose actual value is 2 to the power | ||
286 | # of the value of the field | ||
287 | $info{'blocksize_0'} = 2 << ((ord($buffer) & 0xF0) >> 4); | ||
288 | $info{'blocksize_1'} = 2 << (ord($buffer) & 0x0F); | ||
289 | $byteCount += 1; | ||
290 | |||
291 | # read the framing_flag | ||
292 | read($fh, $buffer, 1); | ||
293 | $info{'framing_flag'} = ord($buffer); | ||
294 | $byteCount += 1; | ||
295 | |||
296 | # bitrate_window is -1 in the current version of vorbisfile | ||
297 | $info{'bitrate_window'} = -1; | ||
298 | |||
299 | $data->{'startCommentHeader'} = $byteCount; | ||
300 | |||
301 | $data->{'INFO'} = \%info; | ||
302 | } | ||
303 | |||
304 | sub _loadComments | ||
305 | { | ||
306 | my $data = shift; | ||
307 | my $fh = $data->{'fileHandle'}; | ||
308 | my $start = $data->{'startCommentHeader'}; | ||
309 | my $buffer; | ||
310 | my $page_segments; | ||
311 | my $vendor_length; | ||
312 | my $user_comment_count; | ||
313 | my $byteCount = $start; | ||
314 | my %comments; | ||
315 | |||
316 | seek $fh, $start, 0; | ||
317 | |||
318 | # check that the first four bytes are 'OggS' | ||
319 | read($fh, $buffer, 4); | ||
320 | if ($buffer ne 'OggS') | ||
321 | { | ||
322 | warn "No comment header?"; | ||
323 | return undef; | ||
324 | } | ||
325 | $byteCount += 4; | ||
326 | |||
327 | # skip over next ten bytes | ||
328 | read($fh, $buffer, 10); | ||
329 | $byteCount += 10; | ||
330 | |||
331 | # read the stream serial number | ||
332 | read($fh, $buffer, 4); | ||
333 | push @{$data->{'commentSerialNumber'}}, _decodeInt($buffer); | ||
334 | $byteCount += 4; | ||
335 | |||
336 | # read the page sequence number (should be 0x01) | ||
337 | read($fh, $buffer, 4); | ||
338 | if (_decodeInt($buffer) != 0x01) | ||
339 | { | ||
340 | warn "Comment header page sequence number is not 0x01: " + | ||
341 | _decodeInt($buffer); | ||
342 | warn "Going to keep going anyway."; | ||
343 | } | ||
344 | $byteCount += 4; | ||
345 | |||
346 | # and ignore the page checksum for now | ||
347 | read($fh, $buffer, 4); | ||
348 | $byteCount += 4; | ||
349 | |||
350 | # get the number of entries in the segment_table... | ||
351 | read($fh, $buffer, 1); | ||
352 | $page_segments = _decodeInt($buffer); | ||
353 | $byteCount += 1; | ||
354 | # then skip on past it | ||
355 | read($fh, $buffer, $page_segments); | ||
356 | $byteCount += $page_segments; | ||
357 | |||
358 | # check the header type (should be 0x03) | ||
359 | read($fh, $buffer, 1); | ||
360 | if (ord($buffer) != 0x03) | ||
361 | { | ||
362 | warn "Wrong header type: " . ord($buffer); | ||
363 | } | ||
364 | $byteCount += 1; | ||
365 | |||
366 | # now we should see 'vorbis' | ||
367 | read($fh, $buffer, 6); | ||
368 | if ($buffer ne 'vorbis') | ||
369 | { | ||
370 | warn "Missing comment header. Should have found 'vorbis', found " . | ||
371 | $buffer; | ||
372 | } | ||
373 | $byteCount += 6; | ||
374 | |||
375 | # get the vendor length | ||
376 | read($fh, $buffer, 4); | ||
377 | $vendor_length = _decodeInt($buffer); | ||
378 | $byteCount += 4; | ||
379 | |||
380 | # read in the vendor | ||
381 | read($fh, $buffer, $vendor_length); | ||
382 | $comments{'vendor'} = $buffer; | ||
383 | $byteCount += $vendor_length; | ||
384 | |||
385 | # read in the number of user comments | ||
386 | read($fh, $buffer, 4); | ||
387 | $user_comment_count = _decodeInt($buffer); | ||
388 | $byteCount += 4; | ||
389 | |||
390 | $data->{'COMMENT_KEYS'} = []; | ||
391 | |||
392 | # finally, read the comments | ||
393 | for (my $i = 0; $i < $user_comment_count; $i++) | ||
394 | { | ||
395 | # first read the length | ||
396 | read($fh, $buffer, 4); | ||
397 | my $comment_length = _decodeInt($buffer); | ||
398 | $byteCount += 4; | ||
399 | |||
400 | # then the comment itself | ||
401 | read($fh, $buffer, $comment_length); | ||
402 | $byteCount += $comment_length; | ||
403 | |||
404 | my ($key) = $buffer =~ /^([^=]+)/; | ||
405 | my ($value) = $buffer =~ /=(.*)$/; | ||
406 | |||
407 | push @{$comments{lc $key}}, $value; | ||
408 | push @{$data->{'COMMENT_KEYS'}}, lc $key; | ||
409 | } | ||
410 | |||
411 | # read past the framing_bit | ||
412 | read($fh, $buffer, 1); | ||
413 | $byteCount += 1; | ||
414 | |||
415 | $data->{'INFO'}{'offset'} = $byteCount; | ||
416 | |||
417 | $data->{'COMMENTS'} = \%comments; | ||
418 | } | ||
419 | |||
420 | sub _calculateTrackLength | ||
421 | { | ||
422 | my $data = shift; | ||
423 | my $fh = $data->{'fileHandle'}; | ||
424 | my $buffer; | ||
425 | my $pageSize; | ||
426 | my $granule_position; | ||
427 | |||
428 | seek($fh,-8500,SEEK_END); # that magic number is from vorbisfile.c | ||
429 | # in the constant CHUNKSIZE, which comes | ||
430 | # with the comment /* a shade over 8k; | ||
431 | # anyone using pages well over 8k gets | ||
432 | # what they deserve */ | ||
433 | |||
434 | # we just keep looking through the headers until we get to the last one | ||
435 | # (there might be a couple of blocks here) | ||
436 | while(_findPage($fh)) | ||
437 | { | ||
438 | # stream structure version - must be 0x00 | ||
439 | read($fh, $buffer, 1); | ||
440 | if (ord($buffer) != 0x00) | ||
441 | { | ||
442 | warn "Invalid stream structure version: " . | ||
443 | sprintf("%x", ord($buffer)); | ||
444 | return; | ||
445 | } | ||
446 | |||
447 | # header type flag | ||
448 | read($fh, $buffer, 1); | ||
449 | # we should check this, but for now we'll just ignore it | ||
450 | |||
451 | # absolute granule position - this is what we need! | ||
452 | read($fh, $buffer, 8); | ||
453 | $granule_position = _decodeInt($buffer); | ||
454 | |||
455 | # skip past stream_serial_number, page_sequence_number, and crc | ||
456 | read($fh, $buffer, 12); | ||
457 | |||
458 | # page_segments | ||
459 | read($fh, $buffer, 1); | ||
460 | my $page_segments = ord($buffer); | ||
461 | |||
462 | # reset pageSize | ||
463 | $pageSize = 0; | ||
464 | |||
465 | # calculate approx. page size | ||
466 | for (my $i = 0; $i < $page_segments; $i++) | ||
467 | { | ||
468 | read($fh, $buffer, 1); | ||
469 | $pageSize += ord($buffer); | ||
470 | } | ||
471 | |||
472 | seek $fh, $pageSize, 1; | ||
473 | } | ||
474 | |||
475 | $data->{'INFO'}{'length'} = | ||
476 | int($granule_position / $data->{'INFO'}{'rate'}); | ||
477 | } | ||
478 | |||
479 | sub _findPage | ||
480 | { | ||
481 | # search forward in the file for the 'OggS' page header | ||
482 | my $fh = shift; | ||
483 | my $char; | ||
484 | my $curStr = ''; | ||
485 | |||
486 | while (read($fh, $char, 1)) | ||
487 | { | ||
488 | $curStr = $char . $curStr; | ||
489 | $curStr = substr($curStr, 0, 4); | ||
490 | |||
491 | # we are actually looking for the string 'SggO' because we | ||
492 | # tack character on to our test string backwards, to make | ||
493 | # trimming it to 4 characters easier. | ||
494 | if ($curStr eq 'SggO') | ||
495 | { | ||
496 | return 1; | ||
497 | } | ||
498 | } | ||
499 | |||
500 | return undef; | ||
501 | } | ||
502 | |||
503 | sub _decodeInt | ||
504 | { | ||
505 | my $bytes = shift; | ||
506 | my $num = 0; | ||
507 | my @byteList = split //, $bytes; | ||
508 | my $numBytes = @byteList; | ||
509 | my $mult = 1; | ||
510 | |||
511 | for (my $i = 0; $i < $numBytes; $i ++) | ||
512 | { | ||
513 | $num += ord($byteList[$i]) * $mult; | ||
514 | $mult *= 256; | ||
515 | } | ||
516 | |||
517 | return $num; | ||
518 | } | ||
519 | |||
520 | sub _decodeInt5Bit | ||
521 | { | ||
522 | my $byte = ord(shift); | ||
523 | |||
524 | $byte = $byte & 0xF8; # clear out the bottm 3 bits | ||
525 | $byte = $byte >> 3; # and shifted down to where it belongs | ||
526 | |||
527 | return $byte; | ||
528 | } | ||
529 | |||
530 | sub _decodeInt4Bit | ||
531 | { | ||
532 | my $byte = ord(shift); | ||
533 | |||
534 | $byte = $byte & 0xFC; # clear out the bottm 4 bits | ||
535 | $byte = $byte >> 4; # and shifted down to where it belongs | ||
536 | |||
537 | return $byte; | ||
538 | } | ||
539 | |||
540 | sub _ilog | ||
541 | { | ||
542 | my $x = shift; | ||
543 | my $ret = 0; | ||
544 | |||
545 | unless ($x > 0) | ||
546 | { | ||
547 | return 0; | ||
548 | } | ||
549 | |||
550 | while ($x > 0) | ||
551 | { | ||
552 | $ret++; | ||
553 | $x = $x >> 1; | ||
554 | } | ||
555 | |||
556 | return $ret; | ||
557 | } | ||
558 | |||
559 | 1; | ||
560 | __DATA__ | ||
561 | |||
562 | =head1 NAME | ||
563 | |||
564 | Ogg::Vorbis::Header::PurePerl - An object-oriented interface to Ogg Vorbis | ||
565 | information and comment fields, implemented entirely in Perl. Intended to be | ||
566 | a drop in replacement for Ogg::Vobis::Header. | ||
567 | |||
568 | Unlike Ogg::Vorbis::Header, this module will go ahead and fill in all of the | ||
569 | information fields as soon as you construct the object. In other words, | ||
570 | the C<new> and C<load> constructors have identical behavior. | ||
571 | |||
572 | =head1 SYNOPSIS | ||
573 | |||
574 | use Ogg::Vorbis::Header::PurePerl; | ||
575 | my $ogg = Ogg::Vorbis::Header::PurePerl->new("song.ogg"); | ||
576 | while (my ($k, $v) = each %{$ogg->info}) { | ||
577 | print "$k: $v\n"; | ||
578 | } | ||
579 | foreach my $com ($ogg->comment_tags) { | ||
580 | print "$com: $_\n" foreach $ogg->comment($com); | ||
581 | } | ||
582 | |||
583 | =head1 DESCRIPTION | ||
584 | |||
585 | This module is intended to be a drop in replacement for Ogg::Vorbis::Header, | ||
586 | implemented entirely in Perl. It provides an object-oriented interface to | ||
587 | Ogg Vorbis information and comment fields. (NOTE: This module currently | ||
588 | supports only read operations). | ||
589 | |||
590 | =head1 CONSTRUCTORS | ||
591 | |||
592 | =head2 C<new ($filename)> | ||
593 | |||
594 | Opens an Ogg Vorbis file, ensuring that it exists and is actually an | ||
595 | Ogg Vorbis stream. This method does not actually read any of the | ||
596 | information or comment fields, and closes the file immediately. | ||
597 | |||
598 | =head2 C<load ([$filename])> | ||
599 | |||
600 | Opens an Ogg Vorbis file, ensuring that it exists and is actually an | ||
601 | Ogg Vorbis stream, then loads the information and comment fields. This | ||
602 | method can also be used without a filename to load the information | ||
603 | and fields of an already constructed instance. | ||
604 | |||
605 | =head1 INSTANCE METHODS | ||
606 | |||
607 | =head2 C<info ([$key])> | ||
608 | |||
609 | Returns a hashref containing information about the Ogg Vorbis file from | ||
610 | the file's information header. Hash fields are: version, channels, rate, | ||
611 | bitrate_upper, bitrate_nominal, bitrate_lower, bitrate_window, and length. | ||
612 | The bitrate_window value is not currently used by the vorbis codec, and | ||
613 | will always be -1. | ||
614 | |||
615 | The optional parameter, key, allows you to retrieve a single value from | ||
616 | the object's hash. Returns C<undef> if the key is not found. | ||
617 | |||
618 | =head2 C<comment_tags ()> | ||
619 | |||
620 | Returns an array containing the key values for the comment fields. | ||
621 | These values can then be passed to C<comment> to retrieve their values. | ||
622 | |||
623 | =head2 C<comment ($key)> | ||
624 | |||
625 | Returns an array of comment values associated with the given key. | ||
626 | |||
627 | =head2 C<add_comments ($key, $value, [$key, $value, ...])> | ||
628 | |||
629 | Unimplemented. | ||
630 | |||
631 | =head2 C<edit_comment ($key, $value, [$num])> | ||
632 | |||
633 | Unimplemented. | ||
634 | |||
635 | =head2 C<delete_comment ($key, [$num])> | ||
636 | |||
637 | Unimplemented. | ||
638 | |||
639 | =head2 C<clear_comments ([@keys])> | ||
640 | |||
641 | Unimplemented. | ||
642 | |||
643 | =head2 C<write_vorbis ()> | ||
644 | |||
645 | Unimplemented. | ||
646 | |||
647 | =head2 C<path ()> | ||
648 | |||
649 | Returns the path/filename of the file the object represents. | ||
650 | |||
651 | =head1 NOTE | ||
652 | |||
653 | This is ALPHA SOFTWARE. It may very well be very broken. Do not use it in | ||
654 | a production environment. You have been warned. | ||
655 | |||
656 | =head1 ACKNOWLEDGEMENTS | ||
657 | |||
658 | Dave Brown <cpan@dagbrown.com> made this module significantly faster | ||
659 | at calculating the length of ogg files. | ||
660 | |||
661 | Robert Moser II <rlmoser@earthlink.net> fixed a problem with files that | ||
662 | have no comments. | ||
663 | |||
664 | =head1 AUTHOR | ||
665 | |||
666 | Andrew Molloy E<lt>amolloy@kaizolabs.comE<gt> | ||
667 | |||
668 | =head1 COPYRIGHT | ||
669 | |||
670 | Copyright (c) 2003, Andrew Molloy. All Rights Reserved. | ||
671 | |||
672 | This program is free software; you can redistribute it and/or modify it | ||
673 | under the terms of the GNU General Public License as published by the | ||
674 | Free Software Foundation; either version 2 of the License, or (at | ||
675 | your option) any later version. A copy of this license is included | ||
676 | with this module (LICENSE.GPL). | ||
677 | |||
678 | =head1 SEE ALSO | ||
679 | |||
680 | L<Ogg::Vorbis::Header>, L<Ogg::Vorbis::Decoder> | ||
681 | |||
682 | =cut | ||