Mercurial > judyates
diff e2gallerypro/e2upload/Backend/Assets/getid3/module.audio.mp3.php @ 3:3f6b44aa6b35 judyates
[svn r4] added ability to buy stuff, from a Prints page, but it doesn't work well with the css, and it also has not been fitted into the perl make system.
author | rlm |
---|---|
date | Mon, 22 Feb 2010 08:02:39 -0500 |
parents | |
children |
line wrap: on
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/e2gallerypro/e2upload/Backend/Assets/getid3/module.audio.mp3.php Mon Feb 22 08:02:39 2010 -0500 1.3 @@ -0,0 +1,1696 @@ 1.4 +<?php 1.5 +// +----------------------------------------------------------------------+ 1.6 +// | PHP version 5 | 1.7 +// +----------------------------------------------------------------------+ 1.8 +// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | 1.9 +// +----------------------------------------------------------------------+ 1.10 +// | This source file is subject to version 2 of the GPL license, | 1.11 +// | that is bundled with this package in the file license.txt and is | 1.12 +// | available through the world-wide-web at the following url: | 1.13 +// | http://www.gnu.org/copyleft/gpl.html | 1.14 +// +----------------------------------------------------------------------+ 1.15 +// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | 1.16 +// +----------------------------------------------------------------------+ 1.17 +// | Authors: James Heinrich <infoØgetid3*org> | 1.18 +// | Allan Hansen <ahØartemis*dk> | 1.19 +// +----------------------------------------------------------------------+ 1.20 +// | module.audio.mp3.php | 1.21 +// | Module for analyzing MPEG Audio Layer 1,2,3 files. | 1.22 +// | dependencies: none | 1.23 +// +----------------------------------------------------------------------+ 1.24 +// 1.25 +// $Id: module.audio.mp3.php,v 1.10 2006/11/16 22:57:57 ah Exp $ 1.26 + 1.27 + 1.28 + 1.29 +class getid3_mp3 extends getid3_handler 1.30 +{ 1.31 + // Number of frames to scan to determine if MPEG-audio sequence is valid. 1.32 + // Lower this number to 5-20 for faster scanning 1.33 + // Increase this number to 50+ for most accurate detection of valid VBR/CBR mpeg-audio streams 1.34 + const VALID_CHECK_FRAMES = 35; 1.35 + 1.36 + 1.37 + public function Analyze() { 1.38 + 1.39 + $this->getAllMPEGInfo($this->getid3->fp, $this->getid3->info); 1.40 + 1.41 + return true; 1.42 + } 1.43 + 1.44 + 1.45 + public function AnalyzeMPEGaudioInfo() { 1.46 + 1.47 + $this->getOnlyMPEGaudioInfo($this->getid3->fp, $this->getid3->info, $this->getid3->info['avdataoffset'], false); 1.48 + } 1.49 + 1.50 + 1.51 + public function getAllMPEGInfo(&$fd, &$info) { 1.52 + 1.53 + $this->getOnlyMPEGaudioInfo($fd, $info, 0 + $info['avdataoffset']); 1.54 + 1.55 + if (isset($info['mpeg']['audio']['bitrate_mode'])) { 1.56 + $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']); 1.57 + } 1.58 + 1.59 + if (((isset($info['id3v2']['headerlength']) && ($info['avdataoffset'] > $info['id3v2']['headerlength'])) || (!isset($info['id3v2']) && ($info['avdataoffset'] > 0)))) { 1.60 + 1.61 + $synch_offset_warning = 'Unknown data before synch '; 1.62 + if (isset($info['id3v2']['headerlength'])) { 1.63 + $synch_offset_warning .= '(ID3v2 header ends at '.$info['id3v2']['headerlength'].', then '.($info['avdataoffset'] - $info['id3v2']['headerlength']).' bytes garbage, '; 1.64 + } else { 1.65 + $synch_offset_warning .= '(should be at beginning of file, '; 1.66 + } 1.67 + $synch_offset_warning .= 'synch detected at '.$info['avdataoffset'].')'; 1.68 + if ($info['audio']['bitrate_mode'] == 'cbr') { 1.69 + 1.70 + if (!empty($info['id3v2']['headerlength']) && (($info['avdataoffset'] - $info['id3v2']['headerlength']) == $info['mpeg']['audio']['framelength'])) { 1.71 + 1.72 + $synch_offset_warning .= '. This is a known problem with some versions of LAME (3.90-3.92) DLL in CBR mode.'; 1.73 + $info['audio']['codec'] = 'LAME'; 1.74 + $current_data_lame_version_string = 'LAME3.'; 1.75 + 1.76 + } elseif (empty($info['id3v2']['headerlength']) && ($info['avdataoffset'] == $info['mpeg']['audio']['framelength'])) { 1.77 + 1.78 + $synch_offset_warning .= '. This is a known problem with some versions of LAME (3.90 - 3.92) DLL in CBR mode.'; 1.79 + $info['audio']['codec'] = 'LAME'; 1.80 + $current_data_lame_version_string = 'LAME3.'; 1.81 + 1.82 + } 1.83 + 1.84 + } 1.85 + $this->getid3->warning($synch_offset_warning); 1.86 + 1.87 + } 1.88 + 1.89 + if (isset($info['mpeg']['audio']['LAME'])) { 1.90 + $info['audio']['codec'] = 'LAME'; 1.91 + if (!empty($info['mpeg']['audio']['LAME']['long_version'])) { 1.92 + $info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['long_version'], "\x00"); 1.93 + } elseif (!empty($info['mpeg']['audio']['LAME']['short_version'])) { 1.94 + $info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['short_version'], "\x00"); 1.95 + } 1.96 + } 1.97 + 1.98 + $current_data_lame_version_string = (!empty($current_data_lame_version_string) ? $current_data_lame_version_string : @$info['audio']['encoder']); 1.99 + if (!empty($current_data_lame_version_string) && (substr($current_data_lame_version_string, 0, 6) == 'LAME3.') && !preg_match('[0-9\)]', substr($current_data_lame_version_string, -1))) { 1.100 + // a version number of LAME that does not end with a number like "LAME3.92" 1.101 + // or with a closing parenthesis like "LAME3.88 (alpha)" 1.102 + // or a version of LAME with the LAMEtag-not-filled-in-DLL-mode bug (3.90-3.92) 1.103 + 1.104 + // not sure what the actual last frame length will be, but will be less than or equal to 1441 1.105 + $possibly_longer_lame_version_frame_length = 1441; 1.106 + 1.107 + // Not sure what version of LAME this is - look in padding of last frame for longer version string 1.108 + $possible_lame_version_string_offset = $info['avdataend'] - $possibly_longer_lame_version_frame_length; 1.109 + fseek($fd, $possible_lame_version_string_offset); 1.110 + $possibly_longer_lame_version_data = fread($fd, $possibly_longer_lame_version_frame_length); 1.111 + switch (substr($current_data_lame_version_string, -1)) { 1.112 + case 'a': 1.113 + case 'b': 1.114 + // "LAME3.94a" will have a longer version string of "LAME3.94 (alpha)" for example 1.115 + // need to trim off "a" to match longer string 1.116 + $current_data_lame_version_string = substr($current_data_lame_version_string, 0, -1); 1.117 + break; 1.118 + } 1.119 + if (($possibly_longer_lame_version_string = strstr($possibly_longer_lame_version_data, $current_data_lame_version_string)) !== false) { 1.120 + if (substr($possibly_longer_lame_version_string, 0, strlen($current_data_lame_version_string)) == $current_data_lame_version_string) { 1.121 + $possibly_longer_lame_version_new_string = substr($possibly_longer_lame_version_string, 0, strspn($possibly_longer_lame_version_string, 'LAME0123456789., (abcdefghijklmnopqrstuvwxyzJFSOND)')); //"LAME3.90.3" "LAME3.87 (beta 1, Sep 27 2000)" "LAME3.88 (beta)" 1.122 + if (strlen($possibly_longer_lame_version_new_string) > strlen(@$info['audio']['encoder'])) { 1.123 + $info['audio']['encoder'] = $possibly_longer_lame_version_new_string; 1.124 + } 1.125 + } 1.126 + } 1.127 + } 1.128 + if (!empty($info['audio']['encoder'])) { 1.129 + $info['audio']['encoder'] = rtrim($info['audio']['encoder'], "\x00 "); 1.130 + } 1.131 + 1.132 + switch (@$info['mpeg']['audio']['layer']) { 1.133 + case 1: 1.134 + case 2: 1.135 + $info['audio']['dataformat'] = 'mp'.$info['mpeg']['audio']['layer']; 1.136 + break; 1.137 + } 1.138 + if (@$info['fileformat'] == 'mp3') { 1.139 + switch ($info['audio']['dataformat']) { 1.140 + case 'mp1': 1.141 + case 'mp2': 1.142 + case 'mp3': 1.143 + $info['fileformat'] = $info['audio']['dataformat']; 1.144 + break; 1.145 + 1.146 + default: 1.147 + $this->getid3->warning('Expecting [audio][dataformat] to be mp1/mp2/mp3 when fileformat == mp3, [audio][dataformat] actually "'.$info['audio']['dataformat'].'"'); 1.148 + break; 1.149 + } 1.150 + } 1.151 + 1.152 + $info['mime_type'] = 'audio/mpeg'; 1.153 + $info['audio']['lossless'] = false; 1.154 + 1.155 + // Calculate playtime 1.156 + if (!isset($info['playtime_seconds']) && isset($info['audio']['bitrate']) && ($info['audio']['bitrate'] > 0)) { 1.157 + $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['audio']['bitrate']; 1.158 + } 1.159 + 1.160 + $info['audio']['encoder_options'] = getid3_mp3::GuessEncoderOptions($info); 1.161 + 1.162 + return true; 1.163 + } 1.164 + 1.165 + 1.166 + 1.167 + public static function GuessEncoderOptions(&$info) { 1.168 + // shortcuts 1.169 + if (!empty($info['mpeg']['audio'])) { 1.170 + $thisfile_mpeg_audio = &$info['mpeg']['audio']; 1.171 + if (!empty($thisfile_mpeg_audio['LAME'])) { 1.172 + $thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME']; 1.173 + } 1.174 + } 1.175 + 1.176 + $encoder_options = ''; 1.177 + static $named_preset_bitrates = array (16, 24, 40, 56, 112, 128, 160, 192, 256); 1.178 + 1.179 + if ((@$thisfile_mpeg_audio['VBR_method'] == 'Fraunhofer') && !empty($thisfile_mpeg_audio['VBR_quality'])) { 1.180 + 1.181 + $encoder_options = 'VBR q'.$thisfile_mpeg_audio['VBR_quality']; 1.182 + 1.183 + } elseif (!empty($thisfile_mpeg_audio_lame['preset_used']) && (!in_array($thisfile_mpeg_audio_lame['preset_used_id'], $named_preset_bitrates))) { 1.184 + 1.185 + $encoder_options = $thisfile_mpeg_audio_lame['preset_used']; 1.186 + 1.187 + } elseif (!empty($thisfile_mpeg_audio_lame['vbr_quality'])) { 1.188 + 1.189 + static $known_encoder_values = array (); 1.190 + if (empty($known_encoder_values)) { 1.191 + 1.192 + //$known_encoder_values[abrbitrate_minbitrate][vbr_quality][raw_vbr_method][raw_noise_shaping][raw_stereo_mode][ath_type][lowpass_frequency] = 'preset name'; 1.193 + $known_encoder_values[0xFF][58][1][1][3][2][20500] = '--alt-preset insane'; // 3.90, 3.90.1, 3.92 1.194 + $known_encoder_values[0xFF][58][1][1][3][2][20600] = '--alt-preset insane'; // 3.90.2, 3.90.3, 3.91 1.195 + $known_encoder_values[0xFF][57][1][1][3][4][20500] = '--alt-preset insane'; // 3.94, 3.95 1.196 + $known_encoder_values['**'][78][3][2][3][2][19500] = '--alt-preset extreme'; // 3.90, 3.90.1, 3.92 1.197 + $known_encoder_values['**'][78][3][2][3][2][19600] = '--alt-preset extreme'; // 3.90.2, 3.91 1.198 + $known_encoder_values['**'][78][3][1][3][2][19600] = '--alt-preset extreme'; // 3.90.3 1.199 + $known_encoder_values['**'][78][4][2][3][2][19500] = '--alt-preset fast extreme'; // 3.90, 3.90.1, 3.92 1.200 + $known_encoder_values['**'][78][4][2][3][2][19600] = '--alt-preset fast extreme'; // 3.90.2, 3.90.3, 3.91 1.201 + $known_encoder_values['**'][78][3][2][3][4][19000] = '--alt-preset standard'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 1.202 + $known_encoder_values['**'][78][3][1][3][4][19000] = '--alt-preset standard'; // 3.90.3 1.203 + $known_encoder_values['**'][78][4][2][3][4][19000] = '--alt-preset fast standard'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 1.204 + $known_encoder_values['**'][78][4][1][3][4][19000] = '--alt-preset fast standard'; // 3.90.3 1.205 + $known_encoder_values['**'][88][4][1][3][3][19500] = '--r3mix'; // 3.90, 3.90.1, 3.92 1.206 + $known_encoder_values['**'][88][4][1][3][3][19600] = '--r3mix'; // 3.90.2, 3.90.3, 3.91 1.207 + $known_encoder_values['**'][67][4][1][3][4][18000] = '--r3mix'; // 3.94, 3.95 1.208 + $known_encoder_values['**'][68][3][2][3][4][18000] = '--alt-preset medium'; // 3.90.3 1.209 + $known_encoder_values['**'][68][4][2][3][4][18000] = '--alt-preset fast medium'; // 3.90.3 1.210 + 1.211 + $known_encoder_values[0xFF][99][1][1][1][2][0] = '--preset studio'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 1.212 + $known_encoder_values[0xFF][58][2][1][3][2][20600] = '--preset studio'; // 3.90.3, 3.93.1 1.213 + $known_encoder_values[0xFF][58][2][1][3][2][20500] = '--preset studio'; // 3.93 1.214 + $known_encoder_values[0xFF][57][2][1][3][4][20500] = '--preset studio'; // 3.94, 3.95 1.215 + $known_encoder_values[0xC0][88][1][1][1][2][0] = '--preset cd'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 1.216 + $known_encoder_values[0xC0][58][2][2][3][2][19600] = '--preset cd'; // 3.90.3, 3.93.1 1.217 + $known_encoder_values[0xC0][58][2][2][3][2][19500] = '--preset cd'; // 3.93 1.218 + $known_encoder_values[0xC0][57][2][1][3][4][19500] = '--preset cd'; // 3.94, 3.95 1.219 + $known_encoder_values[0xA0][78][1][1][3][2][18000] = '--preset hifi'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 1.220 + $known_encoder_values[0xA0][58][2][2][3][2][18000] = '--preset hifi'; // 3.90.3, 3.93, 3.93.1 1.221 + $known_encoder_values[0xA0][57][2][1][3][4][18000] = '--preset hifi'; // 3.94, 3.95 1.222 + $known_encoder_values[0x80][67][1][1][3][2][18000] = '--preset tape'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 1.223 + $known_encoder_values[0x80][67][1][1][3][2][15000] = '--preset radio'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 1.224 + $known_encoder_values[0x70][67][1][1][3][2][15000] = '--preset fm'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 1.225 + $known_encoder_values[0x70][58][2][2][3][2][16000] = '--preset tape/radio/fm'; // 3.90.3, 3.93, 3.93.1 1.226 + $known_encoder_values[0x70][57][2][1][3][4][16000] = '--preset tape/radio/fm'; // 3.94, 3.95 1.227 + $known_encoder_values[0x38][58][2][2][0][2][10000] = '--preset voice'; // 3.90.3, 3.93, 3.93.1 1.228 + $known_encoder_values[0x38][57][2][1][0][4][15000] = '--preset voice'; // 3.94, 3.95 1.229 + $known_encoder_values[0x38][57][2][1][0][4][16000] = '--preset voice'; // 3.94a14 1.230 + $known_encoder_values[0x28][65][1][1][0][2][7500] = '--preset mw-us'; // 3.90, 3.90.1, 3.92 1.231 + $known_encoder_values[0x28][65][1][1][0][2][7600] = '--preset mw-us'; // 3.90.2, 3.91 1.232 + $known_encoder_values[0x28][58][2][2][0][2][7000] = '--preset mw-us'; // 3.90.3, 3.93, 3.93.1 1.233 + $known_encoder_values[0x28][57][2][1][0][4][10500] = '--preset mw-us'; // 3.94, 3.95 1.234 + $known_encoder_values[0x28][57][2][1][0][4][11200] = '--preset mw-us'; // 3.94a14 1.235 + $known_encoder_values[0x28][57][2][1][0][4][8800] = '--preset mw-us'; // 3.94a15 1.236 + $known_encoder_values[0x18][58][2][2][0][2][4000] = '--preset phon+/lw/mw-eu/sw'; // 3.90.3, 3.93.1 1.237 + $known_encoder_values[0x18][58][2][2][0][2][3900] = '--preset phon+/lw/mw-eu/sw'; // 3.93 1.238 + $known_encoder_values[0x18][57][2][1][0][4][5900] = '--preset phon+/lw/mw-eu/sw'; // 3.94, 3.95 1.239 + $known_encoder_values[0x18][57][2][1][0][4][6200] = '--preset phon+/lw/mw-eu/sw'; // 3.94a14 1.240 + $known_encoder_values[0x18][57][2][1][0][4][3200] = '--preset phon+/lw/mw-eu/sw'; // 3.94a15 1.241 + $known_encoder_values[0x10][58][2][2][0][2][3800] = '--preset phone'; // 3.90.3, 3.93.1 1.242 + $known_encoder_values[0x10][58][2][2][0][2][3700] = '--preset phone'; // 3.93 1.243 + $known_encoder_values[0x10][57][2][1][0][4][5600] = '--preset phone'; // 3.94, 3.95 1.244 + } 1.245 + 1.246 + if (isset($known_encoder_values[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) { 1.247 + 1.248 + $encoder_options = $known_encoder_values[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']]; 1.249 + 1.250 + } elseif (isset($known_encoder_values['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) { 1.251 + 1.252 + $encoder_options = $known_encoder_values['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']]; 1.253 + 1.254 + } elseif ($info['audio']['bitrate_mode'] == 'vbr') { 1.255 + 1.256 + // http://gabriel.mp3-tech.org/mp3infotag.html 1.257 + // int Quality = (100 - 10 * gfp->VBR_q - gfp->quality)h 1.258 + 1.259 + 1.260 + $lame_v_value = 10 - ceil($thisfile_mpeg_audio_lame['vbr_quality'] / 10); 1.261 + $lame_q_value = 100 - $thisfile_mpeg_audio_lame['vbr_quality'] - ($lame_v_value * 10); 1.262 + $encoder_options = '-V'.$lame_v_value.' -q'.$lame_q_value; 1.263 + 1.264 + } elseif ($info['audio']['bitrate_mode'] == 'cbr') { 1.265 + 1.266 + $encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000); 1.267 + 1.268 + } else { 1.269 + 1.270 + $encoder_options = strtoupper($info['audio']['bitrate_mode']); 1.271 + 1.272 + } 1.273 + 1.274 + } elseif (!empty($thisfile_mpeg_audio_lame['bitrate_abr'])) { 1.275 + 1.276 + $encoder_options = 'ABR'.$thisfile_mpeg_audio_lame['bitrate_abr']; 1.277 + 1.278 + } elseif (!empty($info['audio']['bitrate'])) { 1.279 + 1.280 + if ($info['audio']['bitrate_mode'] == 'cbr') { 1.281 + $encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000); 1.282 + } else { 1.283 + $encoder_options = strtoupper($info['audio']['bitrate_mode']); 1.284 + } 1.285 + 1.286 + } 1.287 + if (!empty($thisfile_mpeg_audio_lame['bitrate_min'])) { 1.288 + $encoder_options .= ' -b'.$thisfile_mpeg_audio_lame['bitrate_min']; 1.289 + } 1.290 + 1.291 + if (@$thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev'] || @$thisfile_mpeg_audio_lame['encoding_flags']['nogap_next']) { 1.292 + $encoder_options .= ' --nogap'; 1.293 + } 1.294 + 1.295 + if (!empty($thisfile_mpeg_audio_lame['lowpass_frequency'])) { 1.296 + $exploded_options = explode(' ', $encoder_options, 4); 1.297 + if ($exploded_options[0] == '--r3mix') { 1.298 + $exploded_options[1] = 'r3mix'; 1.299 + } 1.300 + switch ($exploded_options[0]) { 1.301 + case '--preset': 1.302 + case '--alt-preset': 1.303 + case '--r3mix': 1.304 + if ($exploded_options[1] == 'fast') { 1.305 + $exploded_options[1] .= ' '.$exploded_options[2]; 1.306 + } 1.307 + switch ($exploded_options[1]) { 1.308 + case 'portable': 1.309 + case 'medium': 1.310 + case 'standard': 1.311 + case 'extreme': 1.312 + case 'insane': 1.313 + case 'fast portable': 1.314 + case 'fast medium': 1.315 + case 'fast standard': 1.316 + case 'fast extreme': 1.317 + case 'fast insane': 1.318 + case 'r3mix': 1.319 + static $expected_lowpass = array ( 1.320 + 'insane|20500' => 20500, 1.321 + 'insane|20600' => 20600, // 3.90.2, 3.90.3, 3.91 1.322 + 'medium|18000' => 18000, 1.323 + 'fast medium|18000' => 18000, 1.324 + 'extreme|19500' => 19500, // 3.90, 3.90.1, 3.92, 3.95 1.325 + 'extreme|19600' => 19600, // 3.90.2, 3.90.3, 3.91, 3.93.1 1.326 + 'fast extreme|19500' => 19500, // 3.90, 3.90.1, 3.92, 3.95 1.327 + 'fast extreme|19600' => 19600, // 3.90.2, 3.90.3, 3.91, 3.93.1 1.328 + 'standard|19000' => 19000, 1.329 + 'fast standard|19000' => 19000, 1.330 + 'r3mix|19500' => 19500, // 3.90, 3.90.1, 3.92 1.331 + 'r3mix|19600' => 19600, // 3.90.2, 3.90.3, 3.91 1.332 + 'r3mix|18000' => 18000, // 3.94, 3.95 1.333 + ); 1.334 + if (!isset($expected_lowpass[$exploded_options[1].'|'.$thisfile_mpeg_audio_lame['lowpass_frequency']]) && ($thisfile_mpeg_audio_lame['lowpass_frequency'] < 22050) && (round($thisfile_mpeg_audio_lame['lowpass_frequency'] / 1000) < round($thisfile_mpeg_audio['sample_rate'] / 2000))) { 1.335 + $encoder_options .= ' --lowpass '.$thisfile_mpeg_audio_lame['lowpass_frequency']; 1.336 + } 1.337 + break; 1.338 + 1.339 + default: 1.340 + break; 1.341 + } 1.342 + break; 1.343 + } 1.344 + } 1.345 + 1.346 + if (isset($thisfile_mpeg_audio_lame['raw']['source_sample_freq'])) { 1.347 + if (($thisfile_mpeg_audio['sample_rate'] == 44100) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 1)) { 1.348 + $encoder_options .= ' --resample 44100'; 1.349 + } elseif (($thisfile_mpeg_audio['sample_rate'] == 48000) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 2)) { 1.350 + $encoder_options .= ' --resample 48000'; 1.351 + } elseif ($thisfile_mpeg_audio['sample_rate'] < 44100) { 1.352 + switch ($thisfile_mpeg_audio_lame['raw']['source_sample_freq']) { 1.353 + case 0: // <= 32000 1.354 + // may or may not be same as source frequency - ignore 1.355 + break; 1.356 + case 1: // 44100 1.357 + case 2: // 48000 1.358 + case 3: // 48000+ 1.359 + $exploded_options = explode(' ', $encoder_options, 4); 1.360 + switch ($exploded_options[0]) { 1.361 + case '--preset': 1.362 + case '--alt-preset': 1.363 + switch ($exploded_options[1]) { 1.364 + case 'fast': 1.365 + case 'portable': 1.366 + case 'medium': 1.367 + case 'standard': 1.368 + case 'extreme': 1.369 + case 'insane': 1.370 + $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate']; 1.371 + break; 1.372 + 1.373 + default: 1.374 + static $expected_resampled_rate = array ( 1.375 + 'phon+/lw/mw-eu/sw|16000' => 16000, 1.376 + 'mw-us|24000' => 24000, // 3.95 1.377 + 'mw-us|32000' => 32000, // 3.93 1.378 + 'mw-us|16000' => 16000, // 3.92 1.379 + 'phone|16000' => 16000, 1.380 + 'phone|11025' => 11025, // 3.94a15 1.381 + 'radio|32000' => 32000, // 3.94a15 1.382 + 'fm/radio|32000' => 32000, // 3.92 1.383 + 'fm|32000' => 32000, // 3.90 1.384 + 'voice|32000' => 32000); 1.385 + if (!isset($expected_resampled_rate[$exploded_options[1].'|'.$thisfile_mpeg_audio['sample_rate']])) { 1.386 + $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate']; 1.387 + } 1.388 + break; 1.389 + } 1.390 + break; 1.391 + 1.392 + case '--r3mix': 1.393 + default: 1.394 + $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate']; 1.395 + break; 1.396 + } 1.397 + break; 1.398 + } 1.399 + } 1.400 + } 1.401 + if (empty($encoder_options) && !empty($info['audio']['bitrate']) && !empty($info['audio']['bitrate_mode'])) { 1.402 + //$encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000); 1.403 + $encoder_options = strtoupper($info['audio']['bitrate_mode']); 1.404 + } 1.405 + 1.406 + return $encoder_options; 1.407 + } 1.408 + 1.409 + 1.410 + 1.411 + public function decodeMPEGaudioHeader($fd, $offset, &$info, $recursive_search=true, $scan_as_cbr=false, $fast_mpeg_header_scan=false) { 1.412 + 1.413 + static $mpeg_audio_version_lookup; 1.414 + static $mpeg_audio_layer_lookup; 1.415 + static $mpeg_audio_bitrate_lookup; 1.416 + static $mpeg_audio_frequency_lookup; 1.417 + static $mpeg_audio_channel_mode_lookup; 1.418 + static $mpeg_audio_mode_extension_lookup; 1.419 + static $mpeg_audio_emphasis_lookup; 1.420 + if (empty($mpeg_audio_version_lookup)) { 1.421 + $mpeg_audio_version_lookup = getid3_mp3::MPEGaudioVersionarray(); 1.422 + $mpeg_audio_layer_lookup = getid3_mp3::MPEGaudioLayerarray(); 1.423 + $mpeg_audio_bitrate_lookup = getid3_mp3::MPEGaudioBitratearray(); 1.424 + $mpeg_audio_frequency_lookup = getid3_mp3::MPEGaudioFrequencyarray(); 1.425 + $mpeg_audio_channel_mode_lookup = getid3_mp3::MPEGaudioChannelModearray(); 1.426 + $mpeg_audio_mode_extension_lookup = getid3_mp3::MPEGaudioModeExtensionarray(); 1.427 + $mpeg_audio_emphasis_lookup = getid3_mp3::MPEGaudioEmphasisarray(); 1.428 + } 1.429 + 1.430 + if ($offset >= $info['avdataend']) { 1.431 + 1.432 + // non-fatal error: 'end of file encounter looking for MPEG synch' 1.433 + return; 1.434 + 1.435 + } 1.436 + fseek($fd, $offset, SEEK_SET); 1.437 + $header_string = fread($fd, 226); // LAME header at offset 36 + 190 bytes of Xing/LAME data 1.438 + 1.439 + // MP3 audio frame structure: 1.440 + // $aa $aa $aa $aa [$bb $bb] $cc... 1.441 + // where $aa..$aa is the four-byte mpeg-audio header (below) 1.442 + // $bb $bb is the optional 2-byte CRC 1.443 + // and $cc... is the audio data 1.444 + 1.445 + $head4 = substr($header_string, 0, 4); 1.446 + 1.447 + if (isset($mpeg_audio_header_decode_cache[$head4])) { 1.448 + $mpeg_header_raw_array= $mpeg_audio_header_decode_cache[$head4]; 1.449 + } else { 1.450 + $mpeg_header_raw_array = getid3_mp3::MPEGaudioHeaderDecode($head4); 1.451 + $mpeg_audio_header_decode_cache[$head4] = $mpeg_header_raw_array; 1.452 + } 1.453 + 1.454 + // Not in cache 1.455 + if (!isset($mpeg_audio_header_valid_cache[$head4])) { 1.456 + $mpeg_audio_header_valid_cache[$head4] = getid3_mp3::MPEGaudioHeaderValid($mpeg_header_raw_array, false, false); 1.457 + } 1.458 + 1.459 + // shortcut 1.460 + if (!isset($info['mpeg']['audio'])) { 1.461 + $info['mpeg']['audio'] = array (); 1.462 + } 1.463 + $thisfile_mpeg_audio = &$info['mpeg']['audio']; 1.464 + 1.465 + 1.466 + if ($mpeg_audio_header_valid_cache[$head4]) { 1.467 + $thisfile_mpeg_audio['raw'] = $mpeg_header_raw_array; 1.468 + } else { 1.469 + 1.470 + // non-fatal error: Invalid MPEG audio header at offset $offset 1.471 + return; 1.472 + } 1.473 + 1.474 + if (!$fast_mpeg_header_scan) { 1.475 + 1.476 + $thisfile_mpeg_audio['version'] = $mpeg_audio_version_lookup[$thisfile_mpeg_audio['raw']['version']]; 1.477 + $thisfile_mpeg_audio['layer'] = $mpeg_audio_layer_lookup[$thisfile_mpeg_audio['raw']['layer']]; 1.478 + 1.479 + $thisfile_mpeg_audio['channelmode'] = $mpeg_audio_channel_mode_lookup[$thisfile_mpeg_audio['raw']['channelmode']]; 1.480 + $thisfile_mpeg_audio['channels'] = (($thisfile_mpeg_audio['channelmode'] == 'mono') ? 1 : 2); 1.481 + $thisfile_mpeg_audio['sample_rate'] = $mpeg_audio_frequency_lookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['raw']['sample_rate']]; 1.482 + $thisfile_mpeg_audio['protection'] = !$thisfile_mpeg_audio['raw']['protection']; 1.483 + $thisfile_mpeg_audio['private'] = (bool) $thisfile_mpeg_audio['raw']['private']; 1.484 + $thisfile_mpeg_audio['modeextension'] = $mpeg_audio_mode_extension_lookup[$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['modeextension']]; 1.485 + $thisfile_mpeg_audio['copyright'] = (bool) $thisfile_mpeg_audio['raw']['copyright']; 1.486 + $thisfile_mpeg_audio['original'] = (bool) $thisfile_mpeg_audio['raw']['original']; 1.487 + $thisfile_mpeg_audio['emphasis'] = $mpeg_audio_emphasis_lookup[$thisfile_mpeg_audio['raw']['emphasis']]; 1.488 + 1.489 + $info['audio']['channels'] = $thisfile_mpeg_audio['channels']; 1.490 + $info['audio']['sample_rate'] = $thisfile_mpeg_audio['sample_rate']; 1.491 + 1.492 + if ($thisfile_mpeg_audio['protection']) { 1.493 + $thisfile_mpeg_audio['crc'] = getid3_lib::BigEndian2Int(substr($header_string, 4, 2)); 1.494 + } 1.495 + 1.496 + } 1.497 + 1.498 + if ($thisfile_mpeg_audio['raw']['bitrate'] == 15) { 1.499 + // http://www.hydrogenaudio.org/?act=ST&f=16&t=9682&st=0 1.500 + $this->getid3->warning('Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1'); 1.501 + $thisfile_mpeg_audio['raw']['bitrate'] = 0; 1.502 + } 1.503 + $thisfile_mpeg_audio['padding'] = (bool) $thisfile_mpeg_audio['raw']['padding']; 1.504 + $thisfile_mpeg_audio['bitrate'] = $mpeg_audio_bitrate_lookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['bitrate']]; 1.505 + 1.506 + if (($thisfile_mpeg_audio['bitrate'] == 'free') && ($offset == $info['avdataoffset'])) { 1.507 + // only skip multiple frame check if free-format bitstream found at beginning of file 1.508 + // otherwise is quite possibly simply corrupted data 1.509 + $recursive_search = false; 1.510 + } 1.511 + 1.512 + // For Layer 2 there are some combinations of bitrate and mode which are not allowed. 1.513 + if (!$fast_mpeg_header_scan && ($thisfile_mpeg_audio['layer'] == '2')) { 1.514 + 1.515 + $info['audio']['dataformat'] = 'mp2'; 1.516 + switch ($thisfile_mpeg_audio['channelmode']) { 1.517 + 1.518 + case 'mono': 1.519 + if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] <= 192000)) { 1.520 + // these are ok 1.521 + } else { 1.522 + 1.523 + // non-fatal error: bitrate not allowed in Layer 2/mono 1.524 + return; 1.525 + } 1.526 + break; 1.527 + 1.528 + case 'stereo': 1.529 + case 'joint stereo': 1.530 + case 'dual channel': 1.531 + if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] == 64000) || ($thisfile_mpeg_audio['bitrate'] >= 96000)) { 1.532 + // these are ok 1.533 + } else { 1.534 + 1.535 + // non-fatal error: bitrate not allowed in Layer 2/stereo/joint stereo/dual channel 1.536 + return; 1.537 + } 1.538 + break; 1.539 + 1.540 + } 1.541 + 1.542 + } 1.543 + 1.544 + 1.545 + if ($info['audio']['sample_rate'] > 0) { 1.546 + $thisfile_mpeg_audio['framelength'] = getid3_mp3::MPEGaudioFrameLength($thisfile_mpeg_audio['bitrate'], $thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['layer'], (int) $thisfile_mpeg_audio['padding'], $info['audio']['sample_rate']); 1.547 + } 1.548 + 1.549 + $next_frame_test_offset = $offset + 1; 1.550 + if ($thisfile_mpeg_audio['bitrate'] != 'free') { 1.551 + 1.552 + $info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate']; 1.553 + 1.554 + if (isset($thisfile_mpeg_audio['framelength'])) { 1.555 + $next_frame_test_offset = $offset + $thisfile_mpeg_audio['framelength']; 1.556 + } else { 1.557 + 1.558 + // non-fatal error: Frame at offset('.$offset.') is has an invalid frame length. 1.559 + return; 1.560 + } 1.561 + 1.562 + } 1.563 + 1.564 + $expected_number_of_audio_bytes = 0; 1.565 + 1.566 + //////////////////////////////////////////////////////////////////////////////////// 1.567 + // Variable-bitrate headers 1.568 + 1.569 + if (substr($header_string, 4 + 32, 4) == 'VBRI') { 1.570 + // Fraunhofer VBR header is hardcoded 'VBRI' at offset 0x24 (36) 1.571 + // specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html 1.572 + 1.573 + $thisfile_mpeg_audio['bitrate_mode'] = 'vbr'; 1.574 + $thisfile_mpeg_audio['VBR_method'] = 'Fraunhofer'; 1.575 + $info['audio']['codec'] = 'Fraunhofer'; 1.576 + 1.577 + $side_info_data = substr($header_string, 4 + 2, 32); 1.578 + 1.579 + $fraunhofer_vbr_offset = 36; 1.580 + 1.581 + $thisfile_mpeg_audio['VBR_encoder_version'] = getid3_lib::BigEndian2Int(substr($header_string, $fraunhofer_vbr_offset + 4, 2)); // VbriVersion 1.582 + $thisfile_mpeg_audio['VBR_encoder_delay'] = getid3_lib::BigEndian2Int(substr($header_string, $fraunhofer_vbr_offset + 6, 2)); // VbriDelay 1.583 + $thisfile_mpeg_audio['VBR_quality'] = getid3_lib::BigEndian2Int(substr($header_string, $fraunhofer_vbr_offset + 8, 2)); // VbriQuality 1.584 + $thisfile_mpeg_audio['VBR_bytes'] = getid3_lib::BigEndian2Int(substr($header_string, $fraunhofer_vbr_offset + 10, 4)); // VbriStreamBytes 1.585 + $thisfile_mpeg_audio['VBR_frames'] = getid3_lib::BigEndian2Int(substr($header_string, $fraunhofer_vbr_offset + 14, 4)); // VbriStreamFrames 1.586 + $thisfile_mpeg_audio['VBR_seek_offsets'] = getid3_lib::BigEndian2Int(substr($header_string, $fraunhofer_vbr_offset + 18, 2)); // VbriTableSize 1.587 + $thisfile_mpeg_audio['VBR_seek_scale'] = getid3_lib::BigEndian2Int(substr($header_string, $fraunhofer_vbr_offset + 20, 2)); // VbriTableScale 1.588 + $thisfile_mpeg_audio['VBR_entry_bytes'] = getid3_lib::BigEndian2Int(substr($header_string, $fraunhofer_vbr_offset + 22, 2)); // VbriEntryBytes 1.589 + $thisfile_mpeg_audio['VBR_entry_frames'] = getid3_lib::BigEndian2Int(substr($header_string, $fraunhofer_vbr_offset + 24, 2)); // VbriEntryFrames 1.590 + 1.591 + $expected_number_of_audio_bytes = $thisfile_mpeg_audio['VBR_bytes']; 1.592 + 1.593 + $previous_byte_offset = $offset; 1.594 + for ($i = 0; $i < $thisfile_mpeg_audio['VBR_seek_offsets']; $i++) { 1.595 + $fraunhofer_offset_n = getid3_lib::BigEndian2Int(substr($header_string, $fraunhofer_vbr_offset, $thisfile_mpeg_audio['VBR_entry_bytes'])); 1.596 + $fraunhofer_vbr_offset += $thisfile_mpeg_audio['VBR_entry_bytes']; 1.597 + $thisfile_mpeg_audio['VBR_offsets_relative'][$i] = ($fraunhofer_offset_n * $thisfile_mpeg_audio['VBR_seek_scale']); 1.598 + $thisfile_mpeg_audio['VBR_offsets_absolute'][$i] = ($fraunhofer_offset_n * $thisfile_mpeg_audio['VBR_seek_scale']) + $previous_byte_offset; 1.599 + $previous_byte_offset += $fraunhofer_offset_n; 1.600 + } 1.601 + 1.602 + 1.603 + } else { 1.604 + 1.605 + // Xing VBR header is hardcoded 'Xing' at a offset 0x0D (13), 0x15 (21) or 0x24 (36) 1.606 + // depending on MPEG layer and number of channels 1.607 + 1.608 + $vbr_id_offset = getid3_mp3::XingVBRidOffset($thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['channelmode']); 1.609 + $side_info_data = substr($header_string, 4 + 2, $vbr_id_offset - 4); 1.610 + 1.611 + if ((substr($header_string, $vbr_id_offset, strlen('Xing')) == 'Xing') || (substr($header_string, $vbr_id_offset, strlen('Info')) == 'Info')) { 1.612 + // 'Xing' is traditional Xing VBR frame 1.613 + // 'Info' is LAME-encoded CBR (This was done to avoid CBR files to be recognized as traditional Xing VBR files by some decoders.) 1.614 + // 'Info' *can* legally be used to specify a VBR file as well, however. 1.615 + 1.616 + // http://www.multiweb.cz/twoinches/MP3inside.htm 1.617 + //00..03 = "Xing" or "Info" 1.618 + //04..07 = Flags: 1.619 + // 0x01 Frames Flag set if value for number of frames in file is stored 1.620 + // 0x02 Bytes Flag set if value for filesize in bytes is stored 1.621 + // 0x04 TOC Flag set if values for TOC are stored 1.622 + // 0x08 VBR Scale Flag set if values for VBR scale is stored 1.623 + //08..11 Frames: Number of frames in file (including the first Xing/Info one) 1.624 + //12..15 Bytes: File length in Bytes 1.625 + //16..115 TOC (Table of Contents): 1.626 + // Contains of 100 indexes (one Byte length) for easier lookup in file. Approximately solves problem with moving inside file. 1.627 + // Each Byte has a value according this formula: 1.628 + // (TOC[i] / 256) * fileLenInBytes 1.629 + // So if song lasts eg. 240 sec. and you want to jump to 60. sec. (and file is 5 000 000 Bytes length) you can use: 1.630 + // TOC[(60/240)*100] = TOC[25] 1.631 + // and corresponding Byte in file is then approximately at: 1.632 + // (TOC[25]/256) * 5000000 1.633 + //116..119 VBR Scale 1.634 + 1.635 + 1.636 + // should be safe to leave this at 'vbr' and let it be overriden to 'cbr' if a CBR preset/mode is used by LAME 1.637 + $thisfile_mpeg_audio['bitrate_mode'] = 'vbr'; 1.638 + $thisfile_mpeg_audio['VBR_method'] = 'Xing'; 1.639 + 1.640 + $thisfile_mpeg_audio['xing_flags_raw'] = getid3_lib::BigEndian2Int(substr($header_string, $vbr_id_offset + 4, 4)); 1.641 + 1.642 + $thisfile_mpeg_audio['xing_flags']['frames'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000001); 1.643 + $thisfile_mpeg_audio['xing_flags']['bytes'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000002); 1.644 + $thisfile_mpeg_audio['xing_flags']['toc'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000004); 1.645 + $thisfile_mpeg_audio['xing_flags']['vbr_scale'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000008); 1.646 + 1.647 + if ($thisfile_mpeg_audio['xing_flags']['frames']) { 1.648 + $thisfile_mpeg_audio['VBR_frames'] = getid3_lib::BigEndian2Int(substr($header_string, $vbr_id_offset + 8, 4)); 1.649 + } 1.650 + if ($thisfile_mpeg_audio['xing_flags']['bytes']) { 1.651 + $thisfile_mpeg_audio['VBR_bytes'] = getid3_lib::BigEndian2Int(substr($header_string, $vbr_id_offset + 12, 4)); 1.652 + } 1.653 + 1.654 + if (!empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) { 1.655 + 1.656 + $frame_lengthfloat = $thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']; 1.657 + 1.658 + if ($thisfile_mpeg_audio['layer'] == '1') { 1.659 + // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12 1.660 + $info['audio']['bitrate'] = ($frame_lengthfloat / 4) * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 12; 1.661 + } else { 1.662 + // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144 1.663 + $info['audio']['bitrate'] = $frame_lengthfloat * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 144; 1.664 + } 1.665 + $thisfile_mpeg_audio['framelength'] = floor($frame_lengthfloat); 1.666 + } 1.667 + 1.668 + if ($thisfile_mpeg_audio['xing_flags']['toc']) { 1.669 + $lame_toc_data = substr($header_string, $vbr_id_offset + 16, 100); 1.670 + for ($i = 0; $i < 100; $i++) { 1.671 + $thisfile_mpeg_audio['toc'][$i] = ord($lame_toc_data{$i}); 1.672 + } 1.673 + } 1.674 + if ($thisfile_mpeg_audio['xing_flags']['vbr_scale']) { 1.675 + $thisfile_mpeg_audio['VBR_scale'] = getid3_lib::BigEndian2Int(substr($header_string, $vbr_id_offset + 116, 4)); 1.676 + } 1.677 + 1.678 + 1.679 + // http://gabriel.mp3-tech.org/mp3infotag.html 1.680 + if (substr($header_string, $vbr_id_offset + 120, 4) == 'LAME') { 1.681 + 1.682 + // shortcut 1.683 + $thisfile_mpeg_audio['LAME'] = array (); 1.684 + $thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME']; 1.685 + 1.686 + 1.687 + $thisfile_mpeg_audio_lame['long_version'] = substr($header_string, $vbr_id_offset + 120, 20); 1.688 + $thisfile_mpeg_audio_lame['short_version'] = substr($thisfile_mpeg_audio_lame['long_version'], 0, 9); 1.689 + 1.690 + if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.90') { 1.691 + 1.692 + // extra 11 chars are not part of version string when LAMEtag present 1.693 + unset($thisfile_mpeg_audio_lame['long_version']); 1.694 + 1.695 + // It the LAME tag was only introduced in LAME v3.90 1.696 + // http://www.hydrogenaudio.org/?act=ST&f=15&t=9933 1.697 + 1.698 + // Offsets of various bytes in http://gabriel.mp3-tech.org/mp3infotag.html 1.699 + // are assuming a 'Xing' identifier offset of 0x24, which is the case for 1.700 + // MPEG-1 non-mono, but not for other combinations 1.701 + $lame_tag_offset_contant = $vbr_id_offset - 0x24; 1.702 + 1.703 + // shortcuts 1.704 + $thisfile_mpeg_audio_lame['RGAD'] = array ('track'=>array(), 'album'=>array()); 1.705 + $thisfile_mpeg_audio_lame_rgad = &$thisfile_mpeg_audio_lame['RGAD']; 1.706 + $thisfile_mpeg_audio_lame_rgad_track = &$thisfile_mpeg_audio_lame_rgad['track']; 1.707 + $thisfile_mpeg_audio_lame_rgad_album = &$thisfile_mpeg_audio_lame_rgad['album']; 1.708 + $thisfile_mpeg_audio_lame['raw'] = array (); 1.709 + $thisfile_mpeg_audio_lame_raw = &$thisfile_mpeg_audio_lame['raw']; 1.710 + 1.711 + // byte $9B VBR Quality 1.712 + // This field is there to indicate a quality level, although the scale was not precised in the original Xing specifications. 1.713 + // Actually overwrites original Xing bytes 1.714 + unset($thisfile_mpeg_audio['VBR_scale']); 1.715 + $thisfile_mpeg_audio_lame['vbr_quality'] = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0x9B, 1)); 1.716 + 1.717 + // bytes $9C-$A4 Encoder short VersionString 1.718 + $thisfile_mpeg_audio_lame['short_version'] = substr($header_string, $lame_tag_offset_contant + 0x9C, 9); 1.719 + 1.720 + // byte $A5 Info Tag revision + VBR method 1.721 + $lame_tagRevisionVBRmethod = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xA5, 1)); 1.722 + 1.723 + $thisfile_mpeg_audio_lame['tag_revision'] = ($lame_tagRevisionVBRmethod & 0xF0) >> 4; 1.724 + $thisfile_mpeg_audio_lame_raw['vbr_method'] = $lame_tagRevisionVBRmethod & 0x0F; 1.725 + $thisfile_mpeg_audio_lame['vbr_method'] = getid3_mp3::LAMEvbrMethodLookup($thisfile_mpeg_audio_lame_raw['vbr_method']); 1.726 + $thisfile_mpeg_audio['bitrate_mode'] = substr($thisfile_mpeg_audio_lame['vbr_method'], 0, 3); // usually either 'cbr' or 'vbr', but truncates 'vbr-old / vbr-rh' to 'vbr' 1.727 + 1.728 + // byte $A6 Lowpass filter value 1.729 + $thisfile_mpeg_audio_lame['lowpass_frequency'] = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xA6, 1)) * 100; 1.730 + 1.731 + // bytes $A7-$AE Replay Gain 1.732 + // http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html 1.733 + // bytes $A7-$AA : 32 bit floating point "Peak signal amplitude" 1.734 + if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.94b') { 1.735 + // LAME 3.94a16 and later - 9.23 fixed point 1.736 + // ie 0x0059E2EE / (2^23) = 5890798 / 8388608 = 0.7022378444671630859375 1.737 + $thisfile_mpeg_audio_lame_rgad['peak_amplitude'] = (float) ((getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xA7, 4))) / 8388608); 1.738 + } else { 1.739 + // LAME 3.94a15 and earlier - 32-bit floating point 1.740 + // Actually 3.94a16 will fall in here too and be WRONG, but is hard to detect 3.94a16 vs 3.94a15 1.741 + $thisfile_mpeg_audio_lame_rgad['peak_amplitude'] = getid3_lib::LittleEndian2Float(substr($header_string, $lame_tag_offset_contant + 0xA7, 4)); 1.742 + } 1.743 + if ($thisfile_mpeg_audio_lame_rgad['peak_amplitude'] == 0) { 1.744 + unset($thisfile_mpeg_audio_lame_rgad['peak_amplitude']); 1.745 + } else { 1.746 + $thisfile_mpeg_audio_lame_rgad['peak_db'] = 20 * log10($thisfile_mpeg_audio_lame_rgad['peak_amplitude']); 1.747 + } 1.748 + 1.749 + $thisfile_mpeg_audio_lame_raw['RGAD_track'] = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xAB, 2)); 1.750 + $thisfile_mpeg_audio_lame_raw['RGAD_album'] = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xAD, 2)); 1.751 + 1.752 + 1.753 + if ($thisfile_mpeg_audio_lame_raw['RGAD_track'] != 0) { 1.754 + 1.755 + $thisfile_mpeg_audio_lame_rgad_track['raw']['name'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0xE000) >> 13; 1.756 + $thisfile_mpeg_audio_lame_rgad_track['raw']['originator'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x1C00) >> 10; 1.757 + $thisfile_mpeg_audio_lame_rgad_track['raw']['sign_bit'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x0200) >> 9; 1.758 + $thisfile_mpeg_audio_lame_rgad_track['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x01FF; 1.759 + $thisfile_mpeg_audio_lame_rgad_track['name'] = getid3_lib_replaygain::NameLookup($thisfile_mpeg_audio_lame_rgad_track['raw']['name']); 1.760 + $thisfile_mpeg_audio_lame_rgad_track['originator'] = getid3_lib_replaygain::OriginatorLookup($thisfile_mpeg_audio_lame_rgad_track['raw']['originator']); 1.761 + $thisfile_mpeg_audio_lame_rgad_track['gain_db'] = getid3_lib_replaygain::AdjustmentLookup($thisfile_mpeg_audio_lame_rgad_track['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_rgad_track['raw']['sign_bit']); 1.762 + 1.763 + if (!empty($thisfile_mpeg_audio_lame_rgad['peak_amplitude'])) { 1.764 + $info['replay_gain']['track']['peak'] = $thisfile_mpeg_audio_lame_rgad['peak_amplitude']; 1.765 + } 1.766 + $info['replay_gain']['track']['originator'] = $thisfile_mpeg_audio_lame_rgad_track['originator']; 1.767 + $info['replay_gain']['track']['adjustment'] = $thisfile_mpeg_audio_lame_rgad_track['gain_db']; 1.768 + } else { 1.769 + unset($thisfile_mpeg_audio_lame_rgad['track']); 1.770 + } 1.771 + if ($thisfile_mpeg_audio_lame_raw['RGAD_album'] != 0) { 1.772 + 1.773 + $thisfile_mpeg_audio_lame_rgad_album['raw']['name'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0xE000) >> 13; 1.774 + $thisfile_mpeg_audio_lame_rgad_album['raw']['originator'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x1C00) >> 10; 1.775 + $thisfile_mpeg_audio_lame_rgad_album['raw']['sign_bit'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x0200) >> 9; 1.776 + $thisfile_mpeg_audio_lame_rgad_album['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x01FF; 1.777 + $thisfile_mpeg_audio_lame_rgad_album['name'] = getid3_lib_replaygain::NameLookup($thisfile_mpeg_audio_lame_rgad_album['raw']['name']); 1.778 + $thisfile_mpeg_audio_lame_rgad_album['originator'] = getid3_lib_replaygain::OriginatorLookup($thisfile_mpeg_audio_lame_rgad_album['raw']['originator']); 1.779 + $thisfile_mpeg_audio_lame_rgad_album['gain_db'] = getid3_lib_replaygain::AdjustmentLookup($thisfile_mpeg_audio_lame_rgad_album['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_rgad_album['raw']['sign_bit']); 1.780 + 1.781 + if (!empty($thisfile_mpeg_audio_lame_rgad['peak_amplitude'])) { 1.782 + $info['replay_gain']['album']['peak'] = $thisfile_mpeg_audio_lame_rgad['peak_amplitude']; 1.783 + } 1.784 + $info['replay_gain']['album']['originator'] = $thisfile_mpeg_audio_lame_rgad_album['originator']; 1.785 + $info['replay_gain']['album']['adjustment'] = $thisfile_mpeg_audio_lame_rgad_album['gain_db']; 1.786 + } else { 1.787 + unset($thisfile_mpeg_audio_lame_rgad['album']); 1.788 + } 1.789 + if (empty($thisfile_mpeg_audio_lame_rgad)) { 1.790 + unset($thisfile_mpeg_audio_lame['RGAD']); 1.791 + } 1.792 + 1.793 + 1.794 + // byte $AF Encoding flags + ATH Type 1.795 + $encoding_flags_ath_type = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xAF, 1)); 1.796 + $thisfile_mpeg_audio_lame['encoding_flags']['nspsytune'] = (bool) ($encoding_flags_ath_type & 0x10); 1.797 + $thisfile_mpeg_audio_lame['encoding_flags']['nssafejoint'] = (bool) ($encoding_flags_ath_type & 0x20); 1.798 + $thisfile_mpeg_audio_lame['encoding_flags']['nogap_next'] = (bool) ($encoding_flags_ath_type & 0x40); 1.799 + $thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev'] = (bool) ($encoding_flags_ath_type & 0x80); 1.800 + $thisfile_mpeg_audio_lame['ath_type'] = $encoding_flags_ath_type & 0x0F; 1.801 + 1.802 + // byte $B0 if ABR {specified bitrate} else {minimal bitrate} 1.803 + $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xB0, 1)); 1.804 + if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 2) { // Average BitRate (ABR) 1.805 + $thisfile_mpeg_audio_lame['bitrate_abr'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']; 1.806 + } elseif ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) { // Constant BitRate (CBR) 1.807 + // ignore 1.808 + } elseif ($thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] > 0) { // Variable BitRate (VBR) - minimum bitrate 1.809 + $thisfile_mpeg_audio_lame['bitrate_min'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']; 1.810 + } 1.811 + 1.812 + // bytes $B1-$B3 Encoder delays 1.813 + $encoder_delays = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xB1, 3)); 1.814 + $thisfile_mpeg_audio_lame['encoder_delay'] = ($encoder_delays & 0xFFF000) >> 12; 1.815 + $thisfile_mpeg_audio_lame['end_padding'] = $encoder_delays & 0x000FFF; 1.816 + 1.817 + // byte $B4 Misc 1.818 + $misc_byte = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xB4, 1)); 1.819 + $thisfile_mpeg_audio_lame_raw['noise_shaping'] = ($misc_byte & 0x03); 1.820 + $thisfile_mpeg_audio_lame_raw['stereo_mode'] = ($misc_byte & 0x1C) >> 2; 1.821 + $thisfile_mpeg_audio_lame_raw['not_optimal_quality'] = ($misc_byte & 0x20) >> 5; 1.822 + $thisfile_mpeg_audio_lame_raw['source_sample_freq'] = ($misc_byte & 0xC0) >> 6; 1.823 + $thisfile_mpeg_audio_lame['noise_shaping'] = $thisfile_mpeg_audio_lame_raw['noise_shaping']; 1.824 + $thisfile_mpeg_audio_lame['stereo_mode'] = getid3_mp3::LAMEmiscStereoModeLookup($thisfile_mpeg_audio_lame_raw['stereo_mode']); 1.825 + $thisfile_mpeg_audio_lame['not_optimal_quality'] = (bool) $thisfile_mpeg_audio_lame_raw['not_optimal_quality']; 1.826 + $thisfile_mpeg_audio_lame['source_sample_freq'] = getid3_mp3::LAMEmiscSourceSampleFrequencyLookup($thisfile_mpeg_audio_lame_raw['source_sample_freq']); 1.827 + 1.828 + // byte $B5 MP3 Gain 1.829 + $thisfile_mpeg_audio_lame_raw['mp3_gain'] = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xB5, 1), false, true); 1.830 + $thisfile_mpeg_audio_lame['mp3_gain_db'] = (20 * log10(2) / 4) * $thisfile_mpeg_audio_lame_raw['mp3_gain']; 1.831 + $thisfile_mpeg_audio_lame['mp3_gain_factor'] = pow(2, ($thisfile_mpeg_audio_lame['mp3_gain_db'] / 6)); 1.832 + 1.833 + // bytes $B6-$B7 Preset and surround info 1.834 + $PresetSurroundBytes = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xB6, 2)); 1.835 + // Reserved = ($PresetSurroundBytes & 0xC000); 1.836 + $thisfile_mpeg_audio_lame_raw['surround_info'] = ($PresetSurroundBytes & 0x3800); 1.837 + $thisfile_mpeg_audio_lame['surround_info'] = getid3_mp3::LAMEsurroundInfoLookup($thisfile_mpeg_audio_lame_raw['surround_info']); 1.838 + $thisfile_mpeg_audio_lame['preset_used_id'] = ($PresetSurroundBytes & 0x07FF); 1.839 + $thisfile_mpeg_audio_lame['preset_used'] = getid3_mp3::LAMEpresetUsedLookup($thisfile_mpeg_audio_lame); 1.840 + if (!empty($thisfile_mpeg_audio_lame['preset_used_id']) && empty($thisfile_mpeg_audio_lame['preset_used'])) { 1.841 + $this->getid3->warning('Unknown LAME preset used ('.$thisfile_mpeg_audio_lame['preset_used_id'].') - please report to info@getid3.org'); 1.842 + } 1.843 + if (($thisfile_mpeg_audio_lame['short_version'] == 'LAME3.90.') && !empty($thisfile_mpeg_audio_lame['preset_used_id'])) { 1.844 + // this may change if 3.90.4 ever comes out 1.845 + $thisfile_mpeg_audio_lame['short_version'] = 'LAME3.90.3'; 1.846 + } 1.847 + 1.848 + // bytes $B8-$BB MusicLength 1.849 + $thisfile_mpeg_audio_lame['audio_bytes'] = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xB8, 4)); 1.850 + $expected_number_of_audio_bytes = (($thisfile_mpeg_audio_lame['audio_bytes'] > 0) ? $thisfile_mpeg_audio_lame['audio_bytes'] : $thisfile_mpeg_audio['VBR_bytes']); 1.851 + 1.852 + // bytes $BC-$BD MusicCRC 1.853 + $thisfile_mpeg_audio_lame['music_crc'] = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xBC, 2)); 1.854 + 1.855 + // bytes $BE-$BF CRC-16 of Info Tag 1.856 + $thisfile_mpeg_audio_lame['lame_tag_crc'] = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xBE, 2)); 1.857 + 1.858 + 1.859 + // LAME CBR 1.860 + if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) { 1.861 + 1.862 + $thisfile_mpeg_audio['bitrate_mode'] = 'cbr'; 1.863 + $thisfile_mpeg_audio['bitrate'] = getid3_mp3::ClosestStandardMP3Bitrate($thisfile_mpeg_audio['bitrate']); 1.864 + $info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate']; 1.865 + 1.866 + } 1.867 + 1.868 + } 1.869 + } 1.870 + 1.871 + } else { 1.872 + 1.873 + // not Fraunhofer or Xing VBR methods, most likely CBR (but could be VBR with no header) 1.874 + $thisfile_mpeg_audio['bitrate_mode'] = 'cbr'; 1.875 + if ($recursive_search) { 1.876 + $thisfile_mpeg_audio['bitrate_mode'] = 'vbr'; 1.877 + if (getid3_mp3::RecursiveFrameScanning($fd, $info, $offset, $next_frame_test_offset, true)) { 1.878 + $recursive_search = false; 1.879 + $thisfile_mpeg_audio['bitrate_mode'] = 'cbr'; 1.880 + } 1.881 + if ($thisfile_mpeg_audio['bitrate_mode'] == 'vbr') { 1.882 + $this->getid3->warning('VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.'); 1.883 + } 1.884 + } 1.885 + 1.886 + } 1.887 + 1.888 + } 1.889 + 1.890 + if (($expected_number_of_audio_bytes > 0) && ($expected_number_of_audio_bytes != ($info['avdataend'] - $info['avdataoffset']))) { 1.891 + if ($expected_number_of_audio_bytes > ($info['avdataend'] - $info['avdataoffset'])) { 1.892 + if (($expected_number_of_audio_bytes - ($info['avdataend'] - $info['avdataoffset'])) == 1) { 1.893 + $this->getid3->warning('Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)'); 1.894 + } else { 1.895 + $this->getid3->warning('Probable truncated file: expecting '.$expected_number_of_audio_bytes.' bytes of audio data, only found '.($info['avdataend'] - $info['avdataoffset']).' (short by '.($expected_number_of_audio_bytes - ($info['avdataend'] - $info['avdataoffset'])).' bytes)'); 1.896 + } 1.897 + } else { 1.898 + if ((($info['avdataend'] - $info['avdataoffset']) - $expected_number_of_audio_bytes) == 1) { 1.899 + $info['avdataend']--; 1.900 + } else { 1.901 + $this->getid3->warning('Too much data in file: expecting '.$expected_number_of_audio_bytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $expected_number_of_audio_bytes).' bytes too many)'); 1.902 + } 1.903 + } 1.904 + } 1.905 + 1.906 + if (($thisfile_mpeg_audio['bitrate'] == 'free') && empty($info['audio']['bitrate'])) { 1.907 + if (($offset == $info['avdataoffset']) && empty($thisfile_mpeg_audio['VBR_frames'])) { 1.908 + $frame_byte_length = getid3_mp3::FreeFormatFrameLength($fd, $offset, $info, true); 1.909 + if ($frame_byte_length > 0) { 1.910 + $thisfile_mpeg_audio['framelength'] = $frame_byte_length; 1.911 + if ($thisfile_mpeg_audio['layer'] == '1') { 1.912 + // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12 1.913 + $info['audio']['bitrate'] = ((($frame_byte_length / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12; 1.914 + } else { 1.915 + // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144 1.916 + $info['audio']['bitrate'] = (($frame_byte_length - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144; 1.917 + } 1.918 + } else { 1.919 + 1.920 + // non-fatal error: Error calculating frame length of free-format MP3 without Xing/LAME header. 1.921 + return; 1.922 + } 1.923 + } 1.924 + } 1.925 + 1.926 + if (@$thisfile_mpeg_audio['VBR_frames']) { 1.927 + switch ($thisfile_mpeg_audio['bitrate_mode']) { 1.928 + case 'vbr': 1.929 + case 'abr': 1.930 + if (($thisfile_mpeg_audio['version'] == '1') && ($thisfile_mpeg_audio['layer'] == 1)) { 1.931 + $thisfile_mpeg_audio['VBR_bitrate'] = ((@$thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($info['audio']['sample_rate'] / 384); 1.932 + } elseif ((($thisfile_mpeg_audio['version'] == '2') || ($thisfile_mpeg_audio['version'] == '2.5')) && ($thisfile_mpeg_audio['layer'] == 3)) { 1.933 + $thisfile_mpeg_audio['VBR_bitrate'] = ((@$thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($info['audio']['sample_rate'] / 576); 1.934 + } else { 1.935 + $thisfile_mpeg_audio['VBR_bitrate'] = ((@$thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($info['audio']['sample_rate'] / 1152); 1.936 + } 1.937 + if ($thisfile_mpeg_audio['VBR_bitrate'] > 0) { 1.938 + $info['audio']['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; 1.939 + $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; // to avoid confusion 1.940 + } 1.941 + break; 1.942 + } 1.943 + } 1.944 + 1.945 + // End variable-bitrate headers 1.946 + //////////////////////////////////////////////////////////////////////////////////// 1.947 + 1.948 + if ($recursive_search) { 1.949 + 1.950 + if (!getid3_mp3::RecursiveFrameScanning($fd, $info, $offset, $next_frame_test_offset, $scan_as_cbr)) { 1.951 + return false; 1.952 + } 1.953 + 1.954 + } 1.955 + 1.956 + return true; 1.957 + } 1.958 + 1.959 + 1.960 + 1.961 + public function RecursiveFrameScanning(&$fd, &$info, &$offset, &$next_frame_test_offset, $scan_as_cbr) { 1.962 + for ($i = 0; $i < getid3_mp3::VALID_CHECK_FRAMES; $i++) { 1.963 + // check next getid3_mp3::VALID_CHECK_FRAMES frames for validity, to make sure we haven't run across a false synch 1.964 + if (($next_frame_test_offset + 4) >= $info['avdataend']) { 1.965 + // end of file 1.966 + return true; 1.967 + } 1.968 + 1.969 + $next_frame_test_array = array ('avdataend' => $info['avdataend'], 'avdataoffset' => $info['avdataoffset']); 1.970 + if ($this->decodeMPEGaudioHeader($fd, $next_frame_test_offset, $next_frame_test_array, false)) { 1.971 + if ($scan_as_cbr) { 1.972 + // force CBR mode, used for trying to pick out invalid audio streams with 1.973 + // valid(?) VBR headers, or VBR streams with no VBR header 1.974 + if (!isset($next_frame_test_array['mpeg']['audio']['bitrate']) || !isset($info['mpeg']['audio']['bitrate']) || ($next_frame_test_array['mpeg']['audio']['bitrate'] != $info['mpeg']['audio']['bitrate'])) { 1.975 + return false; 1.976 + } 1.977 + } 1.978 + 1.979 + 1.980 + // next frame is OK, get ready to check the one after that 1.981 + if (isset($next_frame_test_array['mpeg']['audio']['framelength']) && ($next_frame_test_array['mpeg']['audio']['framelength'] > 0)) { 1.982 + $next_frame_test_offset += $next_frame_test_array['mpeg']['audio']['framelength']; 1.983 + } else { 1.984 + 1.985 + // non-fatal error: Frame at offset $offset has an invalid frame length. 1.986 + return; 1.987 + } 1.988 + 1.989 + } else { 1.990 + 1.991 + // non-fatal error: Next frame is not valid. 1.992 + return; 1.993 + } 1.994 + } 1.995 + return true; 1.996 + } 1.997 + 1.998 + 1.999 + 1.1000 + public function FreeFormatFrameLength($fd, $offset, &$info, $deep_scan=false) { 1.1001 + fseek($fd, $offset, SEEK_SET); 1.1002 + $mpeg_audio_data = fread($fd, 32768); 1.1003 + 1.1004 + $sync_pattern1 = substr($mpeg_audio_data, 0, 4); 1.1005 + // may be different pattern due to padding 1.1006 + $sync_pattern2 = $sync_pattern1{0}.$sync_pattern1{1}.chr(ord($sync_pattern1{2}) | 0x02).$sync_pattern1{3}; 1.1007 + if ($sync_pattern2 === $sync_pattern1) { 1.1008 + $sync_pattern2 = $sync_pattern1{0}.$sync_pattern1{1}.chr(ord($sync_pattern1{2}) & 0xFD).$sync_pattern1{3}; 1.1009 + } 1.1010 + 1.1011 + $frame_length = false; 1.1012 + $frame_length1 = strpos($mpeg_audio_data, $sync_pattern1, 4); 1.1013 + $frame_length2 = strpos($mpeg_audio_data, $sync_pattern2, 4); 1.1014 + if ($frame_length1 > 4) { 1.1015 + $frame_length = $frame_length1; 1.1016 + } 1.1017 + if (($frame_length2 > 4) && ($frame_length2 < $frame_length1)) { 1.1018 + $frame_length = $frame_length2; 1.1019 + } 1.1020 + if (!$frame_length) { 1.1021 + 1.1022 + // LAME 3.88 has a different value for modeextension on the first frame vs the rest 1.1023 + $frame_length1 = strpos($mpeg_audio_data, substr($sync_pattern1, 0, 3), 4); 1.1024 + $frame_length2 = strpos($mpeg_audio_data, substr($sync_pattern2, 0, 3), 4); 1.1025 + 1.1026 + if ($frame_length1 > 4) { 1.1027 + $frame_length = $frame_length1; 1.1028 + } 1.1029 + if (($frame_length2 > 4) && ($frame_length2 < $frame_length1)) { 1.1030 + $frame_length = $frame_length2; 1.1031 + } 1.1032 + if (!$frame_length) { 1.1033 + throw new getid3_exception('Cannot find next free-format synch pattern ('.getid3_lib::PrintHexBytes($sync_pattern1).' or '.getid3_lib::PrintHexBytes($sync_pattern2).') after offset '.$offset); 1.1034 + } else { 1.1035 + $this->getid3->warning('ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)'); 1.1036 + $info['audio']['codec'] = 'LAME'; 1.1037 + $info['audio']['encoder'] = 'LAME3.88'; 1.1038 + $sync_pattern1 = substr($sync_pattern1, 0, 3); 1.1039 + $sync_pattern2 = substr($sync_pattern2, 0, 3); 1.1040 + } 1.1041 + } 1.1042 + 1.1043 + if ($deep_scan) { 1.1044 + 1.1045 + $actual_frame_length_values = array (); 1.1046 + $next_offset = $offset + $frame_length; 1.1047 + while ($next_offset < ($info['avdataend'] - 6)) { 1.1048 + fseek($fd, $next_offset - 1, SEEK_SET); 1.1049 + $NextSyncPattern = fread($fd, 6); 1.1050 + if ((substr($NextSyncPattern, 1, strlen($sync_pattern1)) == $sync_pattern1) || (substr($NextSyncPattern, 1, strlen($sync_pattern2)) == $sync_pattern2)) { 1.1051 + // good - found where expected 1.1052 + $actual_frame_length_values[] = $frame_length; 1.1053 + } elseif ((substr($NextSyncPattern, 0, strlen($sync_pattern1)) == $sync_pattern1) || (substr($NextSyncPattern, 0, strlen($sync_pattern2)) == $sync_pattern2)) { 1.1054 + // ok - found one byte earlier than expected (last frame wasn't padded, first frame was) 1.1055 + $actual_frame_length_values[] = ($frame_length - 1); 1.1056 + $next_offset--; 1.1057 + } elseif ((substr($NextSyncPattern, 2, strlen($sync_pattern1)) == $sync_pattern1) || (substr($NextSyncPattern, 2, strlen($sync_pattern2)) == $sync_pattern2)) { 1.1058 + // ok - found one byte later than expected (last frame was padded, first frame wasn't) 1.1059 + $actual_frame_length_values[] = ($frame_length + 1); 1.1060 + $next_offset++; 1.1061 + } else { 1.1062 + throw new getid3_exception('Did not find expected free-format sync pattern at offset '.$next_offset); 1.1063 + } 1.1064 + $next_offset += $frame_length; 1.1065 + } 1.1066 + if (count($actual_frame_length_values) > 0) { 1.1067 + $frame_length = intval(round(array_sum($actual_frame_length_values) / count($actual_frame_length_values))); 1.1068 + } 1.1069 + } 1.1070 + return $frame_length; 1.1071 + } 1.1072 + 1.1073 + 1.1074 + 1.1075 + public function getOnlyMPEGaudioInfo($fd, &$info, $avdata_offset, $bit_rate_histogram=false) { 1.1076 + 1.1077 + // looks for synch, decodes MPEG audio header 1.1078 + 1.1079 + fseek($fd, $avdata_offset, SEEK_SET); 1.1080 + 1.1081 + $sync_seek_buffer_size = min(128 * 1024, $info['avdataend'] - $avdata_offset); 1.1082 + $header = fread($fd, $sync_seek_buffer_size); 1.1083 + $sync_seek_buffer_size = strlen($header); 1.1084 + $synch_seek_offset = 0; 1.1085 + 1.1086 + static $mpeg_audio_version_lookup; 1.1087 + static $mpeg_audio_layer_lookup; 1.1088 + static $mpeg_audio_bitrate_lookup; 1.1089 + if (empty($mpeg_audio_version_lookup)) { 1.1090 + $mpeg_audio_version_lookup = getid3_mp3::MPEGaudioVersionarray(); 1.1091 + $mpeg_audio_layer_lookup = getid3_mp3::MPEGaudioLayerarray(); 1.1092 + $mpeg_audio_bitrate_lookup = getid3_mp3::MPEGaudioBitratearray(); 1.1093 + 1.1094 + } 1.1095 + 1.1096 + while ($synch_seek_offset < $sync_seek_buffer_size) { 1.1097 + 1.1098 + if ((($avdata_offset + $synch_seek_offset) < $info['avdataend']) && !feof($fd)) { 1.1099 + 1.1100 + // if a synch's not found within the first 128k bytes, then give up 1.1101 + if ($synch_seek_offset > $sync_seek_buffer_size) { 1.1102 + throw new getid3_exception('Could not find valid MPEG audio synch within the first '.round($sync_seek_buffer_size / 1024).'kB'); 1.1103 + } 1.1104 + 1.1105 + if (feof($fd)) { 1.1106 + throw new getid3_exception('Could not find valid MPEG audio synch before end of file'); 1.1107 + } 1.1108 + } 1.1109 + 1.1110 + if (($synch_seek_offset + 1) >= strlen($header)) { 1.1111 + throw new getid3_exception('Could not find valid MPEG synch before end of file'); 1.1112 + } 1.1113 + 1.1114 + if (($header{$synch_seek_offset} == "\xFF") && ($header{($synch_seek_offset + 1)} > "\xE0")) { // synch detected 1.1115 + 1.1116 + if (!isset($first_frame_info) && !isset($info['mpeg']['audio'])) { 1.1117 + $first_frame_info = $info; 1.1118 + $first_frame_avdata_offset = $avdata_offset + $synch_seek_offset; 1.1119 + if (!getid3_mp3::decodeMPEGaudioHeader($fd, $avdata_offset + $synch_seek_offset, $first_frame_info, false)) { 1.1120 + // if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's 1.1121 + // garbage between this frame and a valid sequence of MPEG-audio frames, to be restored below 1.1122 + unset($first_frame_info); 1.1123 + } 1.1124 + } 1.1125 + 1.1126 + $dummy = $info; // only overwrite real data if valid header found 1.1127 + if (getid3_mp3::decodeMPEGaudioHeader($fd, $avdata_offset + $synch_seek_offset, $dummy, true)) { 1.1128 + $info = $dummy; 1.1129 + $info['avdataoffset'] = $avdata_offset + $synch_seek_offset; 1.1130 + 1.1131 + switch (@$info['fileformat']) { 1.1132 + case '': 1.1133 + case 'mp3': 1.1134 + $info['fileformat'] = 'mp3'; 1.1135 + $info['audio']['dataformat'] = 'mp3'; 1.1136 + break; 1.1137 + } 1.1138 + if (isset($first_frame_info['mpeg']['audio']['bitrate_mode']) && ($first_frame_info['mpeg']['audio']['bitrate_mode'] == 'vbr')) { 1.1139 + if (!(abs($info['audio']['bitrate'] - $first_frame_info['audio']['bitrate']) <= 1)) { 1.1140 + // If there is garbage data between a valid VBR header frame and a sequence 1.1141 + // of valid MPEG-audio frames the VBR data is no longer discarded. 1.1142 + $info = $first_frame_info; 1.1143 + $info['avdataoffset'] = $first_frame_avdata_offset; 1.1144 + $info['fileformat'] = 'mp3'; 1.1145 + $info['audio']['dataformat'] = 'mp3'; 1.1146 + $dummy = $info; 1.1147 + unset($dummy['mpeg']['audio']); 1.1148 + $GarbageOffsetStart = $first_frame_avdata_offset + $first_frame_info['mpeg']['audio']['framelength']; 1.1149 + $GarbageOffsetEnd = $avdata_offset + $synch_seek_offset; 1.1150 + if (getid3_mp3::decodeMPEGaudioHeader($fd, $GarbageOffsetEnd, $dummy, true, true)) { 1.1151 + 1.1152 + $info = $dummy; 1.1153 + $info['avdataoffset'] = $GarbageOffsetEnd; 1.1154 + $this->getid3->warning('apparently-valid VBR header not used because could not find '.getid3_mp3::VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.'), but did find valid CBR stream starting at '.$GarbageOffsetEnd); 1.1155 + 1.1156 + } else { 1.1157 + 1.1158 + $this->getid3->warning('using data from VBR header even though could not find '.getid3_mp3::VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.')'); 1.1159 + 1.1160 + } 1.1161 + } 1.1162 + } 1.1163 + if (isset($info['mpeg']['audio']['bitrate_mode']) && ($info['mpeg']['audio']['bitrate_mode'] == 'vbr') && !isset($info['mpeg']['audio']['VBR_method'])) { 1.1164 + // VBR file with no VBR header 1.1165 + $bit_rate_histogram = true; 1.1166 + } 1.1167 + 1.1168 + if ($bit_rate_histogram) { 1.1169 + 1.1170 + $info['mpeg']['audio']['stereo_distribution'] = array ('stereo'=>0, 'joint stereo'=>0, 'dual channel'=>0, 'mono'=>0); 1.1171 + $info['mpeg']['audio']['version_distribution'] = array ('1'=>0, '2'=>0, '2.5'=>0); 1.1172 + 1.1173 + if ($info['mpeg']['audio']['version'] == '1') { 1.1174 + if ($info['mpeg']['audio']['layer'] == 3) { 1.1175 + $info['mpeg']['audio']['bitrate_distribution'] = array ('free'=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0); 1.1176 + } elseif ($info['mpeg']['audio']['layer'] == 2) { 1.1177 + $info['mpeg']['audio']['bitrate_distribution'] = array ('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0, 384000=>0); 1.1178 + } elseif ($info['mpeg']['audio']['layer'] == 1) { 1.1179 + $info['mpeg']['audio']['bitrate_distribution'] = array ('free'=>0, 32000=>0, 64000=>0, 96000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 288000=>0, 320000=>0, 352000=>0, 384000=>0, 416000=>0, 448000=>0); 1.1180 + } 1.1181 + } elseif ($info['mpeg']['audio']['layer'] == 1) { 1.1182 + $info['mpeg']['audio']['bitrate_distribution'] = array ('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0, 176000=>0, 192000=>0, 224000=>0, 256000=>0); 1.1183 + } else { 1.1184 + $info['mpeg']['audio']['bitrate_distribution'] = array ('free'=>0, 8000=>0, 16000=>0, 24000=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0); 1.1185 + } 1.1186 + 1.1187 + $dummy = array ('avdataend' => $info['avdataend'], 'avdataoffset' => $info['avdataoffset']); 1.1188 + $synch_start_offset = $info['avdataoffset']; 1.1189 + 1.1190 + $fast_mode = false; 1.1191 + $synch_errors_found = 0; 1.1192 + while ($this->decodeMPEGaudioHeader($fd, $synch_start_offset, $dummy, false, false, $fast_mode)) { 1.1193 + $fast_mode = true; 1.1194 + $thisframebitrate = $mpeg_audio_bitrate_lookup[$mpeg_audio_version_lookup[$dummy['mpeg']['audio']['raw']['version']]][$mpeg_audio_layer_lookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']]; 1.1195 + 1.1196 + if (empty($dummy['mpeg']['audio']['framelength'])) { 1.1197 + $synch_errors_found++; 1.1198 + } 1.1199 + else { 1.1200 + @$info['mpeg']['audio']['bitrate_distribution'][$thisframebitrate]++; 1.1201 + @$info['mpeg']['audio']['stereo_distribution'][$dummy['mpeg']['audio']['channelmode']]++; 1.1202 + @$info['mpeg']['audio']['version_distribution'][$dummy['mpeg']['audio']['version']]++; 1.1203 + 1.1204 + $synch_start_offset += $dummy['mpeg']['audio']['framelength']; 1.1205 + } 1.1206 + } 1.1207 + if ($synch_errors_found > 0) { 1.1208 + $this->getid3->warning('Found '.$synch_errors_found.' synch errors in histogram analysis'); 1.1209 + } 1.1210 + 1.1211 + $bit_total = 0; 1.1212 + $frame_counter = 0; 1.1213 + foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bit_rate_value => $bit_rate_count) { 1.1214 + $frame_counter += $bit_rate_count; 1.1215 + if ($bit_rate_value != 'free') { 1.1216 + $bit_total += ($bit_rate_value * $bit_rate_count); 1.1217 + } 1.1218 + } 1.1219 + if ($frame_counter == 0) { 1.1220 + throw new getid3_exception('Corrupt MP3 file: framecounter == zero'); 1.1221 + } 1.1222 + $info['mpeg']['audio']['frame_count'] = $frame_counter; 1.1223 + $info['mpeg']['audio']['bitrate'] = ($bit_total / $frame_counter); 1.1224 + 1.1225 + $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate']; 1.1226 + 1.1227 + 1.1228 + // Definitively set VBR vs CBR, even if the Xing/LAME/VBRI header says differently 1.1229 + $distinct_bit_rates = 0; 1.1230 + foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bit_rate_value => $bit_rate_count) { 1.1231 + if ($bit_rate_count > 0) { 1.1232 + $distinct_bit_rates++; 1.1233 + } 1.1234 + } 1.1235 + if ($distinct_bit_rates > 1) { 1.1236 + $info['mpeg']['audio']['bitrate_mode'] = 'vbr'; 1.1237 + } else { 1.1238 + $info['mpeg']['audio']['bitrate_mode'] = 'cbr'; 1.1239 + } 1.1240 + $info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode']; 1.1241 + 1.1242 + } 1.1243 + 1.1244 + break; // exit while() 1.1245 + } 1.1246 + } 1.1247 + 1.1248 + $synch_seek_offset++; 1.1249 + if (($avdata_offset + $synch_seek_offset) >= $info['avdataend']) { 1.1250 + // end of file/data 1.1251 + 1.1252 + if (empty($info['mpeg']['audio'])) { 1.1253 + 1.1254 + throw new getid3_exception('could not find valid MPEG synch before end of file'); 1.1255 + } 1.1256 + break; 1.1257 + } 1.1258 + 1.1259 + } 1.1260 + 1.1261 + $info['audio']['channels'] = $info['mpeg']['audio']['channels']; 1.1262 + $info['audio']['channelmode'] = $info['mpeg']['audio']['channelmode']; 1.1263 + $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate']; 1.1264 + return true; 1.1265 + } 1.1266 + 1.1267 + 1.1268 + 1.1269 + public static function MPEGaudioVersionarray() { 1.1270 + 1.1271 + static $array = array ('2.5', false, '2', '1'); 1.1272 + return $array; 1.1273 + } 1.1274 + 1.1275 + 1.1276 + 1.1277 + public static function MPEGaudioLayerarray() { 1.1278 + 1.1279 + static $array = array (false, 3, 2, 1); 1.1280 + return $array; 1.1281 + } 1.1282 + 1.1283 + 1.1284 + 1.1285 + public static function MPEGaudioBitratearray() { 1.1286 + 1.1287 + static $array; 1.1288 + if (empty($array)) { 1.1289 + $array = array ( 1.1290 + '1' => array (1 => array ('free', 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000), 1.1291 + 2 => array ('free', 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000), 1.1292 + 3 => array ('free', 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000) 1.1293 + ), 1.1294 + 1.1295 + '2' => array (1 => array ('free', 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000), 1.1296 + 2 => array ('free', 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000), 1.1297 + ) 1.1298 + ); 1.1299 + $array['2'][3] = $array['2'][2]; 1.1300 + $array['2.5'] = $array['2']; 1.1301 + } 1.1302 + return $array; 1.1303 + } 1.1304 + 1.1305 + 1.1306 + 1.1307 + public static function MPEGaudioFrequencyarray() { 1.1308 + 1.1309 + static $array = array ( 1.1310 + '1' => array (44100, 48000, 32000), 1.1311 + '2' => array (22050, 24000, 16000), 1.1312 + '2.5' => array (11025, 12000, 8000) 1.1313 + ); 1.1314 + return $array; 1.1315 + } 1.1316 + 1.1317 + 1.1318 + 1.1319 + public static function MPEGaudioChannelModearray() { 1.1320 + 1.1321 + static $array = array ('stereo', 'joint stereo', 'dual channel', 'mono'); 1.1322 + return $array; 1.1323 + } 1.1324 + 1.1325 + 1.1326 + 1.1327 + public static function MPEGaudioModeExtensionarray() { 1.1328 + 1.1329 + static $array = array ( 1.1330 + 1 => array ('4-31', '8-31', '12-31', '16-31'), 1.1331 + 2 => array ('4-31', '8-31', '12-31', '16-31'), 1.1332 + 3 => array ('', 'IS', 'MS', 'IS+MS') 1.1333 + ); 1.1334 + return $array; 1.1335 + } 1.1336 + 1.1337 + 1.1338 + 1.1339 + public static function MPEGaudioEmphasisarray() { 1.1340 + 1.1341 + static $array = array ('none', '50/15ms', false, 'CCIT J.17'); 1.1342 + return $array; 1.1343 + } 1.1344 + 1.1345 + 1.1346 + 1.1347 + public static function MPEGaudioHeaderBytesValid($head4, $allow_bitrate_15=false) { 1.1348 + 1.1349 + return getid3_mp3::MPEGaudioHeaderValid(getid3_mp3::MPEGaudioHeaderDecode($head4), false, $allow_bitrate_15); 1.1350 + } 1.1351 + 1.1352 + 1.1353 + 1.1354 + public static function MPEGaudioHeaderValid($raw_array, $echo_errors=false, $allow_bitrate_15=false) { 1.1355 + 1.1356 + if (($raw_array['synch'] & 0x0FFE) != 0x0FFE) { 1.1357 + return false; 1.1358 + } 1.1359 + 1.1360 + static $mpeg_audio_version_lookup; 1.1361 + static $mpeg_audio_layer_lookup; 1.1362 + static $mpeg_audio_bitrate_lookup; 1.1363 + static $mpeg_audio_frequency_lookup; 1.1364 + static $mpeg_audio_channel_mode_lookup; 1.1365 + static $mpeg_audio_mode_extension_lookup; 1.1366 + static $mpeg_audio_emphasis_lookup; 1.1367 + if (empty($mpeg_audio_version_lookup)) { 1.1368 + $mpeg_audio_version_lookup = getid3_mp3::MPEGaudioVersionarray(); 1.1369 + $mpeg_audio_layer_lookup = getid3_mp3::MPEGaudioLayerarray(); 1.1370 + $mpeg_audio_bitrate_lookup = getid3_mp3::MPEGaudioBitratearray(); 1.1371 + $mpeg_audio_frequency_lookup = getid3_mp3::MPEGaudioFrequencyarray(); 1.1372 + $mpeg_audio_channel_mode_lookup = getid3_mp3::MPEGaudioChannelModearray(); 1.1373 + $mpeg_audio_mode_extension_lookup = getid3_mp3::MPEGaudioModeExtensionarray(); 1.1374 + $mpeg_audio_emphasis_lookup = getid3_mp3::MPEGaudioEmphasisarray(); 1.1375 + } 1.1376 + 1.1377 + if (isset($mpeg_audio_version_lookup[$raw_array['version']])) { 1.1378 + $decodedVersion = $mpeg_audio_version_lookup[$raw_array['version']]; 1.1379 + } else { 1.1380 + echo ($echo_errors ? "\n".'invalid Version ('.$raw_array['version'].')' : ''); 1.1381 + return false; 1.1382 + } 1.1383 + if (isset($mpeg_audio_layer_lookup[$raw_array['layer']])) { 1.1384 + $decodedLayer = $mpeg_audio_layer_lookup[$raw_array['layer']]; 1.1385 + } else { 1.1386 + echo ($echo_errors ? "\n".'invalid Layer ('.$raw_array['layer'].')' : ''); 1.1387 + return false; 1.1388 + } 1.1389 + if (!isset($mpeg_audio_bitrate_lookup[$decodedVersion][$decodedLayer][$raw_array['bitrate']])) { 1.1390 + echo ($echo_errors ? "\n".'invalid Bitrate ('.$raw_array['bitrate'].')' : ''); 1.1391 + if ($raw_array['bitrate'] == 15) { 1.1392 + // known issue in LAME 3.90 - 3.93.1 where free-format has bitrate ID of 15 instead of 0 1.1393 + // let it go through here otherwise file will not be identified 1.1394 + if (!$allow_bitrate_15) { 1.1395 + return false; 1.1396 + } 1.1397 + } else { 1.1398 + return false; 1.1399 + } 1.1400 + } 1.1401 + if (!isset($mpeg_audio_frequency_lookup[$decodedVersion][$raw_array['sample_rate']])) { 1.1402 + echo ($echo_errors ? "\n".'invalid Frequency ('.$raw_array['sample_rate'].')' : ''); 1.1403 + return false; 1.1404 + } 1.1405 + if (!isset($mpeg_audio_channel_mode_lookup[$raw_array['channelmode']])) { 1.1406 + echo ($echo_errors ? "\n".'invalid ChannelMode ('.$raw_array['channelmode'].')' : ''); 1.1407 + return false; 1.1408 + } 1.1409 + if (!isset($mpeg_audio_mode_extension_lookup[$decodedLayer][$raw_array['modeextension']])) { 1.1410 + echo ($echo_errors ? "\n".'invalid Mode Extension ('.$raw_array['modeextension'].')' : ''); 1.1411 + return false; 1.1412 + } 1.1413 + if (!isset($mpeg_audio_emphasis_lookup[$raw_array['emphasis']])) { 1.1414 + echo ($echo_errors ? "\n".'invalid Emphasis ('.$raw_array['emphasis'].')' : ''); 1.1415 + return false; 1.1416 + } 1.1417 + // These are just either set or not set, you can't mess that up :) 1.1418 + // $raw_array['protection']; 1.1419 + // $raw_array['padding']; 1.1420 + // $raw_array['private']; 1.1421 + // $raw_array['copyright']; 1.1422 + // $raw_array['original']; 1.1423 + 1.1424 + return true; 1.1425 + } 1.1426 + 1.1427 + 1.1428 + 1.1429 + public static function MPEGaudioHeaderDecode($header_four_bytes) { 1.1430 + // AAAA AAAA AAAB BCCD EEEE FFGH IIJJ KLMM 1.1431 + // A - Frame sync (all bits set) 1.1432 + // B - MPEG Audio version ID 1.1433 + // C - Layer description 1.1434 + // D - Protection bit 1.1435 + // E - Bitrate index 1.1436 + // F - Sampling rate frequency index 1.1437 + // G - Padding bit 1.1438 + // H - Private bit 1.1439 + // I - Channel Mode 1.1440 + // J - Mode extension (Only if Joint stereo) 1.1441 + // K - Copyright 1.1442 + // L - Original 1.1443 + // M - Emphasis 1.1444 + 1.1445 + if (strlen($header_four_bytes) != 4) { 1.1446 + return false; 1.1447 + } 1.1448 + 1.1449 + $mpeg_raw_header['synch'] = (getid3_lib::BigEndian2Int(substr($header_four_bytes, 0, 2)) & 0xFFE0) >> 4; 1.1450 + $mpeg_raw_header['version'] = (ord($header_four_bytes{1}) & 0x18) >> 3; // BB 1.1451 + $mpeg_raw_header['layer'] = (ord($header_four_bytes{1}) & 0x06) >> 1; // CC 1.1452 + $mpeg_raw_header['protection'] = (ord($header_four_bytes{1}) & 0x01); // D 1.1453 + $mpeg_raw_header['bitrate'] = (ord($header_four_bytes{2}) & 0xF0) >> 4; // EEEE 1.1454 + $mpeg_raw_header['sample_rate'] = (ord($header_four_bytes{2}) & 0x0C) >> 2; // FF 1.1455 + $mpeg_raw_header['padding'] = (ord($header_four_bytes{2}) & 0x02) >> 1; // G 1.1456 + $mpeg_raw_header['private'] = (ord($header_four_bytes{2}) & 0x01); // H 1.1457 + $mpeg_raw_header['channelmode'] = (ord($header_four_bytes{3}) & 0xC0) >> 6; // II 1.1458 + $mpeg_raw_header['modeextension'] = (ord($header_four_bytes{3}) & 0x30) >> 4; // JJ 1.1459 + $mpeg_raw_header['copyright'] = (ord($header_four_bytes{3}) & 0x08) >> 3; // K 1.1460 + $mpeg_raw_header['original'] = (ord($header_four_bytes{3}) & 0x04) >> 2; // L 1.1461 + $mpeg_raw_header['emphasis'] = (ord($header_four_bytes{3}) & 0x03); // MM 1.1462 + 1.1463 + return $mpeg_raw_header; 1.1464 + } 1.1465 + 1.1466 + 1.1467 + 1.1468 + public static function MPEGaudioFrameLength(&$bit_rate, &$version, &$layer, $padding, &$sample_rate) { 1.1469 + 1.1470 + if (!isset($cache[$bit_rate][$version][$layer][$padding][$sample_rate])) { 1.1471 + $cache[$bit_rate][$version][$layer][$padding][$sample_rate] = false; 1.1472 + if ($bit_rate != 'free') { 1.1473 + 1.1474 + if ($version == '1') { 1.1475 + 1.1476 + if ($layer == '1') { 1.1477 + 1.1478 + // For Layer I slot is 32 bits long 1.1479 + $frame_length_coefficient = 48; 1.1480 + $slot_length = 4; 1.1481 + 1.1482 + } else { // Layer 2 / 3 1.1483 + 1.1484 + // for Layer 2 and Layer 3 slot is 8 bits long. 1.1485 + $frame_length_coefficient = 144; 1.1486 + $slot_length = 1; 1.1487 + 1.1488 + } 1.1489 + 1.1490 + } else { // MPEG-2 / MPEG-2.5 1.1491 + 1.1492 + if ($layer == '1') { 1.1493 + 1.1494 + // For Layer I slot is 32 bits long 1.1495 + $frame_length_coefficient = 24; 1.1496 + $slot_length = 4; 1.1497 + 1.1498 + } elseif ($layer == '2') { 1.1499 + 1.1500 + // for Layer 2 and Layer 3 slot is 8 bits long. 1.1501 + $frame_length_coefficient = 144; 1.1502 + $slot_length = 1; 1.1503 + 1.1504 + } else { // layer 3 1.1505 + 1.1506 + // for Layer 2 and Layer 3 slot is 8 bits long. 1.1507 + $frame_length_coefficient = 72; 1.1508 + $slot_length = 1; 1.1509 + 1.1510 + } 1.1511 + 1.1512 + } 1.1513 + 1.1514 + // FrameLengthInBytes = ((Coefficient * BitRate) / SampleRate) + Padding 1.1515 + if ($sample_rate > 0) { 1.1516 + $new_frame_length = ($frame_length_coefficient * $bit_rate) / $sample_rate; 1.1517 + $new_frame_length = floor($new_frame_length / $slot_length) * $slot_length; // round to next-lower multiple of SlotLength (1 byte for Layer 2/3, 4 bytes for Layer I) 1.1518 + if ($padding) { 1.1519 + $new_frame_length += $slot_length; 1.1520 + } 1.1521 + $cache[$bit_rate][$version][$layer][$padding][$sample_rate] = (int) $new_frame_length; 1.1522 + } 1.1523 + } 1.1524 + } 1.1525 + return $cache[$bit_rate][$version][$layer][$padding][$sample_rate]; 1.1526 + } 1.1527 + 1.1528 + 1.1529 + 1.1530 + public static function ClosestStandardMP3Bitrate($bit_rate) { 1.1531 + 1.1532 + static $standard_bit_rates = array (320000, 256000, 224000, 192000, 160000, 128000, 112000, 96000, 80000, 64000, 56000, 48000, 40000, 32000, 24000, 16000, 8000); 1.1533 + static $bit_rate_table = array (0=>'-'); 1.1534 + $round_bit_rate = intval(round($bit_rate, -3)); 1.1535 + if (!isset($bit_rate_table[$round_bit_rate])) { 1.1536 + if ($round_bit_rate > 320000) { 1.1537 + $bit_rate_table[$round_bit_rate] = round($bit_rate, -4); 1.1538 + } else { 1.1539 + $last_bit_rate = 320000; 1.1540 + foreach ($standard_bit_rates as $standard_bit_rate) { 1.1541 + $bit_rate_table[$round_bit_rate] = $standard_bit_rate; 1.1542 + if ($round_bit_rate >= $standard_bit_rate - (($last_bit_rate - $standard_bit_rate) / 2)) { 1.1543 + break; 1.1544 + } 1.1545 + $last_bit_rate = $standard_bit_rate; 1.1546 + } 1.1547 + } 1.1548 + } 1.1549 + return $bit_rate_table[$round_bit_rate]; 1.1550 + } 1.1551 + 1.1552 + 1.1553 + 1.1554 + public static function XingVBRidOffset($version, $channel_mode) { 1.1555 + 1.1556 + static $lookup = array ( 1.1557 + '1' => array ('mono' => 0x15, // 4 + 17 = 21 1.1558 + 'stereo' => 0x24, // 4 + 32 = 36 1.1559 + 'joint stereo' => 0x24, 1.1560 + 'dual channel' => 0x24 1.1561 + ), 1.1562 + 1.1563 + '2' => array ('mono' => 0x0D, // 4 + 9 = 13 1.1564 + 'stereo' => 0x15, // 4 + 17 = 21 1.1565 + 'joint stereo' => 0x15, 1.1566 + 'dual channel' => 0x15 1.1567 + ), 1.1568 + 1.1569 + '2.5' => array ('mono' => 0x15, 1.1570 + 'stereo' => 0x15, 1.1571 + 'joint stereo' => 0x15, 1.1572 + 'dual channel' => 0x15 1.1573 + ) 1.1574 + ); 1.1575 + 1.1576 + return $lookup[$version][$channel_mode]; 1.1577 + } 1.1578 + 1.1579 + 1.1580 + 1.1581 + public static function LAMEvbrMethodLookup($vbr_method_id) { 1.1582 + 1.1583 + static $lookup = array ( 1.1584 + 0x00 => 'unknown', 1.1585 + 0x01 => 'cbr', 1.1586 + 0x02 => 'abr', 1.1587 + 0x03 => 'vbr-old / vbr-rh', 1.1588 + 0x04 => 'vbr-new / vbr-mtrh', 1.1589 + 0x05 => 'vbr-mt', 1.1590 + 0x06 => 'Full VBR Method 4', 1.1591 + 0x08 => 'constant bitrate 2 pass', 1.1592 + 0x09 => 'abr 2 pass', 1.1593 + 0x0F => 'reserved' 1.1594 + ); 1.1595 + return (isset($lookup[$vbr_method_id]) ? $lookup[$vbr_method_id] : ''); 1.1596 + } 1.1597 + 1.1598 + 1.1599 + 1.1600 + public static function LAMEmiscStereoModeLookup($stereo_mode_id) { 1.1601 + 1.1602 + static $lookup = array ( 1.1603 + 0 => 'mono', 1.1604 + 1 => 'stereo', 1.1605 + 2 => 'dual mono', 1.1606 + 3 => 'joint stereo', 1.1607 + 4 => 'forced stereo', 1.1608 + 5 => 'auto', 1.1609 + 6 => 'intensity stereo', 1.1610 + 7 => 'other' 1.1611 + ); 1.1612 + return (isset($lookup[$stereo_mode_id]) ? $lookup[$stereo_mode_id] : ''); 1.1613 + } 1.1614 + 1.1615 + 1.1616 + 1.1617 + public static function LAMEmiscSourceSampleFrequencyLookup($source_sample_frequency_id) { 1.1618 + 1.1619 + static $lookup = array ( 1.1620 + 0 => '<= 32 kHz', 1.1621 + 1 => '44.1 kHz', 1.1622 + 2 => '48 kHz', 1.1623 + 3 => '> 48kHz' 1.1624 + ); 1.1625 + return (isset($lookup[$source_sample_frequency_id]) ? $lookup[$source_sample_frequency_id] : ''); 1.1626 + } 1.1627 + 1.1628 + 1.1629 + 1.1630 + public static function LAMEsurroundInfoLookup($surround_info_id) { 1.1631 + 1.1632 + static $lookup = array ( 1.1633 + 0 => 'no surround info', 1.1634 + 1 => 'DPL encoding', 1.1635 + 2 => 'DPL2 encoding', 1.1636 + 3 => 'Ambisonic encoding' 1.1637 + ); 1.1638 + return (isset($lookup[$surround_info_id]) ? $lookup[$surround_info_id] : 'reserved'); 1.1639 + } 1.1640 + 1.1641 + 1.1642 + 1.1643 + public static function LAMEpresetUsedLookup($lame_tag) { 1.1644 + 1.1645 + if ($lame_tag['preset_used_id'] == 0) { 1.1646 + // no preset used (LAME >=3.93) 1.1647 + // no preset recorded (LAME <3.93) 1.1648 + return ''; 1.1649 + } 1.1650 + 1.1651 + $lame_preset_used_lookup = array (); 1.1652 + 1.1653 + for ($i = 8; $i <= 320; $i++) { 1.1654 + switch ($lame_tag['vbr_method']) { 1.1655 + case 'cbr': 1.1656 + $lame_preset_used_lookup[$i] = '--alt-preset '.$lame_tag['vbr_method'].' '.$i; 1.1657 + break; 1.1658 + case 'abr': 1.1659 + default: // other VBR modes shouldn't be here(?) 1.1660 + $lame_preset_used_lookup[$i] = '--alt-preset '.$i; 1.1661 + break; 1.1662 + } 1.1663 + } 1.1664 + 1.1665 + // named old-style presets (studio, phone, voice, etc) are handled in GuessEncoderOptions() 1.1666 + 1.1667 + // named alt-presets 1.1668 + $lame_preset_used_lookup[1000] = '--r3mix'; 1.1669 + $lame_preset_used_lookup[1001] = '--alt-preset standard'; 1.1670 + $lame_preset_used_lookup[1002] = '--alt-preset extreme'; 1.1671 + $lame_preset_used_lookup[1003] = '--alt-preset insane'; 1.1672 + $lame_preset_used_lookup[1004] = '--alt-preset fast standard'; 1.1673 + $lame_preset_used_lookup[1005] = '--alt-preset fast extreme'; 1.1674 + $lame_preset_used_lookup[1006] = '--alt-preset medium'; 1.1675 + $lame_preset_used_lookup[1007] = '--alt-preset fast medium'; 1.1676 + 1.1677 + // LAME 3.94 additions/changes 1.1678 + $lame_preset_used_lookup[1010] = '--preset portable'; // 3.94a15 Oct 21 2003 1.1679 + $lame_preset_used_lookup[1015] = '--preset radio'; // 3.94a15 Oct 21 2003 1.1680 + 1.1681 + $lame_preset_used_lookup[320] = '--preset insane'; // 3.94a15 Nov 12 2003 1.1682 + $lame_preset_used_lookup[410] = '-V9'; 1.1683 + $lame_preset_used_lookup[420] = '-V8'; 1.1684 + $lame_preset_used_lookup[430] = '--preset radio'; // 3.94a15 Nov 12 2003 1.1685 + $lame_preset_used_lookup[440] = '-V6'; 1.1686 + $lame_preset_used_lookup[450] = '--preset '.(($lame_tag['raw']['vbr_method'] == 4) ? 'fast ' : '').'portable'; // 3.94a15 Nov 12 2003 1.1687 + $lame_preset_used_lookup[460] = '--preset '.(($lame_tag['raw']['vbr_method'] == 4) ? 'fast ' : '').'medium'; // 3.94a15 Nov 12 2003 1.1688 + $lame_preset_used_lookup[470] = '--r3mix'; // 3.94b1 Dec 18 2003 1.1689 + $lame_preset_used_lookup[480] = '--preset '.(($lame_tag['raw']['vbr_method'] == 4) ? 'fast ' : '').'standard'; // 3.94a15 Nov 12 2003 1.1690 + $lame_preset_used_lookup[490] = '-V1'; 1.1691 + $lame_preset_used_lookup[500] = '--preset '.(($lame_tag['raw']['vbr_method'] == 4) ? 'fast ' : '').'extreme'; // 3.94a15 Nov 12 2003 1.1692 + 1.1693 + return (isset($lame_preset_used_lookup[$lame_tag['preset_used_id']]) ? $lame_preset_used_lookup[$lame_tag['preset_used_id']] : 'new/unknown preset: '.$lame_tag['preset_used_id'].' - report to info@getid3.org'); 1.1694 + } 1.1695 + 1.1696 + 1.1697 +} 1.1698 + 1.1699 +?> 1.1700 \ No newline at end of file