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