PageRenderTime 35ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/common/libraries/plugin/getid3/module.audio.mp3.php

https://bitbucket.org/renaatdemuynck/chamilo
PHP | 2377 lines | 1761 code | 308 blank | 308 comment | 354 complexity | 1454326416f5ae4e585d2b4e57f864bf MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, LGPL-3.0, GPL-3.0, MIT, GPL-2.0
  1. <?php
  2. /////////////////////////////////////////////////////////////////
  3. /// getID3() by James Heinrich <info@getid3.org> //
  4. // available at http://getid3.sourceforge.net //
  5. // or http://www.getid3.org //
  6. /////////////////////////////////////////////////////////////////
  7. // See readme.txt for more details //
  8. /////////////////////////////////////////////////////////////////
  9. // //
  10. // module.audio.mp3.php //
  11. // module for analyzing MP3 files //
  12. // dependencies: NONE //
  13. // ///
  14. /////////////////////////////////////////////////////////////////
  15. // number of frames to scan to determine if MPEG-audio sequence is valid
  16. // Lower this number to 5-20 for faster scanning
  17. // Increase this number to 50+ for most accurate detection of valid VBR/CBR
  18. // mpeg-audio streams
  19. define('GETID3_MP3_VALID_CHECK_FRAMES', 35);
  20. class getid3_mp3
  21. {
  22. var $allow_bruteforce = false; // forces getID3() to scan the file byte-by-byte and log all the valid audio frame headers - extremely slow, unrecommended, but may provide data from otherwise-unusuable files
  23. function __construct(&$fd, &$ThisFileInfo)
  24. {
  25. if (! $this->getOnlyMPEGaudioInfo($fd, $ThisFileInfo, $ThisFileInfo['avdataoffset']))
  26. {
  27. if ($this->allow_bruteforce)
  28. {
  29. $ThisFileInfo['error'][] = 'Rescanning file in BruteForce mode';
  30. $this->getOnlyMPEGaudioInfoBruteForce($fd, $ThisFileInfo);
  31. }
  32. }
  33. if (isset($ThisFileInfo['mpeg']['audio']['bitrate_mode']))
  34. {
  35. $ThisFileInfo['audio']['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']);
  36. }
  37. if (((isset($ThisFileInfo['id3v2']['headerlength']) && ($ThisFileInfo['avdataoffset'] > $ThisFileInfo['id3v2']['headerlength'])) || (! isset($ThisFileInfo['id3v2']) && ($ThisFileInfo['avdataoffset'] > 0))))
  38. {
  39. $synchoffsetwarning = 'Unknown data before synch ';
  40. if (isset($ThisFileInfo['id3v2']['headerlength']))
  41. {
  42. $synchoffsetwarning .= '(ID3v2 header ends at ' . $ThisFileInfo['id3v2']['headerlength'] . ', then ' . ($ThisFileInfo['avdataoffset'] - $ThisFileInfo['id3v2']['headerlength']) . ' bytes garbage, ';
  43. }
  44. else
  45. {
  46. $synchoffsetwarning .= '(should be at beginning of file, ';
  47. }
  48. $synchoffsetwarning .= 'synch detected at ' . $ThisFileInfo['avdataoffset'] . ')';
  49. if (@$ThisFileInfo['audio']['bitrate_mode'] == 'cbr')
  50. {
  51. if (! empty($ThisFileInfo['id3v2']['headerlength']) && (($ThisFileInfo['avdataoffset'] - $ThisFileInfo['id3v2']['headerlength']) == $ThisFileInfo['mpeg']['audio']['framelength']))
  52. {
  53. $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90-3.92) DLL in CBR mode.';
  54. $ThisFileInfo['audio']['codec'] = 'LAME';
  55. $CurrentDataLAMEversionString = 'LAME3.';
  56. }
  57. elseif (empty($ThisFileInfo['id3v2']['headerlength']) && ($ThisFileInfo['avdataoffset'] == $ThisFileInfo['mpeg']['audio']['framelength']))
  58. {
  59. $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90 - 3.92) DLL in CBR mode.';
  60. $ThisFileInfo['audio']['codec'] = 'LAME';
  61. $CurrentDataLAMEversionString = 'LAME3.';
  62. }
  63. }
  64. $ThisFileInfo['warning'][] = $synchoffsetwarning;
  65. }
  66. if (isset($ThisFileInfo['mpeg']['audio']['LAME']))
  67. {
  68. $ThisFileInfo['audio']['codec'] = 'LAME';
  69. if (! empty($ThisFileInfo['mpeg']['audio']['LAME']['long_version']))
  70. {
  71. $ThisFileInfo['audio']['encoder'] = rtrim($ThisFileInfo['mpeg']['audio']['LAME']['long_version'], "\x00");
  72. }
  73. elseif (! empty($ThisFileInfo['mpeg']['audio']['LAME']['short_version']))
  74. {
  75. $ThisFileInfo['audio']['encoder'] = rtrim($ThisFileInfo['mpeg']['audio']['LAME']['short_version'], "\x00");
  76. }
  77. }
  78. $CurrentDataLAMEversionString = (! empty($CurrentDataLAMEversionString) ? $CurrentDataLAMEversionString : @$ThisFileInfo['audio']['encoder']);
  79. if (! empty($CurrentDataLAMEversionString) && (substr($CurrentDataLAMEversionString, 0, 6) == 'LAME3.') && ! preg_match('[0-9\)]', substr($CurrentDataLAMEversionString, - 1)))
  80. {
  81. // a version number of LAME that does not end with a number like "LAME3.92"
  82. // or with a closing parenthesis like "LAME3.88 (alpha)"
  83. // or a version of LAME with the LAMEtag-not-filled-in-DLL-mode bug (3.90-3.92)
  84. // not sure what the actual last frame length will be, but will be less than or equal to 1441
  85. $PossiblyLongerLAMEversion_FrameLength = 1441;
  86. // Not sure what version of LAME this is - look in padding of last frame for longer version string
  87. $PossibleLAMEversionStringOffset = $ThisFileInfo['avdataend'] - $PossiblyLongerLAMEversion_FrameLength;
  88. fseek($fd, $PossibleLAMEversionStringOffset);
  89. $PossiblyLongerLAMEversion_Data = fread($fd, $PossiblyLongerLAMEversion_FrameLength);
  90. switch (substr($CurrentDataLAMEversionString, - 1))
  91. {
  92. case 'a' :
  93. case 'b' :
  94. // "LAME3.94a" will have a longer version string of "LAME3.94 (alpha)" for example
  95. // need to trim off "a" to match longer string
  96. $CurrentDataLAMEversionString = substr($CurrentDataLAMEversionString, 0, - 1);
  97. break;
  98. }
  99. if (($PossiblyLongerLAMEversion_String = strstr($PossiblyLongerLAMEversion_Data, $CurrentDataLAMEversionString)) !== false)
  100. {
  101. if (substr($PossiblyLongerLAMEversion_String, 0, strlen($CurrentDataLAMEversionString)) == $CurrentDataLAMEversionString)
  102. {
  103. $PossiblyLongerLAMEversion_NewString = substr($PossiblyLongerLAMEversion_String, 0, strspn($PossiblyLongerLAMEversion_String, 'LAME0123456789., (abcdefghijklmnopqrstuvwxyzJFSOND)')); //"LAME3.90.3" "LAME3.87 (beta 1, Sep 27 2000)" "LAME3.88 (beta)"
  104. if (strlen($PossiblyLongerLAMEversion_NewString) > strlen(@$ThisFileInfo['audio']['encoder']))
  105. {
  106. $ThisFileInfo['audio']['encoder'] = $PossiblyLongerLAMEversion_NewString;
  107. }
  108. }
  109. }
  110. }
  111. if (! empty($ThisFileInfo['audio']['encoder']))
  112. {
  113. $ThisFileInfo['audio']['encoder'] = rtrim($ThisFileInfo['audio']['encoder'], "\x00 ");
  114. }
  115. switch (@$ThisFileInfo['mpeg']['audio']['layer'])
  116. {
  117. case 1 :
  118. case 2 :
  119. $ThisFileInfo['audio']['dataformat'] = 'mp' . $ThisFileInfo['mpeg']['audio']['layer'];
  120. break;
  121. }
  122. if (@$ThisFileInfo['fileformat'] == 'mp3')
  123. {
  124. switch ($ThisFileInfo['audio']['dataformat'])
  125. {
  126. case 'mp1' :
  127. case 'mp2' :
  128. case 'mp3' :
  129. $ThisFileInfo['fileformat'] = $ThisFileInfo['audio']['dataformat'];
  130. break;
  131. default :
  132. $ThisFileInfo['warning'][] = 'Expecting [audio][dataformat] to be mp1/mp2/mp3 when fileformat == mp3, [audio][dataformat] actually "' . $ThisFileInfo['audio']['dataformat'] . '"';
  133. break;
  134. }
  135. }
  136. if (empty($ThisFileInfo['fileformat']))
  137. {
  138. unset($ThisFileInfo['fileformat']);
  139. unset($ThisFileInfo['audio']['bitrate_mode']);
  140. unset($ThisFileInfo['avdataoffset']);
  141. unset($ThisFileInfo['avdataend']);
  142. return false;
  143. }
  144. $ThisFileInfo['mime_type'] = 'audio/mpeg';
  145. $ThisFileInfo['audio']['lossless'] = false;
  146. // Calculate playtime
  147. if (! isset($ThisFileInfo['playtime_seconds']) && isset($ThisFileInfo['audio']['bitrate']) && ($ThisFileInfo['audio']['bitrate'] > 0))
  148. {
  149. $ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['audio']['bitrate'];
  150. }
  151. $ThisFileInfo['audio']['encoder_options'] = $this->GuessEncoderOptions($ThisFileInfo);
  152. return true;
  153. }
  154. function GuessEncoderOptions(&$ThisFileInfo)
  155. {
  156. // shortcuts
  157. if (! empty($ThisFileInfo['mpeg']['audio']))
  158. {
  159. $thisfile_mpeg_audio = &$ThisFileInfo['mpeg']['audio'];
  160. if (! empty($thisfile_mpeg_audio['LAME']))
  161. {
  162. $thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME'];
  163. }
  164. }
  165. $encoder_options = '';
  166. static $NamedPresetBitrates = array(16, 24, 40, 56, 112, 128, 160, 192, 256);
  167. if ((@$thisfile_mpeg_audio['VBR_method'] == 'Fraunhofer') && ! empty($thisfile_mpeg_audio['VBR_quality']))
  168. {
  169. $encoder_options = 'VBR q' . $thisfile_mpeg_audio['VBR_quality'];
  170. }
  171. elseif (! empty($thisfile_mpeg_audio_lame['preset_used']) && (! in_array($thisfile_mpeg_audio_lame['preset_used_id'], $NamedPresetBitrates)))
  172. {
  173. $encoder_options = $thisfile_mpeg_audio_lame['preset_used'];
  174. }
  175. elseif (! empty($thisfile_mpeg_audio_lame['vbr_quality']))
  176. {
  177. static $KnownEncoderValues = array();
  178. if (empty($KnownEncoderValues))
  179. {
  180. //$KnownEncoderValues[abrbitrate_minbitrate][vbr_quality][raw_vbr_method][raw_noise_shaping][raw_stereo_mode][ath_type][lowpass_frequency] = 'preset name';
  181. $KnownEncoderValues[0xFF][58][1][1][3][2][20500] = '--alt-preset insane'; // 3.90, 3.90.1, 3.92
  182. $KnownEncoderValues[0xFF][58][1][1][3][2][20600] = '--alt-preset insane'; // 3.90.2, 3.90.3, 3.91
  183. $KnownEncoderValues[0xFF][57][1][1][3][4][20500] = '--alt-preset insane'; // 3.94, 3.95
  184. $KnownEncoderValues['**'][78][3][2][3][2][19500] = '--alt-preset extreme'; // 3.90, 3.90.1, 3.92
  185. $KnownEncoderValues['**'][78][3][2][3][2][19600] = '--alt-preset extreme'; // 3.90.2, 3.91
  186. $KnownEncoderValues['**'][78][3][1][3][2][19600] = '--alt-preset extreme'; // 3.90.3
  187. $KnownEncoderValues['**'][78][4][2][3][2][19500] = '--alt-preset fast extreme'; // 3.90, 3.90.1, 3.92
  188. $KnownEncoderValues['**'][78][4][2][3][2][19600] = '--alt-preset fast extreme'; // 3.90.2, 3.90.3, 3.91
  189. $KnownEncoderValues['**'][78][3][2][3][4][19000] = '--alt-preset standard'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
  190. $KnownEncoderValues['**'][78][3][1][3][4][19000] = '--alt-preset standard'; // 3.90.3
  191. $KnownEncoderValues['**'][78][4][2][3][4][19000] = '--alt-preset fast standard'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
  192. $KnownEncoderValues['**'][78][4][1][3][4][19000] = '--alt-preset fast standard'; // 3.90.3
  193. $KnownEncoderValues['**'][88][4][1][3][3][19500] = '--r3mix'; // 3.90, 3.90.1, 3.92
  194. $KnownEncoderValues['**'][88][4][1][3][3][19600] = '--r3mix'; // 3.90.2, 3.90.3, 3.91
  195. $KnownEncoderValues['**'][67][4][1][3][4][18000] = '--r3mix'; // 3.94, 3.95
  196. $KnownEncoderValues['**'][68][3][2][3][4][18000] = '--alt-preset medium'; // 3.90.3
  197. $KnownEncoderValues['**'][68][4][2][3][4][18000] = '--alt-preset fast medium'; // 3.90.3
  198. $KnownEncoderValues[0xFF][99][1][1][1][2][0] = '--preset studio'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
  199. $KnownEncoderValues[0xFF][58][2][1][3][2][20600] = '--preset studio'; // 3.90.3, 3.93.1
  200. $KnownEncoderValues[0xFF][58][2][1][3][2][20500] = '--preset studio'; // 3.93
  201. $KnownEncoderValues[0xFF][57][2][1][3][4][20500] = '--preset studio'; // 3.94, 3.95
  202. $KnownEncoderValues[0xC0][88][1][1][1][2][0] = '--preset cd'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
  203. $KnownEncoderValues[0xC0][58][2][2][3][2][19600] = '--preset cd'; // 3.90.3, 3.93.1
  204. $KnownEncoderValues[0xC0][58][2][2][3][2][19500] = '--preset cd'; // 3.93
  205. $KnownEncoderValues[0xC0][57][2][1][3][4][19500] = '--preset cd'; // 3.94, 3.95
  206. $KnownEncoderValues[0xA0][78][1][1][3][2][18000] = '--preset hifi'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
  207. $KnownEncoderValues[0xA0][58][2][2][3][2][18000] = '--preset hifi'; // 3.90.3, 3.93, 3.93.1
  208. $KnownEncoderValues[0xA0][57][2][1][3][4][18000] = '--preset hifi'; // 3.94, 3.95
  209. $KnownEncoderValues[0x80][67][1][1][3][2][18000] = '--preset tape'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
  210. $KnownEncoderValues[0x80][67][1][1][3][2][15000] = '--preset radio'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
  211. $KnownEncoderValues[0x70][67][1][1][3][2][15000] = '--preset fm'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
  212. $KnownEncoderValues[0x70][58][2][2][3][2][16000] = '--preset tape/radio/fm'; // 3.90.3, 3.93, 3.93.1
  213. $KnownEncoderValues[0x70][57][2][1][3][4][16000] = '--preset tape/radio/fm'; // 3.94, 3.95
  214. $KnownEncoderValues[0x38][58][2][2][0][2][10000] = '--preset voice'; // 3.90.3, 3.93, 3.93.1
  215. $KnownEncoderValues[0x38][57][2][1][0][4][15000] = '--preset voice'; // 3.94, 3.95
  216. $KnownEncoderValues[0x38][57][2][1][0][4][16000] = '--preset voice'; // 3.94a14
  217. $KnownEncoderValues[0x28][65][1][1][0][2][7500] = '--preset mw-us'; // 3.90, 3.90.1, 3.92
  218. $KnownEncoderValues[0x28][65][1][1][0][2][7600] = '--preset mw-us'; // 3.90.2, 3.91
  219. $KnownEncoderValues[0x28][58][2][2][0][2][7000] = '--preset mw-us'; // 3.90.3, 3.93, 3.93.1
  220. $KnownEncoderValues[0x28][57][2][1][0][4][10500] = '--preset mw-us'; // 3.94, 3.95
  221. $KnownEncoderValues[0x28][57][2][1][0][4][11200] = '--preset mw-us'; // 3.94a14
  222. $KnownEncoderValues[0x28][57][2][1][0][4][8800] = '--preset mw-us'; // 3.94a15
  223. $KnownEncoderValues[0x18][58][2][2][0][2][4000] = '--preset phon+/lw/mw-eu/sw'; // 3.90.3, 3.93.1
  224. $KnownEncoderValues[0x18][58][2][2][0][2][3900] = '--preset phon+/lw/mw-eu/sw'; // 3.93
  225. $KnownEncoderValues[0x18][57][2][1][0][4][5900] = '--preset phon+/lw/mw-eu/sw'; // 3.94, 3.95
  226. $KnownEncoderValues[0x18][57][2][1][0][4][6200] = '--preset phon+/lw/mw-eu/sw'; // 3.94a14
  227. $KnownEncoderValues[0x18][57][2][1][0][4][3200] = '--preset phon+/lw/mw-eu/sw'; // 3.94a15
  228. $KnownEncoderValues[0x10][58][2][2][0][2][3800] = '--preset phone'; // 3.90.3, 3.93.1
  229. $KnownEncoderValues[0x10][58][2][2][0][2][3700] = '--preset phone'; // 3.93
  230. $KnownEncoderValues[0x10][57][2][1][0][4][5600] = '--preset phone'; // 3.94, 3.95
  231. }
  232. if (isset($KnownEncoderValues[$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']]))
  233. {
  234. $encoder_options = $KnownEncoderValues[$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']];
  235. }
  236. elseif (isset($KnownEncoderValues['**'][$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']]))
  237. {
  238. $encoder_options = $KnownEncoderValues['**'][$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']];
  239. }
  240. elseif ($ThisFileInfo['audio']['bitrate_mode'] == 'vbr')
  241. {
  242. // http://gabriel.mp3-tech.org/mp3infotag.html
  243. // int Quality = (100 - 10 * gfp->VBR_q - gfp->quality)h
  244. $LAME_V_value = 10 - ceil($thisfile_mpeg_audio_lame['vbr_quality'] / 10);
  245. $LAME_q_value = 100 - $thisfile_mpeg_audio_lame['vbr_quality'] - ($LAME_V_value * 10);
  246. $encoder_options = '-V' . $LAME_V_value . ' -q' . $LAME_q_value;
  247. }
  248. elseif ($ThisFileInfo['audio']['bitrate_mode'] == 'cbr')
  249. {
  250. $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']) . ceil($ThisFileInfo['audio']['bitrate'] / 1000);
  251. }
  252. else
  253. {
  254. $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']);
  255. }
  256. }
  257. elseif (! empty($thisfile_mpeg_audio_lame['bitrate_abr']))
  258. {
  259. $encoder_options = 'ABR' . $thisfile_mpeg_audio_lame['bitrate_abr'];
  260. }
  261. elseif (! empty($ThisFileInfo['audio']['bitrate']))
  262. {
  263. if ($ThisFileInfo['audio']['bitrate_mode'] == 'cbr')
  264. {
  265. $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']) . ceil($ThisFileInfo['audio']['bitrate'] / 1000);
  266. }
  267. else
  268. {
  269. $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']);
  270. }
  271. }
  272. if (! empty($thisfile_mpeg_audio_lame['bitrate_min']))
  273. {
  274. $encoder_options .= ' -b' . $thisfile_mpeg_audio_lame['bitrate_min'];
  275. }
  276. if (@$thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev'] || @$thisfile_mpeg_audio_lame['encoding_flags']['nogap_next'])
  277. {
  278. $encoder_options .= ' --nogap';
  279. }
  280. if (! empty($thisfile_mpeg_audio_lame['lowpass_frequency']))
  281. {
  282. $ExplodedOptions = explode(' ', $encoder_options, 4);
  283. if ($ExplodedOptions[0] == '--r3mix')
  284. {
  285. $ExplodedOptions[1] = 'r3mix';
  286. }
  287. switch ($ExplodedOptions[0])
  288. {
  289. case '--preset' :
  290. case '--alt-preset' :
  291. case '--r3mix' :
  292. if ($ExplodedOptions[1] == 'fast')
  293. {
  294. $ExplodedOptions[1] .= ' ' . $ExplodedOptions[2];
  295. }
  296. switch ($ExplodedOptions[1])
  297. {
  298. case 'portable' :
  299. case 'medium' :
  300. case 'standard' :
  301. case 'extreme' :
  302. case 'insane' :
  303. case 'fast portable' :
  304. case 'fast medium' :
  305. case 'fast standard' :
  306. case 'fast extreme' :
  307. case 'fast insane' :
  308. case 'r3mix' :
  309. static $ExpectedLowpass = array('insane|20500' => 20500, 'insane|20600' => 20600, // 3.90.2, 3.90.3, 3.91
  310. 'medium|18000' => 18000,
  311. 'fast medium|18000' => 18000, 'extreme|19500' => 19500, // 3.90, 3.90.1, 3.92, 3.95
  312. 'extreme|19600' => 19600, // 3.90.2, 3.90.3, 3.91, 3.93.1
  313. 'fast extreme|19500' => 19500, // 3.90, 3.90.1, 3.92, 3.95
  314. 'fast extreme|19600' => 19600, // 3.90.2, 3.90.3, 3.91, 3.93.1
  315. 'standard|19000' => 19000,
  316. 'fast standard|19000' => 19000, 'r3mix|19500' => 19500, // 3.90, 3.90.1, 3.92
  317. 'r3mix|19600' => 19600, // 3.90.2, 3.90.3, 3.91
  318. 'r3mix|18000' => 18000)// 3.94, 3.95
  319. ;
  320. if (! isset($ExpectedLowpass[$ExplodedOptions[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)))
  321. {
  322. $encoder_options .= ' --lowpass ' . $thisfile_mpeg_audio_lame['lowpass_frequency'];
  323. }
  324. break;
  325. default :
  326. break;
  327. }
  328. break;
  329. }
  330. }
  331. if (isset($thisfile_mpeg_audio_lame['raw']['source_sample_freq']))
  332. {
  333. if (($thisfile_mpeg_audio['sample_rate'] == 44100) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 1))
  334. {
  335. $encoder_options .= ' --resample 44100';
  336. }
  337. elseif (($thisfile_mpeg_audio['sample_rate'] == 48000) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 2))
  338. {
  339. $encoder_options .= ' --resample 48000';
  340. }
  341. elseif ($thisfile_mpeg_audio['sample_rate'] < 44100)
  342. {
  343. switch ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'])
  344. {
  345. case 0 : // <= 32000
  346. // may or may not be same as source frequency - ignore
  347. break;
  348. case 1 : // 44100
  349. case 2 : // 48000
  350. case 3 : // 48000+
  351. $ExplodedOptions = explode(' ', $encoder_options, 4);
  352. switch ($ExplodedOptions[0])
  353. {
  354. case '--preset' :
  355. case '--alt-preset' :
  356. switch ($ExplodedOptions[1])
  357. {
  358. case 'fast' :
  359. case 'portable' :
  360. case 'medium' :
  361. case 'standard' :
  362. case 'extreme' :
  363. case 'insane' :
  364. $encoder_options .= ' --resample ' . $thisfile_mpeg_audio['sample_rate'];
  365. break;
  366. default :
  367. static $ExpectedResampledRate = array('phon+/lw/mw-eu/sw|16000' => 16000,
  368. 'mw-us|24000' => 24000, // 3.95
  369. 'mw-us|32000' => 32000, // 3.93
  370. 'mw-us|16000' => 16000, // 3.92
  371. 'phone|16000' => 16000,
  372. 'phone|11025' => 11025, // 3.94a15
  373. 'radio|32000' => 32000, // 3.94a15
  374. 'fm/radio|32000' => 32000, // 3.92
  375. 'fm|32000' => 32000, // 3.90
  376. 'voice|32000' => 32000);
  377. if (! isset($ExpectedResampledRate[$ExplodedOptions[1] . '|' . $thisfile_mpeg_audio['sample_rate']]))
  378. {
  379. $encoder_options .= ' --resample ' . $thisfile_mpeg_audio['sample_rate'];
  380. }
  381. break;
  382. }
  383. break;
  384. case '--r3mix' :
  385. default :
  386. $encoder_options .= ' --resample ' . $thisfile_mpeg_audio['sample_rate'];
  387. break;
  388. }
  389. break;
  390. }
  391. }
  392. }
  393. if (empty($encoder_options) && ! empty($ThisFileInfo['audio']['bitrate']) && ! empty($ThisFileInfo['audio']['bitrate_mode']))
  394. {
  395. //$encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']).ceil($ThisFileInfo['audio']['bitrate'] / 1000);
  396. $encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']);
  397. }
  398. return $encoder_options;
  399. }
  400. function decodeMPEGaudioHeader($fd, $offset, &$ThisFileInfo, $recursivesearch = true, $ScanAsCBR = false, $FastMPEGheaderScan = false)
  401. {
  402. static $MPEGaudioVersionLookup;
  403. static $MPEGaudioLayerLookup;
  404. static $MPEGaudioBitrateLookup;
  405. static $MPEGaudioFrequencyLookup;
  406. static $MPEGaudioChannelModeLookup;
  407. static $MPEGaudioModeExtensionLookup;
  408. static $MPEGaudioEmphasisLookup;
  409. if (empty($MPEGaudioVersionLookup))
  410. {
  411. $MPEGaudioVersionLookup = getid3_mp3 :: MPEGaudioVersionArray();
  412. $MPEGaudioLayerLookup = getid3_mp3 :: MPEGaudioLayerArray();
  413. $MPEGaudioBitrateLookup = getid3_mp3 :: MPEGaudioBitrateArray();
  414. $MPEGaudioFrequencyLookup = getid3_mp3 :: MPEGaudioFrequencyArray();
  415. $MPEGaudioChannelModeLookup = getid3_mp3 :: MPEGaudioChannelModeArray();
  416. $MPEGaudioModeExtensionLookup = getid3_mp3 :: MPEGaudioModeExtensionArray();
  417. $MPEGaudioEmphasisLookup = getid3_mp3 :: MPEGaudioEmphasisArray();
  418. }
  419. if ($offset >= $ThisFileInfo['avdataend'])
  420. {
  421. $ThisFileInfo['error'][] = 'end of file encounter looking for MPEG synch';
  422. return false;
  423. }
  424. fseek($fd, $offset, SEEK_SET);
  425. //$headerstring = fread($fd, 1441); // worst-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame
  426. $headerstring = fread($fd, 226); // LAME header at offset 36 + 190 bytes of Xing/LAME data
  427. // MP3 audio frame structure:
  428. // $aa $aa $aa $aa [$bb $bb] $cc...
  429. // where $aa..$aa is the four-byte mpeg-audio header (below)
  430. // $bb $bb is the optional 2-byte CRC
  431. // and $cc... is the audio data
  432. $head4 = substr($headerstring, 0, 4);
  433. static $MPEGaudioHeaderDecodeCache = array();
  434. if (isset($MPEGaudioHeaderDecodeCache[$head4]))
  435. {
  436. $MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4];
  437. }
  438. else
  439. {
  440. $MPEGheaderRawArray = getid3_mp3 :: MPEGaudioHeaderDecode($head4);
  441. $MPEGaudioHeaderDecodeCache[$head4] = $MPEGheaderRawArray;
  442. }
  443. static $MPEGaudioHeaderValidCache = array();
  444. // Not in cache
  445. if (! isset($MPEGaudioHeaderValidCache[$head4]))
  446. {
  447. //$MPEGaudioHeaderValidCache[$head4] = getid3_mp3::MPEGaudioHeaderValid($MPEGheaderRawArray, false, true); // allow badly-formatted freeformat (from LAME 3.90 - 3.93.1)
  448. $MPEGaudioHeaderValidCache[$head4] = getid3_mp3 :: MPEGaudioHeaderValid($MPEGheaderRawArray, false, false);
  449. }
  450. // shortcut
  451. if (! isset($ThisFileInfo['mpeg']['audio']))
  452. {
  453. $ThisFileInfo['mpeg']['audio'] = array();
  454. }
  455. $thisfile_mpeg_audio = &$ThisFileInfo['mpeg']['audio'];
  456. if ($MPEGaudioHeaderValidCache[$head4])
  457. {
  458. $thisfile_mpeg_audio['raw'] = $MPEGheaderRawArray;
  459. }
  460. else
  461. {
  462. $ThisFileInfo['error'][] = 'Invalid MPEG audio header at offset ' . $offset;
  463. return false;
  464. }
  465. if (! $FastMPEGheaderScan)
  466. {
  467. $thisfile_mpeg_audio['version'] = $MPEGaudioVersionLookup[$thisfile_mpeg_audio['raw']['version']];
  468. $thisfile_mpeg_audio['layer'] = $MPEGaudioLayerLookup[$thisfile_mpeg_audio['raw']['layer']];
  469. $thisfile_mpeg_audio['channelmode'] = $MPEGaudioChannelModeLookup[$thisfile_mpeg_audio['raw']['channelmode']];
  470. $thisfile_mpeg_audio['channels'] = (($thisfile_mpeg_audio['channelmode'] == 'mono') ? 1 : 2);
  471. $thisfile_mpeg_audio['sample_rate'] = $MPEGaudioFrequencyLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['raw']['sample_rate']];
  472. $thisfile_mpeg_audio['protection'] = ! $thisfile_mpeg_audio['raw']['protection'];
  473. $thisfile_mpeg_audio['private'] = (bool) $thisfile_mpeg_audio['raw']['private'];
  474. $thisfile_mpeg_audio['modeextension'] = $MPEGaudioModeExtensionLookup[$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['modeextension']];
  475. $thisfile_mpeg_audio['copyright'] = (bool) $thisfile_mpeg_audio['raw']['copyright'];
  476. $thisfile_mpeg_audio['original'] = (bool) $thisfile_mpeg_audio['raw']['original'];
  477. $thisfile_mpeg_audio['emphasis'] = $MPEGaudioEmphasisLookup[$thisfile_mpeg_audio['raw']['emphasis']];
  478. $ThisFileInfo['audio']['channels'] = $thisfile_mpeg_audio['channels'];
  479. $ThisFileInfo['audio']['sample_rate'] = $thisfile_mpeg_audio['sample_rate'];
  480. if ($thisfile_mpeg_audio['protection'])
  481. {
  482. $thisfile_mpeg_audio['crc'] = getid3_lib :: BigEndian2Int(substr($headerstring, 4, 2));
  483. }
  484. }
  485. if ($thisfile_mpeg_audio['raw']['bitrate'] == 15)
  486. {
  487. // http://www.hydrogenaudio.org/?act=ST&f=16&t=9682&st=0
  488. $ThisFileInfo['warning'][] = 'Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1';
  489. $thisfile_mpeg_audio['raw']['bitrate'] = 0;
  490. }
  491. $thisfile_mpeg_audio['padding'] = (bool) $thisfile_mpeg_audio['raw']['padding'];
  492. $thisfile_mpeg_audio['bitrate'] = $MPEGaudioBitrateLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['bitrate']];
  493. if (($thisfile_mpeg_audio['bitrate'] == 'free') && ($offset == $ThisFileInfo['avdataoffset']))
  494. {
  495. // only skip multiple frame check if free-format bitstream found at beginning of file
  496. // otherwise is quite possibly simply corrupted data
  497. $recursivesearch = false;
  498. }
  499. // For Layer 2 there are some combinations of bitrate and mode which are not allowed.
  500. if (! $FastMPEGheaderScan && ($thisfile_mpeg_audio['layer'] == '2'))
  501. {
  502. $ThisFileInfo['audio']['dataformat'] = 'mp2';
  503. switch ($thisfile_mpeg_audio['channelmode'])
  504. {
  505. case 'mono' :
  506. if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] <= 192000))
  507. {
  508. // these are ok
  509. }
  510. else
  511. {
  512. $ThisFileInfo['error'][] = $thisfile_mpeg_audio['bitrate'] . 'kbps not allowed in Layer 2, ' . $thisfile_mpeg_audio['channelmode'] . '.';
  513. return false;
  514. }
  515. break;
  516. case 'stereo' :
  517. case 'joint stereo' :
  518. case 'dual channel' :
  519. if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] == 64000) || ($thisfile_mpeg_audio['bitrate'] >= 96000))
  520. {
  521. // these are ok
  522. }
  523. else
  524. {
  525. $ThisFileInfo['error'][] = intval(round($thisfile_mpeg_audio['bitrate'] / 1000)) . 'kbps not allowed in Layer 2, ' . $thisfile_mpeg_audio['channelmode'] . '.';
  526. return false;
  527. }
  528. break;
  529. }
  530. }
  531. if ($ThisFileInfo['audio']['sample_rate'] > 0)
  532. {
  533. $thisfile_mpeg_audio['framelength'] = getid3_mp3 :: MPEGaudioFrameLength($thisfile_mpeg_audio['bitrate'], $thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['layer'], (int) $thisfile_mpeg_audio['padding'], $ThisFileInfo['audio']['sample_rate']);
  534. }
  535. $nextframetestoffset = $offset + 1;
  536. if ($thisfile_mpeg_audio['bitrate'] != 'free')
  537. {
  538. $ThisFileInfo['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
  539. if (isset($thisfile_mpeg_audio['framelength']))
  540. {
  541. $nextframetestoffset = $offset + $thisfile_mpeg_audio['framelength'];
  542. }
  543. else
  544. {
  545. $ThisFileInfo['error'][] = 'Frame at offset(' . $offset . ') is has an invalid frame length.';
  546. return false;
  547. }
  548. }
  549. $ExpectedNumberOfAudioBytes = 0;
  550. ////////////////////////////////////////////////////////////////////////////////////
  551. // Variable-bitrate headers
  552. if (substr($headerstring, 4 + 32, 4) == 'VBRI')
  553. {
  554. // Fraunhofer VBR header is hardcoded 'VBRI' at offset 0x24 (36)
  555. // specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html
  556. $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
  557. $thisfile_mpeg_audio['VBR_method'] = 'Fraunhofer';
  558. $ThisFileInfo['audio']['codec'] = 'Fraunhofer';
  559. $SideInfoData = substr($headerstring, 4 + 2, 32);
  560. $FraunhoferVBROffset = 36;
  561. $thisfile_mpeg_audio['VBR_encoder_version'] = getid3_lib :: BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 4, 2)); // VbriVersion
  562. $thisfile_mpeg_audio['VBR_encoder_delay'] = getid3_lib :: BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 6, 2)); // VbriDelay
  563. $thisfile_mpeg_audio['VBR_quality'] = getid3_lib :: BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 8, 2)); // VbriQuality
  564. $thisfile_mpeg_audio['VBR_bytes'] = getid3_lib :: BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 10, 4)); // VbriStreamBytes
  565. $thisfile_mpeg_audio['VBR_frames'] = getid3_lib :: BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 14, 4)); // VbriStreamFrames
  566. $thisfile_mpeg_audio['VBR_seek_offsets'] = getid3_lib :: BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 18, 2)); // VbriTableSize
  567. $thisfile_mpeg_audio['VBR_seek_scale'] = getid3_lib :: BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 20, 2)); // VbriTableScale
  568. $thisfile_mpeg_audio['VBR_entry_bytes'] = getid3_lib :: BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 22, 2)); // VbriEntryBytes
  569. $thisfile_mpeg_audio['VBR_entry_frames'] = getid3_lib :: BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 24, 2)); // VbriEntryFrames
  570. $ExpectedNumberOfAudioBytes = $thisfile_mpeg_audio['VBR_bytes'];
  571. $previousbyteoffset = $offset;
  572. for($i = 0; $i < $thisfile_mpeg_audio['VBR_seek_offsets']; $i ++)
  573. {
  574. $Fraunhofer_OffsetN = getid3_lib :: BigEndian2Int(substr($headerstring, $FraunhoferVBROffset, $thisfile_mpeg_audio['VBR_entry_bytes']));
  575. $FraunhoferVBROffset += $thisfile_mpeg_audio['VBR_entry_bytes'];
  576. $thisfile_mpeg_audio['VBR_offsets_relative'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']);
  577. $thisfile_mpeg_audio['VBR_offsets_absolute'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']) + $previousbyteoffset;
  578. $previousbyteoffset += $Fraunhofer_OffsetN;
  579. }
  580. }
  581. else
  582. {
  583. // Xing VBR header is hardcoded 'Xing' at a offset 0x0D (13), 0x15 (21) or 0x24 (36)
  584. // depending on MPEG layer and number of channels
  585. $VBRidOffset = getid3_mp3 :: XingVBRidOffset($thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['channelmode']);
  586. $SideInfoData = substr($headerstring, 4 + 2, $VBRidOffset - 4);
  587. if ((substr($headerstring, $VBRidOffset, strlen('Xing')) == 'Xing') || (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Info'))
  588. {
  589. // 'Xing' is traditional Xing VBR frame
  590. // 'Info' is LAME-encoded CBR (This was done to avoid CBR files to be recognized as traditional Xing VBR files by some decoders.)
  591. // 'Info' *can* legally be used to specify a VBR file as well, however.
  592. // http://www.multiweb.cz/twoinches/MP3inside.htm
  593. //00..03 = "Xing" or "Info"
  594. //04..07 = Flags:
  595. // 0x01 Frames Flag set if value for number of frames in file is stored
  596. // 0x02 Bytes Flag set if value for filesize in bytes is stored
  597. // 0x04 TOC Flag set if values for TOC are stored
  598. // 0x08 VBR Scale Flag set if values for VBR scale is stored
  599. //08..11 Frames: Number of frames in file (including the first Xing/Info one)
  600. //12..15 Bytes: File length in Bytes
  601. //16..115 TOC (Table of Contents):
  602. // Contains of 100 indexes (one Byte length) for easier lookup in file. Approximately solves problem with moving inside file.
  603. // Each Byte has a value according this formula:
  604. // (TOC[i] / 256) * fileLenInBytes
  605. // 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:
  606. // TOC[(60/240)*100] = TOC[25]
  607. // and corresponding Byte in file is then approximately at:
  608. // (TOC[25]/256) * 5000000
  609. //116..119 VBR Scale
  610. // should be safe to leave this at 'vbr' and let it be overriden to 'cbr' if a CBR preset/mode is used by LAME
  611. // if (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Xing') {
  612. $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
  613. $thisfile_mpeg_audio['VBR_method'] = 'Xing';
  614. // } else {
  615. // $ScanAsCBR = true;
  616. // $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
  617. // }
  618. $thisfile_mpeg_audio['xing_flags_raw'] = getid3_lib :: BigEndian2Int(substr($headerstring, $VBRidOffset + 4, 4));
  619. $thisfile_mpeg_audio['xing_flags']['frames'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000001);
  620. $thisfile_mpeg_audio['xing_flags']['bytes'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000002);
  621. $thisfile_mpeg_audio['xing_flags']['toc'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000004);
  622. $thisfile_mpeg_audio['xing_flags']['vbr_scale'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000008);
  623. if ($thisfile_mpeg_audio['xing_flags']['frames'])
  624. {
  625. $thisfile_mpeg_audio['VBR_frames'] = getid3_lib :: BigEndian2Int(substr($headerstring, $VBRidOffset + 8, 4));
  626. //$thisfile_mpeg_audio['VBR_frames']--; // don't count header Xing/Info frame
  627. }
  628. if ($thisfile_mpeg_audio['xing_flags']['bytes'])
  629. {
  630. $thisfile_mpeg_audio['VBR_bytes'] = getid3_lib :: BigEndian2Int(substr($headerstring, $VBRidOffset + 12, 4));
  631. }
  632. //if (($thisfile_mpeg_audio['bitrate'] == 'free') && !empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
  633. if (! empty($thisfile_mpeg_audio['VBR_frames']) && ! empty($thisfile_mpeg_audio['VBR_bytes']))
  634. {
  635. $framelengthfloat = $thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames'];
  636. if ($thisfile_mpeg_audio['layer'] == '1')
  637. {
  638. // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
  639. //$ThisFileInfo['audio']['bitrate'] = ((($framelengthfloat / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
  640. $ThisFileInfo['audio']['bitrate'] = ($framelengthfloat / 4) * $thisfile_mpeg_audio['sample_rate'] * (2 / $ThisFileInfo['audio']['channels']) / 12;
  641. }
  642. else
  643. {
  644. // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
  645. //$ThisFileInfo['audio']['bitrate'] = (($framelengthfloat - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
  646. $ThisFileInfo['audio']['bitrate'] = $framelengthfloat * $thisfile_mpeg_audio['sample_rate'] * (2 / $ThisFileInfo['audio']['channels']) / 144;
  647. }
  648. $thisfile_mpeg_audio['framelength'] = floor($framelengthfloat);
  649. }
  650. if ($thisfile_mpeg_audio['xing_flags']['toc'])
  651. {
  652. $LAMEtocData = substr($headerstring, $VBRidOffset + 16, 100);
  653. for($i = 0; $i < 100; $i ++)
  654. {
  655. $thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData{$i});
  656. }
  657. }
  658. if ($thisfile_mpeg_audio['xing_flags']['vbr_scale'])
  659. {
  660. $thisfile_mpeg_audio['VBR_scale'] = getid3_lib :: BigEndian2Int(substr($headerstring, $VBRidOffset + 116, 4));
  661. }
  662. // http://gabriel.mp3-tech.org/mp3infotag.html
  663. if (substr($headerstring, $VBRidOffset + 120, 4) == 'LAME')
  664. {
  665. // shortcut
  666. $thisfile_mpeg_audio['LAME'] = array();
  667. $thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME'];
  668. $thisfile_mpeg_audio_lame['long_version'] = substr($headerstring, $VBRidOffset + 120, 20);
  669. $thisfile_mpeg_audio_lame['short_version'] = substr($thisfile_mpeg_audio_lame['long_version'], 0, 9);
  670. if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.90')
  671. {
  672. // extra 11 chars are not part of version string when LAMEtag present
  673. unset($thisfile_mpeg_audio_lame['long_version']);
  674. // It the LAME tag was only introduced in LAME v3.90
  675. // http://www.hydrogenaudio.org/?act=ST&f=15&t=9933
  676. // Offsets of various bytes in http://gabriel.mp3-tech.org/mp3infotag.html
  677. // are assuming a 'Xing' identifier offset of 0x24, which is the case for
  678. // MPEG-1 non-mono, but not for other combinations
  679. $LAMEtagOffsetContant = $VBRidOffset - 0x24;
  680. // shortcuts
  681. $thisfile_mpeg_audio_lame['RGAD'] = array('track' => array(),
  682. 'album' => array());
  683. $thisfile_mpeg_audio_lame_RGAD = &$thisfile_mpeg_audio_lame['RGAD'];
  684. $thisfile_mpeg_audio_lame_RGAD_track = &$thisfile_mpeg_audio_lame_RGAD['track'];
  685. $thisfile_mpeg_audio_lame_RGAD_album = &$thisfile_mpeg_audio_lame_RGAD['album'];
  686. $thisfile_mpeg_audio_lame['raw'] = array();
  687. $thisfile_mpeg_audio_lame_raw = &$thisfile_mpeg_audio_lame['raw'];
  688. // byte $9B VBR Quality
  689. // This field is there to indicate a quality level, although the scale was not precised in the original Xing specifications.
  690. // Actually overwrites original Xing bytes
  691. unset($thisfile_mpeg_audio['VBR_scale']);
  692. $thisfile_mpeg_audio_lame['vbr_quality'] = getid3_lib :: BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0x9B, 1));
  693. // bytes $9C-$A4 Encoder short VersionString
  694. $thisfile_mpeg_audio_lame['short_version'] = substr($headerstring, $LAMEtagOffsetContant + 0x9C, 9);
  695. // byte $A5 Info Tag revision + VBR method
  696. $LAMEtagRevisionVBRmethod = getid3_lib :: BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA5, 1));
  697. $thisfile_mpeg_audio_lame['tag_revision'] = ($LAMEtagRevisionVBRmethod & 0xF0) >> 4;
  698. $thisfile_mpeg_audio_lame_raw['vbr_method'] = $LAMEtagRevisionVBRmethod & 0x0F;
  699. $thisfile_mpeg_audio_lame['vbr_method'] = getid3_mp3 :: LAMEvbrMethodLookup($thisfile_mpeg_audio_lame_raw['vbr_method']);
  700. $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'
  701. // byte $A6 Lowpass filter value
  702. $thisfile_mpeg_audio_lame['lowpass_frequency'] = getid3_lib :: BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA6, 1)) * 100;
  703. // bytes $A7-$AE Replay Gain
  704. // http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html
  705. // bytes $A7-$AA : 32 bit floating point "Peak signal amplitude"
  706. if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.94b')
  707. {
  708. // LAME 3.94a16 and later - 9.23 fixed point
  709. // ie 0x0059E2EE / (2^23) = 5890798 / 8388608 = 0.7022378444671630859375
  710. $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = (float) ((getid3_lib :: BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4))) / 8388608);
  711. }
  712. else
  713. {
  714. // LAME 3.94a15 and earlier - 32-bit floating point
  715. // Actually 3.94a16 will fall in here too and be WRONG, but is hard to detect 3.94a16 vs 3.94a15
  716. $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = getid3_lib :: LittleEndian2Float(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4));
  717. }
  718. if ($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] == 0)
  719. {
  720. unset($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
  721. }
  722. else
  723. {
  724. $thisfile_mpeg_audio_lame_RGAD['peak_db'] = getid3_lib :: RGADamplitude2dB($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
  725. }
  726. $thisfile_mpeg_audio_lame_raw['RGAD_track'] = getid3_lib :: BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAB, 2));
  727. $thisfile_mpeg_audio_lame_raw['RGAD_album'] = getid3_lib :: BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAD, 2));
  728. if ($thisfile_mpeg_audio_lame_raw['RGAD_track'] != 0)
  729. {
  730. $thisfile_mpeg_audio_lame_RGAD_track['raw']['name'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0xE000) >> 13;
  731. $thisfile_mpeg_audio_lame_RGAD_track['raw']['originator'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x1C00) >> 10;
  732. $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x0200) >> 9;
  733. $thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x01FF;
  734. $thisfile_mpeg_audio_lame_RGAD_track['name'] = getid3_lib :: RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['name']);
  735. $thisfile_mpeg_audio_lame_RGAD_track['originator'] = getid3_lib :: RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['originator']);
  736. $thisfile_mpeg_audio_lame_RGAD_track['gain_db'] = getid3_lib :: RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit']);
  737. if (! empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']))
  738. {
  739. $ThisFileInfo['replay_gain']['track']['peak'] = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
  740. }
  741. $ThisFileInfo['replay_gain']['track']['originator'] = $thisfile_mpeg_audio_lame_RGAD_track['originator'];
  742. $ThisFileInfo['replay_gain']['track']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_track['gain_db'];
  743. }
  744. else
  745. {
  746. unset($thisfile_mpeg_audio_lame_RGAD['track']);
  747. }
  748. if ($thisfile_mpeg_audio_lame_raw['RGAD_album'] != 0)
  749. {
  750. $thisfile_mpeg_audio_lame_RGAD_album['raw']['name'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0xE000) >> 13;
  751. $thisfile_mpeg_audio_lame_RGAD_album['raw']['originator'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x1C00) >> 10;
  752. $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x0200) >> 9;
  753. $thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x01FF;
  754. $thisfile_mpeg_audio_lame_RGAD_album['name'] = getid3_lib :: RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['name']);
  755. $thisfile_mpeg_audio_lame_RGAD_album['originator'] = getid3_lib :: RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['originator']);
  756. $thisfile_mpeg_audio_lame_RGAD_album['gain_db'] = getid3_lib :: RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit']);
  757. if (! empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']))
  758. {
  759. $ThisFileInfo['replay_gain']['album']['peak'] = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
  760. }
  761. $ThisFileInfo['replay_gain']['album']['originator'] = $thisfile_mpeg_audio_lame_RGAD_album['originator'];
  762. $ThisFileInfo['replay_gain']['album']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_album['gain_db'];
  763. }
  764. else
  765. {
  766. unset($thisfile_mpeg_audio_lame_RGAD['album']);
  767. }
  768. if (empty($thisfile_mpeg_audio_lame_RGAD))
  769. {
  770. unset($thisfile_mpeg_audio_lame['RGAD']);
  771. }
  772. // byte $AF Encoding flags + ATH Type
  773. $EncodingFlagsATHtype = getid3_lib :: BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAF, 1));
  774. $thisfile_mpeg_audio_lame['encoding_flags']['nspsytune'] = (bool) ($EncodingFlagsATHtype & 0x10);
  775. $thisfile_mpeg_audio_lame['encoding_flags']['nssafejoint'] = (bool) ($EncodingFlagsATHtype & 0x20);
  776. $thisfile_mpeg_audio_lame['encoding_flags']['nogap_next'] = (bool) ($EncodingFlagsATHtype & 0x40);
  777. $thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev'] = (bool) ($EncodingFlagsATHtype & 0x80);
  778. $thisfile_mpeg_audio_lame['ath_type'] = $EncodingFlagsATHtype & 0x0F;
  779. // byte $B0 if ABR {specified bitrate} else {minimal bitrate}
  780. $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] = getid3_lib :: BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB0, 1));
  781. if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 2)
  782. { // Average BitRate (ABR)
  783. $thisfile_mpeg_audio_lame['bitrate_abr'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
  784. }
  785. elseif ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1)
  786. { // Constant BitRate (CBR)
  787. // ignore
  788. }
  789. elseif ($thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] > 0)
  790. { // Variable BitRate (VBR) - minimum bitrate
  791. $thisfile_mpeg_audio_lame['bitrate_min'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
  792. }
  793. // bytes $B1-$B3 Encoder delays
  794. $EncoderDelays = getid3_lib :: BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB1, 3));
  795. $thisfile_mpeg_audio_lame['encoder_delay'] = ($EncoderDelays & 0xFFF000) >> 12;
  796. $thisfile_mpeg_audio_lame['end_padding'] = $EncoderDelays & 0x000FFF;
  797. // byte $B4 Misc
  798. $MiscByte = getid3_lib :: BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB4, 1));
  799. $thisfile_mpeg_audio_lame_raw['noise_shaping'] = ($MiscByte & 0x03);
  800. $thisfile_mpeg_audio_lame_raw['stereo_mode'] = ($MiscByte & 0x1C) >> 2;
  801. $thisfile_mpeg_audio_lame_raw['not_optimal_quality'] = ($MiscByte & 0x20) >> 5;
  802. $thisfile_mpeg_audio_lame_raw['source_sample_freq'] = ($MiscByte & 0xC0) >> 6;
  803. $thisfile_mpeg_audio_lame['noise_shaping'] = $thisfile_mpeg_audio_lame_raw['noise_shaping'];
  804. $thisfile_mpeg_audio_lame['stereo_mode'] = getid3_mp3 :: LAMEmiscStereoModeLookup($thisfile_mpeg_audio_lame_raw['stereo_mode']);
  805. $thisfile_mpeg_audio_lame['not_optimal_quality'] = (bool) $thisfile_mpeg_audio_lame_raw['not_optimal_quality'];
  806. $thisfile_mpeg_audio_lame['source_sample_freq'] = getid3_mp3 :: LAMEmiscSourceSampleFrequencyLookup($thisfile_mpeg_audio_lame_raw['source_sample_freq']);
  807. // byte $B5 MP3 Gain
  808. $thisfile_mpeg_audio_lame_raw['mp3_gain'] = getid3_lib :: BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB5, 1), false, true);
  809. $thisfile_mpeg_audio_lame['mp3_gain_db'] = (getid3_lib :: RGADamplitude2dB(2) / 4) * $thisfile_mpeg_audio_lame_raw['mp3_gain'];
  810. $thisfile_mpeg_audio_lame['mp3_gain_factor'] = pow(2, ($thisfile_mpeg_audio_lame['mp3_gain_db'] / 6));
  811. // bytes $B6-$B7 Preset and surround info
  812. $PresetSurroundBytes = getid3_lib :: BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB6, 2));
  813. // Reserved = ($PresetSurroundBytes & 0xC000);
  814. $thisfile_mpeg_audio_lame_raw['surround_info'] = ($PresetSurroundBytes & 0x3800);
  815. $thisfile_mpeg_audio_lame['surround_info'] = getid3_mp3 :: LAMEsurroundInfoLookup($thisfile_mpeg_audio_lame_raw['surround_info']);
  816. $thisfile_mpeg_audio_lame['preset_used_id'] = ($PresetSurroundBytes & 0x07FF);
  817. $thisfile_mpeg_audio_lame['preset_used'] = getid3_mp3 :: LAMEpresetUsedLookup($thisfile_mpeg_audio_lame);
  818. if (! empty($thisfile_mpeg_audio_lame['preset_used_id']) && empty($thisfile_mpeg_audio_lame['preset_used']))
  819. {
  820. $ThisFileInfo['warning'][] = 'Unknown LAME preset used (' . $thisfile_mpeg_audio_lame['preset_used_id'] . ') - please report to info@getid3.org';
  821. }
  822. if (($thisfile_mpeg_audio_lame['short_version'] == 'LAME3.90.') && ! empty($thisfile_mpeg_audio_lame['preset_used_id']))
  823. {
  824. // this may change if 3.90.4 ever comes out
  825. $thisfile_mpeg_audio_lame['short_version'] = 'LAME3.90.3';
  826. }
  827. // bytes $B8-$BB MusicLength
  828. $thisfile_mpeg_audio_lame['audio_bytes'] = getid3_lib :: BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB8, 4));
  829. $ExpectedNumberOfAudioBytes = (($thisfile_mpeg_audio_lame['audio_bytes'] > 0) ? $thisfile_mpeg_audio_lame['audio_bytes'] : $thisfile_mpeg_audio['VBR_bytes']);
  830. // bytes $BC-$BD MusicCRC
  831. $thisfile_mpeg_audio_lame['music_crc'] = getid3_lib :: BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBC, 2));
  832. // bytes $BE-$BF CRC-16 of Info Tag
  833. $thisfile_mpeg_audio_lame['lame_tag_crc'] = getid3_lib :: BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBE, 2));
  834. // LAME CBR
  835. if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1)
  836. {
  837. $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
  838. $thisfile_mpeg_audio['bitrate'] = getid3_mp3 :: ClosestStandardMP3Bitrate($thisfile_mpeg_audio['bitrate']);
  839. $ThisFileInfo['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
  840. //if (empty($thisfile_mpeg_audio['bitrate']) || (!empty($thisfile_mpeg_audio_lame['bitrate_min']) && ($thisfile_mpeg_audio_lame['bitrate_min'] != 255))) {
  841. // $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio_lame['bitrate_min'];
  842. //}
  843. }
  844. }
  845. }
  846. }
  847. else
  848. {
  849. // not Fraunhofer or Xing VBR methods, most likely CBR (but could be VBR with no header)
  850. $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
  851. if ($recursivesearch)
  852. {
  853. $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
  854. if (getid3_mp3 :: RecursiveFrameScanning($fd, $ThisFileInfo, $offset, $nextframetestoffset, true))
  855. {
  856. $recursivesearch = false;
  857. $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
  858. }
  859. if ($thisfile_mpeg_audio['bitrate_mode'] == 'vbr')
  860. {
  861. $ThisFileInfo['warning'][] = 'VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.';
  862. }
  863. }
  864. }
  865. }
  866. if (($ExpectedNumberOfAudioBytes > 0) && ($ExpectedNumberOfAudioBytes != ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])))
  867. {
  868. if ($ExpectedNumberOfAudioBytes > ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']))
  869. {
  870. if (@$ThisFileInfo['fileformat'] == 'riff')
  871. {
  872. // ignore, audio data is broken into chunks so will always be data "missing"
  873. }
  874. elseif (($ExpectedNumberOfAudioBytes - ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])) == 1)
  875. {
  876. $ThisFileInfo['warning'][] = 'Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)';
  877. }
  878. else
  879. {
  880. $ThisFileInfo['warning'][] = 'Probable truncated file: expecting ' . $ExpectedNumberOfAudioBytes . ' bytes of audio data, only found ' . ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) . ' (short by ' . ($ExpectedNumberOfAudioBytes - ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])) . ' bytes)';
  881. }
  882. }
  883. else
  884. {
  885. if ((($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) - $ExpectedNumberOfAudioBytes) == 1)
  886. {
  887. // $prenullbytefileoffset = ftell($fd);
  888. // fseek($fd, $ThisFileInfo['avdataend'], SEEK_SET);
  889. // $PossibleNullByte = fread($fd, 1);
  890. // fseek($fd, $prenullbytefileoffset, SEEK_SET);
  891. // if ($PossibleNullByte === "\x00") {
  892. $ThisFileInfo['avdataend'] --;
  893. // $ThisFileInfo['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
  894. // } else {
  895. // $ThisFileInfo['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' ('.(($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)';
  896. // }
  897. }
  898. else
  899. {
  900. $ThisFileInfo['warning'][] = 'Too much data in file: expecting ' . $ExpectedNumberOfAudioBytes . ' bytes of audio data, found ' . ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) . ' (' . (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) - $ExpectedNumberOfAudioBytes) . ' bytes too many)';
  901. }
  902. }
  903. }
  904. if (($thisfile_mpeg_audio['bitrate'] == 'free') && empty($ThisFileInfo['audio']['bitrate']))
  905. {
  906. if (($offset == $ThisFileInfo['avdataoffset']) && empty($thisfile_mpeg_audio['VBR_frames']))
  907. {
  908. $framebytelength = getid3_mp3 :: FreeFormatFrameLength($fd, $offset, $ThisFileInfo, true);
  909. if ($framebytelength > 0)
  910. {
  911. $thisfile_mpeg_audio['framelength'] = $framebytelength;
  912. if ($thisfile_mpeg_audio['layer'] == '1')
  913. {
  914. // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
  915. $ThisFileInfo['audio']['bitrate'] = ((($framebytelength / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
  916. }
  917. else
  918. {
  919. // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
  920. $ThisFileInfo['audio']['bitrate'] = (($framebytelength - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
  921. }
  922. }
  923. else
  924. {
  925. $ThisFileInfo['error'][] = 'Error calculating frame length of free-format MP3 without Xing/LAME header';
  926. }
  927. }
  928. }
  929. if (@$thisfile_mpeg_audio['VBR_frames'])
  930. {
  931. switch ($thisfile_mpeg_audio['bitrate_mode'])
  932. {
  933. case 'vbr' :
  934. case 'abr' :
  935. if (($thisfile_mpeg_audio['version'] == '1') && ($thisfile_mpeg_audio['layer'] == 1))
  936. {
  937. $thisfile_mpeg_audio['VBR_bitrate'] = ((@$thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($ThisFileInfo['audio']['sample_rate'] / 384);
  938. }
  939. elseif ((($thisfile_mpeg_audio['version'] == '2') || ($thisfile_mpeg_audio['version'] == '2.5')) && ($thisfile_mpeg_audio['layer'] == 3))
  940. {
  941. $thisfile_mpeg_audio['VBR_bitrate'] = ((@$thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($ThisFileInfo['audio']['sample_rate'] / 576);
  942. }
  943. else
  944. {
  945. $thisfile_mpeg_audio['VBR_bitrate'] = ((@$thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($ThisFileInfo['audio']['sample_rate'] / 1152);
  946. }
  947. if ($thisfile_mpeg_audio['VBR_bitrate'] > 0)
  948. {
  949. $ThisFileInfo['audio']['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate'];
  950. $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; // to avoid confusion
  951. }
  952. break;
  953. }
  954. }
  955. // End variable-bitrate headers
  956. ////////////////////////////////////////////////////////////////////////////////////
  957. if ($recursivesearch)
  958. {
  959. if (! getid3_mp3 :: RecursiveFrameScanning($fd, $ThisFileInfo, $offset, $nextframetestoffset, $ScanAsCBR))
  960. {
  961. return false;
  962. }
  963. }
  964. //if (false) {
  965. // // experimental side info parsing section - not returning anything useful yet
  966. //
  967. // $SideInfoBitstream = getid3_lib::BigEndian2Bin($SideInfoData);
  968. // $SideInfoOffset = 0;
  969. //
  970. // if ($thisfile_mpeg_audio['version'] == '1') {
  971. // if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
  972. // // MPEG-1 (mono)
  973. // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
  974. // $SideInfoOffset += 9;
  975. // $SideInfoOffset += 5;
  976. // } else {
  977. // // MPEG-1 (stereo, joint-stereo, dual-channel)
  978. // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
  979. // $SideInfoOffset += 9;
  980. // $SideInfoOffset += 3;
  981. // }
  982. // } else { // 2 or 2.5
  983. // if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
  984. // // MPEG-2, MPEG-2.5 (mono)
  985. // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
  986. // $SideInfoOffset += 8;
  987. // $SideInfoOffset += 1;
  988. // } else {
  989. // // MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel)
  990. // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
  991. // $SideInfoOffset += 8;
  992. // $SideInfoOffset += 2;
  993. // }
  994. // }
  995. //
  996. // if ($thisfile_mpeg_audio['version'] == '1') {
  997. // for ($channel = 0; $channel < $ThisFileInfo['audio']['channels']; $channel++) {
  998. // for ($scfsi_band = 0; $scfsi_band < 4; $scfsi_band++) {
  999. // $thisfile_mpeg_audio['scfsi'][$channel][$scfsi_band] = substr($SideInfoBitstream, $SideInfoOffset, 1);
  1000. // $SideInfoOffset += 2;
  1001. // }
  1002. // }
  1003. // }
  1004. // for ($granule = 0; $granule < (($thisfile_mpeg_audio['version'] == '1') ? 2 : 1); $granule++) {
  1005. // for ($channel = 0; $channel < $ThisFileInfo['audio']['channels']; $channel++) {
  1006. // $thisfile_mpeg_audio['part2_3_length'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 12);
  1007. // $SideInfoOffset += 12;
  1008. // $thisfile_mpeg_audio['big_values'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
  1009. // $SideInfoOffset += 9;
  1010. // $thisfile_mpeg_audio['global_gain'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 8);
  1011. // $SideInfoOffset += 8;
  1012. // if ($thisfile_mpeg_audio['version'] == '1') {
  1013. // $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
  1014. // $SideInfoOffset += 4;
  1015. // } else {
  1016. // $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
  1017. // $SideInfoOffset += 9;
  1018. // }
  1019. // $thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
  1020. // $SideInfoOffset += 1;
  1021. //
  1022. // if ($thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] == '1') {
  1023. //
  1024. // $thisfile_mpeg_audio['block_type'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 2);
  1025. // $SideInfoOffset += 2;
  1026. // $thisfile_mpeg_audio['mixed_block_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
  1027. // $SideInfoOffset += 1;
  1028. //
  1029. // for ($region = 0; $region < 2; $region++) {
  1030. // $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
  1031. // $SideInfoOffset += 5;
  1032. // }
  1033. // $thisfile_mpeg_audio['table_select'][$granule][$channel][2] = 0;
  1034. //
  1035. // for ($window = 0; $window < 3; $window++) {
  1036. // $thisfile_mpeg_audio['subblock_gain'][$granule][$channel][$window] = substr($SideInfoBitstream, $SideInfoOffset, 3);
  1037. // $SideInfoOffset += 3;
  1038. // }
  1039. //
  1040. // } else {
  1041. //
  1042. // for ($region = 0; $region < 3; $region++) {
  1043. // $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
  1044. // $SideInfoOffset += 5;
  1045. // }
  1046. //
  1047. // $thisfile_mpeg_audio['region0_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
  1048. // $SideInfoOffset += 4;
  1049. // $thisfile_mpeg_audio['region1_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 3);
  1050. // $SideInfoOffset += 3;
  1051. // $thisfile_mpeg_audio['block_type'][$granule][$channel] = 0;
  1052. // }
  1053. //
  1054. // if ($thisfile_mpeg_audio['version'] == '1') {
  1055. // $thisfile_mpeg_audio['preflag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
  1056. // $SideInfoOffset += 1;
  1057. // }
  1058. // $thisfile_mpeg_audio['scalefac_scale'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
  1059. // $SideInfoOffset += 1;
  1060. // $thisfile_mpeg_audio['count1table_select'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
  1061. // $SideInfoOffset += 1;
  1062. // }
  1063. // }
  1064. //}
  1065. return true;
  1066. }
  1067. function RecursiveFrameScanning(&$fd, &$ThisFileInfo, &$offset, &$nextframetestoffset, $ScanAsCBR)
  1068. {
  1069. for($i = 0; $i < GETID3_MP3_VALID_CHECK_FRAMES; $i ++)
  1070. {
  1071. // check next GETID3_MP3_VALID_CHECK_FRAMES frames for validity, to make sure we haven't run across a false synch
  1072. if (($nextframetestoffset + 4) >= $ThisFileInfo['avdataend'])
  1073. {
  1074. // end of file
  1075. return true;
  1076. }
  1077. $nextframetestarray = array('error' => '', 'warning' => '', 'avdataend' => $ThisFileInfo['avdataend'],
  1078. 'avdataoffset' => $ThisFileInfo['avdataoffset']);
  1079. if (getid3_mp3 :: decodeMPEGaudioHeader($fd, $nextframetestoffset, $nextframetestarray, false))
  1080. {
  1081. if ($ScanAsCBR)
  1082. {
  1083. // force CBR mode, used for trying to pick out invalid audio streams with
  1084. // valid(?) VBR headers, or VBR streams with no VBR header
  1085. if (! isset($nextframetestarray['mpeg']['audio']['bitrate']) || ! isset($ThisFileInfo['mpeg']['audio']['bitrate']) || ($nextframetestarray['mpeg']['audio']['bitrate'] != $ThisFileInfo['mpeg']['audio']['bitrate']))
  1086. {
  1087. return false;
  1088. }
  1089. }
  1090. // next frame is OK, get ready to check the one after that
  1091. if (isset($nextframetestarray['mpeg']['audio']['framelength']) && ($nextframetestarray['mpeg']['audio']['framelength'] > 0))
  1092. {
  1093. $nextframetestoffset += $nextframetestarray['mpeg']['audio']['framelength'];
  1094. }
  1095. else
  1096. {
  1097. $ThisFileInfo['error'][] = 'Frame at offset (' . $offset . ') is has an invalid frame length.';
  1098. return false;
  1099. }
  1100. }
  1101. else
  1102. {
  1103. // next frame is not valid, note the error and fail, so scanning can contiue for a valid frame sequence
  1104. $ThisFileInfo['error'][] = 'Frame at offset (' . $offset . ') is valid, but the next one at (' . $nextframetestoffset . ') is not.';
  1105. return false;
  1106. }
  1107. }
  1108. return true;
  1109. }
  1110. function FreeFormatFrameLength($fd, $offset, &$ThisFileInfo, $deepscan = false)
  1111. {
  1112. fseek($fd, $offset, SEEK_SET);
  1113. $MPEGaudioData = fread($fd, 32768);
  1114. $SyncPattern1 = substr($MPEGaudioData, 0, 4);
  1115. // may be different pattern due to padding
  1116. $SyncPattern2 = $SyncPattern1{0} . $SyncPattern1{1} . chr(ord($SyncPattern1{2}) | 0x02) . $SyncPattern1{3};
  1117. if ($SyncPattern2 === $SyncPattern1)
  1118. {
  1119. $SyncPattern2 = $SyncPattern1{0} . $SyncPattern1{1} . chr(ord($SyncPattern1{2}) & 0xFD) . $SyncPattern1{3};
  1120. }
  1121. $framelength = false;
  1122. $framelength1 = strpos($MPEGaudioData, $SyncPattern1, 4);
  1123. $framelength2 = strpos($MPEGaudioData, $SyncPattern2, 4);
  1124. if ($framelength1 > 4)
  1125. {
  1126. $framelength = $framelength1;
  1127. }
  1128. if (($framelength2 > 4) && ($framelength2 < $framelength1))
  1129. {
  1130. $framelength = $framelength2;
  1131. }
  1132. if (! $framelength)
  1133. {
  1134. // LAME 3.88 has a different value for modeextension on the first frame vs the rest
  1135. $framelength1 = strpos($MPEGaudioData, substr($SyncPattern1, 0, 3), 4);
  1136. $framelength2 = strpos($MPEGaudioData, substr($SyncPattern2, 0, 3), 4);
  1137. if ($framelength1 > 4)
  1138. {
  1139. $framelength = $framelength1;
  1140. }
  1141. if (($framelength2 > 4) && ($framelength2 < $framelength1))
  1142. {
  1143. $framelength = $framelength2;
  1144. }
  1145. if (! $framelength)
  1146. {
  1147. $ThisFileInfo['error'][] = 'Cannot find next free-format synch pattern (' . getid3_lib :: PrintHexBytes($SyncPattern1) . ' or ' . getid3_lib :: PrintHexBytes($SyncPattern2) . ') after offset ' . $offset;
  1148. return false;
  1149. }
  1150. else
  1151. {
  1152. $ThisFileInfo['warning'][] = 'ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)';
  1153. $ThisFileInfo['audio']['codec'] = 'LAME';
  1154. $ThisFileInfo['audio']['encoder'] = 'LAME3.88';
  1155. $SyncPattern1 = substr($SyncPattern1, 0, 3);
  1156. $SyncPattern2 = substr($SyncPattern2, 0, 3);
  1157. }
  1158. }
  1159. if ($deepscan)
  1160. {
  1161. $ActualFrameLengthValues = array();
  1162. $nextoffset = $offset + $framelength;
  1163. while ($nextoffset < ($ThisFileInfo['avdataend'] - 6))
  1164. {
  1165. fseek($fd, $nextoffset - 1, SEEK_SET);
  1166. $NextSyncPattern = fread($fd, 6);
  1167. if ((substr($NextSyncPattern, 1, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 1, strlen($SyncPattern2)) == $SyncPattern2))
  1168. {
  1169. // good - found where expected
  1170. $ActualFrameLengthValues[] = $framelength;
  1171. }
  1172. elseif ((substr($NextSyncPattern, 0, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 0, strlen($SyncPattern2)) == $SyncPattern2))
  1173. {
  1174. // ok - found one byte earlier than expected (last frame wasn't padded, first frame was)
  1175. $ActualFrameLengthValues[] = ($framelength - 1);
  1176. $nextoffset --;
  1177. }
  1178. elseif ((substr($NextSyncPattern, 2, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 2, strlen($SyncPattern2)) == $SyncPattern2))
  1179. {
  1180. // ok - found one byte later than expected (last frame was padded, first frame wasn't)
  1181. $ActualFrameLengthValues[] = ($framelength + 1);
  1182. $nextoffset ++;
  1183. }
  1184. else
  1185. {
  1186. $ThisFileInfo['error'][] = 'Did not find expected free-format sync pattern at offset ' . $nextoffset;
  1187. return false;
  1188. }
  1189. $nextoffset += $framelength;
  1190. }
  1191. if (count($ActualFrameLengthValues) > 0)
  1192. {
  1193. $framelength = intval(round(array_sum($ActualFrameLengthValues) / count($ActualFrameLengthValues)));
  1194. }
  1195. }
  1196. return $framelength;
  1197. }
  1198. function getOnlyMPEGaudioInfoBruteForce($fd, &$ThisFileInfo)
  1199. {
  1200. $MPEGaudioHeaderDecodeCache = array();
  1201. $MPEGaudioHeaderValidCache = array();
  1202. $MPEGaudioHeaderLengthCache = array();
  1203. $MPEGaudioVersionLookup = getid3_mp3 :: MPEGaudioVersionArray();
  1204. $MPEGaudioLayerLookup = getid3_mp3 :: MPEGaudioLayerArray();
  1205. $MPEGaudioBitrateLookup = getid3_mp3 :: MPEGaudioBitrateArray();
  1206. $MPEGaudioFrequencyLookup = getid3_mp3 :: MPEGaudioFrequencyArray();
  1207. $MPEGaudioChannelModeLookup = getid3_mp3 :: MPEGaudioChannelModeArray();
  1208. $MPEGaudioModeExtensionLookup = getid3_mp3 :: MPEGaudioModeExtensionArray();
  1209. $MPEGaudioEmphasisLookup = getid3_mp3 :: MPEGaudioEmphasisArray();
  1210. $LongMPEGversionLookup = array();
  1211. $LongMPEGlayerLookup = array();
  1212. $LongMPEGbitrateLookup = array();
  1213. $LongMPEGpaddingLookup = array();
  1214. $LongMPEGfrequencyLookup = array();
  1215. $Distribution['bitrate'] = array();
  1216. $Distribution['frequency'] = array();
  1217. $Distribution['layer'] = array();
  1218. $Distribution['version'] = array();
  1219. $Distribution['padding'] = array();
  1220. fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
  1221. $max_frames_scan = 5000;
  1222. $frames_scanned = 0;
  1223. $previousvalidframe = $ThisFileInfo['avdataoffset'];
  1224. while (ftell($fd) < $ThisFileInfo['avdataend'])
  1225. {
  1226. set_time_limit(30);
  1227. $head4 = fread($fd, 4);
  1228. if (strlen($head4) < 4)
  1229. {
  1230. break;
  1231. }
  1232. if ($head4{0} != "\xFF")
  1233. {
  1234. for($i = 1; $i < 4; $i ++)
  1235. {
  1236. if ($head4{$i} == "\xFF")
  1237. {
  1238. fseek($fd, $i - 4, SEEK_CUR);
  1239. continue 2;
  1240. }
  1241. }
  1242. continue;
  1243. }
  1244. if (! isset($MPEGaudioHeaderDecodeCache[$head4]))
  1245. {
  1246. $MPEGaudioHeaderDecodeCache[$head4] = getid3_mp3 :: MPEGaudioHeaderDecode($head4);
  1247. }
  1248. if (! isset($MPEGaudioHeaderValidCache[$head4]))
  1249. {
  1250. $MPEGaudioHeaderValidCache[$head4] = getid3_mp3 :: MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$head4], false, false);
  1251. }
  1252. if ($MPEGaudioHeaderValidCache[$head4])
  1253. {
  1254. if (! isset($MPEGaudioHeaderLengthCache[$head4]))
  1255. {
  1256. $LongMPEGversionLookup[$head4] = $MPEGaudioVersionLookup[$MPEGaudioHeaderDecodeCache[$head4]['version']];
  1257. $LongMPEGlayerLookup[$head4] = $MPEGaudioLayerLookup[$MPEGaudioHeaderDecodeCache[$head4]['layer']];
  1258. $LongMPEGbitrateLookup[$head4] = $MPEGaudioBitrateLookup[$LongMPEGversionLookup[$head4]][$LongMPEGlayerLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['bitrate']];
  1259. $LongMPEGpaddingLookup[$head4] = (bool) $MPEGaudioHeaderDecodeCache[$head4]['padding'];
  1260. $LongMPEGfrequencyLookup[$head4] = $MPEGaudioFrequencyLookup[$LongMPEGversionLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['sample_rate']];
  1261. $MPEGaudioHeaderLengthCache[$head4] = getid3_mp3 :: MPEGaudioFrameLength($LongMPEGbitrateLookup[$head4], $LongMPEGversionLookup[$head4], $LongMPEGlayerLookup[$head4], $LongMPEGpaddingLookup[$head4], $LongMPEGfrequencyLookup[$head4]);
  1262. }
  1263. if ($MPEGaudioHeaderLengthCache[$head4] > 4)
  1264. {
  1265. $WhereWeWere = ftell($fd);
  1266. fseek($fd, $MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR);
  1267. $next4 = fread($fd, 4);
  1268. if ($next4{0} == "\xFF")
  1269. {
  1270. if (! isset($MPEGaudioHeaderDecodeCache[$next4]))
  1271. {
  1272. $MPEGaudioHeaderDecodeCache[$next4] = getid3_mp3 :: MPEGaudioHeaderDecode($next4);
  1273. }
  1274. if (! isset($MPEGaudioHeaderValidCache[$next4]))
  1275. {
  1276. $MPEGaudioHeaderValidCache[$next4] = getid3_mp3 :: MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$next4], false, false);
  1277. }
  1278. if ($MPEGaudioHeaderValidCache[$next4])
  1279. {
  1280. fseek($fd, - 4, SEEK_CUR);
  1281. @$Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]] ++;
  1282. @$Distribution['layer'][$LongMPEGlayerLookup[$head4]] ++;
  1283. @$Distribution['version'][$LongMPEGversionLookup[$head4]] ++;
  1284. @$Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])] ++;
  1285. @$Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]] ++;
  1286. if ($max_frames_scan && (++ $frames_scanned >= $max_frames_scan))
  1287. {
  1288. $pct_data_scanned = (ftell($fd) - $ThisFileInfo['avdataoffset']) / ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']);
  1289. $ThisFileInfo['warning'][] = 'too many MPEG audio frames to scan, only scanned first ' . $max_frames_scan . ' frames (' . number_format($pct_data_scanned * 100, 1) . '% of file) and extrapolated distribution, playtime and bitrate may be incorrect.';
  1290. foreach ($Distribution as $key1 => $value1)
  1291. {
  1292. foreach ($value1 as $key2 => $value2)
  1293. {
  1294. $Distribution[$key1][$key2] = round($value2 / $pct_data_scanned);
  1295. }
  1296. }
  1297. break;
  1298. }
  1299. continue;
  1300. }
  1301. }
  1302. unset($next4);
  1303. fseek($fd, $WhereWeWere - 3, SEEK_SET);
  1304. }
  1305. }
  1306. }
  1307. foreach ($Distribution as $key => $value)
  1308. {
  1309. ksort($Distribution[$key], SORT_NUMERIC);
  1310. }
  1311. ksort($Distribution['version'], SORT_STRING);
  1312. $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = $Distribution['bitrate'];
  1313. $ThisFileInfo['mpeg']['audio']['frequency_distribution'] = $Distribution['frequency'];
  1314. $ThisFileInfo['mpeg']['audio']['layer_distribution'] = $Distribution['layer'];
  1315. $ThisFileInfo['mpeg']['audio']['version_distribution'] = $Distribution['version'];
  1316. $ThisFileInfo['mpeg']['audio']['padding_distribution'] = $Distribution['padding'];
  1317. if (count($Distribution['version']) > 1)
  1318. {
  1319. $ThisFileInfo['error'][] = 'Corrupt file - more than one MPEG version detected';
  1320. }
  1321. if (count($Distribution['layer']) > 1)
  1322. {
  1323. $ThisFileInfo['error'][] = 'Corrupt file - more than one MPEG layer detected';
  1324. }
  1325. if (count($Distribution['frequency']) > 1)
  1326. {
  1327. $ThisFileInfo['error'][] = 'Corrupt file - more than one MPEG sample rate detected';
  1328. }
  1329. $bittotal = 0;
  1330. foreach ($Distribution['bitrate'] as $bitratevalue => $bitratecount)
  1331. {
  1332. if ($bitratevalue != 'free')
  1333. {
  1334. $bittotal += ($bitratevalue * $bitratecount);
  1335. }
  1336. }
  1337. $ThisFileInfo['mpeg']['audio']['frame_count'] = array_sum($Distribution['bitrate']);
  1338. if ($ThisFileInfo['mpeg']['audio']['frame_count'] == 0)
  1339. {
  1340. $ThisFileInfo['error'][] = 'no MPEG audio frames found';
  1341. return false;
  1342. }
  1343. $ThisFileInfo['mpeg']['audio']['bitrate'] = ($bittotal / $ThisFileInfo['mpeg']['audio']['frame_count']);
  1344. $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = ((count($Distribution['bitrate']) > 0) ? 'vbr' : 'cbr');
  1345. $ThisFileInfo['mpeg']['audio']['sample_rate'] = getid3_lib :: array_max($Distribution['frequency'], true);
  1346. $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate'];
  1347. $ThisFileInfo['audio']['bitrate_mode'] = $ThisFileInfo['mpeg']['audio']['bitrate_mode'];
  1348. $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate'];
  1349. $ThisFileInfo['audio']['dataformat'] = 'mp' . getid3_lib :: array_max($Distribution['layer'], true);
  1350. $ThisFileInfo['fileformat'] = $ThisFileInfo['audio']['dataformat'];
  1351. return true;
  1352. }
  1353. function getOnlyMPEGaudioInfo($fd, &$ThisFileInfo, $avdataoffset, $BitrateHistogram = false)
  1354. {
  1355. // looks for synch, decodes MPEG audio header
  1356. static $MPEGaudioVersionLookup;
  1357. static $MPEGaudioLayerLookup;
  1358. static $MPEGaudioBitrateLookup;
  1359. if (empty($MPEGaudioVersionLookup))
  1360. {
  1361. $MPEGaudioVersionLookup = getid3_mp3 :: MPEGaudioVersionArray();
  1362. $MPEGaudioLayerLookup = getid3_mp3 :: MPEGaudioLayerArray();
  1363. $MPEGaudioBitrateLookup = getid3_mp3 :: MPEGaudioBitrateArray();
  1364. }
  1365. fseek($fd, $avdataoffset, SEEK_SET);
  1366. $sync_seek_buffer_size = min(128 * 1024, $ThisFileInfo['avdataend'] - $avdataoffset);
  1367. if ($sync_seek_buffer_size <= 0)
  1368. {
  1369. $ThisFileInfo['error'][] = 'Invalid $sync_seek_buffer_size at offset ' . $avdataoffset;
  1370. return false;
  1371. }
  1372. $header = fread($fd, $sync_seek_buffer_size);
  1373. $sync_seek_buffer_size = strlen($header);
  1374. $SynchSeekOffset = 0;
  1375. while ($SynchSeekOffset < $sync_seek_buffer_size)
  1376. {
  1377. if ((($avdataoffset + $SynchSeekOffset) < $ThisFileInfo['avdataend']) && ! feof($fd))
  1378. {
  1379. if ($SynchSeekOffset > $sync_seek_buffer_size)
  1380. {
  1381. // if a synch's not found within the first 128k bytes, then give up
  1382. $ThisFileInfo['error'][] = 'Could not find valid MPEG audio synch within the first ' . round($sync_seek_buffer_size / 1024) . 'kB';
  1383. if (isset($ThisFileInfo['audio']['bitrate']))
  1384. {
  1385. unset($ThisFileInfo['audio']['bitrate']);
  1386. }
  1387. if (isset($ThisFileInfo['mpeg']['audio']))
  1388. {
  1389. unset($ThisFileInfo['mpeg']['audio']);
  1390. }
  1391. if (empty($ThisFileInfo['mpeg']))
  1392. {
  1393. unset($ThisFileInfo['mpeg']);
  1394. }
  1395. return false;
  1396. }
  1397. elseif (feof($fd))
  1398. {
  1399. $ThisFileInfo['error'][] = 'Could not find valid MPEG audio synch before end of file';
  1400. if (isset($ThisFileInfo['audio']['bitrate']))
  1401. {
  1402. unset($ThisFileInfo['audio']['bitrate']);
  1403. }
  1404. if (isset($ThisFileInfo['mpeg']['audio']))
  1405. {
  1406. unset($ThisFileInfo['mpeg']['audio']);
  1407. }
  1408. if (isset($ThisFileInfo['mpeg']) && (! is_array($ThisFileInfo['mpeg']) || (count($ThisFileInfo['mpeg']) == 0)))
  1409. {
  1410. unset($ThisFileInfo['mpeg']);
  1411. }
  1412. return false;
  1413. }
  1414. }
  1415. if (($SynchSeekOffset + 1) >= strlen($header))
  1416. {
  1417. $ThisFileInfo['error'][] = 'Could not find valid MPEG synch before end of file';
  1418. return false;
  1419. }
  1420. if (($header{$SynchSeekOffset} == "\xFF") && ($header{($SynchSeekOffset + 1)} > "\xE0"))
  1421. { // synch detected
  1422. if (! isset($FirstFrameThisfileInfo) && ! isset($ThisFileInfo['mpeg']['audio']))
  1423. {
  1424. $FirstFrameThisfileInfo = $ThisFileInfo;
  1425. $FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset;
  1426. if (! getid3_mp3 :: decodeMPEGaudioHeader($fd, $avdataoffset + $SynchSeekOffset, $FirstFrameThisfileInfo, false))
  1427. {
  1428. // if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's
  1429. // garbage between this frame and a valid sequence of MPEG-audio frames, to be restored below
  1430. unset($FirstFrameThisfileInfo);
  1431. }
  1432. }
  1433. $dummy = $ThisFileInfo; // only overwrite real data if valid header found
  1434. if (getid3_mp3 :: decodeMPEGaudioHeader($fd, $avdataoffset + $SynchSeekOffset, $dummy, true))
  1435. {
  1436. $ThisFileInfo = $dummy;
  1437. $ThisFileInfo['avdataoffset'] = $avdataoffset + $SynchSeekOffset;
  1438. switch (@$ThisFileInfo['fileformat'])
  1439. {
  1440. case '' :
  1441. case 'id3' :
  1442. case 'ape' :
  1443. case 'mp3' :
  1444. $ThisFileInfo['fileformat'] = 'mp3';
  1445. $ThisFileInfo['audio']['dataformat'] = 'mp3';
  1446. break;
  1447. }
  1448. if (isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr'))
  1449. {
  1450. if (! (abs($ThisFileInfo['audio']['bitrate'] - $FirstFrameThisfileInfo['audio']['bitrate']) <= 1))
  1451. {
  1452. // If there is garbage data between a valid VBR header frame and a sequence
  1453. // of valid MPEG-audio frames the VBR data is no longer discarded.
  1454. $ThisFileInfo = $FirstFrameThisfileInfo;
  1455. $ThisFileInfo['avdataoffset'] = $FirstFrameAVDataOffset;
  1456. $ThisFileInfo['fileformat'] = 'mp3';
  1457. $ThisFileInfo['audio']['dataformat'] = 'mp3';
  1458. $dummy = $ThisFileInfo;
  1459. unset($dummy['mpeg']['audio']);
  1460. $GarbageOffsetStart = $FirstFrameAVDataOffset + $FirstFrameThisfileInfo['mpeg']['audio']['framelength'];
  1461. $GarbageOffsetEnd = $avdataoffset + $SynchSeekOffset;
  1462. if (getid3_mp3 :: decodeMPEGaudioHeader($fd, $GarbageOffsetEnd, $dummy, true, true))
  1463. {
  1464. $ThisFileInfo = $dummy;
  1465. $ThisFileInfo['avdataoffset'] = $GarbageOffsetEnd;
  1466. $ThisFileInfo['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;
  1467. }
  1468. else
  1469. {
  1470. $ThisFileInfo['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 . ')';
  1471. }
  1472. }
  1473. }
  1474. if (isset($ThisFileInfo['mpeg']['audio']['bitrate_mode']) && ($ThisFileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr') && ! isset($ThisFileInfo['mpeg']['audio']['VBR_method']))
  1475. {
  1476. // VBR file with no VBR header
  1477. $BitrateHistogram = true;
  1478. }
  1479. if ($BitrateHistogram)
  1480. {
  1481. $ThisFileInfo['mpeg']['audio']['stereo_distribution'] = array('stereo' => 0, 'joint stereo' => 0,
  1482. 'dual channel' => 0, 'mono' => 0);
  1483. $ThisFileInfo['mpeg']['audio']['version_distribution'] = array('1' => 0, '2' => 0, '2.5' => 0);
  1484. if ($ThisFileInfo['mpeg']['audio']['version'] == '1')
  1485. {
  1486. if ($ThisFileInfo['mpeg']['audio']['layer'] == 3)
  1487. {
  1488. $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free' => 0, 32000 => 0,
  1489. 40000 => 0, 48000 => 0, 56000 => 0, 64000 => 0, 80000 => 0, 96000 => 0,
  1490. 112000 => 0, 128000 => 0, 160000 => 0, 192000 => 0, 224000 => 0, 256000 => 0,
  1491. 320000 => 0);
  1492. }
  1493. elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 2)
  1494. {
  1495. $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free' => 0, 32000 => 0,
  1496. 48000 => 0, 56000 => 0, 64000 => 0, 80000 => 0, 96000 => 0, 112000 => 0,
  1497. 128000 => 0, 160000 => 0, 192000 => 0, 224000 => 0, 256000 => 0, 320000 => 0,
  1498. 384000 => 0);
  1499. }
  1500. elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 1)
  1501. {
  1502. $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free' => 0, 32000 => 0,
  1503. 64000 => 0, 96000 => 0, 128000 => 0, 160000 => 0, 192000 => 0, 224000 => 0,
  1504. 256000 => 0, 288000 => 0, 320000 => 0, 352000 => 0, 384000 => 0, 416000 => 0,
  1505. 448000 => 0);
  1506. }
  1507. }
  1508. elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 1)
  1509. {
  1510. $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free' => 0, 32000 => 0,
  1511. 48000 => 0, 56000 => 0, 64000 => 0, 80000 => 0, 96000 => 0, 112000 => 0, 128000 => 0,
  1512. 144000 => 0, 160000 => 0, 176000 => 0, 192000 => 0, 224000 => 0, 256000 => 0);
  1513. }
  1514. else
  1515. {
  1516. $ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free' => 0, 8000 => 0,
  1517. 16000 => 0, 24000 => 0, 32000 => 0, 40000 => 0, 48000 => 0, 56000 => 0, 64000 => 0,
  1518. 80000 => 0, 96000 => 0, 112000 => 0, 128000 => 0, 144000 => 0, 160000 => 0);
  1519. }
  1520. $dummy = array('error' => $ThisFileInfo['error'], 'warning' => $ThisFileInfo['warning'],
  1521. 'avdataend' => $ThisFileInfo['avdataend'], 'avdataoffset' => $ThisFileInfo['avdataoffset']);
  1522. $synchstartoffset = $ThisFileInfo['avdataoffset'];
  1523. fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
  1524. // you can play with these numbers:
  1525. $max_frames_scan = 50000;
  1526. $max_scan_segments = 10;
  1527. // don't play with these numbers:
  1528. $FastMode = false;
  1529. $SynchErrorsFound = 0;
  1530. $frames_scanned = 0;
  1531. $this_scan_segment = 0;
  1532. $frames_scan_per_segment = ceil($max_frames_scan / $max_scan_segments);
  1533. $pct_data_scanned = 0;
  1534. for($current_segment = 0; $current_segment < $max_scan_segments; $current_segment ++)
  1535. {
  1536. //echo 'was at '.ftell($fd).'<br>';
  1537. $frames_scanned_this_segment = 0;
  1538. if (ftell($fd) >= $ThisFileInfo['avdataend'])
  1539. {
  1540. //echo 'breaking because current position ('.ftell($fd).') is >= $ThisFileInfo[avdataend] ('.$ThisFileInfo['avdataend'].')<br>';
  1541. break;
  1542. }
  1543. $scan_start_offset[$current_segment] = max(ftell($fd), $ThisFileInfo['avdataoffset'] + round($current_segment * (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $max_scan_segments)));
  1544. //echo 'start at '.$scan_start_offset[$current_segment].'<br>';
  1545. if ($current_segment > 0)
  1546. {
  1547. fseek($fd, $scan_start_offset[$current_segment], SEEK_SET);
  1548. $buffer_4k = fread($fd, 4096);
  1549. for($j = 0; $j < (strlen($buffer_4k) - 4); $j ++)
  1550. {
  1551. if (($buffer_4k{$j} == "\xFF") && ($buffer_4k{($j + 1)} > "\xE0"))
  1552. { // synch detected
  1553. if (getid3_mp3 :: decodeMPEGaudioHeader($fd, $scan_start_offset[$current_segment] + $j, $dummy, false, false, $FastMode))
  1554. {
  1555. $calculated_next_offset = $scan_start_offset[$current_segment] + $j + $dummy['mpeg']['audio']['framelength'];
  1556. if (getid3_mp3 :: decodeMPEGaudioHeader($fd, $calculated_next_offset, $dummy, false, false, $FastMode))
  1557. {
  1558. $scan_start_offset[$current_segment] += $j;
  1559. break;
  1560. }
  1561. else
  1562. {
  1563. //echo 'header['.__LINE__.'] at '.($calculated_next_offset).' invalid<br>';
  1564. }
  1565. }
  1566. else
  1567. {
  1568. //echo 'header['.__LINE__.'] at '.($scan_start_offset[$current_segment] + $j).' invalid<br>';
  1569. }
  1570. }
  1571. }
  1572. }
  1573. //echo 'actually start at '.$scan_start_offset[$current_segment].'<br>';
  1574. $synchstartoffset = $scan_start_offset[$current_segment];
  1575. while (getid3_mp3 :: decodeMPEGaudioHeader($fd, $synchstartoffset, $dummy, false, false, $FastMode))
  1576. {
  1577. $FastMode = true;
  1578. $thisframebitrate = $MPEGaudioBitrateLookup[$MPEGaudioVersionLookup[$dummy['mpeg']['audio']['raw']['version']]][$MPEGaudioLayerLookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']];
  1579. if (empty($dummy['mpeg']['audio']['framelength']))
  1580. {
  1581. $SynchErrorsFound ++;
  1582. $synchstartoffset ++;
  1583. //echo ' [ďż˝] ';
  1584. }
  1585. else
  1586. {
  1587. //echo ' . ';
  1588. @$ThisFileInfo['mpeg']['audio']['bitrate_distribution'][$thisframebitrate] ++;
  1589. @$ThisFileInfo['mpeg']['audio']['stereo_distribution'][$dummy['mpeg']['audio']['channelmode']] ++;
  1590. @$ThisFileInfo['mpeg']['audio']['version_distribution'][$dummy['mpeg']['audio']['version']] ++;
  1591. $synchstartoffset += $dummy['mpeg']['audio']['framelength'];
  1592. }
  1593. $frames_scanned ++;
  1594. if ($frames_scan_per_segment && (++ $frames_scanned_this_segment >= $frames_scan_per_segment))
  1595. {
  1596. $this_pct_scanned = (ftell($fd) - $scan_start_offset[$current_segment]) / ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']);
  1597. if (($current_segment == 0) && (($this_pct_scanned * $max_scan_segments) >= 1))
  1598. {
  1599. // file likely contains < $max_frames_scan, just scan as one segment
  1600. $max_scan_segments = 1;
  1601. $frames_scan_per_segment = $max_frames_scan;
  1602. }
  1603. else
  1604. {
  1605. $pct_data_scanned += $this_pct_scanned;
  1606. //var_dump($pct_data_scanned);
  1607. //exit;
  1608. break;
  1609. }
  1610. }
  1611. }
  1612. //echo '<hr>';
  1613. }
  1614. if ($pct_data_scanned > 0)
  1615. {
  1616. $ThisFileInfo['warning'][] = 'too many MPEG audio frames to scan, only scanned ' . $frames_scanned . ' frames in ' . $max_scan_segments . ' segments (' . number_format($pct_data_scanned * 100, 1) . '% of file) and extrapolated distribution, playtime and bitrate may be incorrect.';
  1617. foreach ($ThisFileInfo['mpeg']['audio'] as $key1 => $value1)
  1618. {
  1619. if (! eregi('_distribution$', $key1))
  1620. {
  1621. continue;
  1622. }
  1623. foreach ($value1 as $key2 => $value2)
  1624. {
  1625. $ThisFileInfo['mpeg']['audio'][$key1][$key2] = round($value2 / $pct_data_scanned);
  1626. }
  1627. }
  1628. }
  1629. if ($SynchErrorsFound > 0)
  1630. {
  1631. $ThisFileInfo['warning'][] = 'Found ' . $SynchErrorsFound . ' synch errors in histogram analysis';
  1632. //return false;
  1633. }
  1634. $bittotal = 0;
  1635. $framecounter = 0;
  1636. foreach ($ThisFileInfo['mpeg']['audio']['bitrate_distribution'] as $bitratevalue => $bitratecount)
  1637. {
  1638. $framecounter += $bitratecount;
  1639. if ($bitratevalue != 'free')
  1640. {
  1641. $bittotal += ($bitratevalue * $bitratecount);
  1642. }
  1643. }
  1644. if ($framecounter == 0)
  1645. {
  1646. $ThisFileInfo['error'][] = 'Corrupt MP3 file: framecounter == zero';
  1647. return false;
  1648. }
  1649. $ThisFileInfo['mpeg']['audio']['frame_count'] = getid3_lib :: CastAsInt($framecounter);
  1650. $ThisFileInfo['mpeg']['audio']['bitrate'] = ($bittotal / $framecounter);
  1651. $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate'];
  1652. // Definitively set VBR vs CBR, even if the Xing/LAME/VBRI header says differently
  1653. $distinct_bitrates = 0;
  1654. foreach ($ThisFileInfo['mpeg']['audio']['bitrate_distribution'] as $bitrate_value => $bitrate_count)
  1655. {
  1656. if ($bitrate_count > 0)
  1657. {
  1658. $distinct_bitrates ++;
  1659. }
  1660. }
  1661. if ($distinct_bitrates > 1)
  1662. {
  1663. $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'vbr';
  1664. }
  1665. else
  1666. {
  1667. $ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'cbr';
  1668. }
  1669. $ThisFileInfo['audio']['bitrate_mode'] = $ThisFileInfo['mpeg']['audio']['bitrate_mode'];
  1670. }
  1671. break; // exit while()
  1672. }
  1673. }
  1674. $SynchSeekOffset ++;
  1675. if (($avdataoffset + $SynchSeekOffset) >= $ThisFileInfo['avdataend'])
  1676. {
  1677. // end of file/data
  1678. if (empty($ThisFileInfo['mpeg']['audio']))
  1679. {
  1680. $ThisFileInfo['error'][] = 'could not find valid MPEG synch before end of file';
  1681. if (isset($ThisFileInfo['audio']['bitrate']))
  1682. {
  1683. unset($ThisFileInfo['audio']['bitrate']);
  1684. }
  1685. if (isset($ThisFileInfo['mpeg']['audio']))
  1686. {
  1687. unset($ThisFileInfo['mpeg']['audio']);
  1688. }
  1689. if (isset($ThisFileInfo['mpeg']) && (! is_array($ThisFileInfo['mpeg']) || empty($ThisFileInfo['mpeg'])))
  1690. {
  1691. unset($ThisFileInfo['mpeg']);
  1692. }
  1693. return false;
  1694. }
  1695. break;
  1696. }
  1697. }
  1698. $ThisFileInfo['audio']['channels'] = $ThisFileInfo['mpeg']['audio']['channels'];
  1699. $ThisFileInfo['audio']['channelmode'] = $ThisFileInfo['mpeg']['audio']['channelmode'];
  1700. $ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate'];
  1701. return true;
  1702. }
  1703. function MPEGaudioVersionArray()
  1704. {
  1705. static $MPEGaudioVersion = array('2.5', false, '2', '1');
  1706. return $MPEGaudioVersion;
  1707. }
  1708. function MPEGaudioLayerArray()
  1709. {
  1710. static $MPEGaudioLayer = array(false, 3, 2, 1);
  1711. return $MPEGaudioLayer;
  1712. }
  1713. function MPEGaudioBitrateArray()
  1714. {
  1715. static $MPEGaudioBitrate;
  1716. if (empty($MPEGaudioBitrate))
  1717. {
  1718. $MPEGaudioBitrate = array(
  1719. '1' => array(
  1720. 1 => array('free', 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000,
  1721. 320000, 352000, 384000, 416000, 448000),
  1722. 2 => array('free', 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000,
  1723. 224000, 256000, 320000, 384000),
  1724. 3 => array('free', 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000,
  1725. 192000, 224000, 256000, 320000)),
  1726. '2' => array(
  1727. 1 => array('free', 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000,
  1728. 176000, 192000, 224000, 256000),
  1729. 2 => array('free', 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000,
  1730. 112000, 128000, 144000, 160000)));
  1731. $MPEGaudioBitrate['2'][3] = $MPEGaudioBitrate['2'][2];
  1732. $MPEGaudioBitrate['2.5'] = $MPEGaudioBitrate['2'];
  1733. }
  1734. return $MPEGaudioBitrate;
  1735. }
  1736. function MPEGaudioFrequencyArray()
  1737. {
  1738. static $MPEGaudioFrequency;
  1739. if (empty($MPEGaudioFrequency))
  1740. {
  1741. $MPEGaudioFrequency = array('1' => array(44100, 48000, 32000), '2' => array(22050, 24000, 16000),
  1742. '2.5' => array(11025, 12000, 8000));
  1743. }
  1744. return $MPEGaudioFrequency;
  1745. }
  1746. function MPEGaudioChannelModeArray()
  1747. {
  1748. static $MPEGaudioChannelMode = array('stereo', 'joint stereo', 'dual channel', 'mono');
  1749. return $MPEGaudioChannelMode;
  1750. }
  1751. function MPEGaudioModeExtensionArray()
  1752. {
  1753. static $MPEGaudioModeExtension;
  1754. if (empty($MPEGaudioModeExtension))
  1755. {
  1756. $MPEGaudioModeExtension = array(1 => array('4-31', '8-31', '12-31', '16-31'),
  1757. 2 => array('4-31', '8-31', '12-31', '16-31'), 3 => array('', 'IS', 'MS', 'IS+MS'));
  1758. }
  1759. return $MPEGaudioModeExtension;
  1760. }
  1761. function MPEGaudioEmphasisArray()
  1762. {
  1763. static $MPEGaudioEmphasis = array('none', '50/15ms', false, 'CCIT J.17');
  1764. return $MPEGaudioEmphasis;
  1765. }
  1766. function MPEGaudioHeaderBytesValid($head4, $allowBitrate15 = false)
  1767. {
  1768. return getid3_mp3 :: MPEGaudioHeaderValid(getid3_mp3 :: MPEGaudioHeaderDecode($head4), false, $allowBitrate15);
  1769. }
  1770. function MPEGaudioHeaderValid($rawarray, $echoerrors = false, $allowBitrate15 = false)
  1771. {
  1772. if (($rawarray['synch'] & 0x0FFE) != 0x0FFE)
  1773. {
  1774. return false;
  1775. }
  1776. static $MPEGaudioVersionLookup;
  1777. static $MPEGaudioLayerLookup;
  1778. static $MPEGaudioBitrateLookup;
  1779. static $MPEGaudioFrequencyLookup;
  1780. static $MPEGaudioChannelModeLookup;
  1781. static $MPEGaudioModeExtensionLookup;
  1782. static $MPEGaudioEmphasisLookup;
  1783. if (empty($MPEGaudioVersionLookup))
  1784. {
  1785. $MPEGaudioVersionLookup = getid3_mp3 :: MPEGaudioVersionArray();
  1786. $MPEGaudioLayerLookup = getid3_mp3 :: MPEGaudioLayerArray();
  1787. $MPEGaudioBitrateLookup = getid3_mp3 :: MPEGaudioBitrateArray();
  1788. $MPEGaudioFrequencyLookup = getid3_mp3 :: MPEGaudioFrequencyArray();
  1789. $MPEGaudioChannelModeLookup = getid3_mp3 :: MPEGaudioChannelModeArray();
  1790. $MPEGaudioModeExtensionLookup = getid3_mp3 :: MPEGaudioModeExtensionArray();
  1791. $MPEGaudioEmphasisLookup = getid3_mp3 :: MPEGaudioEmphasisArray();
  1792. }
  1793. if (isset($MPEGaudioVersionLookup[$rawarray['version']]))
  1794. {
  1795. $decodedVersion = $MPEGaudioVersionLookup[$rawarray['version']];
  1796. }
  1797. else
  1798. {
  1799. echo ($echoerrors ? "\n" . 'invalid Version (' . $rawarray['version'] . ')' : '');
  1800. return false;
  1801. }
  1802. if (isset($MPEGaudioLayerLookup[$rawarray['layer']]))
  1803. {
  1804. $decodedLayer = $MPEGaudioLayerLookup[$rawarray['layer']];
  1805. }
  1806. else
  1807. {
  1808. echo ($echoerrors ? "\n" . 'invalid Layer (' . $rawarray['layer'] . ')' : '');
  1809. return false;
  1810. }
  1811. if (! isset($MPEGaudioBitrateLookup[$decodedVersion][$decodedLayer][$rawarray['bitrate']]))
  1812. {
  1813. echo ($echoerrors ? "\n" . 'invalid Bitrate (' . $rawarray['bitrate'] . ')' : '');
  1814. if ($rawarray['bitrate'] == 15)
  1815. {
  1816. // known issue in LAME 3.90 - 3.93.1 where free-format has bitrate ID of 15 instead of 0
  1817. // let it go through here otherwise file will not be identified
  1818. if (! $allowBitrate15)
  1819. {
  1820. return false;
  1821. }
  1822. }
  1823. else
  1824. {
  1825. return false;
  1826. }
  1827. }
  1828. if (! isset($MPEGaudioFrequencyLookup[$decodedVersion][$rawarray['sample_rate']]))
  1829. {
  1830. echo ($echoerrors ? "\n" . 'invalid Frequency (' . $rawarray['sample_rate'] . ')' : '');
  1831. return false;
  1832. }
  1833. if (! isset($MPEGaudioChannelModeLookup[$rawarray['channelmode']]))
  1834. {
  1835. echo ($echoerrors ? "\n" . 'invalid ChannelMode (' . $rawarray['channelmode'] . ')' : '');
  1836. return false;
  1837. }
  1838. if (! isset($MPEGaudioModeExtensionLookup[$decodedLayer][$rawarray['modeextension']]))
  1839. {
  1840. echo ($echoerrors ? "\n" . 'invalid Mode Extension (' . $rawarray['modeextension'] . ')' : '');
  1841. return false;
  1842. }
  1843. if (! isset($MPEGaudioEmphasisLookup[$rawarray['emphasis']]))
  1844. {
  1845. echo ($echoerrors ? "\n" . 'invalid Emphasis (' . $rawarray['emphasis'] . ')' : '');
  1846. return false;
  1847. }
  1848. // These are just either set or not set, you can't mess that up :)
  1849. // $rawarray['protection'];
  1850. // $rawarray['padding'];
  1851. // $rawarray['private'];
  1852. // $rawarray['copyright'];
  1853. // $rawarray['original'];
  1854. return true;
  1855. }
  1856. function MPEGaudioHeaderDecode($Header4Bytes)
  1857. {
  1858. // AAAA AAAA AAAB BCCD EEEE FFGH IIJJ KLMM
  1859. // A - Frame sync (all bits set)
  1860. // B - MPEG Audio version ID
  1861. // C - Layer description
  1862. // D - Protection bit
  1863. // E - Bitrate index
  1864. // F - Sampling rate frequency index
  1865. // G - Padding bit
  1866. // H - Private bit
  1867. // I - Channel Mode
  1868. // J - Mode extension (Only if Joint stereo)
  1869. // K - Copyright
  1870. // L - Original
  1871. // M - Emphasis
  1872. if (strlen($Header4Bytes) != 4)
  1873. {
  1874. return false;
  1875. }
  1876. $MPEGrawHeader['synch'] = (getid3_lib :: BigEndian2Int(substr($Header4Bytes, 0, 2)) & 0xFFE0) >> 4;
  1877. $MPEGrawHeader['version'] = (ord($Header4Bytes{1}) & 0x18) >> 3; // BB
  1878. $MPEGrawHeader['layer'] = (ord($Header4Bytes{1}) & 0x06) >> 1; // CC
  1879. $MPEGrawHeader['protection'] = (ord($Header4Bytes{1}) & 0x01); // D
  1880. $MPEGrawHeader['bitrate'] = (ord($Header4Bytes{2}) & 0xF0) >> 4; // EEEE
  1881. $MPEGrawHeader['sample_rate'] = (ord($Header4Bytes{2}) & 0x0C) >> 2; // FF
  1882. $MPEGrawHeader['padding'] = (ord($Header4Bytes{2}) & 0x02) >> 1; // G
  1883. $MPEGrawHeader['private'] = (ord($Header4Bytes{2}) & 0x01); // H
  1884. $MPEGrawHeader['channelmode'] = (ord($Header4Bytes{3}) & 0xC0) >> 6; // II
  1885. $MPEGrawHeader['modeextension'] = (ord($Header4Bytes{3}) & 0x30) >> 4; // JJ
  1886. $MPEGrawHeader['copyright'] = (ord($Header4Bytes{3}) & 0x08) >> 3; // K
  1887. $MPEGrawHeader['original'] = (ord($Header4Bytes{3}) & 0x04) >> 2; // L
  1888. $MPEGrawHeader['emphasis'] = (ord($Header4Bytes{3}) & 0x03); // MM
  1889. return $MPEGrawHeader;
  1890. }
  1891. function MPEGaudioFrameLength(&$bitrate, &$version, &$layer, $padding, &$samplerate)
  1892. {
  1893. static $AudioFrameLengthCache = array();
  1894. if (! isset($AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate]))
  1895. {
  1896. $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = false;
  1897. if ($bitrate != 'free')
  1898. {
  1899. if ($version == '1')
  1900. {
  1901. if ($layer == '1')
  1902. {
  1903. // For Layer I slot is 32 bits long
  1904. $FrameLengthCoefficient = 48;
  1905. $SlotLength = 4;
  1906. }
  1907. else
  1908. { // Layer 2 / 3
  1909. // for Layer 2 and Layer 3 slot is 8 bits long.
  1910. $FrameLengthCoefficient = 144;
  1911. $SlotLength = 1;
  1912. }
  1913. }
  1914. else
  1915. { // MPEG-2 / MPEG-2.5
  1916. if ($layer == '1')
  1917. {
  1918. // For Layer I slot is 32 bits long
  1919. $FrameLengthCoefficient = 24;
  1920. $SlotLength = 4;
  1921. }
  1922. elseif ($layer == '2')
  1923. {
  1924. // for Layer 2 and Layer 3 slot is 8 bits long.
  1925. $FrameLengthCoefficient = 144;
  1926. $SlotLength = 1;
  1927. }
  1928. else
  1929. { // layer 3
  1930. // for Layer 2 and Layer 3 slot is 8 bits long.
  1931. $FrameLengthCoefficient = 72;
  1932. $SlotLength = 1;
  1933. }
  1934. }
  1935. // FrameLengthInBytes = ((Coefficient * BitRate) / SampleRate) + Padding
  1936. if ($samplerate > 0)
  1937. {
  1938. $NewFramelength = ($FrameLengthCoefficient * $bitrate) / $samplerate;
  1939. $NewFramelength = floor($NewFramelength / $SlotLength) * $SlotLength; // round to next-lower multiple of SlotLength (1 byte for Layer 2/3, 4 bytes for Layer I)
  1940. if ($padding)
  1941. {
  1942. $NewFramelength += $SlotLength;
  1943. }
  1944. $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = (int) $NewFramelength;
  1945. }
  1946. }
  1947. }
  1948. return $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate];
  1949. }
  1950. function ClosestStandardMP3Bitrate($bitrate)
  1951. {
  1952. static $StandardBitrates = array(320000, 256000, 224000, 192000, 160000, 128000, 112000, 96000, 80000, 64000,
  1953. 56000, 48000, 40000, 32000, 24000, 16000, 8000);
  1954. static $BitrateTable = array(0 => '-');
  1955. $roundbitrate = intval(round($bitrate, - 3));
  1956. if (! isset($BitrateTable[$roundbitrate]))
  1957. {
  1958. if ($roundbitrate > 320000)
  1959. {
  1960. $BitrateTable[$roundbitrate] = round($bitrate, - 4);
  1961. }
  1962. else
  1963. {
  1964. $LastBitrate = 320000;
  1965. foreach ($StandardBitrates as $StandardBitrate)
  1966. {
  1967. $BitrateTable[$roundbitrate] = $StandardBitrate;
  1968. if ($roundbitrate >= $StandardBitrate - (($LastBitrate - $StandardBitrate) / 2))
  1969. {
  1970. break;
  1971. }
  1972. $LastBitrate = $StandardBitrate;
  1973. }
  1974. }
  1975. }
  1976. return $BitrateTable[$roundbitrate];
  1977. }
  1978. function XingVBRidOffset($version, $channelmode)
  1979. {
  1980. static $XingVBRidOffsetCache = array();
  1981. if (empty($XingVBRidOffset))
  1982. {
  1983. $XingVBRidOffset = array(
  1984. '1' => array('mono' => 0x15, // 4 + 17 = 21
  1985. 'stereo' => 0x24, // 4 + 32 = 36
  1986. 'joint stereo' => 0x24, 'dual channel' => 0x24),
  1987. '2' => array('mono' => 0x0D, // 4 + 9 = 13
  1988. 'stereo' => 0x15, // 4 + 17 = 21
  1989. 'joint stereo' => 0x15, 'dual channel' => 0x15),
  1990. '2.5' => array('mono' => 0x15, 'stereo' => 0x15, 'joint stereo' => 0x15, 'dual channel' => 0x15));
  1991. }
  1992. return $XingVBRidOffset[$version][$channelmode];
  1993. }
  1994. function LAMEvbrMethodLookup($VBRmethodID)
  1995. {
  1996. static $LAMEvbrMethodLookup = array(0x00 => 'unknown', 0x01 => 'cbr', 0x02 => 'abr', 0x03 => 'vbr-old / vbr-rh',
  1997. 0x04 => 'vbr-new / vbr-mtrh', 0x05 => 'vbr-mt', 0x06 => 'vbr (full vbr method 4)',
  1998. 0x08 => 'cbr (constant bitrate 2 pass)', 0x09 => 'abr (2 pass)', 0x0F => 'reserved');
  1999. return (isset($LAMEvbrMethodLookup[$VBRmethodID]) ? $LAMEvbrMethodLookup[$VBRmethodID] : '');
  2000. }
  2001. function LAMEmiscStereoModeLookup($StereoModeID)
  2002. {
  2003. static $LAMEmiscStereoModeLookup = array(0 => 'mono', 1 => 'stereo', 2 => 'dual mono', 3 => 'joint stereo',
  2004. 4 => 'forced stereo', 5 => 'auto', 6 => 'intensity stereo', 7 => 'other');
  2005. return (isset($LAMEmiscStereoModeLookup[$StereoModeID]) ? $LAMEmiscStereoModeLookup[$StereoModeID] : '');
  2006. }
  2007. function LAMEmiscSourceSampleFrequencyLookup($SourceSampleFrequencyID)
  2008. {
  2009. static $LAMEmiscSourceSampleFrequencyLookup = array(0 => '<= 32 kHz', 1 => '44.1 kHz', 2 => '48 kHz',
  2010. 3 => '> 48kHz');
  2011. return (isset($LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID]) ? $LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID] : '');
  2012. }
  2013. function LAMEsurroundInfoLookup($SurroundInfoID)
  2014. {
  2015. static $LAMEsurroundInfoLookup = array(0 => 'no surround info', 1 => 'DPL encoding', 2 => 'DPL2 encoding',
  2016. 3 => 'Ambisonic encoding');
  2017. return (isset($LAMEsurroundInfoLookup[$SurroundInfoID]) ? $LAMEsurroundInfoLookup[$SurroundInfoID] : 'reserved');
  2018. }
  2019. function LAMEpresetUsedLookup($LAMEtag)
  2020. {
  2021. if ($LAMEtag['preset_used_id'] == 0)
  2022. {
  2023. // no preset used (LAME >=3.93)
  2024. // no preset recorded (LAME <3.93)
  2025. return '';
  2026. }
  2027. $LAMEpresetUsedLookup = array();
  2028. ///// THIS PART CANNOT BE STATIC .
  2029. for($i = 8; $i <= 320; $i ++)
  2030. {
  2031. switch ($LAMEtag['vbr_method'])
  2032. {
  2033. case 'cbr' :
  2034. $LAMEpresetUsedLookup[$i] = '--alt-preset ' . $LAMEtag['vbr_method'] . ' ' . $i;
  2035. break;
  2036. case 'abr' :
  2037. default : // other VBR modes shouldn't be here(?)
  2038. $LAMEpresetUsedLookup[$i] = '--alt-preset ' . $i;
  2039. break;
  2040. }
  2041. }
  2042. // named old-style presets (studio, phone, voice, etc) are handled in GuessEncoderOptions()
  2043. // named alt-presets
  2044. $LAMEpresetUsedLookup[1000] = '--r3mix';
  2045. $LAMEpresetUsedLookup[1001] = '--alt-preset standard';
  2046. $LAMEpresetUsedLookup[1002] = '--alt-preset extreme';
  2047. $LAMEpresetUsedLookup[1003] = '--alt-preset insane';
  2048. $LAMEpresetUsedLookup[1004] = '--alt-preset fast standard';
  2049. $LAMEpresetUsedLookup[1005] = '--alt-preset fast extreme';
  2050. $LAMEpresetUsedLookup[1006] = '--alt-preset medium';
  2051. $LAMEpresetUsedLookup[1007] = '--alt-preset fast medium';
  2052. // LAME 3.94 additions/changes
  2053. $LAMEpresetUsedLookup[1010] = '--preset portable'; // 3.94a15 Oct 21 2003
  2054. $LAMEpresetUsedLookup[1015] = '--preset radio'; // 3.94a15 Oct 21 2003
  2055. $LAMEpresetUsedLookup[320] = '--preset insane'; // 3.94a15 Nov 12 2003
  2056. $LAMEpresetUsedLookup[410] = '-V9';
  2057. $LAMEpresetUsedLookup[420] = '-V8';
  2058. $LAMEpresetUsedLookup[440] = '-V6';
  2059. $LAMEpresetUsedLookup[430] = '--preset radio'; // 3.94a15 Nov 12 2003
  2060. $LAMEpresetUsedLookup[450] = '--preset ' . (($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '') . 'portable'; // 3.94a15 Nov 12 2003
  2061. $LAMEpresetUsedLookup[460] = '--preset ' . (($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '') . 'medium'; // 3.94a15 Nov 12 2003
  2062. $LAMEpresetUsedLookup[470] = '--r3mix'; // 3.94b1 Dec 18 2003
  2063. $LAMEpresetUsedLookup[480] = '--preset ' . (($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '') . 'standard'; // 3.94a15 Nov 12 2003
  2064. $LAMEpresetUsedLookup[490] = '-V1';
  2065. $LAMEpresetUsedLookup[500] = '--preset ' . (($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '') . 'extreme'; // 3.94a15 Nov 12 2003
  2066. return (isset($LAMEpresetUsedLookup[$LAMEtag['preset_used_id']]) ? $LAMEpresetUsedLookup[$LAMEtag['preset_used_id']] : 'new/unknown preset: ' . $LAMEtag['preset_used_id'] . ' - report to info@getid3.org');
  2067. }
  2068. }
  2069. ?>