=head1 NAME Convert::CD::Track - desribe track layout and offer data access methods. =head1 SYNOPSIS use Convert::CD::Track; # to be done =head1 DESCRIPTION Kidding? =head2 EXPORTED CONSTANTS FLAG_DCP digital copy permitted FLAG_FCA four channel audio FLAG_PRE track is pre-emphasized FLAG_SCMS protected by serial copy management system (see DCP) FLAG_DATA disk is data track (automatically set for MODE1) MODE_AUDIO MODE_CDG CD+G (Karaoke) MODE_MODE0 MODE 0 (unused, 2336 bps) MODE_MODE1 MODE 1 (standard yellow book, 2048 bps) MODE_MODE2 MODe 2 (CD/XA, 2336 bps) MODE_CDI CD-i, CD Interactive =cut package Convert::CD::Track; use POSIX qw(ceil); use Carp; use base Exporter; $VERSION = 0.01; @EXPORT = qw( FLAG_DCP FLAG_FCA FLAG_PRE FLAG_SCMS FLAG_DATA MODE_AUDIO MODE_CDG MODE_MODE1 MODE_MODE2 MODE_CDI ); sub FLAG_DCP () { 0x0001 } sub FLAG_FCA () { 0x0002 } sub FLAG_PRE () { 0x0004 } sub FLAG_DATA () { 0x0008 } sub FLAG_SCMS () { 0x1000 } sub MODE_AUDIO () { 0x0001 } sub MODE_CDG () { 0x0002 } sub MODE_MODE1 () { 0x0004 } sub MODE_MODE2 () { 0x0008 } sub MODE_CDI () { 0x0010 } sub MODE_MODE0 () { 0x1000 } =head2 Convert::CD::Track METHODS This class is used to repesent single track objects. =over 4 =item new param => value... Create a new track object. Parameters that are not given are usually guess, depending on the mode and other parameters. Parameters: path the file that contains the track offset the offste within the file that contains the first sector (may be negative) rawsize the raw size (in bytes) of the track. (guessed if missing) mode a mode string (MODE1, MODE2, AUDIO, CDI, CDG....) datasize size of the data part of the sector (e.g. 2048). secsize size of a sector in disk image index an array of index times (0 is pregap start, 1 is track start) =cut sub new { my $class = shift; my $self = bless { @_, }, $class; $self->{rawsize} ||= (-s $self->{path}) - $self->{offset}; $self->{flags} ||= FLAG_DATA if $self->{mode} == MODE_MODE1; $self->{datasize} ||= $self->{mode} == MODE_MODE1 ? 2048 : $self->{mode} == MODE_MODE2 ? 2336 : $self->{mode} == MODE_AUDIO ? 2352 : $self->{secsize}; $self->{datasize} = $self->{secsize} if $self->{secsize} < $self->{datasize}; $self; } # create and cache a filehandle of the given mode sub fh { my ($self, $mode) = @_; $self->{fh}{$mode} ||= do { open my $fh, $mode, $self->{path} or croak "$self->{path}: $!"; binmode $fh; # *seufz* $fh; }; } =item $track->read ($offset, $length) Reads and returns a part of the track. C<$offset> should be a multiple of the datasize. =cut sub read { my ($self, $offset, $length) = @_; my $buf; my $fh = $self->fh ("<"); $offset = $offset / $self->{secsize} * $self->{secsize}; while ($length > 0) { sysseek $fh, $self->{offset} + $offset, 0 or croak "seek($self->{path}): $!"; sysread $fh, $buf, ($length < $self->{datasize} ? $length : $self->{datasize}), length $buf or croak "read($self->{path}): $!"; $length -= $self->{datasize}; $offset += $self->{secsize}; } $buf; } =item $track->size Returns the (cooked) size of the track in bytes, i.e. logical size of the track without any ECC etc. information. =cut sub size { my ($self) = @_; ceil ($self->{rawsize} / $self->{secsize}) * $self->{datasize}; } =item $track->as_string Returns a single-line, textual description of the track. =cut sub as_string { my ($self) = @_; my %data = %$self; $data{index} = "<" . (join "|", @{$data{index}}) .">"; sprintf "%s %08x+%08x %s/%d (%s)", delete @data{qw(path offset rawsize mode secsize)}, join " ", %data; } =item $track->to_msf ($offset) Converts the given byte offste into M:S:F format. =cut sub to_msf { my ($self, $offset) = @_; my $frame = $offset / $self->{datasize}; sprintf "%02d:%02d:%02d", $frame / (60 * 75), $frame / 75 % 60, $frame % 75; } 1; =back =head1 AUTHOR Marc Lehmann =cut