Ignore:
Timestamp:
Jul 7, 2009, 10:27:37 PM (15 years ago)
Author:
vdigital
Message:

Change: getid3 upgraded to -> 1.7.9

File:
1 edited

Legend:

Unmodified
Added
Removed
  • extensions/charlies_content/getid3/getid3/getid3.php

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