home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress / wp-includes / ID3 / module.audio.mp3.php < prev    next >
Encoding:
PHP Script  |  2017-07-31  |  97.4 KB  |  2,024 lines

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