APE.pm 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. #------------------------------------------------------------------------------
  2. # File: APE.pm
  3. #
  4. # Description: Read Monkey's Audio meta information
  5. #
  6. # Revisions: 11/13/2006 - P. Harvey Created
  7. #
  8. # References: 1) http://www.monkeysaudio.com/
  9. # 2) http://www.personal.uni-jena.de/~pfk/mpp/sv8/apetag.html
  10. #------------------------------------------------------------------------------
  11. package Image::ExifTool::APE;
  12. use strict;
  13. use vars qw($VERSION);
  14. use Image::ExifTool qw(:DataAccess :Utils);
  15. $VERSION = '1.04';
  16. # APE metadata blocks
  17. %Image::ExifTool::APE::Main = (
  18. GROUPS => { 2 => 'Audio' },
  19. NOTES => q{
  20. Tags found in Monkey's Audio (APE) information. Only a few common tags are
  21. listed below, but ExifTool will extract any tag found. ExifTool supports
  22. APEv1 and APEv2 tags, as well as ID3 information in APE files, and will also
  23. read APE metadata from MP3 and MPC files.
  24. },
  25. Album => { },
  26. Artist => { },
  27. Genre => { },
  28. Title => { },
  29. Track => { },
  30. Year => { },
  31. 'Tool Version' => { Name => 'ToolVersion' },
  32. 'Tool Name' => { Name => 'ToolName' },
  33. );
  34. # APE MAC header version 3.97 or earlier
  35. %Image::ExifTool::APE::OldHeader = (
  36. PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
  37. GROUPS => { 1 => 'MAC', 2 => 'Audio' },
  38. FORMAT => 'int16u',
  39. NOTES => 'APE MAC audio header for version 3.97 or earlier.',
  40. 0 => {
  41. Name => 'APEVersion',
  42. ValueConv => '$val / 1000',
  43. },
  44. 1 => 'CompressionLevel',
  45. # 2 => 'FormatFlags',
  46. 3 => 'Channels',
  47. 4 => { Name => 'SampleRate', Format => 'int32u' },
  48. # 6 => { Name => 'HeaderBytes', Format => 'int32u' }, # WAV header bytes
  49. # 8 => { Name => 'TerminatingBytes', Format => 'int32u' },
  50. 10 => { Name => 'TotalFrames', Format => 'int32u' },
  51. 12 => { Name => 'FinalFrameBlocks', Format => 'int32u' },
  52. );
  53. # APE MAC header version 3.98 or later
  54. %Image::ExifTool::APE::NewHeader = (
  55. PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
  56. GROUPS => { 1 => 'MAC', 2 => 'Audio' },
  57. FORMAT => 'int16u',
  58. NOTES => 'APE MAC audio header for version 3.98 or later.',
  59. 0 => 'CompressionLevel',
  60. # 1 => 'FormatFlags',
  61. 2 => { Name => 'BlocksPerFrame', Format => 'int32u' },
  62. 4 => { Name => 'FinalFrameBlocks', Format => 'int32u' },
  63. 6 => { Name => 'TotalFrames', Format => 'int32u' },
  64. 8 => 'BitsPerSample',
  65. 9 => 'Channels',
  66. 10 => { Name => 'SampleRate', Format => 'int32u' },
  67. );
  68. #------------------------------------------------------------------------------
  69. # Make tag info hash for specified tag
  70. # Inputs: 0) tag name, 1) tag table ref
  71. # - must only call if tag doesn't exist
  72. sub MakeTag($$)
  73. {
  74. my ($tag, $tagTablePtr) = @_;
  75. my $name = ucfirst(lc($tag));
  76. # remove invalid characters in tag name and capitalize following letters
  77. $name =~ s/[^\w-]+(.?)/\U$1/sg;
  78. $name =~ s/([a-z0-9])_([a-z])/$1\U$2/g;
  79. my %tagInfo = ( Name => $name );
  80. $tagInfo{Groups} = { 2 => 'Preview' } if $tag =~ /^Cover Art/ and $tag !~ /Desc$/;
  81. AddTagToTable($tagTablePtr, $tag, \%tagInfo);
  82. }
  83. #------------------------------------------------------------------------------
  84. # Extract information from an APE file
  85. # Inputs: 0) ExifTool object reference, 1) dirInfo reference
  86. # - Just looks for APE trailer if FileType is already set
  87. # Returns: 1 on success, 0 if this wasn't a valid APE file
  88. sub ProcessAPE($$)
  89. {
  90. my ($et, $dirInfo) = @_;
  91. # must first check for leading/trailing ID3 information
  92. unless ($$et{DoneID3}) {
  93. require Image::ExifTool::ID3;
  94. Image::ExifTool::ID3::ProcessID3($et, $dirInfo) and return 1;
  95. }
  96. my $raf = $$dirInfo{RAF};
  97. my $verbose = $et->Options('Verbose');
  98. my ($buff, $i, $header, $tagTablePtr, $dataPos, $oldIndent);
  99. $$et{DoneAPE} = 1;
  100. # check APE signature and process audio information
  101. # unless this is some other type of file
  102. unless ($$et{VALUE}{FileType}) {
  103. $raf->Read($buff, 32) == 32 or return 0;
  104. $buff =~ /^(MAC |APETAGEX)/ or return 0;
  105. $et->SetFileType();
  106. SetByteOrder('II');
  107. if ($buff =~ /^APETAGEX/) {
  108. # we already read the APE header
  109. $header = 1;
  110. } else {
  111. # process the MAC header
  112. my $vers = Get16u(\$buff, 4);
  113. my $table;
  114. if ($vers <= 3970) {
  115. $buff = substr($buff, 4);
  116. $table = GetTagTable('Image::ExifTool::APE::OldHeader');
  117. } else {
  118. my $dlen = Get32u(\$buff, 8);
  119. my $hlen = Get32u(\$buff, 12);
  120. unless ($dlen & 0x80000000 or $hlen & 0x80000000) {
  121. if ($raf->Seek($dlen, 0) and $raf->Read($buff, $hlen) == $hlen) {
  122. $table = GetTagTable('Image::ExifTool::APE::NewHeader');
  123. }
  124. }
  125. }
  126. $et->ProcessDirectory( { DataPt => \$buff }, $table) if $table;
  127. }
  128. }
  129. # look for APE trailer unless we already found an APE header
  130. unless ($header) {
  131. # look for the APE trailer footer...
  132. my $footPos = -32;
  133. # (...but before the ID3v1 trailer if it exists)
  134. $footPos -= 128 if $$et{DoneID3} == 2;
  135. $raf->Seek($footPos, 2) or return 1;
  136. $raf->Read($buff, 32) == 32 or return 1;
  137. $buff =~ /^APETAGEX/ or return 1;
  138. SetByteOrder('II');
  139. }
  140. #
  141. # Read the APE data (we have just read the APE header or footer into $buff)
  142. #
  143. my ($version, $size, $count, $flags) = unpack('x8V4', $buff);
  144. $version /= 1000;
  145. $size -= 32; # get size of data only
  146. if (($size & 0x80000000) == 0 and
  147. ($header or $raf->Seek(-$size-32, 1)) and
  148. $raf->Read($buff, $size) == $size)
  149. {
  150. if ($verbose) {
  151. $oldIndent = $$et{INDENT};
  152. $$et{INDENT} .= '| ';
  153. $et->VerboseDir("APEv$version", $count, $size);
  154. $et->VerboseDump(\$buff, DataPos => $raf->Tell() - $size);
  155. }
  156. $tagTablePtr = GetTagTable('Image::ExifTool::APE::Main');
  157. $dataPos = $raf->Tell() - $size;
  158. } else {
  159. $count = -1;
  160. }
  161. #
  162. # Process the APE tags
  163. #
  164. my $pos = 0;
  165. for ($i=0; $i<$count; ++$i) {
  166. # read next APE tag
  167. last if $pos + 8 > $size;
  168. my $len = Get32u(\$buff, $pos);
  169. my $flags = Get32u(\$buff, $pos + 4);
  170. pos($buff) = $pos + 8;
  171. last unless $buff =~ /\G(.*?)\0/sg;
  172. my $tag = $1;
  173. # avoid conflicts with our special table entries
  174. $tag .= '.' if $Image::ExifTool::specialTags{$tag};
  175. $pos = pos($buff);
  176. last if $pos + $len > $size;
  177. my $val = substr($buff, $pos, $len);
  178. MakeTag($tag, $tagTablePtr) unless $$tagTablePtr{$tag};
  179. # handle binary-value tags
  180. if (($flags & 0x06) == 0x02) {
  181. my $buf2 = $val;
  182. $val = \$buf2;
  183. # extract cover art description separately (hackitty hack)
  184. if ($tag =~ /^Cover Art/) {
  185. $buf2 =~ s/^([\x20-\x7f]*)\0//;
  186. if ($1) {
  187. my $t = "$tag Desc";
  188. my $v = $1;
  189. MakeTag($t, $tagTablePtr) unless $$tagTablePtr{$t};
  190. $et->HandleTag($tagTablePtr, $t, $v);
  191. }
  192. }
  193. }
  194. $et->HandleTag($tagTablePtr, $tag, $val,
  195. Index => $i,
  196. DataPt => \$buff,
  197. DataPos => $dataPos,
  198. Start => $pos,
  199. Size => $len,
  200. );
  201. $pos += $len;
  202. }
  203. $i == $count or $et->Warn('Bad APE trailer');
  204. $$et{INDENT} = $oldIndent if defined $oldIndent;
  205. return 1;
  206. }
  207. 1; # end
  208. __END__
  209. =head1 NAME
  210. Image::ExifTool::APE - Read Monkey's Audio meta information
  211. =head1 SYNOPSIS
  212. This module is used by Image::ExifTool
  213. =head1 DESCRIPTION
  214. This module contains definitions required by Image::ExifTool to extract meta
  215. information from Monkey's Audio (APE) audio files.
  216. =head1 BUGS
  217. Currently doesn't parse MAC header unless it is at the start of the file.
  218. =head1 AUTHOR
  219. Copyright 2003-2016, Phil Harvey (phil at owl.phy.queensu.ca)
  220. This library is free software; you can redistribute it and/or modify it
  221. under the same terms as Perl itself.
  222. =head1 REFERENCES
  223. =over 4
  224. =item L<http://www.monkeysaudio.com/>
  225. =item L<http://www.personal.uni-jena.de/~pfk/mpp/sv8/apetag.html>
  226. =back
  227. =head1 SEE ALSO
  228. L<Image::ExifTool::TagNames/APE Tags>,
  229. L<Image::ExifTool(3pm)|Image::ExifTool>
  230. =cut