dokeos
[ class tree: dokeos ] [ index: dokeos ] [ all elements ]

Source for file getid3.php

Documentation is available at getid3.php

  1. <?php
  2. // +----------------------------------------------------------------------+
  3. // | PHP version 5                                                        |
  4. // +----------------------------------------------------------------------+
  5. // | Copyright (c) 2002-2006 James Heinrich, Allan Hansen                 |
  6. // +----------------------------------------------------------------------+
  7. // | This source file is subject to version 2 of the GPL license,         |
  8. // | that is bundled with this package in the file license.txt and is     |
  9. // | available through the world-wide-web at the following url:           |
  10. // | http://www.gnu.org/copyleft/gpl.html                                 |
  11. // +----------------------------------------------------------------------+
  12. // | getID3() - http://getid3.sourceforge.net or http://www.getid3.org    |
  13. // +----------------------------------------------------------------------+
  14. // | Authors: James Heinrich <infogetid3*org>                            |
  15. // |          Allan Hansen <ahartemis*dk>                                |
  16. // +----------------------------------------------------------------------+
  17. // | getid3.php                                                           |
  18. // | Main getID3() file.                                                  |
  19. // | dependencies: modules.                                               |
  20. // +----------------------------------------------------------------------+
  21. //
  22. // $Id: getid3.php,v 1.26 2006/12/25 23:44:23 ah Exp $
  23.  
  24.  
  25. class getid3
  26. {
  27.     //// Settings Section - do NOT modify this file - change setting after newing getid3!
  28.  
  29.     // Encoding
  30.     public $encoding                 = 'ISO-8859-1';      // CASE SENSITIVE! - i.e. (must be supported by iconv() - see http://www.gnu.org/software/libiconv/).  Examples:  ISO-8859-1  UTF-8  UTF-16  UTF-16BE.
  31.     public $encoding_id3v1           = 'ISO-8859-1';      // Override SPECIFICATION encoding for broken ID3v1 tags caused by bad tag programs. Examples: 'EUC-CN' for "Chinese MP3s" and 'CP1251' for "Cyrillic".
  32.     public $encoding_id3v2           = 'ISO-8859-1';      // Override ISO-8859-1 encoding for broken ID3v2 tags caused by BRAINDEAD tag programs that writes system codepage as 'ISO-8859-1' instead of UTF-8.
  33.  
  34.     // Tags - disable for speed
  35.     public $option_tag_id3v1         = false;              // Read and process ID3v1 tags.
  36.     public $option_tag_id3v2         = false;              // Read and process ID3v2 tags.
  37.     public $option_tag_lyrics3       = false;              // Read and process Lyrics3 tags.
  38.     public $option_tag_apetag        = false;              // Read and process APE tags.
  39.  
  40.     // Misc calucations - disable for speed
  41.     public $option_analyze           = true;              // Analyze file - disable if you only need to detect file format.
  42.     public $option_accurate_results  = true;              // Disable to greatly speed up parsing of some file formats at the cost of accuracy.
  43.     public $option_tags_process      = true;              // Copy tags to root key 'tags' and 'comments' and encode to $this->encoding.
  44.     public $option_tags_images       = false;             // Scan tags for binary image data - ID3v2 and vorbiscomments only.
  45.     public $option_extra_info        = true;              // Calculate/return additional info such as bitrate, channelmode etc.
  46.     public $option_max_2gb_check     = false;             // Check whether file is larger than 2 Gb and thus not supported by PHP.
  47.  
  48.     // Misc data hashes - slow - require hash module
  49.     public $option_md5_data          = false;             // Get MD5 sum of data part - slow.
  50.     public $option_md5_data_source   = false;             // Use MD5 of source file if available - only FLAC, MAC, OptimFROG and Wavpack4.
  51.     public $option_sha1_data         = false;             // Get SHA1 sum of data part - slow.
  52.  
  53.     // Public variables
  54.     public $filename;                                     // Filename of file being analysed.
  55.     public $fp;                                           // Filepointer to file being analysed.
  56.     public $info;                                         // Result array.
  57.  
  58.     // Protected variables
  59.     protected $include_path;                              // getid3 include path.
  60.     protected $warnings = array ();
  61.     protected $iconv_present;
  62.  
  63.     // Class constants
  64.     const VERSION           '2.0.0b4';
  65.     const FREAD_BUFFER_SIZE 16384;                      // Read buffer size in bytes.
  66.     const ICONV_TEST_STRING ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ';
  67.  
  68.  
  69.  
  70.     // Constructor - check PHP enviroment and load library.
  71.     public function __construct({
  72.  
  73.         // Static varibles - no need to recalc every time we new getid3.
  74.         static $include_path;
  75.         static $iconv_present;
  76.  
  77.  
  78.         static $initialized;
  79.         if ($initialized{
  80.  
  81.             // Import static variables
  82.             $this->include_path  = $include_path;
  83.             $this->iconv_present = $iconv_present;
  84.  
  85.             // Run init checks only on first instance.
  86.             return;
  87.         }
  88.  
  89.         // Get include_path
  90.         $this->include_path = $include_path dirname(__FILE__'/';
  91.  
  92.         // Check for presence of iconv() and make sure it works (simpel test only).
  93.         if (function_exists('iconv'&& @iconv('UTF-16LE''ISO-8859-1'@iconv('ISO-8859-1''UTF-16LE'getid3::ICONV_TEST_STRING)) == getid3::ICONV_TEST_STRING{
  94.             $this->iconv_present = $iconv_present true;
  95.         }
  96.  
  97.         // iconv() not present - load replacement module.
  98.         else {
  99.             $this->include_module('lib.iconv_replacement');
  100.             $this->iconv_present = $iconv_present false;
  101.         }
  102.  
  103.  
  104.         // Require magic_quotes_runtime off
  105.         if (get_magic_quotes_runtime()) {
  106.             throw new getid3_exception('magic_quotes_runtime must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_runtime(0) and set_magic_quotes_runtime(1).');
  107.         }
  108.  
  109.  
  110.         // Check memory limit.
  111.         $memory_limit ini_get('memory_limit');
  112.         if (eregi('([0-9]+)M'$memory_limit$matches)) {
  113.             // could be stored as "16M" rather than 16777216 for example
  114.             $memory_limit $matches[11048576;
  115.         }
  116.         if ($memory_limit <= 0{
  117.             // Should not happen.
  118.         elseif ($memory_limit <= 4194304{
  119.             $this->warning('[SERIOUS] PHP has less than 4 Mb available memory and will very likely run out. Increase memory_limit in php.ini.');
  120.         elseif ($memory_limit <= 12582912{
  121.             $this->warning('PHP has less than 12 Mb available memory and might run out if all modules are loaded. Increase memory_limit in php.ini if needed.');
  122.         }
  123.  
  124.  
  125.         // Check safe_mode off
  126.         if ((bool)ini_get('safe_mode')) {
  127.             $this->warning('Safe mode is on, shorten support disabled, md5data/sha1data for ogg vorbis disabled, ogg vorbis/flac tag writing disabled.');
  128.         }
  129.  
  130.         $initialized true;
  131.     }
  132.  
  133.  
  134.  
  135.     // Analyze file by name
  136.     public function Analyze($filename{
  137.  
  138.         // Init and save values
  139.         $this->filename = $filename;
  140.         $this->warnings = array ();
  141.  
  142.         // Init result array and set parameters
  143.         $this->info = array ();
  144.         $this->info['GETID3_VERSION'getid3::VERSION;
  145.  
  146.         // Remote files not supported
  147.         if (preg_match('/^(ht|f)tp:\/\//'$filename)) {
  148.             throw new getid3_exception('Remote files are not supported - please copy the file locally first.');
  149.         }
  150.  
  151.         // Open local file
  152.         if (!$this->fp = @fopen($filename'rb')) {
  153.             throw new getid3_exception('Could not open file "'.$filename.'"');
  154.         }
  155.  
  156.         // Set filesize related parameters
  157.         $this->info['filesize']     filesize($filename);
  158.         $this->info['avdataoffset'0;
  159.         $this->info['avdataend']    $this->info['filesize'];
  160.  
  161.         // Option_max_2gb_check
  162.         if ($this->option_max_2gb_check{
  163.             // PHP doesn't support integers larger than 31-bit (~2GB)
  164.             // filesize() simply returns (filesize % (pow(2, 32)), no matter the actual filesize
  165.             // ftell() returns 0 if seeking to the end is beyond the range of unsigned integer
  166.             fseek($this->fp0SEEK_END);
  167.             if ((($this->info['filesize'!= 0&& (ftell($this->fp== 0)) ||
  168.                 ($this->info['filesize'0||
  169.                 (ftell($this->fp0)) {
  170.                     unset($this->info['filesize']);
  171.                     fclose($this->fp);
  172.                     throw new getid3_exception('File is most likely larger than 2GB and is not supported by PHP.');
  173.             }
  174.         }
  175.  
  176.  
  177.         // ID3v2 detection (NOT parsing) done to make fileformat easier.
  178.         if (!$this->option_tag_id3v2{
  179.  
  180.             fseek($this->fp0SEEK_SET);
  181.             $header fread($this->fp10);
  182.             if (substr($header03== 'ID3'  &&  strlen($header== 10{
  183.                 $this->info['id3v2']['header']        true;
  184.                 $this->info['id3v2']['majorversion']  ord($header{3});
  185.                 $this->info['id3v2']['minorversion']  ord($header{4});
  186.                 $this->info['avdataoffset']          += getid3_lib::BigEndian2Int(substr($header64)110// length of ID3v2 tag in 10-byte header doesn't include 10-byte header length
  187.             }
  188.         }
  189.  
  190.  
  191.         // Handle tags
  192.         foreach (array ("id3v2""id3v1""apetag""lyrics3"as $tag_name{
  193.  
  194.             $option_tag 'option_tag_' $tag_name;
  195.             if ($this->$option_tag{
  196.                 $this->include_module('tag.'.$tag_name);
  197.                 try {
  198.                     $tag_class 'getid3_' $tag_name;
  199.                     $tag new $tag_class($this);
  200.                     $tag->Analyze();
  201.                 }
  202.                 catch (getid3_exception $e{
  203.                     throw $e;
  204.                 }
  205.             }
  206.         }
  207.  
  208.  
  209.  
  210.         //// Determine file format by magic bytes in file header.
  211.  
  212.         // Read 32 kb file data
  213.         fseek($this->fp$this->info['avdataoffset']SEEK_SET);
  214.         $filedata fread($this->fp32774);
  215.  
  216.         // Get huge FileFormatArray
  217.         $file_format_array getid3::GetFileFormatArray();
  218.  
  219.         // Identify file format - loop through $format_info and detect with reg expr
  220.         foreach ($file_format_array as $name => $info{
  221.  
  222.             if (preg_match('/'.$info['pattern'].'/s'$filedata)) {                         // The /s switch on preg_match() forces preg_match() NOT to treat newline (0x0A) characters as special chars but do a binary match
  223.  
  224.                 // Format detected but not supported
  225.                 if (!@$info['module'|| !@$info['group']{
  226.                     fclose($this->fp);
  227.                     $this->info['fileformat'$name;
  228.                     $this->info['mime_type']  $info['mime_type'];
  229.                     $this->warning('Format only detected. Parsing not available yet.');
  230.                     $this->info['warning'$this->warnings;
  231.                     return $this->info;
  232.                 }
  233.  
  234.                 $determined_format $info;  // copy $info deleted by foreach()
  235.                 continue;
  236.             }
  237.         }
  238.  
  239.         // Unable to determine file format
  240.         if (!@$determined_format{
  241.  
  242.             // Too many mp3 encoders on the market put gabage in front of mpeg files
  243.             // use assume format on these if format detection failed
  244.             if (preg_match('/\.mp[123a]$/i'$filename)) {
  245.                 $determined_format $file_format_array['mp3'];
  246.             }
  247.  
  248.             else {
  249.                 fclose($this->fp);
  250.                 throw new getid3_exception('Unable to determine file format');
  251.             }
  252.         }
  253.  
  254.         // Free memory
  255.         unset($file_format_array);
  256.  
  257.         // Check for illegal ID3 tags
  258.         if (@$determined_format['fail_id3'&& (@$this->info['id3v1'|| @$this->info['id3v2'])) {
  259.             if ($determined_format['fail_id3'=== 'ERROR'{
  260.                 fclose($this->fp);
  261.                 throw new getid3_exception('ID3 tags not allowed on this file type.');
  262.             }
  263.             elseif ($determined_format['fail_id3'=== 'WARNING'{
  264.                 @$this->info['id3v1'and $this->warning('ID3v1 tags not allowed on this file type.');
  265.                 @$this->info['id3v2'and $this->warning('ID3v2 tags not allowed on this file type.');
  266.             }
  267.         }
  268.  
  269.         // Check for illegal APE tags
  270.         if (@$determined_format['fail_ape'&& @$this->info['tags']['ape']{
  271.             if ($determined_format['fail_ape'=== 'ERROR'{
  272.                 fclose($this->fp);
  273.                 throw new getid3_exception('APE tags not allowed on this file type.');
  274.             elseif ($determined_format['fail_ape'=== 'WARNING'{
  275.                 $this->warning('APE tags not allowed on this file type.');
  276.             }
  277.         }
  278.  
  279.  
  280.         // Set mime type
  281.         $this->info['mime_type'$determined_format['mime_type'];
  282.  
  283.         // Calc module file name
  284.         $determined_format['include''module.'.$determined_format['group'].'.'.$determined_format['module'].'.php';
  285.  
  286.         // Supported format signature pattern detected, but module deleted.
  287.         if (!file_exists($this->include_path.$determined_format['include'])) {
  288.             fclose($this->fp);
  289.             throw new getid3_exception('Format not supported, module, '.$determined_format['include'].', was removed.');
  290.         }
  291.  
  292.         // Include module
  293.         $this->include_module($determined_format['group'].'.'.$determined_format['module']);
  294.  
  295.         // Instantiate module class and analyze
  296.         $class_name 'getid3_'.$determined_format['module'];
  297.         if (!class_exists($class_name)) {
  298.             throw new getid3_exception('Format not supported, module, '.$determined_format['include'].', is corrupt.');
  299.         }
  300.         $class new $class_name($this);
  301.  
  302.         try {
  303.              $this->option_analyze and $class->Analyze();
  304.             }
  305.         catch (getid3_exception $e{
  306.             throw $e;
  307.         }
  308.         catch (Exception $e{
  309.             throw new getid3_exception('Corrupt file.');
  310.         }
  311.  
  312.         // Close file
  313.         fclose($this->fp);
  314.  
  315.         // Optional - Process all tags - copy to 'tags' and convert charsets
  316.         if ($this->option_tags_process{
  317.             $this->HandleAllTags();
  318.         }
  319.  
  320.  
  321.         //// Optional - perform more calculations
  322.         if ($this->option_extra_info{
  323.  
  324.             // Set channelmode on audio
  325.             if (@$this->info['audio']['channels'== '1'{
  326.                 $this->info['audio']['channelmode''mono';
  327.             elseif (@$this->info['audio']['channels'== '2'{
  328.                 $this->info['audio']['channelmode''stereo';
  329.             }
  330.  
  331.             // Calculate combined bitrate - audio + video
  332.             $combined_bitrate  0;
  333.             $combined_bitrate += (isset($this->info['audio']['bitrate']$this->info['audio']['bitrate'0);
  334.             $combined_bitrate += (isset($this->info['video']['bitrate']$this->info['video']['bitrate'0);
  335.             if (($combined_bitrate 0&& empty($this->info['bitrate'])) {
  336.                 $this->info['bitrate'$combined_bitrate;
  337.             }
  338.             if (!isset($this->info['playtime_seconds']&& !empty($this->info['bitrate'])) {
  339.                 $this->info['playtime_seconds'(($this->info['avdataend'$this->info['avdataoffset']8$this->info['bitrate'];
  340.             }
  341.  
  342.             // Set playtime string
  343.             if (!empty($this->info['playtime_seconds']&& empty($this->info['playtime_string'])) {
  344.                 $this->info['playtime_string'=  floor(round($this->info['playtime_seconds']60':' str_pad(floor(round($this->info['playtime_seconds']60)20STR_PAD_LEFT);;
  345.             }
  346.  
  347.  
  348.             // CalculateCompressionRatioVideo() {
  349.             if (@$this->info['video'&& @$this->info['video']['resolution_x'&& @$this->info['video']['resolution_y'&& @$this->info['video']['bits_per_sample']{
  350.  
  351.                 // From static image formats
  352.                 if (in_array($this->info['video']['dataformat']array ('bmp''gif''jpeg''jpg''png''tiff'))) {
  353.                     $frame_rate         1;
  354.                     $bitrate_compressed $this->info['filesize'8;
  355.                 }
  356.  
  357.                 // From video formats
  358.                 else {
  359.                     $frame_rate         @$this->info['video']['frame_rate'];
  360.                     $bitrate_compressed @$this->info['video']['bitrate'];
  361.                 }
  362.  
  363.                 if ($frame_rate && $bitrate_compressed{
  364.                     $this->info['video']['compression_ratio'$bitrate_compressed ($this->info['video']['resolution_x'$this->info['video']['resolution_y'$this->info['video']['bits_per_sample'$frame_rate);
  365.                 }
  366.             }
  367.  
  368.  
  369.             // CalculateCompressionRatioAudio() {
  370.             if (@$this->info['audio']['bitrate'&& @$this->info['audio']['channels'&& @$this->info['audio']['sample_rate']{
  371.                 $this->info['audio']['compression_ratio'$this->info['audio']['bitrate'($this->info['audio']['channels'$this->info['audio']['sample_rate'(@$this->info['audio']['bits_per_sample'$this->info['audio']['bits_per_sample'16));
  372.             }
  373.  
  374.             if (@$this->info['audio']['streams']{
  375.                 foreach ($this->info['audio']['streams'as $stream_number => $stream_data{
  376.                     if (@$stream_data['bitrate'&& @$stream_data['channels'&& @$stream_data['sample_rate']{
  377.                         $this->info['audio']['streams'][$stream_number]['compression_ratio'$stream_data['bitrate'($stream_data['channels'$stream_data['sample_rate'(@$stream_data['bits_per_sample'$stream_data['bits_per_sample'16));
  378.                     }
  379.                 }
  380.             }
  381.  
  382.  
  383.             // CalculateReplayGain() {
  384.             if (@$this->info['replay_gain']{
  385.                 if (!@$this->info['replay_gain']['reference_volume']{
  386.                      $this->info['replay_gain']['reference_volume'89;
  387.                 }
  388.                 if (isset($this->info['replay_gain']['track']['adjustment'])) {
  389.                     $this->info['replay_gain']['track']['volume'$this->info['replay_gain']['reference_volume'$this->info['replay_gain']['track']['adjustment'];
  390.                 }
  391.                 if (isset($this->info['replay_gain']['album']['adjustment'])) {
  392.                     $this->info['replay_gain']['album']['volume'$this->info['replay_gain']['reference_volume'$this->info['replay_gain']['album']['adjustment'];
  393.                 }
  394.  
  395.                 if (isset($this->info['replay_gain']['track']['peak'])) {
  396.                     $this->info['replay_gain']['track']['max_noclip_gain'20 log10($this->info['replay_gain']['track']['peak']);
  397.                 }
  398.                 if (isset($this->info['replay_gain']['album']['peak'])) {
  399.                     $this->info['replay_gain']['album']['max_noclip_gain'20 log10($this->info['replay_gain']['album']['peak']);
  400.                 }
  401.             }
  402.  
  403.  
  404.             // ProcessAudioStreams() {
  405.             if (@!$this->info['audio']['streams'&& (@$this->info['audio']['bitrate'|| @$this->info['audio']['channels'|| @$this->info['audio']['sample_rate'])) {
  406.                   foreach ($this->info['audio'as $key => $value{
  407.                     if ($key != 'streams'{
  408.                         $this->info['audio']['streams'][0][$key$value;
  409.                     }
  410.                 }
  411.             }
  412.         }
  413.  
  414.  
  415.         // Get the md5/sha1sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags.
  416.         if ($this->option_md5_data || $this->option_sha1_data{
  417.  
  418.             // Load data-hash library if needed
  419.             $this->include_module('lib.data_hash');
  420.  
  421.             if ($this->option_sha1_data{
  422.                 new getid3_lib_data_hash($this'sha1');
  423.             }
  424.  
  425.             if ($this->option_md5_data{
  426.  
  427.                 // no md5_data_source or option disabled -- md5_data_source supported by FLAC, MAC, OptimFROG, Wavpack4
  428.                 if (!$this->option_md5_data_source || !@$this->info['md5_data_source']{
  429.                     new getid3_lib_data_hash($this'md5');
  430.                 }
  431.  
  432.                 // copy md5_data_source to md5_data if option set to true
  433.                 elseif ($this->option_md5_data_source && @$this->info['md5_data_source']{
  434.                     $this->info['md5_data'$this->info['md5_data_source'];
  435.                 }
  436.             }
  437.         }
  438.  
  439.         // Set warnings
  440.         if ($this->warnings{
  441.             $this->info['warning'$this->warnings;
  442.         }
  443.  
  444.         // Return result
  445.         return $this->info;
  446.     }
  447.  
  448.  
  449.  
  450.     // Return array of warnings
  451.     public function warnings({
  452.  
  453.         return $this->warnings;
  454.     }
  455.  
  456.  
  457.  
  458.     // Add warning(s) to $this->warnings[]
  459.     public function warning($message{
  460.  
  461.         if (is_array($message)) {
  462.             $this->warnings = array_merge($this->warnings$message);
  463.         }
  464.         else {
  465.             $this->warnings[$message;
  466.         }
  467.     }
  468.  
  469.  
  470.  
  471.     //  Clear all warnings when cloning
  472.     public function __clone({
  473.  
  474.         $this->warnings = array ();
  475.  
  476.         // Copy info array, otherwise it will be a reference.
  477.         $temp $this->info;
  478.         unset($this->info);
  479.         $this->info = $temp;
  480.     }
  481.  
  482.  
  483.  
  484.     // Convert string between charsets -- iconv() wrapper
  485.     public function iconv($in_charset$out_charset$string$drop01 false{
  486.  
  487.         if ($drop01 && ($string === "\x00" || $string === "\x01")) {
  488.             return '';
  489.         }
  490.  
  491.  
  492.         if (!$this->iconv_present{
  493.             return getid3_iconv_replacement::iconv($in_charset$out_charset$string);
  494.         }
  495.  
  496.  
  497.         // iconv() present
  498.         if ($result @iconv($in_charset$out_charset.'//TRANSLIT'$string)) {
  499.  
  500.             if ($out_charset == 'ISO-8859-1'{
  501.                 return rtrim($result"\x00");
  502.             }
  503.             return $result;
  504.         }
  505.  
  506.         $this->warning('iconv() was unable to convert the string: "' $string '" from ' $in_charset ' to ' $out_charset);
  507.         return $string;
  508.     }
  509.  
  510.  
  511.  
  512.     public function include_module($name{
  513.  
  514.         if (!file_exists($this->include_path.'module.'.$name.'.php')) {
  515.             throw new getid3_exception('Required module.'.$name.'.php is missing.');
  516.         }
  517.  
  518.         include_once($this->include_path.'module.'.$name.'.php');
  519.     }
  520.  
  521.  
  522.  
  523.     public function include_module_optional($name{
  524.  
  525.         if (!file_exists($this->include_path.'module.'.$name.'.php')) {
  526.             return;
  527.         }
  528.  
  529.         include_once($this->include_path.'module.'.$name.'.php');
  530.         return true;
  531.     }
  532.  
  533.  
  534.     // Return array containing information about all supported formats
  535.     public static function GetFileFormatArray({
  536.  
  537.         static $format_info array (
  538.  
  539.                 // Audio formats
  540.  
  541.                 // AC-3   - audio      - Dolby AC-3 / Dolby Digital
  542.                 'ac3'  => array (
  543.                             'pattern'   => '^\x0B\x77',
  544.                             'group'     => 'audio',
  545.                             'module'    => 'ac3',
  546.                             'mime_type' => 'audio/ac3',
  547.                           ),
  548.  
  549.                 // AAC  - audio       - Advanced Audio Coding (AAC) - ADIF format
  550.                 'adif' => array (
  551.                             'pattern'   => '^ADIF',
  552.                             'group'     => 'audio',
  553.                             'module'    => 'aac_adif',
  554.                             'mime_type' => 'application/octet-stream',
  555.                             'fail_ape'  => 'WARNING',
  556.                           ),
  557.  
  558.  
  559.                 // AAC  - audio       - Advanced Audio Coding (AAC) - ADTS format (very similar to MP3)
  560.                 'adts' => array (
  561.                             'pattern'   => '^\xFF[\xF0-\xF1\xF8-\xF9]',
  562.                             'group'     => 'audio',
  563.                             'module'    => 'aac_adts',
  564.                             'mime_type' => 'application/octet-stream',
  565.                             'fail_ape'  => 'WARNING',
  566.                           ),
  567.  
  568.  
  569.                 // AU   - audio       - NeXT/Sun AUdio (AU)
  570.                 'au'   => array (
  571.                             'pattern'   => '^\.snd',
  572.                             'group'     => 'audio',
  573.                             'module'    => 'au',
  574.                             'mime_type' => 'audio/basic',
  575.                           ),
  576.  
  577.                 // AVR  - audio       - Audio Visual Research
  578.                 'avr'  => array (
  579.                             'pattern'   => '^2BIT',
  580.                             'group'     => 'audio',
  581.                             'module'    => 'avr',
  582.                             'mime_type' => 'application/octet-stream',
  583.                           ),
  584.  
  585.                 // BONK - audio       - Bonk v0.9+
  586.                 'bonk' => array (
  587.                             'pattern'   => '^\x00(BONK|INFO|META| ID3)',
  588.                             'group'     => 'audio',
  589.                             'module'    => 'bonk',
  590.                             'mime_type' => 'audio/xmms-bonk',
  591.                           ),
  592.  
  593.                 // DTS  - audio       - Dolby Theatre System
  594.                 'dts'  => array(
  595.                             'pattern'   => '^\x7F\xFE\x80\x01',
  596.                             'group'     => 'audio',
  597.                             'module'    => 'dts',
  598.                             'mime_type' => 'audio/dts',
  599.                         ),
  600.  
  601.                 // FLAC - audio       - Free Lossless Audio Codec
  602.                 'flac' => array (
  603.                             'pattern'   => '^fLaC',
  604.                             'group'     => 'audio',
  605.                             'module'    => 'xiph',
  606.                             'mime_type' => 'audio/x-flac',
  607.                           ),
  608.  
  609.                 // LA   - audio       - Lossless Audio (LA)
  610.                 'la'   => array (
  611.                             'pattern'   => '^LA0[2-4]',
  612.                             'group'     => 'audio',
  613.                             'module'    => 'la',
  614.                             'mime_type' => 'application/octet-stream',
  615.                           ),
  616.  
  617.                 // LPAC - audio       - Lossless Predictive Audio Compression (LPAC)
  618.                 'lpac' => array (
  619.                             'pattern'   => '^LPAC',
  620.                             'group'     => 'audio',
  621.                             'module'    => 'lpac',
  622.                             'mime_type' => 'application/octet-stream',
  623.                           ),
  624.  
  625.                 // MIDI - audio       - MIDI (Musical Instrument Digital Interface)
  626.                 'midi' => array (
  627.                             'pattern'   => '^MThd',
  628.                             'group'     => 'audio',
  629.                             'module'    => 'midi',
  630.                             'mime_type' => 'audio/midi',
  631.                           ),
  632.  
  633.                 // MAC  - audio       - Monkey's Audio Compressor
  634.                 'mac'  => array (
  635.                             'pattern'   => '^MAC ',
  636.                             'group'     => 'audio',
  637.                             'module'    => 'monkey',
  638.                             'mime_type' => 'application/octet-stream',
  639.                           ),
  640.  
  641.                 // MOD  - audio       - MODule (assorted sub-formats)
  642.                 'mod'  => array (
  643.                             'pattern'   => '^.{1080}(M.K.|[5-9]CHN|[1-3][0-9]CH)',
  644.                             'mime_type' => 'audio/mod',
  645.                           ),
  646.  
  647.                 // MOD  - audio       - MODule (Impulse Tracker)
  648.                 'it'   => array (
  649.                             'pattern'   => '^IMPM',
  650.                             'mime_type' => 'audio/it',
  651.                           ),
  652.  
  653.                 // MOD  - audio       - MODule (eXtended Module, various sub-formats)
  654.                 'xm'   => array (
  655.                             'pattern'   => '^Extended Module',
  656.                             'mime_type' => 'audio/xm',
  657.                           ),
  658.  
  659.                 // MOD  - audio       - MODule (ScreamTracker)
  660.                 's3m'  => array (
  661.                             'pattern'   => '^.{44}SCRM',
  662.                             'mime_type' => 'audio/s3m',
  663.                           ),
  664.  
  665.                 // MPC  - audio       - Musepack / MPEGplus SV7+
  666.                 'mpc'  => array (
  667.                             'pattern'   => '^(MP\+)',
  668.                             'group'     => 'audio',
  669.                             'module'    => 'mpc',
  670.                             'mime_type' => 'audio/x-musepack',
  671.                           ),
  672.  
  673.                 // MPC  - audio       - Musepack / MPEGplus SV4-6
  674.                 'mpc_old' => array (
  675.                             'pattern'   => '^([\x00\x01\x10\x11\x40\x41\x50\x51\x80\x81\x90\x91\xC0\xC1\xD0\xD1][\x20-37][\x00\x20\x40\x60\x80\xA0\xC0\xE0])',
  676.                             'group'     => 'audio',
  677.                             'module'    => 'mpc_old',
  678.                             'mime_type' => 'application/octet-stream',
  679.                           ),
  680.  
  681.  
  682.                 // MP3  - audio       - MPEG-audio Layer 3 (very similar to AAC-ADTS)
  683.                 'mp3'  => array (
  684.                             'pattern'   => '^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]',
  685.                             'group'     => 'audio',
  686.                             'module'    => 'mp3',
  687.                             'mime_type' => 'audio/mpeg',
  688.                           ),
  689.  
  690.                 // OFR  - audio       - OptimFROG
  691.                 'ofr'  => array (
  692.                             'pattern'   => '^(\*RIFF|OFR)',
  693.                             'group'     => 'audio',
  694.                             'module'    => 'optimfrog',
  695.                             'mime_type' => 'application/octet-stream',
  696.                           ),
  697.  
  698.                 // RKAU - audio       - RKive AUdio compressor
  699.                 'rkau' => array (
  700.                             'pattern'   => '^RKA',
  701.                             'group'     => 'audio',
  702.                             'module'    => 'rkau',
  703.                             'mime_type' => 'application/octet-stream',
  704.                           ),
  705.  
  706.                 // SHN  - audio       - Shorten
  707.                 'shn'  => array (
  708.                             'pattern'   => '^ajkg',
  709.                             'group'     => 'audio',
  710.                             'module'    => 'shorten',
  711.                             'mime_type' => 'audio/xmms-shn',
  712.                             'fail_id3'  => 'ERROR',
  713.                             'fail_ape'  => 'ERROR',
  714.                           ),
  715.  
  716.                 // TTA  - audio       - TTA Lossless Audio Compressor (http://tta.corecodec.org)
  717.                 'tta'  => array (
  718.                             'pattern'   => '^TTA',  // could also be '^TTA(\x01|\x02|\x03|2|1)'
  719.                             'group'     => 'audio',
  720.                             'module'    => 'tta',
  721.                             'mime_type' => 'application/octet-stream',
  722.                           ),
  723.  
  724.                 // VOC  - audio       - Creative Voice (VOC)
  725.                 'voc'  => array (
  726.                             'pattern'   => '^Creative Voice File',
  727.                             'group'     => 'audio',
  728.                             'module'    => 'voc',
  729.                             'mime_type' => 'audio/voc',
  730.                           ),
  731.  
  732.                 // VQF  - audio       - transform-domain weighted interleave Vector Quantization Format (VQF)
  733.                 'vqf'  => array (
  734.                             'pattern'   => '^TWIN',
  735.                             'group'     => 'audio',
  736.                             'module'    => 'vqf',
  737.                             'mime_type' => 'application/octet-stream',
  738.                           ),
  739.  
  740.                 // WV  - audio        - WavPack (v4.0+)
  741.                 'vw'  => array(
  742.                             'pattern'   => '^wvpk',
  743.                             'group'     => 'audio',
  744.                             'module'    => 'wavpack',
  745.                             'mime_type' => 'application/octet-stream',
  746.                           ),
  747.  
  748.  
  749.                 // Audio-Video formats
  750.  
  751.                 // ASF  - audio/video - Advanced Streaming Format, Windows Media Video, Windows Media Audio
  752.                 'asf'  => array (
  753.                             'pattern'   => '^\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C',
  754.                             'group'     => 'audio-video',
  755.                             'module'    => 'asf',
  756.                             'mime_type' => 'video/x-ms-asf',
  757.                           ),
  758.  
  759.                 // BINK  - audio/video - Bink / Smacker
  760.                 'bink' => array(
  761.                             'pattern'   => '^(BIK|SMK)',
  762.                             'mime_type' => 'application/octet-stream',
  763.                           ),
  764.  
  765.                 // FLV  - audio/video - FLash Video
  766.                 'flv' => array(
  767.                             'pattern'   => '^FLV\x01',
  768.                             'group'     => 'audio-video',
  769.                             'module'    => 'flv',
  770.                             'mime_type' => 'video/x-flv',
  771.                           ),
  772.  
  773.                 // MKAV - audio/video - Mastroka
  774.                 'matroska' => array (
  775.                             'pattern'   => '^\x1A\x45\xDF\xA3',
  776.                             'mime_type' => 'application/octet-stream',
  777.                           ),
  778.  
  779.                 // MPEG - audio/video - MPEG (Moving Pictures Experts Group)
  780.                 'mpeg' => array (
  781.                             'pattern'   => '^\x00\x00\x01(\xBA|\xB3)',
  782.                             'group'     => 'audio-video',
  783.                             'module'    => 'mpeg',
  784.                             'mime_type' => 'video/mpeg',
  785.                           ),
  786.  
  787.                 // NSV  - audio/video - Nullsoft Streaming Video (NSV)
  788.                 'nsv'  => array (
  789.                             'pattern'   => '^NSV[sf]',
  790.                             'group'     => 'audio-video',
  791.                             'module'    => 'nsv',
  792.                             'mime_type' => 'application/octet-stream',
  793.                           ),
  794.  
  795.                 // Ogg  - audio/video - Ogg (Ogg Vorbis, OggFLAC, Speex, Ogg Theora(*), Ogg Tarkin(*))
  796.                 'ogg'  => array (
  797.                             'pattern'   => '^OggS',
  798.                             'group'     => 'audio',
  799.                             'module'    => 'xiph',
  800.                             'mime_type' => 'application/ogg',
  801.                             'fail_id3'  => 'WARNING',
  802.                             'fail_ape'  => 'WARNING',
  803.                           ),
  804.  
  805.                 // QT   - audio/video - Quicktime
  806.                 'quicktime' => array (
  807.                             'pattern'   => '^.{4}(cmov|free|ftyp|mdat|moov|pnot|skip|wide)',
  808.                             'group'     => 'audio-video',
  809.                             'module'    => 'quicktime',
  810.                             'mime_type' => 'video/quicktime',
  811.                           ),
  812.  
  813.                 // RIFF - audio/video - Resource Interchange File Format (RIFF) / WAV / AVI / CD-audio / SDSS = renamed variant used by SmartSound QuickTracks (www.smartsound.com) / FORM = Audio Interchange File Format (AIFF)
  814.                 'riff' => array (
  815.                             'pattern'   => '^(RIFF|SDSS|FORM)',
  816.                             'group'     => 'audio-video',
  817.                             'module'    => 'riff',
  818.                             'mime_type' => 'audio/x-wave',
  819.                             'fail_ape'  => 'WARNING',
  820.                           ),
  821.  
  822.                 // Real - audio/video - RealAudio, RealVideo
  823.                 'real' => array (
  824.                             'pattern'   => '^(\.RMF|.ra)',
  825.                             'group'     => 'audio-video',
  826.                             'module'    => 'real',
  827.                             'mime_type' => 'audio/x-realaudio',
  828.                           ),
  829.  
  830.                 // SWF - audio/video - ShockWave Flash
  831.                 'swf' => array (
  832.                             'pattern'   => '^(F|C)WS',
  833.                             'group'     => 'audio-video',
  834.                             'module'    => 'swf',
  835.                             'mime_type' => 'application/x-shockwave-flash',
  836.                           ),
  837.  
  838.  
  839.                 // Still-Image formats
  840.  
  841.                 // BMP  - still image - Bitmap (Windows, OS/2; uncompressed, RLE8, RLE4)
  842.                 'bmp'  => array (
  843.                             'pattern'   => '^BM',
  844.                             'group'     => 'graphic',
  845.                             'module'    => 'bmp',
  846.                             'mime_type' => 'image/bmp',
  847.                             'fail_id3'  => 'ERROR',
  848.                             'fail_ape'  => 'ERROR',
  849.                           ),
  850.  
  851.                 // GIF  - still image - Graphics Interchange Format
  852.                 'gif'  => array (
  853.                             'pattern'   => '^GIF',
  854.                             'group'     => 'graphic',
  855.                             'module'    => 'gif',
  856.                             'mime_type' => 'image/gif',
  857.                             'fail_id3'  => 'ERROR',
  858.                             'fail_ape'  => 'ERROR',
  859.                           ),
  860.  
  861.                 // JPEG - still image - Joint Photographic Experts Group (JPEG)
  862.                 'jpeg'  => array (
  863.                             'pattern'   => '^\xFF\xD8\xFF',
  864.                             'group'     => 'graphic',
  865.                             'module'    => 'jpeg',
  866.                             'mime_type' => 'image/jpeg',
  867.                             'fail_id3'  => 'ERROR',
  868.                             'fail_ape'  => 'ERROR',
  869.                           ),
  870.  
  871.                 // PCD  - still image - Kodak Photo CD
  872.                 'pcd'  => array (
  873.                             'pattern'   => '^.{2048}PCD_IPI\x00',
  874.                             'group'     => 'graphic',
  875.                             'module'    => 'pcd',
  876.                             'mime_type' => 'image/x-photo-cd',
  877.                             'fail_id3'  => 'ERROR',
  878.                             'fail_ape'  => 'ERROR',
  879.                           ),
  880.  
  881.  
  882.                 // PNG  - still image - Portable Network Graphics (PNG)
  883.                 'png'  => array (
  884.                             'pattern'   => '^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A',
  885.                             'group'     => 'graphic',
  886.                             'module'    => 'png',
  887.                             'mime_type' => 'image/png',
  888.                             'fail_id3'  => 'ERROR',
  889.                             'fail_ape'  => 'ERROR',
  890.                           ),
  891.  
  892.  
  893.                 // SVG  - still image - Scalable Vector Graphics (SVG)
  894.                 'svg'  => array(
  895.                             'pattern'   => '<!DOCTYPE svg PUBLIC ',
  896.                             'mime_type' => 'image/svg+xml',
  897.                             'fail_id3'  => 'ERROR',
  898.                             'fail_ape'  => 'ERROR',
  899.                         ),
  900.  
  901.  
  902.                 // TIFF  - still image - Tagged Information File Format (TIFF)
  903.                 'tiff' => array (
  904.                             'pattern'   => '^(II\x2A\x00|MM\x00\x2A)',
  905.                             'group'     => 'graphic',
  906.                             'module'    => 'tiff',
  907.                             'mime_type' => 'image/tiff',
  908.                             'fail_id3'  => 'ERROR',
  909.                             'fail_ape'  => 'ERROR',
  910.                           ),
  911.  
  912.  
  913.                 // Data formats
  914.  
  915.                 'exe'  => array(
  916.                             'pattern'   => '^MZ',
  917.                             'mime_type' => 'application/octet-stream',
  918.                             'fail_id3'  => 'ERROR',
  919.                             'fail_ape'  => 'ERROR',
  920.                           ),
  921.  
  922.                 // ISO  - data        - International Standards Organization (ISO) CD-ROM Image
  923.                 'iso'  => array (
  924.                             'pattern'   => '^.{32769}CD001',
  925.                             'group'     => 'misc',
  926.                             'module'    => 'iso',
  927.                             'mime_type' => 'application/octet-stream',
  928.                             'fail_id3'  => 'ERROR',
  929.                             'fail_ape'  => 'ERROR',
  930.                           ),
  931.  
  932.                 // RAR  - data        - RAR compressed data
  933.                 'rar'  => array(
  934.                             'pattern'   => '^Rar\!',
  935.                             'mime_type' => 'application/octet-stream',
  936.                             'fail_id3'  => 'ERROR',
  937.                             'fail_ape'  => 'ERROR',
  938.                           ),
  939.  
  940.                 // SZIP - audio       - SZIP compressed data
  941.                 'szip' => array (
  942.                             'pattern'   => '^SZ\x0A\x04',
  943.                             'group'     => 'archive',
  944.                             'module'    => 'szip',
  945.                             'mime_type' => 'application/octet-stream',
  946.                             'fail_id3'  => 'ERROR',
  947.                             'fail_ape'  => 'ERROR',
  948.                           ),
  949.  
  950.                 // TAR  - data        - TAR compressed data
  951.                 'tar'  => array(
  952.                             'pattern'   => '^.{100}[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20\x00]{12}[0-9\x20\x00]{12}',
  953.                             'group'     => 'archive',
  954.                             'module'    => 'tar',
  955.                             'mime_type' => 'application/x-tar',
  956.                             'fail_id3'  => 'ERROR',
  957.                             'fail_ape'  => 'ERROR',
  958.                           ),
  959.  
  960.                 // GZIP  - data        - GZIP compressed data
  961.                 'gz'  => array(
  962.                             'pattern'   => '^\x1F\x8B\x08',
  963.                             'group'     => 'archive',
  964.                             'module'    => 'gzip',
  965.                             'mime_type' => 'application/x-gzip',
  966.                             'fail_id3'  => 'ERROR',
  967.                             'fail_ape'  => 'ERROR',
  968.                           ),
  969.  
  970.  
  971.                 // ZIP  - data        - ZIP compressed data
  972.                 'zip'  => array (
  973.                             'pattern'   => '^PK\x03\x04',
  974.                             'group'     => 'archive',
  975.                             'module'    => 'zip',
  976.                             'mime_type' => 'application/zip',
  977.                             'fail_id3'  => 'ERROR',
  978.                             'fail_ape'  => 'ERROR',
  979.                           ),
  980.  
  981.  
  982.                 // PAR2 - data        - Parity Volume Set Specification 2.0
  983.                 'par2' => array (
  984.                             'pattern'   => '^PAR2\x00PKT',
  985.                             'mime_type' => 'application/octet-stream',
  986.                             'fail_id3'  => 'ERROR',
  987.                             'fail_ape'  => 'ERROR',
  988.                         ),
  989.  
  990.  
  991.                  // PDF  - data       - Portable Document Format
  992.                  'pdf' => array(
  993.                             'pattern'   => '^\x25PDF',
  994.                             'mime_type' => 'application/pdf',
  995.                             'fail_id3'  => 'ERROR',
  996.                             'fail_ape'  => 'ERROR',
  997.                            ),
  998.  
  999.                  // DOC  - data       - Microsoft Word
  1000.                  'msoffice' => array(
  1001.                             'pattern'   => '^\xD0\xCF\x11\xE0'// D0CF11E == DOCFILE == Microsoft Office Document
  1002.                             'mime_type' => 'application/octet-stream',
  1003.                             'fail_id3'  => 'ERROR',
  1004.                             'fail_ape'  => 'ERROR',
  1005.                           ),
  1006.             );
  1007.  
  1008.         return $format_info;
  1009.     }
  1010.  
  1011.  
  1012.  
  1013.     // Recursive over array - converts array to $encoding charset from $this->encoding
  1014.     function CharConvert(&$array$encoding{
  1015.  
  1016.         // Identical encoding - end here
  1017.         if ($encoding == $this->encoding{
  1018.             return;
  1019.         }
  1020.  
  1021.         // Loop thru array
  1022.         foreach ($array as $key => $value{
  1023.  
  1024.             // Go recursive
  1025.             if (is_array($value)) {
  1026.                 $this->CharConvert($array[$key]$encoding);
  1027.             }
  1028.  
  1029.             // Convert string
  1030.             elseif (is_string($value)) {
  1031.                 $array[$key$this->iconv($encoding$this->encoding$value);
  1032.             }
  1033.         }
  1034.     }
  1035.  
  1036.  
  1037.  
  1038.     // Convert and copy tags
  1039.     protected function HandleAllTags({
  1040.  
  1041.         // Key name => array (tag name, character encoding)
  1042.         static $tags array (
  1043.             'asf'       => array ('asf',           'UTF-16LE'),
  1044.             'midi'      => array ('midi',          'ISO-8859-1'),
  1045.             'nsv'       => array ('nsv',           'ISO-8859-1'),
  1046.             'ogg'       => array ('vorbiscomment''UTF-8'),
  1047.             'png'       => array ('png',           'UTF-8'),
  1048.             'tiff'      => array ('tiff',          'ISO-8859-1'),
  1049.             'quicktime' => array ('quicktime',     'ISO-8859-1'),
  1050.             'real'      => array ('real',          'ISO-8859-1'),
  1051.             'vqf'       => array ('vqf',           'ISO-8859-1'),
  1052.             'zip'       => array ('zip',           'ISO-8859-1'),
  1053.             'riff'      => array ('riff',          'ISO-8859-1'),
  1054.             'lyrics3'   => array ('lyrics3',       'ISO-8859-1'),
  1055.             'id3v1'     => array ('id3v1',         ''),            // change below - cannot assign variable to static array
  1056.             'id3v2'     => array ('id3v2',         'UTF-8'),       // module converts all frames to UTF-8
  1057.             'ape'       => array ('ape',           'UTF-8')
  1058.         );
  1059.         $tags['id3v1'][1$this->encoding_id3v1;
  1060.  
  1061.         // Loop thru tags array
  1062.         foreach ($tags as $comment_name => $tag_name_encoding_array{
  1063.             list($tag_name$encoding$tag_name_encoding_array;
  1064.  
  1065.             // Fill in default encoding type if not already present
  1066.             @$this->info[$comment_name]  and  $this->info[$comment_name]['encoding'$encoding;
  1067.  
  1068.             // Copy comments if key name set
  1069.             if (@$this->info[$comment_name]['comments']{
  1070.  
  1071.                 foreach ($this->info[$comment_name]['comments'as $tag_key => $value_array{
  1072.                     foreach ($value_array as $key => $value{
  1073.                         if (strlen(trim($value)) 0{
  1074.                             $this->info['tags'][$tag_name][trim($tag_key)][$value// do not trim!! Unicode characters will get mangled if trailing nulls are removed!
  1075.                         }
  1076.                     }
  1077.  
  1078.                 }
  1079.  
  1080.                 if (!@$this->info['tags'][$tag_name]{
  1081.                     // comments are set but contain nothing but empty strings, so skip
  1082.                     continue;
  1083.                 }
  1084.  
  1085.                 $this->CharConvert($this->info['tags'][$tag_name]$encoding);
  1086.             }
  1087.         }
  1088.  
  1089.  
  1090.         // Merge comments from ['tags'] into common ['comments']
  1091.         if (@$this->info['tags']{
  1092.  
  1093.             foreach ($this->info['tags'as $tag_type => $tag_array{
  1094.  
  1095.                 foreach ($tag_array as $tag_name => $tagdata{
  1096.  
  1097.                     foreach ($tagdata as $key => $value{
  1098.  
  1099.                         if (!empty($value)) {
  1100.  
  1101.                             if (empty($this->info['comments'][$tag_name])) {
  1102.  
  1103.                                 // fall through and append value
  1104.                             }
  1105.                             elseif ($tag_type == 'id3v1'{
  1106.  
  1107.                                 $new_value_length strlen(trim($value));
  1108.                                 foreach ($this->info['comments'][$tag_nameas $existing_key => $existing_value{
  1109.                                     $old_value_length strlen(trim($existing_value));
  1110.                                     if (($new_value_length <= $old_value_length&& (substr($existing_value0$new_value_length== trim($value))) {
  1111.                                         // new value is identical but shorter-than (or equal-length to) one already in comments - skip
  1112.                                         break 2;
  1113.                                     }
  1114.                                 }
  1115.                             }
  1116.                             else {
  1117.  
  1118.                                 $new_value_length strlen(trim($value));
  1119.                                 foreach ($this->info['comments'][$tag_nameas $existing_key => $existing_value{
  1120.                                     $old_value_length strlen(trim($existing_value));
  1121.                                     if (($new_value_length $old_value_length&& (substr(trim($value)0strlen($existing_value)) == $existing_value)) {
  1122.                                         $this->info['comments'][$tag_name][$existing_keytrim($value);
  1123.                                         break 2;
  1124.                                     }
  1125.                                 }
  1126.                             }
  1127.  
  1128.                             if (empty($this->info['comments'][$tag_name]|| !in_array(trim($value)$this->info['comments'][$tag_name])) {
  1129.                                 $this->info['comments'][$tag_name][trim($value);
  1130.                             }
  1131.                         }
  1132.                     }
  1133.                 }
  1134.             }
  1135.         }
  1136.  
  1137.         return true;
  1138.     }
  1139. }
  1140.  
  1141.  
  1142. abstract class getid3_handler
  1143. {
  1144.  
  1145.     protected $getid3;                          // pointer
  1146.  
  1147.     protected $data_string_flag = false;        // analyzing filepointer or string
  1148.     protected $data_string;                     // string to analyze
  1149.     protected $data_string_position = 0;        // seek position in string
  1150.  
  1151.  
  1152.     public function __construct(getID3 $getid3{
  1153.  
  1154.         $this->getid3 = $getid3;
  1155.     }
  1156.  
  1157.  
  1158.     // Analyze from file pointer
  1159.     abstract public function Analyze();
  1160.  
  1161.  
  1162.  
  1163.     // Analyze from string instead
  1164.     public function AnalyzeString(&$string{
  1165.  
  1166.         // Enter string mode
  1167.         $this->data_string_flag = true;
  1168.         $this->data_string      = $string;
  1169.  
  1170.         // Save info
  1171.         $saved_avdataoffset $this->getid3->info['avdataoffset'];
  1172.         $saved_avdataend    $this->getid3->info['avdataend'];
  1173.         $saved_filesize     $this->getid3->info['filesize'];
  1174.  
  1175.         // Reset some info
  1176.         $this->getid3->info['avdataoffset'0;
  1177.         $this->getid3->info['avdataend']    $this->getid3->info['filesize'strlen($string);
  1178.  
  1179.         // Analyze
  1180.         $this->Analyze();
  1181.  
  1182.         // Restore some info
  1183.         $this->getid3->info['avdataoffset'$saved_avdataoffset;
  1184.         $this->getid3->info['avdataend']    $saved_avdataend;
  1185.         $this->getid3->info['filesize']     $saved_filesize;
  1186.  
  1187.         // Exit string mode
  1188.         $this->data_string_flag = false;
  1189.     }
  1190.  
  1191.  
  1192.     protected function ftell({
  1193.  
  1194.         if ($this->data_string_flag{
  1195.             return $this->data_string_position;
  1196.         }
  1197.         return ftell($this->getid3->fp);
  1198.     }
  1199.  
  1200.  
  1201.     protected function fread($bytes{
  1202.  
  1203.         if ($this->data_string_flag{
  1204.             $this->data_string_position += $bytes;
  1205.             return substr($this->data_string$this->data_string_position - $bytes$bytes);
  1206.         }
  1207.         return fread($this->getid3->fp$bytes);
  1208.     }
  1209.  
  1210.  
  1211.     protected function fseek($bytes$whence SEEK_SET{
  1212.  
  1213.         if ($this->data_string_flag{
  1214.             switch ($whence{
  1215.                 case SEEK_SET:
  1216.                     $this->data_string_position = $bytes;
  1217.                     return;
  1218.  
  1219.                 case SEEK_CUR:
  1220.                     $this->data_string_position += $bytes;
  1221.                     return;
  1222.  
  1223.                 case SEEK_END:
  1224.                     $this->data_string_position = strlen($this->data_string$bytes;
  1225.                     return;
  1226.             }
  1227.         }
  1228.         return fseek($this->getid3->fp$bytes$whence);
  1229.     }
  1230.  
  1231. }
  1232.  
  1233.  
  1234.  
  1235.  
  1236. abstract class getid3_handler_write
  1237. {
  1238.     protected $filename;
  1239.     protected $user_abort;
  1240.  
  1241.     private $fp_lock;
  1242.     private $owner;
  1243.     private $group;
  1244.     private $perms;
  1245.  
  1246.  
  1247.     public function __construct($filename{
  1248.  
  1249.         if (!file_exists($filename)) {
  1250.             throw new getid3_exception('File does not exist: "' $filename '"');
  1251.         }
  1252.  
  1253.         if (!is_writeable($filename)) {
  1254.             throw new getid3_exception('File is not writeable: "' $filename '"');
  1255.         }
  1256.  
  1257.         if (!is_writeable(dirname($filename))) {
  1258.             throw new getid3_exception('Directory is not writeable: ' dirname($filename' (need to write lock file).');
  1259.         }
  1260.  
  1261.         $this->user_abort = ignore_user_abort(true);
  1262.  
  1263.         $this->fp_lock = fopen($filename '.getid3.lock''w');
  1264.         flock($this->fp_lockLOCK_EX);
  1265.  
  1266.         $this->filename = $filename;
  1267.     }
  1268.  
  1269.  
  1270.     public function __destruct({
  1271.  
  1272.         flock($this->fp_lockLOCK_UN);
  1273.         fclose($this->fp_lock);
  1274.         unlink($this->filename . '.getid3.lock');
  1275.  
  1276.         ignore_user_abort($this->user_abort);
  1277.     }
  1278.     
  1279.     
  1280.     protected function save_permissions({
  1281.         
  1282.         $this->owner = fileowner($this->filename);
  1283.         $this->group = filegroup($this->filename);
  1284.         $this->perms = fileperms($this->filename);
  1285.     }
  1286.     
  1287.     
  1288.     protected function restore_permissions({
  1289.         
  1290.         @chown($this->filename$this->owner);
  1291.         @chgrp($this->filename$this->group);
  1292.         @chmod($this->filename$this->perms);
  1293.     }
  1294.  
  1295.  
  1296.     abstract public function read();
  1297.  
  1298.     abstract public function write();
  1299.  
  1300.     abstract public function remove();
  1301.  
  1302. }
  1303.  
  1304.  
  1305.  
  1306.  
  1307. class getid3_exception extends Exception
  1308. {
  1309.     public $message;
  1310.  
  1311. }
  1312.  
  1313.  
  1314.  
  1315.  
  1316. class getid3_lib
  1317. {
  1318.  
  1319.     // Convert Little Endian byte string to int - max 32 bits
  1320.     public static function LittleEndian2Int($byte_word$signed false{
  1321.  
  1322.         return getid3_lib::BigEndian2Int(strrev($byte_word)$signed);
  1323.     }
  1324.  
  1325.  
  1326.  
  1327.     // Convert number to Little Endian byte string
  1328.     public static function LittleEndian2String($number$minbytes=1$synchsafe=false{
  1329.         $intstring '';
  1330.         while ($number 0{
  1331.             if ($synchsafe{
  1332.                 $intstring $intstring.chr($number 127);
  1333.                 $number >>= 7;
  1334.             else {
  1335.                 $intstring $intstring.chr($number 255);
  1336.                 $number >>= 8;
  1337.             }
  1338.         }
  1339.         return str_pad($intstring$minbytes"\x00"STR_PAD_RIGHT);
  1340.     }
  1341.  
  1342.  
  1343.  
  1344.     // Convert Big Endian byte string to int - max 32 bits
  1345.     public static function BigEndian2Int($byte_word$signed false{
  1346.  
  1347.         $int_value 0;
  1348.         $byte_wordlen strlen($byte_word);
  1349.  
  1350.         for ($i 0$i $byte_wordlen$i++{
  1351.             $int_value += ord($byte_word{$i}pow(256($byte_wordlen $i));
  1352.         }
  1353.  
  1354.         if ($signed{
  1355.             $sign_mask_bit 0x80 << (($byte_wordlen 1));
  1356.             if ($int_value $sign_mask_bit{
  1357.                 $int_value ($int_value ($sign_mask_bit 1));
  1358.             }
  1359.         }
  1360.  
  1361.         return $int_value;
  1362.     }
  1363.  
  1364.  
  1365.  
  1366.     // Convert Big Endian byte sybc safe string to int - max 32 bits
  1367.     public static function BigEndianSyncSafe2Int($byte_word{
  1368.  
  1369.         $int_value 0;
  1370.         $byte_wordlen strlen($byte_word);
  1371.  
  1372.         // disregard MSB, effectively 7-bit bytes
  1373.         for ($i 0$i $byte_wordlen$i++{
  1374.             $int_value $int_value (ord($byte_word{$i}0x7F<< (($byte_wordlen $i7);
  1375.         }
  1376.         return $int_value;
  1377.     }
  1378.  
  1379.  
  1380.  
  1381.     // Convert Big Endian byte string to bit string
  1382.     public static function BigEndian2Bin($byte_word{
  1383.  
  1384.         $bin_value '';
  1385.         $byte_wordlen strlen($byte_word);
  1386.         for ($i 0$i $byte_wordlen$i++{
  1387.             $bin_value .= str_pad(decbin(ord($byte_word{$i}))8'0'STR_PAD_LEFT);
  1388.         }
  1389.         return $bin_value;
  1390.     }
  1391.  
  1392.  
  1393.  
  1394.     public static function BigEndian2Float($byte_word{
  1395.  
  1396.         // ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic
  1397.         // http://www.psc.edu/general/software/packages/ieee/ieee.html
  1398.         // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html
  1399.  
  1400.         $bit_word getid3_lib::BigEndian2Bin($byte_word);
  1401.         if (!$bit_word{
  1402.             return 0;
  1403.         }
  1404.         $sign_bit $bit_word{0};
  1405.  
  1406.         switch (strlen($byte_word8{
  1407.             case 32:
  1408.                 $exponent_bits 8;
  1409.                 $fraction_bits 23;
  1410.                 break;
  1411.  
  1412.             case 64:
  1413.                 $exponent_bits 11;
  1414.                 $fraction_bits 52;
  1415.                 break;
  1416.  
  1417.             case 80:
  1418.                 // 80-bit Apple SANE format
  1419.                 // http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/
  1420.                 $exponent_string substr($bit_word115);
  1421.                 $is_normalized intval($bit_word{16});
  1422.                 $fraction_string substr($bit_word1763);
  1423.                 $exponent pow(2getid3_lib::Bin2Dec($exponent_string16383);
  1424.                 $fraction $is_normalized getid3_lib::DecimalBinary2Float($fraction_string);
  1425.                 $float_value $exponent $fraction;
  1426.                 if ($sign_bit == '1'{
  1427.                     $float_value *= -1;
  1428.                 }
  1429.                 return $float_value;
  1430.                 break;
  1431.  
  1432.             default:
  1433.                 return false;
  1434.                 break;
  1435.         }
  1436.         $exponent_string substr($bit_word1$exponent_bits);
  1437.         $fraction_string substr($bit_word$exponent_bits 1$fraction_bits);
  1438.         $exponent bindec($exponent_string);
  1439.         $fraction bindec($fraction_string);
  1440.  
  1441.         if (($exponent == (pow(2$exponent_bits1)) && ($fraction != 0)) {
  1442.             // Not a Number
  1443.             $float_value false;
  1444.         elseif (($exponent == (pow(2$exponent_bits1)) && ($fraction == 0)) {
  1445.             if ($sign_bit == '1'{
  1446.                 $float_value '-infinity';
  1447.             else {
  1448.                 $float_value '+infinity';
  1449.             }
  1450.         elseif (($exponent == 0&& ($fraction == 0)) {
  1451.             if ($sign_bit == '1'{
  1452.                 $float_value = -0;
  1453.             else {
  1454.                 $float_value 0;
  1455.             }
  1456.             $float_value ($sign_bit : -0);
  1457.         elseif (($exponent == 0&& ($fraction != 0)) {
  1458.             // These are 'unnormalized' values
  1459.             $float_value pow(2(-(pow(2$exponent_bits 12))) getid3_lib::DecimalBinary2Float($fraction_string);
  1460.             if ($sign_bit == '1'{
  1461.                 $float_value *= -1;
  1462.             }
  1463.         elseif ($exponent != 0{
  1464.             $float_value pow(2($exponent (pow(2$exponent_bits 11))) (getid3_lib::DecimalBinary2Float($fraction_string));
  1465.             if ($sign_bit == '1'{
  1466.                 $float_value *= -1;
  1467.             }
  1468.         }
  1469.         return (float) $float_value;
  1470.     }
  1471.  
  1472.  
  1473.  
  1474.     public static function LittleEndian2Float($byte_word{
  1475.  
  1476.         return getid3_lib::BigEndian2Float(strrev($byte_word));
  1477.     }
  1478.  
  1479.  
  1480.  
  1481.     public static function DecimalBinary2Float($binary_numerator{
  1482.         $numerator   bindec($binary_numerator);
  1483.         $denominator bindec('1'.str_repeat('0'strlen($binary_numerator)));
  1484.         return ($numerator $denominator);
  1485.     }
  1486.  
  1487.  
  1488.     public static function PrintHexBytes($string$hex=true$spaces=true$html_safe=true{
  1489.  
  1490.         $return_string '';
  1491.         for ($i 0$i strlen($string)$i++{
  1492.             if ($hex{
  1493.                 $return_string .= str_pad(dechex(ord($string{$i}))2'0'STR_PAD_LEFT);
  1494.             else {
  1495.                 $return_string .= ' '.(ereg("[\x20-\x7E]"$string{$i}$string{$i'');
  1496.             }
  1497.             if ($spaces{
  1498.                 $return_string .= ' ';
  1499.             }
  1500.         }
  1501.         if ($html_safe{
  1502.             $return_string htmlentities($return_string);
  1503.         }
  1504.         return $return_string;
  1505.     }
  1506.  
  1507.  
  1508.  
  1509.     // Process header data string - read several values with algorithm and add to target
  1510.     //   algorithm is one one the getid3_lib::Something2Something() function names
  1511.     //   parts_array is  index => length    -  $target[index] = algorithm(substring(data))
  1512.     //   - OR just substring(data) if length is negative!
  1513.     //  indexes == 'IGNORE**' are ignored
  1514.  
  1515.     public static function ReadSequence($algorithm&$target&$data$offset$parts_array{
  1516.  
  1517.         // Loop thru $parts_array
  1518.         foreach ($parts_array as $target_string => $length{
  1519.  
  1520.             // Add to target
  1521.             if (!strstr($target_string'IGNORE')) {
  1522.  
  1523.                 // substr(....length)
  1524.                 if ($length 0{
  1525.                     $target[$target_stringsubstr($data$offset-$length);
  1526.                 }
  1527.  
  1528.                 // algorithm(substr(...length))
  1529.                 else {
  1530.                     $target[$target_stringgetid3_lib::$algorithm(substr($data$offset$length));
  1531.                 }
  1532.             }
  1533.  
  1534.             // Move pointer
  1535.             $offset += abs($length);
  1536.         }
  1537.     }
  1538.  
  1539. }
  1540.  
  1541.  
  1542.  
  1543. {
  1544.  
  1545.     public static function NameLookup($name_code{
  1546.  
  1547.         static $lookup array (
  1548.             => 'not set',
  1549.             => 'Track Gain Adjustment',
  1550.             => 'Album Gain Adjustment'
  1551.         );
  1552.  
  1553.         return @$lookup[$name_code];
  1554.     }
  1555.  
  1556.  
  1557.  
  1558.     public static function OriginatorLookup($originator_code{
  1559.  
  1560.         static $lookup array (
  1561.             => 'unspecified',
  1562.             => 'pre-set by artist/producer/mastering engineer',
  1563.             => 'set by user',
  1564.             => 'determined automatically'
  1565.         );
  1566.  
  1567.         return @$lookup[$originator_code];
  1568.     }
  1569.  
  1570.  
  1571.  
  1572.     public static function AdjustmentLookup($raw_adjustment$sign_bit{
  1573.  
  1574.         return (float)$raw_adjustment 10 ($sign_bit == ? -1);
  1575.     }
  1576.  
  1577.  
  1578.  
  1579.     public static function GainString($name_code$originator_code$replaygain{
  1580.  
  1581.         $sign_bit $replaygain 0;
  1582.  
  1583.         $stored_replaygain intval(round($replaygain 10));
  1584.         $gain_string  str_pad(decbin($name_code)3'0'STR_PAD_LEFT);
  1585.         $gain_string .= str_pad(decbin($originator_code)3'0'STR_PAD_LEFT);
  1586.         $gain_string .= $sign_bit;
  1587.         $gain_string .= str_pad(decbin($stored_replaygain)9'0'STR_PAD_LEFT);
  1588.  
  1589.         return $gain_string;
  1590.     }
  1591.  
  1592. }
  1593.  
  1594.  
  1595.  
  1596.  
  1597. ?>

Documentation generated on Thu, 12 Jun 2008 13:33:05 -0500 by phpDocumentor 1.4.1