FLAC.pm 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. #------------------------------------------------------------------------------
  2. # File: FLAC.pm
  3. #
  4. # Description: Read Free Lossless Audio Codec information
  5. #
  6. # Revisions: 11/13/2006 - P. Harvey Created
  7. #
  8. # References: 1) http://flac.sourceforge.net/
  9. #------------------------------------------------------------------------------
  10. package Image::ExifTool::FLAC;
  11. use strict;
  12. use vars qw($VERSION);
  13. use Image::ExifTool qw(:DataAccess :Utils);
  14. $VERSION = '1.07';
  15. sub ProcessBitStream($$$);
  16. # FLAC metadata blocks
  17. %Image::ExifTool::FLAC::Main = (
  18. NOTES => q{
  19. Free Lossless Audio Codec (FLAC) meta information. ExifTool also extracts
  20. ID3 information from these files.
  21. },
  22. 0 => {
  23. Name => 'StreamInfo',
  24. SubDirectory => { TagTable => 'Image::ExifTool::FLAC::StreamInfo' },
  25. },
  26. 1 => { Name => 'Padding', Binary => 1, Unknown => 1 },
  27. 2 => { Name => 'Application', Binary => 1, Unknown => 1 },
  28. 3 => { Name => 'SeekTable', Binary => 1, Unknown => 1 },
  29. 4 => {
  30. Name => 'VorbisComment',
  31. SubDirectory => { TagTable => 'Image::ExifTool::Vorbis::Comments' },
  32. },
  33. 5 => { Name => 'CueSheet', Binary => 1, Unknown => 1 },
  34. 6 => {
  35. Name => 'Picture',
  36. SubDirectory => { TagTable => 'Image::ExifTool::FLAC::Picture' },
  37. },
  38. # 7-126 - Reserved
  39. # 127 - Invalid
  40. );
  41. %Image::ExifTool::FLAC::StreamInfo = (
  42. PROCESS_PROC => \&ProcessBitStream,
  43. NOTES => 'FLAC is big-endian, so bit 0 is the high-order bit in this table.',
  44. GROUPS => { 2 => 'Audio' },
  45. 'Bit000-015' => 'BlockSizeMin',
  46. 'Bit016-031' => 'BlockSizeMax',
  47. 'Bit032-055' => 'FrameSizeMin',
  48. 'Bit056-079' => 'FrameSizeMax',
  49. 'Bit080-099' => 'SampleRate',
  50. 'Bit100-102' => {
  51. Name => 'Channels',
  52. ValueConv => '$val + 1',
  53. },
  54. 'Bit103-107' => {
  55. Name => 'BitsPerSample',
  56. ValueConv => '$val + 1',
  57. },
  58. 'Bit108-143' => 'TotalSamples',
  59. );
  60. %Image::ExifTool::FLAC::Picture = (
  61. PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
  62. GROUPS => { 2 => 'Image' },
  63. FORMAT => 'int32u',
  64. 0 => {
  65. Name => 'PictureType',
  66. PrintConv => { # (Note: Duplicated in ID3, ASF and FLAC modules!)
  67. 0 => 'Other',
  68. 1 => '32x32 PNG Icon',
  69. 2 => 'Other Icon',
  70. 3 => 'Front Cover',
  71. 4 => 'Back Cover',
  72. 5 => 'Leaflet',
  73. 6 => 'Media',
  74. 7 => 'Lead Artist',
  75. 8 => 'Artist',
  76. 9 => 'Conductor',
  77. 10 => 'Band',
  78. 11 => 'Composer',
  79. 12 => 'Lyricist',
  80. 13 => 'Recording Studio or Location',
  81. 14 => 'Recording Session',
  82. 15 => 'Performance',
  83. 16 => 'Capture from Movie or Video',
  84. 17 => 'Bright(ly) Colored Fish',
  85. 18 => 'Illustration',
  86. 19 => 'Band Logo',
  87. 20 => 'Publisher Logo',
  88. },
  89. },
  90. 1 => {
  91. Name => 'PictureMIMEType',
  92. Format => 'var_pstr32',
  93. },
  94. 2 => {
  95. Name => 'PictureDescription',
  96. Format => 'var_pstr32',
  97. ValueConv => '$self->Decode($val, "UTF8")',
  98. },
  99. 3 => 'PictureWidth',
  100. 4 => 'PictureHeight',
  101. 5 => 'PictureBitsPerPixel',
  102. 6 => 'PictureIndexedColors',
  103. 7 => 'PictureLength',
  104. 8 => {
  105. Name => 'Picture',
  106. Groups => { 2 => 'Preview' },
  107. Format => 'undef[$val{7}]',
  108. Binary => 1,
  109. },
  110. );
  111. # FLAC composite tags
  112. %Image::ExifTool::FLAC::Composite = (
  113. Duration => {
  114. Require => {
  115. 0 => 'FLAC:SampleRate',
  116. 1 => 'FLAC:TotalSamples',
  117. },
  118. ValueConv => '($val[0] and $val[1]) ? $val[1] / $val[0] : undef',
  119. PrintConv => 'ConvertDuration($val)',
  120. },
  121. );
  122. # add our composite tags
  123. Image::ExifTool::AddCompositeTags('Image::ExifTool::FLAC');
  124. #------------------------------------------------------------------------------
  125. # Process information in a bit stream
  126. # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
  127. # Notes: Byte order is used to determine the ordering of bits in the stream:
  128. # 'MM' = bit 0 is most significant, 'II' = bit 0 is least significant
  129. # - can handle arbitrarily wide values (eg. 8-byte or larger integers)
  130. sub ProcessBitStream($$$)
  131. {
  132. my ($et, $dirInfo, $tagTablePtr) = @_;
  133. my $dataPt = $$dirInfo{DataPt};
  134. my $dataPos = $$dirInfo{DataPos};
  135. my $dirStart = $$dirInfo{DirStart} || 0;
  136. my $dirLen = $$dirInfo{DirLen} || (length($$dataPt) - $dirStart);
  137. my $verbose = $et->Options('Verbose');
  138. my $byteOrder = GetByteOrder();
  139. my $tag;
  140. if ($verbose) {
  141. $et->VPrint(0, " + [BitStream directory, $dirLen bytes, '$byteOrder' order]\n");
  142. }
  143. foreach $tag (sort keys %$tagTablePtr) {
  144. next unless $tag =~ /^Bit(\d+)-?(\d+)?/;
  145. my ($b1, $b2) = ($1, $2 || $1); # start/end bit numbers in stream
  146. my ($i1, $i2) = (int($b1 / 8), int($b2 / 8)); # start/end byte numbers
  147. my ($f1, $f2) = ($b1 % 8, $b2 % 8); # start/end bit numbers within each byte
  148. last if $i2 >= $dirLen;
  149. my $val = 0;
  150. my ($i, $mask, $extra);
  151. $extra = ', Mask=0x' if $verbose and ($f1 != 0 or $f2 != 7);
  152. if ($byteOrder eq 'MM') {
  153. # loop from high byte to low byte
  154. for ($i=$i1; $i<=$i2; ++$i) {
  155. $mask = 0xff;
  156. if ($i == $i1 and $f1) {
  157. # mask off high bits in first word (0 is high bit)
  158. foreach ((8-$f1) .. 7) { $mask ^= (1 << $_) }
  159. }
  160. if ($i == $i2 and $f2 < 7) {
  161. # mask off low bits in last word (7 is low bit)
  162. foreach (0 .. (6-$f2)) { $mask ^= (1 << $_) }
  163. }
  164. $val = $val * 256 + ($mask & Get8u($dataPt, $i + $dirStart));
  165. $extra .= sprintf('%.2x', $mask) if $extra;
  166. }
  167. } else {
  168. # (FLAC is big-endian, but support little-endian bit streams
  169. # so this routine can be used by other modules)
  170. # loop from high byte to low byte
  171. for ($i=$i2; $i>=$i1; --$i) {
  172. $mask = 0xff;
  173. if ($i == $i1 and $f1) {
  174. # mask off low bits in first word (0 is low bit)
  175. foreach (0 .. ($f1-1)) { $mask ^= (1 << $_) }
  176. }
  177. if ($i == $i2 and $f2 < 7) {
  178. # mask off high bits in last word (7 is high bit)
  179. foreach (($f2+1) .. 7) { $mask ^= (1 << $_) }
  180. }
  181. $val = $val * 256 + ($mask & Get8u($dataPt, $i + $dirStart));
  182. $extra .= sprintf('%.2x', $mask) if $extra;
  183. }
  184. }
  185. # shift word down until low bit is in position 0
  186. until ($mask & 0x01) {
  187. $val /= 2;
  188. $mask >>= 1;
  189. }
  190. $et->HandleTag($tagTablePtr, $tag, $val,
  191. DataPt => $dataPt,
  192. DataPos => $dataPos,
  193. Start => $dirStart + $i1,
  194. Size => $i2 - $i1 + 1,
  195. Extra => $extra,
  196. );
  197. }
  198. return 1;
  199. }
  200. #------------------------------------------------------------------------------
  201. # Extract information from an Ogg FLAC file
  202. # Inputs: 0) ExifTool object reference, 1) dirInfo reference
  203. # Returns: 1 on success, 0 if this wasn't a valid Ogg FLAC file
  204. sub ProcessFLAC($$)
  205. {
  206. my ($et, $dirInfo) = @_;
  207. # must first check for leading/trailing ID3 information
  208. unless ($$et{DoneID3}) {
  209. require Image::ExifTool::ID3;
  210. Image::ExifTool::ID3::ProcessID3($et, $dirInfo) and return 1;
  211. }
  212. my $raf = $$dirInfo{RAF};
  213. my $verbose = $et->Options('Verbose');
  214. my $out = $et->Options('TextOut');
  215. my ($buff, $err);
  216. # check FLAC signature
  217. $raf->Read($buff, 4) == 4 and $buff eq 'fLaC' or return 0;
  218. $et->SetFileType();
  219. SetByteOrder('MM');
  220. my $tagTablePtr = GetTagTable('Image::ExifTool::FLAC::Main');
  221. for (;;) {
  222. # read next metadata block header
  223. $raf->Read($buff, 4) == 4 or last;
  224. my $flag = unpack('C', $buff);
  225. my $size = unpack('N', $buff) & 0x00ffffff;
  226. $raf->Read($buff, $size) == $size or $err = 1, last;
  227. my $last = $flag & 0x80; # last-metadata-block flag
  228. my $tag = $flag & 0x7f; # tag bits
  229. if ($verbose) {
  230. print $out "FLAC metadata block, type $tag:\n";
  231. $et->VerboseDump(\$buff, DataPos => $raf->Tell() - $size);
  232. }
  233. $et->HandleTag($tagTablePtr, $tag, undef,
  234. DataPt => \$buff,
  235. DataPos => $raf->Tell() - $size,
  236. );
  237. last if $last; # all done if is set
  238. }
  239. $err and $et->Warn('Format error in FLAC file');
  240. return 1;
  241. }
  242. 1; # end
  243. __END__
  244. =head1 NAME
  245. Image::ExifTool::FLAC - Read Free Lossless Audio Codec information
  246. =head1 SYNOPSIS
  247. This module is used by Image::ExifTool
  248. =head1 DESCRIPTION
  249. This module contains definitions required by Image::ExifTool to extract meta
  250. information from Free Lossless Audio Codec (FLAC) audio files.
  251. =head1 AUTHOR
  252. Copyright 2003-2016, Phil Harvey (phil at owl.phy.queensu.ca)
  253. This library is free software; you can redistribute it and/or modify it
  254. under the same terms as Perl itself.
  255. =head1 REFERENCES
  256. =over 4
  257. =item L<http://flac.sourceforge.net/>
  258. =back
  259. =head1 SEE ALSO
  260. L<Image::ExifTool::TagNames/FLAC Tags>,
  261. L<Image::ExifTool::TagNames/Ogg Tags>,
  262. L<Image::ExifTool(3pm)|Image::ExifTool>
  263. =cut