Mini Shell

Direktori : /home/admin/web/mcpv.demarco.ddnsfree.com/public_html/wp-includes/ID3/
Upload File :
Current File : /home/admin/web/mcpv.demarco.ddnsfree.com/public_html/wp-includes/ID3/module.audio.mp3.php

<?php

/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org>               //
//  available at https://github.com/JamesHeinrich/getID3       //
//            or https://www.getid3.org                        //
//            or http://getid3.sourceforge.net                 //
//  see readme.txt for more details                            //
/////////////////////////////////////////////////////////////////
//                                                             //
// module.audio.mp3.php                                        //
// module for analyzing MP3 files                              //
// dependencies: NONE                                          //
//                                                            ///
/////////////////////////////////////////////////////////////////

if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
	exit;
}


class getid3_mp3 extends getid3_handler
{
	/**
	 * 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-unusable files.
	 *
	 * @var bool
	 */
	public $allow_bruteforce = false;

	/**
	 * number of frames to scan to determine if MPEG-audio sequence is valid
	 * Lower this number to 5-20 for faster scanning
	 * Increase this number to 50+ for most accurate detection of valid VBR/CBR mpeg-audio streams
	 *
	 * @var int
	 */
	public $mp3_valid_check_frames = 50;

	/**
	 * @return bool
	 */
	public function Analyze() {
		$info = &$this->getid3->info;

		$initialOffset = $info['avdataoffset'];

		if (!$this->getOnlyMPEGaudioInfo($info['avdataoffset'])) {
			if ($this->allow_bruteforce) {
				$this->error('Rescanning file in BruteForce mode');
				$this->getOnlyMPEGaudioInfoBruteForce();
			}
		}


		if (isset($info['mpeg']['audio']['bitrate_mode'])) {
			$info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
		}

		$CurrentDataLAMEversionString = null;
		if (((isset($info['id3v2']['headerlength']) && ($info['avdataoffset'] > $info['id3v2']['headerlength'])) || (!isset($info['id3v2']) && ($info['avdataoffset'] > 0) && ($info['avdataoffset'] != $initialOffset)))) {

			$synchoffsetwarning = 'Unknown data before synch ';
			if (isset($info['id3v2']['headerlength'])) {
				$synchoffsetwarning .= '(ID3v2 header ends at '.$info['id3v2']['headerlength'].', then '.($info['avdataoffset'] - $info['id3v2']['headerlength']).' bytes garbage, ';
			} elseif ($initialOffset > 0) {
				$synchoffsetwarning .= '(should be at '.$initialOffset.', ';
			} else {
				$synchoffsetwarning .= '(should be at beginning of file, ';
			}
			$synchoffsetwarning .= 'synch detected at '.$info['avdataoffset'].')';
			if (isset($info['audio']['bitrate_mode']) && ($info['audio']['bitrate_mode'] == 'cbr')) {

				if (!empty($info['id3v2']['headerlength']) && (($info['avdataoffset'] - $info['id3v2']['headerlength']) == $info['mpeg']['audio']['framelength'])) {

					$synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90-3.92) DLL in CBR mode.';
					$info['audio']['codec'] = 'LAME';
					$CurrentDataLAMEversionString = 'LAME3.';

				} elseif (empty($info['id3v2']['headerlength']) && ($info['avdataoffset'] == $info['mpeg']['audio']['framelength'])) {

					$synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90 - 3.92) DLL in CBR mode.';
					$info['audio']['codec'] = 'LAME';
					$CurrentDataLAMEversionString = 'LAME3.';

				}

			}
			$this->warning($synchoffsetwarning);

		}

		if (isset($info['mpeg']['audio']['LAME'])) {
			$info['audio']['codec'] = 'LAME';
			if (!empty($info['mpeg']['audio']['LAME']['long_version'])) {
				$info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['long_version'], "\x00");
			} elseif (!empty($info['mpeg']['audio']['LAME']['short_version'])) {
				$info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['short_version'], "\x00");
			}
		}

		$CurrentDataLAMEversionString = (!empty($CurrentDataLAMEversionString) ? $CurrentDataLAMEversionString : (isset($info['audio']['encoder']) ? $info['audio']['encoder'] : ''));
		if (!empty($CurrentDataLAMEversionString) && (substr($CurrentDataLAMEversionString, 0, 6) == 'LAME3.') && !preg_match('[0-9\)]', substr($CurrentDataLAMEversionString, -1))) {
			// a version number of LAME that does not end with a number like "LAME3.92"
			// or with a closing parenthesis like "LAME3.88 (alpha)"
			// or a version of LAME with the LAMEtag-not-filled-in-DLL-mode bug (3.90-3.92)

			// not sure what the actual last frame length will be, but will be less than or equal to 1441
			$PossiblyLongerLAMEversion_FrameLength = 1441;

			// Not sure what version of LAME this is - look in padding of last frame for longer version string
			$PossibleLAMEversionStringOffset = $info['avdataend'] - $PossiblyLongerLAMEversion_FrameLength;
			$this->fseek($PossibleLAMEversionStringOffset);
			$PossiblyLongerLAMEversion_Data = $this->fread($PossiblyLongerLAMEversion_FrameLength);
			switch (substr($CurrentDataLAMEversionString, -1)) {
				case 'a':
				case 'b':
					// "LAME3.94a" will have a longer version string of "LAME3.94 (alpha)" for example
					// need to trim off "a" to match longer string
					$CurrentDataLAMEversionString = substr($CurrentDataLAMEversionString, 0, -1);
					break;
			}
			if (($PossiblyLongerLAMEversion_String = strstr($PossiblyLongerLAMEversion_Data, $CurrentDataLAMEversionString)) !== false) {
				if (substr($PossiblyLongerLAMEversion_String, 0, strlen($CurrentDataLAMEversionString)) == $CurrentDataLAMEversionString) {
					$PossiblyLongerLAMEversion_NewString = substr($PossiblyLongerLAMEversion_String, 0, strspn($PossiblyLongerLAMEversion_String, 'LAME0123456789., (abcdefghijklmnopqrstuvwxyzJFSOND)')); //"LAME3.90.3"  "LAME3.87 (beta 1, Sep 27 2000)" "LAME3.88 (beta)"
					if (empty($info['audio']['encoder']) || (strlen($PossiblyLongerLAMEversion_NewString) > strlen($info['audio']['encoder']))) {
						if (!empty($info['audio']['encoder']) && !empty($info['mpeg']['audio']['LAME']['short_version']) && ($info['audio']['encoder'] == $info['mpeg']['audio']['LAME']['short_version'])) {
							if (preg_match('#^LAME[0-9\\.]+#', $PossiblyLongerLAMEversion_NewString, $matches)) {
								// "LAME3.100" -> "LAME3.100.1", but avoid including "(alpha)" and similar
								$info['mpeg']['audio']['LAME']['short_version'] = $matches[0];
							}
						}
						$info['audio']['encoder'] = $PossiblyLongerLAMEversion_NewString;
					}
				}
			}
		}
		if (!empty($info['audio']['encoder'])) {
			$info['audio']['encoder'] = rtrim($info['audio']['encoder'], "\x00 ");
		}

		switch (isset($info['mpeg']['audio']['layer']) ? $info['mpeg']['audio']['layer'] : '') {
			case 1:
			case 2:
				$info['audio']['dataformat'] = 'mp'.$info['mpeg']['audio']['layer'];
				break;
		}
		if (isset($info['fileformat']) && ($info['fileformat'] == 'mp3')) {
			switch ($info['audio']['dataformat']) {
				case 'mp1':
				case 'mp2':
				case 'mp3':
					$info['fileformat'] = $info['audio']['dataformat'];
					break;

				default:
					$this->warning('Expecting [audio][dataformat] to be mp1/mp2/mp3 when fileformat == mp3, [audio][dataformat] actually "'.$info['audio']['dataformat'].'"');
					break;
			}
		}

		if (empty($info['fileformat'])) {
			unset($info['fileformat']);
			unset($info['audio']['bitrate_mode']);
			unset($info['avdataoffset']);
			unset($info['avdataend']);
			return false;
		}

		$info['mime_type']         = 'audio/mpeg';
		$info['audio']['lossless'] = false;

		// Calculate playtime
		if (!isset($info['playtime_seconds']) && isset($info['audio']['bitrate']) && ($info['audio']['bitrate'] > 0)) {
			// https://github.com/JamesHeinrich/getID3/issues/161
			// VBR header frame contains ~0.026s of silent audio data, but is not actually part of the original encoding and should be ignored
			$xingVBRheaderFrameLength = ((isset($info['mpeg']['audio']['VBR_frames']) && isset($info['mpeg']['audio']['framelength'])) ? $info['mpeg']['audio']['framelength'] : 0);

			$info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset'] - $xingVBRheaderFrameLength) * 8 / $info['audio']['bitrate'];
		}

		$info['audio']['encoder_options'] = $this->GuessEncoderOptions();

		return true;
	}

	/**
	 * @return string
	 */
	public function GuessEncoderOptions() {
		// shortcuts
		$info = &$this->getid3->info;
		$thisfile_mpeg_audio = array();
		$thisfile_mpeg_audio_lame = array();
		if (!empty($info['mpeg']['audio'])) {
			$thisfile_mpeg_audio = &$info['mpeg']['audio'];
			if (!empty($thisfile_mpeg_audio['LAME'])) {
				$thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME'];
			}
		}

		$encoder_options = '';
		static $NamedPresetBitrates = array(16, 24, 40, 56, 112, 128, 160, 192, 256);

		if (isset($thisfile_mpeg_audio['VBR_method']) && ($thisfile_mpeg_audio['VBR_method'] == 'Fraunhofer') && !empty($thisfile_mpeg_audio['VBR_quality'])) {

			$encoder_options = 'VBR q'.$thisfile_mpeg_audio['VBR_quality'];

		} elseif (!empty($thisfile_mpeg_audio_lame['preset_used']) && isset($thisfile_mpeg_audio_lame['preset_used_id']) && (!in_array($thisfile_mpeg_audio_lame['preset_used_id'], $NamedPresetBitrates))) {

			$encoder_options = $thisfile_mpeg_audio_lame['preset_used'];

		} elseif (!empty($thisfile_mpeg_audio_lame['vbr_quality'])) {

			static $KnownEncoderValues = array();
			if (empty($KnownEncoderValues)) {

				//$KnownEncoderValues[abrbitrate_minbitrate][vbr_quality][raw_vbr_method][raw_noise_shaping][raw_stereo_mode][ath_type][lowpass_frequency] = 'preset name';
				$KnownEncoderValues[0xFF][58][1][1][3][2][20500] = '--alt-preset insane';        // 3.90,   3.90.1, 3.92
				$KnownEncoderValues[0xFF][58][1][1][3][2][20600] = '--alt-preset insane';        // 3.90.2, 3.90.3, 3.91
				$KnownEncoderValues[0xFF][57][1][1][3][4][20500] = '--alt-preset insane';        // 3.94,   3.95
				$KnownEncoderValues['**'][78][3][2][3][2][19500] = '--alt-preset extreme';       // 3.90,   3.90.1, 3.92
				$KnownEncoderValues['**'][78][3][2][3][2][19600] = '--alt-preset extreme';       // 3.90.2, 3.91
				$KnownEncoderValues['**'][78][3][1][3][2][19600] = '--alt-preset extreme';       // 3.90.3
				$KnownEncoderValues['**'][78][4][2][3][2][19500] = '--alt-preset fast extreme';  // 3.90,   3.90.1, 3.92
				$KnownEncoderValues['**'][78][4][2][3][2][19600] = '--alt-preset fast extreme';  // 3.90.2, 3.90.3, 3.91
				$KnownEncoderValues['**'][78][3][2][3][4][19000] = '--alt-preset standard';      // 3.90,   3.90.1, 3.90.2, 3.91, 3.92
				$KnownEncoderValues['**'][78][3][1][3][4][19000] = '--alt-preset standard';      // 3.90.3
				$KnownEncoderValues['**'][78][4][2][3][4][19000] = '--alt-preset fast standard'; // 3.90,   3.90.1, 3.90.2, 3.91, 3.92
				$KnownEncoderValues['**'][78][4][1][3][4][19000] = '--alt-preset fast standard'; // 3.90.3
				$KnownEncoderValues['**'][88][4][1][3][3][19500] = '--r3mix';                    // 3.90,   3.90.1, 3.92
				$KnownEncoderValues['**'][88][4][1][3][3][19600] = '--r3mix';                    // 3.90.2, 3.90.3, 3.91
				$KnownEncoderValues['**'][67][4][1][3][4][18000] = '--r3mix';                    // 3.94,   3.95
				$KnownEncoderValues['**'][68][3][2][3][4][18000] = '--alt-preset medium';        // 3.90.3
				$KnownEncoderValues['**'][68][4][2][3][4][18000] = '--alt-preset fast medium';   // 3.90.3

				$KnownEncoderValues[0xFF][99][1][1][1][2][0]     = '--preset studio';            // 3.90,   3.90.1, 3.90.2, 3.91, 3.92
				$KnownEncoderValues[0xFF][58][2][1][3][2][20600] = '--preset studio';            // 3.90.3, 3.93.1
				$KnownEncoderValues[0xFF][58][2][1][3][2][20500] = '--preset studio';            // 3.93
				$KnownEncoderValues[0xFF][57][2][1][3][4][20500] = '--preset studio';            // 3.94,   3.95
				$KnownEncoderValues[0xC0][88][1][1][1][2][0]     = '--preset cd';                // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
				$KnownEncoderValues[0xC0][58][2][2][3][2][19600] = '--preset cd';                // 3.90.3, 3.93.1
				$KnownEncoderValues[0xC0][58][2][2][3][2][19500] = '--preset cd';                // 3.93
				$KnownEncoderValues[0xC0][57][2][1][3][4][19500] = '--preset cd';                // 3.94,   3.95
				$KnownEncoderValues[0xA0][78][1][1][3][2][18000] = '--preset hifi';              // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
				$KnownEncoderValues[0xA0][58][2][2][3][2][18000] = '--preset hifi';              // 3.90.3, 3.93,   3.93.1
				$KnownEncoderValues[0xA0][57][2][1][3][4][18000] = '--preset hifi';              // 3.94,   3.95
				$KnownEncoderValues[0x80][67][1][1][3][2][18000] = '--preset tape';              // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
				$KnownEncoderValues[0x80][67][1][1][3][2][15000] = '--preset radio';             // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
				$KnownEncoderValues[0x70][67][1][1][3][2][15000] = '--preset fm';                // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
				$KnownEncoderValues[0x70][58][2][2][3][2][16000] = '--preset tape/radio/fm';     // 3.90.3, 3.93,   3.93.1
				$KnownEncoderValues[0x70][57][2][1][3][4][16000] = '--preset tape/radio/fm';     // 3.94,   3.95
				$KnownEncoderValues[0x38][58][2][2][0][2][10000] = '--preset voice';             // 3.90.3, 3.93,   3.93.1
				$KnownEncoderValues[0x38][57][2][1][0][4][15000] = '--preset voice';             // 3.94,   3.95
				$KnownEncoderValues[0x38][57][2][1][0][4][16000] = '--preset voice';             // 3.94a14
				$KnownEncoderValues[0x28][65][1][1][0][2][7500]  = '--preset mw-us';             // 3.90,   3.90.1, 3.92
				$KnownEncoderValues[0x28][65][1][1][0][2][7600]  = '--preset mw-us';             // 3.90.2, 3.91
				$KnownEncoderValues[0x28][58][2][2][0][2][7000]  = '--preset mw-us';             // 3.90.3, 3.93,   3.93.1
				$KnownEncoderValues[0x28][57][2][1][0][4][10500] = '--preset mw-us';             // 3.94,   3.95
				$KnownEncoderValues[0x28][57][2][1][0][4][11200] = '--preset mw-us';             // 3.94a14
				$KnownEncoderValues[0x28][57][2][1][0][4][8800]  = '--preset mw-us';             // 3.94a15
				$KnownEncoderValues[0x18][58][2][2][0][2][4000]  = '--preset phon+/lw/mw-eu/sw'; // 3.90.3, 3.93.1
				$KnownEncoderValues[0x18][58][2][2][0][2][3900]  = '--preset phon+/lw/mw-eu/sw'; // 3.93
				$KnownEncoderValues[0x18][57][2][1][0][4][5900]  = '--preset phon+/lw/mw-eu/sw'; // 3.94,   3.95
				$KnownEncoderValues[0x18][57][2][1][0][4][6200]  = '--preset phon+/lw/mw-eu/sw'; // 3.94a14
				$KnownEncoderValues[0x18][57][2][1][0][4][3200]  = '--preset phon+/lw/mw-eu/sw'; // 3.94a15
				$KnownEncoderValues[0x10][58][2][2][0][2][3800]  = '--preset phone';             // 3.90.3, 3.93.1
				$KnownEncoderValues[0x10][58][2][2][0][2][3700]  = '--preset phone';             // 3.93
				$KnownEncoderValues[0x10][57][2][1][0][4][5600]  = '--preset phone';             // 3.94,   3.95
			}

			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']])) {

				$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']];

			} 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']])) {

				$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']];

			} elseif ($info['audio']['bitrate_mode'] == 'vbr') {

				// http://gabriel.mp3-tech.org/mp3infotag.html
				// int    Quality = (100 - 10 * gfp->VBR_q - gfp->quality)h


				$LAME_V_value = 10 - ceil($thisfile_mpeg_audio_lame['vbr_quality'] / 10);
				$LAME_q_value = 100 - $thisfile_mpeg_audio_lame['vbr_quality'] - ($LAME_V_value * 10);
				$encoder_options = '-V'.$LAME_V_value.' -q'.$LAME_q_value;

			} elseif ($info['audio']['bitrate_mode'] == 'cbr') {

				$encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);

			} else {

				$encoder_options = strtoupper($info['audio']['bitrate_mode']);

			}

		} elseif (!empty($thisfile_mpeg_audio_lame['bitrate_abr'])) {

			$encoder_options = 'ABR'.$thisfile_mpeg_audio_lame['bitrate_abr'];

		} elseif (!empty($info['audio']['bitrate'])) {

			if ($info['audio']['bitrate_mode'] == 'cbr') {
				$encoder_options = strtoupper($info['audio']['bitrate_mode']).round($info['audio']['bitrate'] / 1000);
			} else {
				$encoder_options = strtoupper($info['audio']['bitrate_mode']);
			}

		}
		if (!empty($thisfile_mpeg_audio_lame['bitrate_min'])) {
			$encoder_options .= ' -b'.$thisfile_mpeg_audio_lame['bitrate_min'];
		}

		if (isset($thisfile_mpeg_audio['bitrate']) && $thisfile_mpeg_audio['bitrate'] === 'free') {
			$encoder_options .= ' --freeformat';
		}

		if (!empty($thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev']) || !empty($thisfile_mpeg_audio_lame['encoding_flags']['nogap_next'])) {
			$encoder_options .= ' --nogap';
		}

		if (!empty($thisfile_mpeg_audio_lame['lowpass_frequency'])) {
			$ExplodedOptions = explode(' ', $encoder_options, 4);
			if ($ExplodedOptions[0] == '--r3mix') {
				$ExplodedOptions[1] = 'r3mix';
			}
			switch ($ExplodedOptions[0]) {
				case '--preset':
				case '--alt-preset':
				case '--r3mix':
					if ($ExplodedOptions[1] == 'fast') {
						$ExplodedOptions[1] .= ' '.$ExplodedOptions[2];
					}
					switch ($ExplodedOptions[1]) {
						case 'portable':
						case 'medium':
						case 'standard':
						case 'extreme':
						case 'insane':
						case 'fast portable':
						case 'fast medium':
						case 'fast standard':
						case 'fast extreme':
						case 'fast insane':
						case 'r3mix':
							static $ExpectedLowpass = array(
									'insane|20500'        => 20500,
									'insane|20600'        => 20600,  // 3.90.2, 3.90.3, 3.91
									'medium|18000'        => 18000,
									'fast medium|18000'   => 18000,
									'extreme|19500'       => 19500,  // 3.90,   3.90.1, 3.92, 3.95
									'extreme|19600'       => 19600,  // 3.90.2, 3.90.3, 3.91, 3.93.1
									'fast extreme|19500'  => 19500,  // 3.90,   3.90.1, 3.92, 3.95
									'fast extreme|19600'  => 19600,  // 3.90.2, 3.90.3, 3.91, 3.93.1
									'standard|19000'      => 19000,
									'fast standard|19000' => 19000,
									'r3mix|19500'         => 19500,  // 3.90,   3.90.1, 3.92
									'r3mix|19600'         => 19600,  // 3.90.2, 3.90.3, 3.91
									'r3mix|18000'         => 18000,  // 3.94,   3.95
								);
							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))) {
								$encoder_options .= ' --lowpass '.$thisfile_mpeg_audio_lame['lowpass_frequency'];
							}
							break;

						default:
							break;
					}
					break;
			}
		}

		if (isset($thisfile_mpeg_audio_lame['raw']['source_sample_freq'])) {
			if (($thisfile_mpeg_audio['sample_rate'] == 44100) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 1)) {
				$encoder_options .= ' --resample 44100';
			} elseif (($thisfile_mpeg_audio['sample_rate'] == 48000) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 2)) {
				$encoder_options .= ' --resample 48000';
			} elseif ($thisfile_mpeg_audio['sample_rate'] < 44100) {
				switch ($thisfile_mpeg_audio_lame['raw']['source_sample_freq']) {
					case 0: // <= 32000
						// may or may not be same as source frequency - ignore
						break;
					case 1: // 44100
					case 2: // 48000
					case 3: // 48000+
						$ExplodedOptions = explode(' ', $encoder_options, 4);
						switch ($ExplodedOptions[0]) {
							case '--preset':
							case '--alt-preset':
								switch ($ExplodedOptions[1]) {
									case 'fast':
									case 'portable':
									case 'medium':
									case 'standard':
									case 'extreme':
									case 'insane':
										$encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
										break;

									default:
										static $ExpectedResampledRate = array(
												'phon+/lw/mw-eu/sw|16000' => 16000,
												'mw-us|24000'             => 24000, // 3.95
												'mw-us|32000'             => 32000, // 3.93
												'mw-us|16000'             => 16000, // 3.92
												'phone|16000'             => 16000,
												'phone|11025'             => 11025, // 3.94a15
												'radio|32000'             => 32000, // 3.94a15
												'fm/radio|32000'          => 32000, // 3.92
												'fm|32000'                => 32000, // 3.90
												'voice|32000'             => 32000);
										if (!isset($ExpectedResampledRate[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio['sample_rate']])) {
											$encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
										}
										break;
								}
								break;

							case '--r3mix':
							default:
								$encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
								break;
						}
						break;
				}
			}
		}
		if (empty($encoder_options) && !empty($info['audio']['bitrate']) && !empty($info['audio']['bitrate_mode'])) {
			//$encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
			$encoder_options = strtoupper($info['audio']['bitrate_mode']);
		}

		return $encoder_options;
	}

	/**
	 * @param int   $offset
	 * @param array $info
	 * @param bool  $recursivesearch
	 * @param bool  $ScanAsCBR
	 * @param bool  $FastMPEGheaderScan
	 *
	 * @return bool
	 */
	public function decodeMPEGaudioHeader($offset, &$info, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false) {
		static $MPEGaudioVersionLookup;
		static $MPEGaudioLayerLookup;
		static $MPEGaudioBitrateLookup;
		static $MPEGaudioFrequencyLookup;
		static $MPEGaudioChannelModeLookup;
		static $MPEGaudioModeExtensionLookup;
		static $MPEGaudioEmphasisLookup;
		if (empty($MPEGaudioVersionLookup)) {
			$MPEGaudioVersionLookup       = self::MPEGaudioVersionArray();
			$MPEGaudioLayerLookup         = self::MPEGaudioLayerArray();
			$MPEGaudioBitrateLookup       = self::MPEGaudioBitrateArray();
			$MPEGaudioFrequencyLookup     = self::MPEGaudioFrequencyArray();
			$MPEGaudioChannelModeLookup   = self::MPEGaudioChannelModeArray();
			$MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
			$MPEGaudioEmphasisLookup      = self::MPEGaudioEmphasisArray();
		}

		if ($this->fseek($offset) != 0) {
			$this->error('decodeMPEGaudioHeader() failed to seek to next offset at '.$offset);
			return false;
		}
		//$headerstring = $this->fread(1441); // worst-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame
		$headerstring = $this->fread(226); // LAME header at offset 36 + 190 bytes of Xing/LAME data

		// MP3 audio frame structure:
		// $aa $aa $aa $aa [$bb $bb] $cc...
		// where $aa..$aa is the four-byte mpeg-audio header (below)
		// $bb $bb is the optional 2-byte CRC
		// and $cc... is the audio data

		$head4 = substr($headerstring, 0, 4);
		$head4_key = getid3_lib::PrintHexBytes($head4, true, false, false);
		static $MPEGaudioHeaderDecodeCache = array();
		if (isset($MPEGaudioHeaderDecodeCache[$head4_key])) {
			$MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4_key];
		} else {
			$MPEGheaderRawArray = self::MPEGaudioHeaderDecode($head4);
			$MPEGaudioHeaderDecodeCache[$head4_key] = $MPEGheaderRawArray;
		}

		static $MPEGaudioHeaderValidCache = array();
		if (!isset($MPEGaudioHeaderValidCache[$head4_key])) { // Not in cache
			//$MPEGaudioHeaderValidCache[$head4_key] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, true);  // allow badly-formatted freeformat (from LAME 3.90 - 3.93.1)
			$MPEGaudioHeaderValidCache[$head4_key] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, false);
		}

		// shortcut
		if (!isset($info['mpeg']['audio'])) {
			$info['mpeg']['audio'] = array();
		}
		$thisfile_mpeg_audio = &$info['mpeg']['audio'];

		if ($MPEGaudioHeaderValidCache[$head4_key]) {
			$thisfile_mpeg_audio['raw'] = $MPEGheaderRawArray;
		} else {
			$this->warning('Invalid MPEG audio header ('.getid3_lib::PrintHexBytes($head4).') at offset '.$offset);
			return false;
		}

		if (!$FastMPEGheaderScan) {
			$thisfile_mpeg_audio['version']       = $MPEGaudioVersionLookup[$thisfile_mpeg_audio['raw']['version']];
			$thisfile_mpeg_audio['layer']         = $MPEGaudioLayerLookup[$thisfile_mpeg_audio['raw']['layer']];

			$thisfile_mpeg_audio['channelmode']   = $MPEGaudioChannelModeLookup[$thisfile_mpeg_audio['raw']['channelmode']];
			$thisfile_mpeg_audio['channels']      = (($thisfile_mpeg_audio['channelmode'] == 'mono') ? 1 : 2);
			$thisfile_mpeg_audio['sample_rate']   = $MPEGaudioFrequencyLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['raw']['sample_rate']];
			$thisfile_mpeg_audio['protection']    = !$thisfile_mpeg_audio['raw']['protection'];
			$thisfile_mpeg_audio['private']       = (bool) $thisfile_mpeg_audio['raw']['private'];
			$thisfile_mpeg_audio['modeextension'] = $MPEGaudioModeExtensionLookup[$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['modeextension']];
			$thisfile_mpeg_audio['copyright']     = (bool) $thisfile_mpeg_audio['raw']['copyright'];
			$thisfile_mpeg_audio['original']      = (bool) $thisfile_mpeg_audio['raw']['original'];
			$thisfile_mpeg_audio['emphasis']      = $MPEGaudioEmphasisLookup[$thisfile_mpeg_audio['raw']['emphasis']];

			$info['audio']['channels']    = $thisfile_mpeg_audio['channels'];
			$info['audio']['sample_rate'] = $thisfile_mpeg_audio['sample_rate'];

			if ($thisfile_mpeg_audio['protection']) {
				$thisfile_mpeg_audio['crc'] = getid3_lib::BigEndian2Int(substr($headerstring, 4, 2));
			}
		}

		if ($thisfile_mpeg_audio['raw']['bitrate'] == 15) {
			// http://www.hydrogenaudio.org/?act=ST&f=16&t=9682&st=0
			$this->warning('Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1');
			$thisfile_mpeg_audio['raw']['bitrate'] = 0;
		}
		$thisfile_mpeg_audio['padding'] = (bool) $thisfile_mpeg_audio['raw']['padding'];
		$thisfile_mpeg_audio['bitrate'] = $MPEGaudioBitrateLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['bitrate']];

		if (($thisfile_mpeg_audio['bitrate'] == 'free') && ($offset == $info['avdataoffset'])) {
			// only skip multiple frame check if free-format bitstream found at beginning of file
			// otherwise is quite possibly simply corrupted data
			$recursivesearch = false;
		}

		// For Layer 2 there are some combinations of bitrate and mode which are not allowed.
		if (!$FastMPEGheaderScan && ($thisfile_mpeg_audio['layer'] == '2')) {

			$info['audio']['dataformat'] = 'mp2';
			switch ($thisfile_mpeg_audio['channelmode']) {

				case 'mono':
					if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] <= 192000)) {
						// these are ok
					} else {
						$this->error($thisfile_mpeg_audio['bitrate'].'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.');
						return false;
					}
					break;

				case 'stereo':
				case 'joint stereo':
				case 'dual channel':
					if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] == 64000) || ($thisfile_mpeg_audio['bitrate'] >= 96000)) {
						// these are ok
					} else {
						$this->error(intval(round($thisfile_mpeg_audio['bitrate'] / 1000)).'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.');
						return false;
					}
					break;

			}

		}


		if ($info['audio']['sample_rate'] > 0) {
			$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']);
		}

		$nextframetestoffset = $offset + 1;
		if ($thisfile_mpeg_audio['bitrate'] != 'free') {

			$info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];

			if (isset($thisfile_mpeg_audio['framelength'])) {
				$nextframetestoffset = $offset + $thisfile_mpeg_audio['framelength'];
			} else {
				$this->error('Frame at offset('.$offset.') is has an invalid frame length.');
				return false;
			}

		}

		$ExpectedNumberOfAudioBytes = 0;

		////////////////////////////////////////////////////////////////////////////////////
		// Variable-bitrate headers

		if (substr($headerstring, 4 + 32, 4) == 'VBRI') {
			// Fraunhofer VBR header is hardcoded 'VBRI' at offset 0x24 (36)
			// specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html

			$thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
			$thisfile_mpeg_audio['VBR_method']   = 'Fraunhofer';
			$info['audio']['codec']              = 'Fraunhofer';

			$SideInfoData = substr($headerstring, 4 + 2, 32);

			$FraunhoferVBROffset = 36;

			$thisfile_mpeg_audio['VBR_encoder_version']     = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset +  4, 2)); // VbriVersion
			$thisfile_mpeg_audio['VBR_encoder_delay']       = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset +  6, 2)); // VbriDelay
			$thisfile_mpeg_audio['VBR_quality']             = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset +  8, 2)); // VbriQuality
			$thisfile_mpeg_audio['VBR_bytes']               = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 10, 4)); // VbriStreamBytes
			$thisfile_mpeg_audio['VBR_frames']              = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 14, 4)); // VbriStreamFrames
			$thisfile_mpeg_audio['VBR_seek_offsets']        = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 18, 2)); // VbriTableSize
			$thisfile_mpeg_audio['VBR_seek_scale']          = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 20, 2)); // VbriTableScale
			$thisfile_mpeg_audio['VBR_entry_bytes']         = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 22, 2)); // VbriEntryBytes
			$thisfile_mpeg_audio['VBR_entry_frames']        = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 24, 2)); // VbriEntryFrames

			$ExpectedNumberOfAudioBytes = $thisfile_mpeg_audio['VBR_bytes'];

			$previousbyteoffset = $offset;
			for ($i = 0; $i < $thisfile_mpeg_audio['VBR_seek_offsets']; $i++) {
				$Fraunhofer_OffsetN = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset, $thisfile_mpeg_audio['VBR_entry_bytes']));
				$FraunhoferVBROffset += $thisfile_mpeg_audio['VBR_entry_bytes'];
				$thisfile_mpeg_audio['VBR_offsets_relative'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']);
				$thisfile_mpeg_audio['VBR_offsets_absolute'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']) + $previousbyteoffset;
				$previousbyteoffset += $Fraunhofer_OffsetN;
			}


		} else {

			// Xing VBR header is hardcoded 'Xing' at a offset 0x0D (13), 0x15 (21) or 0x24 (36)
			// depending on MPEG layer and number of channels

			$VBRidOffset = self::XingVBRidOffset($thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['channelmode']);
			$SideInfoData = substr($headerstring, 4 + 2, $VBRidOffset - 4);

			if ((substr($headerstring, $VBRidOffset, strlen('Xing')) == 'Xing') || (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Info')) {
				// 'Xing' is traditional Xing VBR frame
				// 'Info' is LAME-encoded CBR (This was done to avoid CBR files to be recognized as traditional Xing VBR files by some decoders.)
				// 'Info' *can* legally be used to specify a VBR file as well, however.

				// http://www.multiweb.cz/twoinches/MP3inside.htm
				//00..03 = "Xing" or "Info"
				//04..07 = Flags:
				//  0x01  Frames Flag     set if value for number of frames in file is stored
				//  0x02  Bytes Flag      set if value for filesize in bytes is stored
				//  0x04  TOC Flag        set if values for TOC are stored
				//  0x08  VBR Scale Flag  set if values for VBR scale is stored
				//08..11  Frames: Number of frames in file (including the first Xing/Info one)
				//12..15  Bytes:  File length in Bytes
				//16..115  TOC (Table of Contents):
				//  Contains of 100 indexes (one Byte length) for easier lookup in file. Approximately solves problem with moving inside file.
				//  Each Byte has a value according this formula:
				//  (TOC[i] / 256) * fileLenInBytes
				//  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:
				//  TOC[(60/240)*100] = TOC[25]
				//  and corresponding Byte in file is then approximately at:
				//  (TOC[25]/256) * 5000000
				//116..119  VBR Scale


				// should be safe to leave this at 'vbr' and let it be overriden to 'cbr' if a CBR preset/mode is used by LAME
//				if (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Xing') {
					$thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
					$thisfile_mpeg_audio['VBR_method']   = 'Xing';
//				} else {
//					$ScanAsCBR = true;
//					$thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
//				}

				$thisfile_mpeg_audio['xing_flags_raw'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 4, 4));

				$thisfile_mpeg_audio['xing_flags']['frames']    = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000001);
				$thisfile_mpeg_audio['xing_flags']['bytes']     = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000002);
				$thisfile_mpeg_audio['xing_flags']['toc']       = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000004);
				$thisfile_mpeg_audio['xing_flags']['vbr_scale'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000008);

				if ($thisfile_mpeg_audio['xing_flags']['frames']) {
					$thisfile_mpeg_audio['VBR_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset +  8, 4));
					//$thisfile_mpeg_audio['VBR_frames']--; // don't count header Xing/Info frame
				}
				if ($thisfile_mpeg_audio['xing_flags']['bytes']) {
					$thisfile_mpeg_audio['VBR_bytes']  = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 12, 4));
				}

				//if (($thisfile_mpeg_audio['bitrate'] == 'free') && !empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
				//if (!empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
				if (!empty($thisfile_mpeg_audio['VBR_frames'])) {
					$used_filesize  = 0;
					if (!empty($thisfile_mpeg_audio['VBR_bytes'])) {
						$used_filesize = $thisfile_mpeg_audio['VBR_bytes'];
					} elseif (!empty($info['filesize'])) {
						$used_filesize  = $info['filesize'];
						$used_filesize -= (isset($info['id3v2']['headerlength']) ? intval($info['id3v2']['headerlength']) : 0);
						$used_filesize -= (isset($info['id3v1']) ? 128 : 0);
						$used_filesize -= (isset($info['tag_offset_end']) ? $info['tag_offset_end'] - $info['tag_offset_start'] : 0);
						$this->warning('MP3.Xing header missing VBR_bytes, assuming MPEG audio portion of file is '.number_format($used_filesize).' bytes');
					}

					$framelengthfloat = $used_filesize / $thisfile_mpeg_audio['VBR_frames'];

					if ($thisfile_mpeg_audio['layer'] == '1') {
						// BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
						//$info['audio']['bitrate'] = ((($framelengthfloat / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
						$info['audio']['bitrate'] = ($framelengthfloat / 4) * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 12;
					} else {
						// Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
						//$info['audio']['bitrate'] = (($framelengthfloat - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
						$info['audio']['bitrate'] = $framelengthfloat * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 144;
					}
					$thisfile_mpeg_audio['framelength'] = floor($framelengthfloat);
				}

				if ($thisfile_mpeg_audio['xing_flags']['toc']) {
					$LAMEtocData = substr($headerstring, $VBRidOffset + 16, 100);
					for ($i = 0; $i < 100; $i++) {
						$thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData[$i]);
					}
				}
				if ($thisfile_mpeg_audio['xing_flags']['vbr_scale']) {
					$thisfile_mpeg_audio['VBR_scale'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 116, 4));
				}


				// http://gabriel.mp3-tech.org/mp3infotag.html
				if (substr($headerstring, $VBRidOffset + 120, 4) == 'LAME') {

					// shortcut
					$thisfile_mpeg_audio['LAME'] = array();
					$thisfile_mpeg_audio_lame    = &$thisfile_mpeg_audio['LAME'];


					$thisfile_mpeg_audio_lame['long_version']  = substr($headerstring, $VBRidOffset + 120, 20);
					$thisfile_mpeg_audio_lame['short_version'] = substr($thisfile_mpeg_audio_lame['long_version'], 0, 9);

					//$thisfile_mpeg_audio_lame['numeric_version'] = str_replace('LAME', '', $thisfile_mpeg_audio_lame['short_version']);
					$thisfile_mpeg_audio_lame['numeric_version'] = '';
					if (preg_match('#^LAME([0-9\\.a-z]*)#', $thisfile_mpeg_audio_lame['long_version'], $matches)) {
						$thisfile_mpeg_audio_lame['short_version']   = $matches[0];
						$thisfile_mpeg_audio_lame['numeric_version'] = $matches[1];
					}
					if (strlen($thisfile_mpeg_audio_lame['numeric_version']) > 0) {
						foreach (explode('.', $thisfile_mpeg_audio_lame['numeric_version']) as $key => $number) {
							$thisfile_mpeg_audio_lame['integer_version'][$key] = intval($number);
						}
						//if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.90') {
						if ((($thisfile_mpeg_audio_lame['integer_version'][0] * 1000) + $thisfile_mpeg_audio_lame['integer_version'][1]) >= 3090) { // cannot use string version compare, may have "LAME3.90" or "LAME3.100" -- see https://github.com/JamesHeinrich/getID3/issues/207

							// extra 11 chars are not part of version string when LAMEtag present
							unset($thisfile_mpeg_audio_lame['long_version']);

							// It the LAME tag was only introduced in LAME v3.90
							// https://wiki.hydrogenaud.io/index.php/LAME#VBR_header_and_LAME_tag
							// https://hydrogenaud.io/index.php?topic=9933

							// Offsets of various bytes in http://gabriel.mp3-tech.org/mp3infotag.html
							// are assuming a 'Xing' identifier offset of 0x24, which is the case for
							// MPEG-1 non-mono, but not for other combinations
							$LAMEtagOffsetContant = $VBRidOffset - 0x24;

							// shortcuts
							$thisfile_mpeg_audio_lame['RGAD']    = array('track'=>array(), 'album'=>array());
							$thisfile_mpeg_audio_lame_RGAD       = &$thisfile_mpeg_audio_lame['RGAD'];
							$thisfile_mpeg_audio_lame_RGAD_track = &$thisfile_mpeg_audio_lame_RGAD['track'];
							$thisfile_mpeg_audio_lame_RGAD_album = &$thisfile_mpeg_audio_lame_RGAD['album'];
							$thisfile_mpeg_audio_lame['raw'] = array();
							$thisfile_mpeg_audio_lame_raw    = &$thisfile_mpeg_audio_lame['raw'];

							// byte $9B  VBR Quality
							// This field is there to indicate a quality level, although the scale was not precised in the original Xing specifications.
							// Actually overwrites original Xing bytes
							unset($thisfile_mpeg_audio['VBR_scale']);
							$thisfile_mpeg_audio_lame['vbr_quality'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0x9B, 1));

							// bytes $9C-$A4  Encoder short VersionString
							$thisfile_mpeg_audio_lame['short_version'] = substr($headerstring, $LAMEtagOffsetContant + 0x9C, 9);

							// byte $A5  Info Tag revision + VBR method
							$LAMEtagRevisionVBRmethod = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA5, 1));

							$thisfile_mpeg_audio_lame['tag_revision']   = ($LAMEtagRevisionVBRmethod & 0xF0) >> 4;
							$thisfile_mpeg_audio_lame_raw['vbr_method'] =  $LAMEtagRevisionVBRmethod & 0x0F;
							$thisfile_mpeg_audio_lame['vbr_method']     = self::LAMEvbrMethodLookup($thisfile_mpeg_audio_lame_raw['vbr_method']);
							$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'

							// byte $A6  Lowpass filter value
							$thisfile_mpeg_audio_lame['lowpass_frequency'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA6, 1)) * 100;

							// bytes $A7-$AE  Replay Gain
							// https://web.archive.org/web/20021015212753/http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html
							// bytes $A7-$AA : 32 bit floating point "Peak signal amplitude"
							if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.94b') {
								// LAME 3.94a16 and later - 9.23 fixed point
								// ie 0x0059E2EE / (2^23) = 5890798 / 8388608 = 0.7022378444671630859375
								$thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = (float) ((getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4))) / 8388608);
							} else {
								// LAME 3.94a15 and earlier - 32-bit floating point
								// Actually 3.94a16 will fall in here too and be WRONG, but is hard to detect 3.94a16 vs 3.94a15
								$thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = getid3_lib::LittleEndian2Float(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4));
							}
							if ($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] == 0) {
								unset($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
							} else {
								$thisfile_mpeg_audio_lame_RGAD['peak_db'] = getid3_lib::RGADamplitude2dB($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
							}

							$thisfile_mpeg_audio_lame_raw['RGAD_track']      =   getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAB, 2));
							$thisfile_mpeg_audio_lame_raw['RGAD_album']      =   getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAD, 2));


							if ($thisfile_mpeg_audio_lame_raw['RGAD_track'] != 0) {

								$thisfile_mpeg_audio_lame_RGAD_track['raw']['name']        = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0xE000) >> 13;
								$thisfile_mpeg_audio_lame_RGAD_track['raw']['originator']  = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x1C00) >> 10;
								$thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit']    = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x0200) >> 9;
								$thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'] =  $thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x01FF;
								$thisfile_mpeg_audio_lame_RGAD_track['name']       = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['name']);
								$thisfile_mpeg_audio_lame_RGAD_track['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['originator']);
								$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']);

								if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) {
									$info['replay_gain']['track']['peak']   = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
								}
								$info['replay_gain']['track']['originator'] = $thisfile_mpeg_audio_lame_RGAD_track['originator'];
								$info['replay_gain']['track']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_track['gain_db'];
							} else {
								unset($thisfile_mpeg_audio_lame_RGAD['track']);
							}
							if ($thisfile_mpeg_audio_lame_raw['RGAD_album'] != 0) {

								$thisfile_mpeg_audio_lame_RGAD_album['raw']['name']        = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0xE000) >> 13;
								$thisfile_mpeg_audio_lame_RGAD_album['raw']['originator']  = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x1C00) >> 10;
								$thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit']    = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x0200) >> 9;
								$thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x01FF;
								$thisfile_mpeg_audio_lame_RGAD_album['name']       = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['name']);
								$thisfile_mpeg_audio_lame_RGAD_album['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['originator']);
								$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']);

								if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) {
									$info['replay_gain']['album']['peak']   = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
								}
								$info['replay_gain']['album']['originator'] = $thisfile_mpeg_audio_lame_RGAD_album['originator'];
								$info['replay_gain']['album']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_album['gain_db'];
							} else {
								unset($thisfile_mpeg_audio_lame_RGAD['album']);
							}
							if (empty($thisfile_mpeg_audio_lame_RGAD)) {
								unset($thisfile_mpeg_audio_lame['RGAD']);
							}


							// byte $AF  Encoding flags + ATH Type
							$EncodingFlagsATHtype = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAF, 1));
							$thisfile_mpeg_audio_lame['encoding_flags']['nspsytune']   = (bool) ($EncodingFlagsATHtype & 0x10);
							$thisfile_mpeg_audio_lame['encoding_flags']['nssafejoint'] = (bool) ($EncodingFlagsATHtype & 0x20);
							$thisfile_mpeg_audio_lame['encoding_flags']['nogap_next']  = (bool) ($EncodingFlagsATHtype & 0x40);
							$thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev']  = (bool) ($EncodingFlagsATHtype & 0x80);
							$thisfile_mpeg_audio_lame['ath_type']                      =         $EncodingFlagsATHtype & 0x0F;

							// byte $B0  if ABR {specified bitrate} else {minimal bitrate}
							$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB0, 1));
							if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 2) { // Average BitRate (ABR)
								$thisfile_mpeg_audio_lame['bitrate_abr'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
							} elseif ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) { // Constant BitRate (CBR)
								// ignore
							} elseif ($thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] > 0) { // Variable BitRate (VBR) - minimum bitrate
								$thisfile_mpeg_audio_lame['bitrate_min'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
							}

							// bytes $B1-$B3  Encoder delays
							$EncoderDelays = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB1, 3));
							$thisfile_mpeg_audio_lame['encoder_delay'] = ($EncoderDelays & 0xFFF000) >> 12;
							$thisfile_mpeg_audio_lame['end_padding']   =  $EncoderDelays & 0x000FFF;

							// byte $B4  Misc
							$MiscByte = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB4, 1));
							$thisfile_mpeg_audio_lame_raw['noise_shaping']       = ($MiscByte & 0x03);
							$thisfile_mpeg_audio_lame_raw['stereo_mode']         = ($MiscByte & 0x1C) >> 2;
							$thisfile_mpeg_audio_lame_raw['not_optimal_quality'] = ($MiscByte & 0x20) >> 5;
							$thisfile_mpeg_audio_lame_raw['source_sample_freq']  = ($MiscByte & 0xC0) >> 6;
							$thisfile_mpeg_audio_lame['noise_shaping']       = $thisfile_mpeg_audio_lame_raw['noise_shaping'];
							$thisfile_mpeg_audio_lame['stereo_mode']         = self::LAMEmiscStereoModeLookup($thisfile_mpeg_audio_lame_raw['stereo_mode']);
							$thisfile_mpeg_audio_lame['not_optimal_quality'] = (bool) $thisfile_mpeg_audio_lame_raw['not_optimal_quality'];
							$thisfile_mpeg_audio_lame['source_sample_freq']  = self::LAMEmiscSourceSampleFrequencyLookup($thisfile_mpeg_audio_lame_raw['source_sample_freq']);

							// byte $B5  MP3 Gain
							$thisfile_mpeg_audio_lame_raw['mp3_gain'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB5, 1), false, true);
							$thisfile_mpeg_audio_lame['mp3_gain_db']     = (getid3_lib::RGADamplitude2dB(2) / 4) * $thisfile_mpeg_audio_lame_raw['mp3_gain'];
							$thisfile_mpeg_audio_lame['mp3_gain_factor'] = pow(2, ($thisfile_mpeg_audio_lame['mp3_gain_db'] / 6));

							// bytes $B6-$B7  Preset and surround info
							$PresetSurroundBytes = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB6, 2));
							// Reserved                                                    = ($PresetSurroundBytes & 0xC000);
							$thisfile_mpeg_audio_lame_raw['surround_info'] = ($PresetSurroundBytes & 0x3800);
							$thisfile_mpeg_audio_lame['surround_info']     = self::LAMEsurroundInfoLookup($thisfile_mpeg_audio_lame_raw['surround_info']);
							$thisfile_mpeg_audio_lame['preset_used_id']    = ($PresetSurroundBytes & 0x07FF);
							$thisfile_mpeg_audio_lame['preset_used']       = self::LAMEpresetUsedLookup($thisfile_mpeg_audio_lame);
							if (!empty($thisfile_mpeg_audio_lame['preset_used_id']) && empty($thisfile_mpeg_audio_lame['preset_used'])) {
								$this->warning('Unknown LAME preset used ('.$thisfile_mpeg_audio_lame['preset_used_id'].') - please report to info@getid3.org');
							}
							if (($thisfile_mpeg_audio_lame['short_version'] == 'LAME3.90.') && !empty($thisfile_mpeg_audio_lame['preset_used_id'])) {
								// this may change if 3.90.4 ever comes out
								$thisfile_mpeg_audio_lame['short_version'] = 'LAME3.90.3';
							}

							// bytes $B8-$BB  MusicLength
							$thisfile_mpeg_audio_lame['audio_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB8, 4));
							$ExpectedNumberOfAudioBytes = (($thisfile_mpeg_audio_lame['audio_bytes'] > 0) ? $thisfile_mpeg_audio_lame['audio_bytes'] : $thisfile_mpeg_audio['VBR_bytes']);

							// bytes $BC-$BD  MusicCRC
							$thisfile_mpeg_audio_lame['music_crc']    = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBC, 2));

							// bytes $BE-$BF  CRC-16 of Info Tag
							$thisfile_mpeg_audio_lame['lame_tag_crc'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBE, 2));


							// LAME CBR
							if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1 && $thisfile_mpeg_audio['bitrate'] !== 'free') {

								$thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
								$thisfile_mpeg_audio['bitrate'] = self::ClosestStandardMP3Bitrate($thisfile_mpeg_audio['bitrate']);
								$info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
								//if (empty($thisfile_mpeg_audio['bitrate']) || (!empty($thisfile_mpeg_audio_lame['bitrate_min']) && ($thisfile_mpeg_audio_lame['bitrate_min'] != 255))) {
								//	$thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio_lame['bitrate_min'];
								//}

							}

						}
					}
				}

			} else {

				// not Fraunhofer or Xing VBR methods, most likely CBR (but could be VBR with no header)
				$thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
				if ($recursivesearch) {
					$thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
					if ($this->RecursiveFrameScanning($offset, $nextframetestoffset, true)) {
						$recursivesearch = false;
						$thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
					}
					if ($thisfile_mpeg_audio['bitrate_mode'] == 'vbr') {
						$this->warning('VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.');
					}
				}

			}

		}

		if (($ExpectedNumberOfAudioBytes > 0) && ($ExpectedNumberOfAudioBytes != ($info['avdataend'] - $info['avdataoffset']))) {
			if ($ExpectedNumberOfAudioBytes > ($info['avdataend'] - $info['avdataoffset'])) {
				if ($this->isDependencyFor('matroska') || $this->isDependencyFor('riff')) {
					// ignore, audio data is broken into chunks so will always be data "missing"
				}
				elseif (($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])) == 1) {
					$this->warning('Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)');
				}
				else {
					$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)');
				}
			} else {
				if ((($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes) == 1) {
				//	$prenullbytefileoffset = $this->ftell();
				//	$this->fseek($info['avdataend']);
				//	$PossibleNullByte = $this->fread(1);
				//	$this->fseek($prenullbytefileoffset);
				//	if ($PossibleNullByte === "\x00") {
						$info['avdataend']--;
				//		$this->warning('Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored');
				//	} else {
				//		$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)');
				//	}
				} else {
					$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)');
				}
			}
		}

		if (($thisfile_mpeg_audio['bitrate'] == 'free') && empty($info['audio']['bitrate'])) {
			if (($offset == $info['avdataoffset']) && empty($thisfile_mpeg_audio['VBR_frames'])) {
				$framebytelength = $this->FreeFormatFrameLength($offset, true);
				if ($framebytelength > 0) {
					$thisfile_mpeg_audio['framelength'] = $framebytelength;
					if ($thisfile_mpeg_audio['layer'] == '1') {
						// BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
						$info['audio']['bitrate'] = ((($framebytelength / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
					} else {
						// Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
						$info['audio']['bitrate'] = (($framebytelength - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
					}
				} else {
					$this->error('Error calculating frame length of free-format MP3 without Xing/LAME header');
				}
			}
		}

		if (isset($thisfile_mpeg_audio['VBR_frames']) ? $thisfile_mpeg_audio['VBR_frames'] : '') {
			switch ($thisfile_mpeg_audio['bitrate_mode']) {
				case 'vbr':
				case 'abr':
					$bytes_per_frame = 1152;
					if (($thisfile_mpeg_audio['version'] == '1') && ($thisfile_mpeg_audio['layer'] == 1)) {
						$bytes_per_frame = 384;
					} elseif ((($thisfile_mpeg_audio['version'] == '2') || ($thisfile_mpeg_audio['version'] == '2.5')) && ($thisfile_mpeg_audio['layer'] == 3)) {
						$bytes_per_frame = 576;
					}
					$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);
					if ($thisfile_mpeg_audio['VBR_bitrate'] > 0) {
						$info['audio']['bitrate']       = $thisfile_mpeg_audio['VBR_bitrate'];
						$thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; // to avoid confusion
					}
					break;
			}
		}

		// End variable-bitrate headers
		////////////////////////////////////////////////////////////////////////////////////

		if ($recursivesearch) {

			if (!$this->RecursiveFrameScanning($offset, $nextframetestoffset, $ScanAsCBR)) {
				return false;
			}
			if (!empty($this->getid3->info['mp3_validity_check_bitrates']) && !empty($thisfile_mpeg_audio['bitrate_mode']) && ($thisfile_mpeg_audio['bitrate_mode'] == 'vbr') && !empty($thisfile_mpeg_audio['VBR_bitrate'])) {
				// https://github.com/JamesHeinrich/getID3/issues/287
				if (count(array_keys($this->getid3->info['mp3_validity_check_bitrates'])) == 1) {
					list($cbr_bitrate_in_short_scan) = array_keys($this->getid3->info['mp3_validity_check_bitrates']);
					$deviation_cbr_from_header_bitrate = abs($thisfile_mpeg_audio['VBR_bitrate'] - $cbr_bitrate_in_short_scan) / $cbr_bitrate_in_short_scan;
					if ($deviation_cbr_from_header_bitrate < 0.01) {
						// VBR header bitrate may differ slightly from true bitrate of frames, perhaps accounting for overhead of VBR header frame itself?
						// If measured CBR bitrate is within 1% of specified bitrate in VBR header then assume that file is truly CBR
						$thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
						//$this->warning('VBR header ignored, assuming CBR '.round($cbr_bitrate_in_short_scan / 1000).'kbps based on scan of '.$this->mp3_valid_check_frames.' frames');
					}
				}
			}
			if (isset($this->getid3->info['mp3_validity_check_bitrates'])) {
				unset($this->getid3->info['mp3_validity_check_bitrates']);
			}

		}


		//if (false) {
		//    // experimental side info parsing section - not returning anything useful yet
		//
		//    $SideInfoBitstream = getid3_lib::BigEndian2Bin($SideInfoData);
		//    $SideInfoOffset = 0;
		//
		//    if ($thisfile_mpeg_audio['version'] == '1') {
		//        if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
		//            // MPEG-1 (mono)
		//            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
		//            $SideInfoOffset += 9;
		//            $SideInfoOffset += 5;
		//        } else {
		//            // MPEG-1 (stereo, joint-stereo, dual-channel)
		//            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
		//            $SideInfoOffset += 9;
		//            $SideInfoOffset += 3;
		//        }
		//    } else { // 2 or 2.5
		//        if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
		//            // MPEG-2, MPEG-2.5 (mono)
		//            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
		//            $SideInfoOffset += 8;
		//            $SideInfoOffset += 1;
		//        } else {
		//            // MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel)
		//            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
		//            $SideInfoOffset += 8;
		//            $SideInfoOffset += 2;
		//        }
		//    }
		//
		//    if ($thisfile_mpeg_audio['version'] == '1') {
		//        for ($channel = 0; $channel < $info['audio']['channels']; $channel++) {
		//            for ($scfsi_band = 0; $scfsi_band < 4; $scfsi_band++) {
		//                $thisfile_mpeg_audio['scfsi'][$channel][$scfsi_band] = substr($SideInfoBitstream, $SideInfoOffset, 1);
		//                $SideInfoOffset += 2;
		//            }
		//        }
		//    }
		//    for ($granule = 0; $granule < (($thisfile_mpeg_audio['version'] == '1') ? 2 : 1); $granule++) {
		//        for ($channel = 0; $channel < $info['audio']['channels']; $channel++) {
		//            $thisfile_mpeg_audio['part2_3_length'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 12);
		//            $SideInfoOffset += 12;
		//            $thisfile_mpeg_audio['big_values'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
		//            $SideInfoOffset += 9;
		//            $thisfile_mpeg_audio['global_gain'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 8);
		//            $SideInfoOffset += 8;
		//            if ($thisfile_mpeg_audio['version'] == '1') {
		//                $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
		//                $SideInfoOffset += 4;
		//            } else {
		//                $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
		//                $SideInfoOffset += 9;
		//            }
		//            $thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
		//            $SideInfoOffset += 1;
		//
		//            if ($thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] == '1') {
		//
		//                $thisfile_mpeg_audio['block_type'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 2);
		//                $SideInfoOffset += 2;
		//                $thisfile_mpeg_audio['mixed_block_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
		//                $SideInfoOffset += 1;
		//
		//                for ($region = 0; $region < 2; $region++) {
		//                    $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
		//                    $SideInfoOffset += 5;
		//                }
		//                $thisfile_mpeg_audio['table_select'][$granule][$channel][2] = 0;
		//
		//                for ($window = 0; $window < 3; $window++) {
		//                    $thisfile_mpeg_audio['subblock_gain'][$granule][$channel][$window] = substr($SideInfoBitstream, $SideInfoOffset, 3);
		//                    $SideInfoOffset += 3;
		//                }
		//
		//            } else {
		//
		//                for ($region = 0; $region < 3; $region++) {
		//                    $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
		//                    $SideInfoOffset += 5;
		//                }
		//
		//                $thisfile_mpeg_audio['region0_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
		//                $SideInfoOffset += 4;
		//                $thisfile_mpeg_audio['region1_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 3);
		//                $SideInfoOffset += 3;
		//                $thisfile_mpeg_audio['block_type'][$granule][$channel] = 0;
		//            }
		//
		//            if ($thisfile_mpeg_audio['version'] == '1') {
		//                $thisfile_mpeg_audio['preflag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
		//                $SideInfoOffset += 1;
		//            }
		//            $thisfile_mpeg_audio['scalefac_scale'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
		//            $SideInfoOffset += 1;
		//            $thisfile_mpeg_audio['count1table_select'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
		//            $SideInfoOffset += 1;
		//        }
		//    }
		//}

		return true;
	}

	/**
	 * @param int $offset
	 * @param int $nextframetestoffset
	 * @param bool $ScanAsCBR
	 *
	 * @return bool
	 */
	public function RecursiveFrameScanning(&$offset, &$nextframetestoffset, $ScanAsCBR) {
		$info = &$this->getid3->info;
		$firstframetestarray = array('error' => array(), 'warning'=> array(), 'avdataend' => $info['avdataend'], 'avdataoffset' => $info['avdataoffset']);
		$this->decodeMPEGaudioHeader($offset, $firstframetestarray, false);

		$info['mp3_validity_check_bitrates'] = array();
		for ($i = 0; $i < $this->mp3_valid_check_frames; $i++) {
			// check next (default: 50) frames for validity, to make sure we haven't run across a false synch
			if (($nextframetestoffset + 4) >= $info['avdataend']) {
				// end of file
				return true;
			}

			$nextframetestarray = array('error' => array(), 'warning' => array(), 'avdataend' => $info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
			if ($this->decodeMPEGaudioHeader($nextframetestoffset, $nextframetestarray, false)) {
				/** @phpstan-ignore-next-line */
				getid3_lib::safe_inc($info['mp3_validity_check_bitrates'][$nextframetestarray['mpeg']['audio']['bitrate']]);
				if ($ScanAsCBR) {
					// force CBR mode, used for trying to pick out invalid audio streams with valid(?) VBR headers, or VBR streams with no VBR header
					if (!isset($nextframetestarray['mpeg']['audio']['bitrate']) || !isset($firstframetestarray['mpeg']['audio']['bitrate']) || ($nextframetestarray['mpeg']['audio']['bitrate'] != $firstframetestarray['mpeg']['audio']['bitrate'])) {
						return false;
					}
				}


				// next frame is OK, get ready to check the one after that
				if (isset($nextframetestarray['mpeg']['audio']['framelength']) && ($nextframetestarray['mpeg']['audio']['framelength'] > 0)) {
					$nextframetestoffset += $nextframetestarray['mpeg']['audio']['framelength'];
				} else {
					$this->error('Frame at offset ('.$offset.') is has an invalid frame length.');
					return false;
				}

			} elseif (!empty($firstframetestarray['mpeg']['audio']['framelength']) && (($nextframetestoffset + $firstframetestarray['mpeg']['audio']['framelength']) > $info['avdataend'])) {

				// 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
				return true;

			} else {

				// next frame is not valid, note the error and fail, so scanning can contiue for a valid frame sequence
				$this->warning('Frame at offset ('.$offset.') is valid, but the next one at ('.$nextframetestoffset.') is not.');

				return false;
			}
		}
		return true;
	}

	/**
	 * @param int  $offset
	 * @param bool $deepscan
	 *
	 * @return int|false
	 */
	public function FreeFormatFrameLength($offset, $deepscan=false) {
		$info = &$this->getid3->info;

		$this->fseek($offset);
		$MPEGaudioData = $this->fread(32768);

		$SyncPattern1 = substr($MPEGaudioData, 0, 4);
		// may be different pattern due to padding
		$SyncPattern2 = $SyncPattern1[0].$SyncPattern1[1].chr(ord($SyncPattern1[2]) | 0x02).$SyncPattern1[3];
		if ($SyncPattern2 === $SyncPattern1) {
			$SyncPattern2 = $SyncPattern1[0].$SyncPattern1[1].chr(ord($SyncPattern1[2]) & 0xFD).$SyncPattern1[3];
		}

		$framelength = false;
		$framelength1 = strpos($MPEGaudioData, $SyncPattern1, 4);
		$framelength2 = strpos($MPEGaudioData, $SyncPattern2, 4);
		if ($framelength1 > 4) {
			$framelength = $framelength1;
		}
		if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
			$framelength = $framelength2;
		}
		if (!$framelength) {

			// LAME 3.88 has a different value for modeextension on the first frame vs the rest
			$framelength1 = strpos($MPEGaudioData, substr($SyncPattern1, 0, 3), 4);
			$framelength2 = strpos($MPEGaudioData, substr($SyncPattern2, 0, 3), 4);

			if ($framelength1 > 4) {
				$framelength = $framelength1;
			}
			if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
				$framelength = $framelength2;
			}
			if (!$framelength) {
				$this->error('Cannot find next free-format synch pattern ('.getid3_lib::PrintHexBytes($SyncPattern1).' or '.getid3_lib::PrintHexBytes($SyncPattern2).') after offset '.$offset);
				return false;
			} else {
				$this->warning('ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)');
				$info['audio']['codec']   = 'LAME';
				$info['audio']['encoder'] = 'LAME3.88';
				$SyncPattern1 = substr($SyncPattern1, 0, 3);
				$SyncPattern2 = substr($SyncPattern2, 0, 3);
			}
		}

		if ($deepscan) {

			$ActualFrameLengthValues = array();
			$nextoffset = $offset + $framelength;
			while ($nextoffset < ($info['avdataend'] - 6)) {
				$this->fseek($nextoffset - 1);
				$NextSyncPattern = $this->fread(6);
				if ((substr($NextSyncPattern, 1, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 1, strlen($SyncPattern2)) == $SyncPattern2)) {
					// good - found where expected
					$ActualFrameLengthValues[] = $framelength;
				} elseif ((substr($NextSyncPattern, 0, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 0, strlen($SyncPattern2)) == $SyncPattern2)) {
					// ok - found one byte earlier than expected (last frame wasn't padded, first frame was)
					$ActualFrameLengthValues[] = ($framelength - 1);
					$nextoffset--;
				} elseif ((substr($NextSyncPattern, 2, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 2, strlen($SyncPattern2)) == $SyncPattern2)) {
					// ok - found one byte later than expected (last frame was padded, first frame wasn't)
					$ActualFrameLengthValues[] = ($framelength + 1);
					$nextoffset++;
				} else {
					$this->error('Did not find expected free-format sync pattern at offset '.$nextoffset);
					return false;
				}
				$nextoffset += $framelength;
			}
			if (count($ActualFrameLengthValues) > 0) {
				$framelength = intval(round(array_sum($ActualFrameLengthValues) / count($ActualFrameLengthValues)));
			}
		}
		return $framelength;
	}

	/**
	 * @return bool
	 */
	public function getOnlyMPEGaudioInfoBruteForce() {
		$MPEGaudioHeaderDecodeCache   = array();
		$MPEGaudioHeaderValidCache    = array();
		$MPEGaudioHeaderLengthCache   = array();
		$MPEGaudioVersionLookup       = self::MPEGaudioVersionArray();
		$MPEGaudioLayerLookup         = self::MPEGaudioLayerArray();
		$MPEGaudioBitrateLookup       = self::MPEGaudioBitrateArray();
		$MPEGaudioFrequencyLookup     = self::MPEGaudioFrequencyArray();
		$MPEGaudioChannelModeLookup   = self::MPEGaudioChannelModeArray();
		$MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
		$MPEGaudioEmphasisLookup      = self::MPEGaudioEmphasisArray();
		$LongMPEGversionLookup        = array();
		$LongMPEGlayerLookup          = array();
		$LongMPEGbitrateLookup        = array();
		$LongMPEGpaddingLookup        = array();
		$LongMPEGfrequencyLookup      = array();
		$Distribution                 = array();
		$Distribution['bitrate']      = array();
		$Distribution['frequency']    = array();
		$Distribution['layer']        = array();
		$Distribution['version']      = array();
		$Distribution['padding']      = array();

		$info = &$this->getid3->info;
		$this->fseek($info['avdataoffset']);

		$max_frames_scan = 5000;
		$frames_scanned  = 0;

		$previousvalidframe = $info['avdataoffset'];
		while ($this->ftell() < $info['avdataend']) {
			set_time_limit(30);
			$head4 = $this->fread(4);
			if (strlen($head4) < 4) {
				break;
			}
			if ($head4[0] != "\xFF") {
				for ($i = 1; $i < 4; $i++) {
					if ($head4[$i] == "\xFF") {
						$this->fseek($i - 4, SEEK_CUR);
						continue 2;
					}
				}
				continue;
			}
			if (!isset($MPEGaudioHeaderDecodeCache[$head4])) {
				$MPEGaudioHeaderDecodeCache[$head4] = self::MPEGaudioHeaderDecode($head4);
			}
			if (!isset($MPEGaudioHeaderValidCache[$head4])) {
				$MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$head4], false, false);
			}
			if ($MPEGaudioHeaderValidCache[$head4]) {

				if (!isset($MPEGaudioHeaderLengthCache[$head4])) {
					$LongMPEGversionLookup[$head4]   = $MPEGaudioVersionLookup[$MPEGaudioHeaderDecodeCache[$head4]['version']];
					$LongMPEGlayerLookup[$head4]     = $MPEGaudioLayerLookup[$MPEGaudioHeaderDecodeCache[$head4]['layer']];
					$LongMPEGbitrateLookup[$head4]   = $MPEGaudioBitrateLookup[$LongMPEGversionLookup[$head4]][$LongMPEGlayerLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['bitrate']];
					$LongMPEGpaddingLookup[$head4]   = (bool) $MPEGaudioHeaderDecodeCache[$head4]['padding'];
					$LongMPEGfrequencyLookup[$head4] = $MPEGaudioFrequencyLookup[$LongMPEGversionLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['sample_rate']];
					$MPEGaudioHeaderLengthCache[$head4] = self::MPEGaudioFrameLength(
						$LongMPEGbitrateLookup[$head4],
						$LongMPEGversionLookup[$head4],
						$LongMPEGlayerLookup[$head4],
						$LongMPEGpaddingLookup[$head4],
						$LongMPEGfrequencyLookup[$head4]);
				}
				if ($MPEGaudioHeaderLengthCache[$head4] > 4) {
					$WhereWeWere = $this->ftell();
					$this->fseek($MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR);
					$next4 = $this->fread(4);
					if ($next4[0] == "\xFF") {
						if (!isset($MPEGaudioHeaderDecodeCache[$next4])) {
							$MPEGaudioHeaderDecodeCache[$next4] = self::MPEGaudioHeaderDecode($next4);
						}
						if (!isset($MPEGaudioHeaderValidCache[$next4])) {
							$MPEGaudioHeaderValidCache[$next4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$next4], false, false);
						}
						if ($MPEGaudioHeaderValidCache[$next4]) {
							$this->fseek(-4, SEEK_CUR);

							$Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]] = isset($Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]]) ? ++$Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]] : 1;
							$Distribution['layer'][$LongMPEGlayerLookup[$head4]] = isset($Distribution['layer'][$LongMPEGlayerLookup[$head4]]) ? ++$Distribution['layer'][$LongMPEGlayerLookup[$head4]] : 1;
							$Distribution['version'][$LongMPEGversionLookup[$head4]] = isset($Distribution['version'][$LongMPEGversionLookup[$head4]]) ? ++$Distribution['version'][$LongMPEGversionLookup[$head4]] : 1;
							$Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])] = isset($Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]) ? ++$Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])] : 1;
							$Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]] = isset($Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]) ? ++$Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]] : 1;
							if (++$frames_scanned >= $max_frames_scan) {
								$pct_data_scanned = ($this->ftell() - $info['avdataoffset']) / ($info['avdataend'] - $info['avdataoffset']);
								$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.');
								foreach ($Distribution as $key1 => $value1) {
									foreach ($value1 as $key2 => $value2) {
										$Distribution[$key1][$key2] = round($value2 / $pct_data_scanned);
									}
								}
								break;
							}
							continue;
						}
					}
					unset($next4);
					$this->fseek($WhereWeWere - 3);
				}

			}
		}
		foreach ($Distribution as $key => $value) {
			ksort($Distribution[$key], SORT_NUMERIC);
		}
		ksort($Distribution['version'], SORT_STRING);
		$info['mpeg']['audio']['bitrate_distribution']   = $Distribution['bitrate'];
		$info['mpeg']['audio']['frequency_distribution'] = $Distribution['frequency'];
		$info['mpeg']['audio']['layer_distribution']     = $Distribution['layer'];
		$info['mpeg']['audio']['version_distribution']   = $Distribution['version'];
		$info['mpeg']['audio']['padding_distribution']   = $Distribution['padding'];
		if (count($Distribution['version']) > 1) {
			$this->error('Corrupt file - more than one MPEG version detected');
		}
		if (count($Distribution['layer']) > 1) {
			$this->error('Corrupt file - more than one MPEG layer detected');
		}
		if (count($Distribution['frequency']) > 1) {
			$this->error('Corrupt file - more than one MPEG sample rate detected');
		}


		$bittotal = 0;
		foreach ($Distribution['bitrate'] as $bitratevalue => $bitratecount) {
			if ($bitratevalue != 'free') {
				$bittotal += ($bitratevalue * $bitratecount);
			}
		}
		$info['mpeg']['audio']['frame_count']  = array_sum($Distribution['bitrate']);
		if ($info['mpeg']['audio']['frame_count'] == 0) {
			$this->error('no MPEG audio frames found');
			return false;
		}
		$info['mpeg']['audio']['bitrate']      = ($bittotal / $info['mpeg']['audio']['frame_count']);
		$info['mpeg']['audio']['bitrate_mode'] = ((count($Distribution['bitrate']) > 0) ? 'vbr' : 'cbr');
		$info['mpeg']['audio']['sample_rate']  = getid3_lib::array_max($Distribution['frequency'], true);

		$info['audio']['bitrate']      = $info['mpeg']['audio']['bitrate'];
		$info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode'];
		$info['audio']['sample_rate']  = $info['mpeg']['audio']['sample_rate'];
		$info['audio']['dataformat']   = 'mp'.getid3_lib::array_max($Distribution['layer'], true);
		$info['fileformat']            = $info['audio']['dataformat'];

		return true;
	}

	/**
	 * @param int  $avdataoffset
	 * @param bool $BitrateHistogram
	 *
	 * @return bool
	 */
	public function getOnlyMPEGaudioInfo($avdataoffset, $BitrateHistogram=false) {
		// looks for synch, decodes MPEG audio header

		$info = &$this->getid3->info;

		static $MPEGaudioVersionLookup;
		static $MPEGaudioLayerLookup;
		static $MPEGaudioBitrateLookup;
		if (empty($MPEGaudioVersionLookup)) {
			$MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
			$MPEGaudioLayerLookup   = self::MPEGaudioLayerArray();
			$MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
		}

		$this->fseek($avdataoffset);
		$sync_seek_buffer_size = min(128 * 1024, $info['avdataend'] - $avdataoffset);
		if ($sync_seek_buffer_size <= 0) {
			$this->error('Invalid $sync_seek_buffer_size at offset '.$avdataoffset);
			return false;
		}
		$header = $this->fread($sync_seek_buffer_size);
		$sync_seek_buffer_size = strlen($header);
		$SynchSeekOffset = 0;
		$SyncSeekAttempts = 0;
		$SyncSeekAttemptsMax = 1000;
		$FirstFrameThisfileInfo = null;
		while ($SynchSeekOffset < $sync_seek_buffer_size) {
			if ((($avdataoffset + $SynchSeekOffset)  < $info['avdataend']) && !feof($this->getid3->fp)) {

				if ($SynchSeekOffset > $sync_seek_buffer_size) {
					// if a synch's not found within the first 128k bytes, then give up
					$this->error('Could not find valid MPEG audio synch within the first '.round($sync_seek_buffer_size / 1024).'kB');
					if (isset($info['audio']['bitrate'])) {
						unset($info['audio']['bitrate']);
					}
					if (isset($info['mpeg']['audio'])) {
						unset($info['mpeg']['audio']);
					}
					if (empty($info['mpeg'])) {
						unset($info['mpeg']);
					}
					return false;

				} elseif (feof($this->getid3->fp)) {

					$this->error('Could not find valid MPEG audio synch before end of file');
					if (isset($info['audio']['bitrate'])) {
						unset($info['audio']['bitrate']);
					}
					if (isset($info['mpeg']['audio'])) {
						unset($info['mpeg']['audio']);
					}
					if (isset($info['mpeg']) && (!is_array($info['mpeg']) || (count($info['mpeg']) == 0))) {
						unset($info['mpeg']);
					}
					return false;
				}
			}

			if (($SynchSeekOffset + 1) >= strlen($header)) {
				$this->error('Could not find valid MPEG synch before end of file');
				return false;
			}

			if (($header[$SynchSeekOffset] == "\xFF") && ($header[($SynchSeekOffset + 1)] > "\xE0")) { // possible synch detected
				if (++$SyncSeekAttempts >= $SyncSeekAttemptsMax) {
					// https://github.com/JamesHeinrich/getID3/issues/286
					// corrupt files claiming to be MP3, with a large number of 0xFF bytes near the beginning, can cause this loop to take a very long time
					// should have escape condition to avoid spending too much time scanning a corrupt file
					// if a synch's not found within the first 128k bytes, then give up
					$this->error('Could not find valid MPEG audio synch after scanning '.$SyncSeekAttempts.' candidate offsets');
					if (isset($info['audio']['bitrate'])) {
						unset($info['audio']['bitrate']);
					}
					if (isset($info['mpeg']['audio'])) {
						unset($info['mpeg']['audio']);
					}
					if (empty($info['mpeg'])) {
						unset($info['mpeg']);
					}
					return false;
				}
				$FirstFrameAVDataOffset = null;
				if (!isset($FirstFrameThisfileInfo) && !isset($info['mpeg']['audio'])) {
					$FirstFrameThisfileInfo = $info;
					$FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset;
					if (!$this->decodeMPEGaudioHeader($FirstFrameAVDataOffset, $FirstFrameThisfileInfo, false)) {
						// if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's
						// garbage between this frame and a valid sequence of MPEG-audio frames, to be restored below
						unset($FirstFrameThisfileInfo);
					}
				}

				$dummy = $info; // only overwrite real data if valid header found
				if ($this->decodeMPEGaudioHeader($avdataoffset + $SynchSeekOffset, $dummy, true)) {
					$info = $dummy;
					$info['avdataoffset'] = $avdataoffset + $SynchSeekOffset;
					switch (isset($info['fileformat']) ? $info['fileformat'] : '') {
						case '':
						case 'id3':
						case 'ape':
						case 'mp3':
							$info['fileformat']          = 'mp3';
							$info['audio']['dataformat'] = 'mp3';
							break;
					}
					if (isset($FirstFrameThisfileInfo) && isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) {
						if (!(abs($info['audio']['bitrate'] - $FirstFrameThisfileInfo['audio']['bitrate']) <= 1)) {
							// If there is garbage data between a valid VBR header frame and a sequence
							// of valid MPEG-audio frames the VBR data is no longer discarded.
							$info = $FirstFrameThisfileInfo;
							$info['avdataoffset']        = $FirstFrameAVDataOffset;
							$info['fileformat']          = 'mp3';
							$info['audio']['dataformat'] = 'mp3';
							$dummy                       = $info;
							unset($dummy['mpeg']['audio']);
							$GarbageOffsetStart = $FirstFrameAVDataOffset + $FirstFrameThisfileInfo['mpeg']['audio']['framelength'];
							$GarbageOffsetEnd   = $avdataoffset + $SynchSeekOffset;
							if ($this->decodeMPEGaudioHeader($GarbageOffsetEnd, $dummy, true, true)) {
								$info = $dummy;
								$info['avdataoffset'] = $GarbageOffsetEnd;
								$this->warning('apparently-valid VBR header not used because could not find '.$this->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);
							} else {
								$this->warning('using data from VBR header even though could not find '.$this->mp3_valid_check_frames.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.')');
							}
						}
					}
					if (isset($info['mpeg']['audio']['bitrate_mode']) && ($info['mpeg']['audio']['bitrate_mode'] == 'vbr') && !isset($info['mpeg']['audio']['VBR_method'])) {
						// VBR file with no VBR header
						$BitrateHistogram = true;
					}

					if ($BitrateHistogram) {

						$info['mpeg']['audio']['stereo_distribution']  = array('stereo'=>0, 'joint stereo'=>0, 'dual channel'=>0, 'mono'=>0);
						$info['mpeg']['audio']['version_distribution'] = array('1'=>0, '2'=>0, '2.5'=>0);

						if ($info['mpeg']['audio']['version'] == '1') {
							if ($info['mpeg']['audio']['layer'] == 3) {
								$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);
							} elseif ($info['mpeg']['audio']['layer'] == 2) {
								$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);
							} elseif ($info['mpeg']['audio']['layer'] == 1) {
								$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);
							}
						} elseif ($info['mpeg']['audio']['layer'] == 1) {
							$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);
						} else {
							$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);
						}

						$dummy = array('error'=>$info['error'], 'warning'=>$info['warning'], 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
						$synchstartoffset = $info['avdataoffset'];
						$this->fseek($info['avdataoffset']);

						// you can play with these numbers:
						$max_frames_scan  = 50000;
						$max_scan_segments = 10;

						// don't play with these numbers:
						$FastMode = false;
						$SynchErrorsFound = 0;
						$frames_scanned   = 0;
						$this_scan_segment = 0;
						$frames_scan_per_segment = ceil($max_frames_scan / $max_scan_segments);
						$pct_data_scanned = 0;
						for ($current_segment = 0; $current_segment < $max_scan_segments; $current_segment++) {
							$frames_scanned_this_segment = 0;
							$scan_start_offset = array();
							if ($this->ftell() >= $info['avdataend']) {
								break;
							}
							$scan_start_offset[$current_segment] = max($this->ftell(), $info['avdataoffset'] + round($current_segment * (($info['avdataend'] - $info['avdataoffset']) / $max_scan_segments)));
							if ($current_segment > 0) {
								$this->fseek($scan_start_offset[$current_segment]);
								$buffer_4k = $this->fread(4096);
								for ($j = 0; $j < (strlen($buffer_4k) - 4); $j++) {
									if (($buffer_4k[$j] == "\xFF") && ($buffer_4k[($j + 1)] > "\xE0")) { // synch detected
										if ($this->decodeMPEGaudioHeader($scan_start_offset[$current_segment] + $j, $dummy, false, false, $FastMode)) {
											$calculated_next_offset = $scan_start_offset[$current_segment] + $j + $dummy['mpeg']['audio']['framelength'];
											if ($this->decodeMPEGaudioHeader($calculated_next_offset, $dummy, false, false, $FastMode)) {
												$scan_start_offset[$current_segment] += $j;
												break;
											}
										}
									}
								}
							}
							$synchstartoffset = $scan_start_offset[$current_segment];
							while (($synchstartoffset < $info['avdataend']) && $this->decodeMPEGaudioHeader($synchstartoffset, $dummy, false, false, $FastMode)) {
								$FastMode = true;
								$thisframebitrate = $MPEGaudioBitrateLookup[$MPEGaudioVersionLookup[$dummy['mpeg']['audio']['raw']['version']]][$MPEGaudioLayerLookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']];

								if (empty($dummy['mpeg']['audio']['framelength'])) {
									$SynchErrorsFound++;
									$synchstartoffset++;
								} else {
									getid3_lib::safe_inc($info['mpeg']['audio']['bitrate_distribution'][$thisframebitrate]);
									getid3_lib::safe_inc($info['mpeg']['audio']['stereo_distribution'][$dummy['mpeg']['audio']['channelmode']]);
									getid3_lib::safe_inc($info['mpeg']['audio']['version_distribution'][$dummy['mpeg']['audio']['version']]);
									$synchstartoffset += $dummy['mpeg']['audio']['framelength'];
								}
								$frames_scanned++;
								if ($frames_scan_per_segment && (++$frames_scanned_this_segment >= $frames_scan_per_segment)) {
									$this_pct_scanned = ($this->ftell() - $scan_start_offset[$current_segment]) / ($info['avdataend'] - $info['avdataoffset']);
									if (($current_segment == 0) && (($this_pct_scanned * $max_scan_segments) >= 1)) {
										// file likely contains < $max_frames_scan, just scan as one segment
										$max_scan_segments = 1;
										$frames_scan_per_segment = $max_frames_scan;
									} else {
										$pct_data_scanned += $this_pct_scanned;
										break;
									}
								}
							}
						}
						if ($pct_data_scanned > 0) {
							$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.');
							foreach ($info['mpeg']['audio'] as $key1 => $value1) {
								if (!preg_match('#_distribution$#i', $key1)) {
									continue;
								}
								foreach ($value1 as $key2 => $value2) {
									$info['mpeg']['audio'][$key1][$key2] = round($value2 / $pct_data_scanned);
								}
							}
						}

						if ($SynchErrorsFound > 0) {
							$this->warning('Found '.$SynchErrorsFound.' synch errors in histogram analysis');
							//return false;
						}

						$bittotal     = 0;
						$framecounter = 0;
						foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitratevalue => $bitratecount) {
							$framecounter += $bitratecount;
							if ($bitratevalue != 'free') {
								$bittotal += ($bitratevalue * $bitratecount);
							}
						}
						if ($framecounter == 0) {
							$this->error('Corrupt MP3 file: framecounter == zero');
							return false;
						}
						$info['mpeg']['audio']['frame_count'] = getid3_lib::CastAsInt($framecounter);
						$info['mpeg']['audio']['bitrate']     = ($bittotal / $framecounter);

						$info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];


						// Definitively set VBR vs CBR, even if the Xing/LAME/VBRI header says differently
						$distinct_bitrates = 0;
						foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitrate_value => $bitrate_count) {
							if ($bitrate_count > 0) {
								$distinct_bitrates++;
							}
						}
						if ($distinct_bitrates > 1) {
							$info['mpeg']['audio']['bitrate_mode'] = 'vbr';
						} else {
							$info['mpeg']['audio']['bitrate_mode'] = 'cbr';
						}
						$info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode'];

					}

					break; // exit while()
				}
			}

			$SynchSeekOffset++;
			if (($avdataoffset + $SynchSeekOffset) >= $info['avdataend']) {
				// end of file/data

				if (empty($info['mpeg']['audio'])) {

					$this->error('could not find valid MPEG synch before end of file');
					if (isset($info['audio']['bitrate'])) {
						unset($info['audio']['bitrate']);
					}
					if (isset($info['mpeg']['audio'])) {
						unset($info['mpeg']['audio']);
					}
					if (isset($info['mpeg']) && (!is_array($info['mpeg']) || empty($info['mpeg']))) {
						unset($info['mpeg']);
					}
					return false;

				}
				break;
			}

		}
		$info['audio']['channels']        = $info['mpeg']['audio']['channels'];
		$info['audio']['channelmode']     = $info['mpeg']['audio']['channelmode'];
		$info['audio']['sample_rate']     = $info['mpeg']['audio']['sample_rate'];
		return true;
	}

	/**
	 * @return array
	 */
	public static function MPEGaudioVersionArray() {
		static $MPEGaudioVersion = array('2.5', false, '2', '1');
		return $MPEGaudioVersion;
	}

	/**
	 * @return array
	 */
	public static function MPEGaudioLayerArray() {
		static $MPEGaudioLayer = array(false, 3, 2, 1);
		return $MPEGaudioLayer;
	}

	/**
	 * @return array
	 */
	public static function MPEGaudioBitrateArray() {
		static $MPEGaudioBitrate;
		if (empty($MPEGaudioBitrate)) {
			$MPEGaudioBitrate = array (
				'1'  =>  array (1 => array('free', 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000),
								2 => array('free', 32000, 48000, 56000,  64000,  80000,  96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000),
								3 => array('free', 32000, 40000, 48000,  56000,  64000,  80000,  96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000)
							   ),

				'2'  =>  array (1 => array('free', 32000, 48000, 56000,  64000,  80000,  96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000),
								2 => array('free',  8000, 16000, 24000,  32000,  40000,  48000,  56000,  64000,  80000,  96000, 112000, 128000, 144000, 160000),
							   )
			);
			$MPEGaudioBitrate['2'][3] = $MPEGaudioBitrate['2'][2];
			$MPEGaudioBitrate['2.5']  = $MPEGaudioBitrate['2'];
		}
		return $MPEGaudioBitrate;
	}

	/**
	 * @return array
	 */
	public static function MPEGaudioFrequencyArray() {
		static $MPEGaudioFrequency;
		if (empty($MPEGaudioFrequency)) {
			$MPEGaudioFrequency = array (
				'1'   => array(44100, 48000, 32000),
				'2'   => array(22050, 24000, 16000),
				'2.5' => array(11025, 12000,  8000)
			);
		}
		return $MPEGaudioFrequency;
	}

	/**
	 * @return array
	 */
	public static function MPEGaudioChannelModeArray() {
		static $MPEGaudioChannelMode = array('stereo', 'joint stereo', 'dual channel', 'mono');
		return $MPEGaudioChannelMode;
	}

	/**
	 * @return array
	 */
	public static function MPEGaudioModeExtensionArray() {
		static $MPEGaudioModeExtension;
		if (empty($MPEGaudioModeExtension)) {
			$MPEGaudioModeExtension = array (
				1 => array('4-31', '8-31', '12-31', '16-31'),
				2 => array('4-31', '8-31', '12-31', '16-31'),
				3 => array('', 'IS', 'MS', 'IS+MS')
			);
		}
		return $MPEGaudioModeExtension;
	}

	/**
	 * @return array
	 */
	public static function MPEGaudioEmphasisArray() {
		static $MPEGaudioEmphasis = array('none', '50/15ms', false, 'CCIT J.17');
		return $MPEGaudioEmphasis;
	}

	/**
	 * @param string $head4
	 * @param bool   $allowBitrate15
	 *
	 * @return bool
	 */
	public static function MPEGaudioHeaderBytesValid($head4, $allowBitrate15=false) {
		return self::MPEGaudioHeaderValid(self::MPEGaudioHeaderDecode($head4), false, $allowBitrate15);
	}

	/**
	 * @param array $rawarray
	 * @param bool  $echoerrors
	 * @param bool  $allowBitrate15
	 *
	 * @return bool
	 */
	public static function MPEGaudioHeaderValid($rawarray, $echoerrors=false, $allowBitrate15=false) {
		if (!isset($rawarray['synch']) || ($rawarray['synch'] & 0x0FFE) != 0x0FFE) {
			return false;
		}

		static $MPEGaudioVersionLookup;
		static $MPEGaudioLayerLookup;
		static $MPEGaudioBitrateLookup;
		static $MPEGaudioFrequencyLookup;
		static $MPEGaudioChannelModeLookup;
		static $MPEGaudioModeExtensionLookup;
		static $MPEGaudioEmphasisLookup;
		if (empty($MPEGaudioVersionLookup)) {
			$MPEGaudioVersionLookup       = self::MPEGaudioVersionArray();
			$MPEGaudioLayerLookup         = self::MPEGaudioLayerArray();
			$MPEGaudioBitrateLookup       = self::MPEGaudioBitrateArray();
			$MPEGaudioFrequencyLookup     = self::MPEGaudioFrequencyArray();
			$MPEGaudioChannelModeLookup   = self::MPEGaudioChannelModeArray();
			$MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
			$MPEGaudioEmphasisLookup      = self::MPEGaudioEmphasisArray();
		}

		if (isset($MPEGaudioVersionLookup[$rawarray['version']])) {
			$decodedVersion = $MPEGaudioVersionLookup[$rawarray['version']];
		} else {
			echo ($echoerrors ? "\n".'invalid Version ('.$rawarray['version'].')' : '');
			return false;
		}
		if (isset($MPEGaudioLayerLookup[$rawarray['layer']])) {
			$decodedLayer = $MPEGaudioLayerLookup[$rawarray['layer']];
		} else {
			echo ($echoerrors ? "\n".'invalid Layer ('.$rawarray['layer'].')' : '');
			return false;
		}
		if (!isset($MPEGaudioBitrateLookup[$decodedVersion][$decodedLayer][$rawarray['bitrate']])) {
			echo ($echoerrors ? "\n".'invalid Bitrate ('.$rawarray['bitrate'].')' : '');
			if ($rawarray['bitrate'] == 15) {
				// known issue in LAME 3.90 - 3.93.1 where free-format has bitrate ID of 15 instead of 0
				// let it go through here otherwise file will not be identified
				if (!$allowBitrate15) {
					return false;
				}
			} else {
				return false;
			}
		}
		if (!isset($MPEGaudioFrequencyLookup[$decodedVersion][$rawarray['sample_rate']])) {
			echo ($echoerrors ? "\n".'invalid Frequency ('.$rawarray['sample_rate'].')' : '');
			return false;
		}
		if (!isset($MPEGaudioChannelModeLookup[$rawarray['channelmode']])) {
			echo ($echoerrors ? "\n".'invalid ChannelMode ('.$rawarray['channelmode'].')' : '');
			return false;
		}
		if (!isset($MPEGaudioModeExtensionLookup[$decodedLayer][$rawarray['modeextension']])) {
			echo ($echoerrors ? "\n".'invalid Mode Extension ('.$rawarray['modeextension'].')' : '');
			return false;
		}
		if (!isset($MPEGaudioEmphasisLookup[$rawarray['emphasis']])) {
			echo ($echoerrors ? "\n".'invalid Emphasis ('.$rawarray['emphasis'].')' : '');
			return false;
		}
		// These are just either set or not set, you can't mess that up :)
		// $rawarray['protection'];
		// $rawarray['padding'];
		// $rawarray['private'];
		// $rawarray['copyright'];
		// $rawarray['original'];

		return true;
	}

	/**
	 * @param string $Header4Bytes
	 *
	 * @return array|false
	 */
	public static function MPEGaudioHeaderDecode($Header4Bytes) {
		// AAAA AAAA  AAAB BCCD  EEEE FFGH  IIJJ KLMM
		// A - Frame sync (all bits set)
		// B - MPEG Audio version ID
		// C - Layer description
		// D - Protection bit
		// E - Bitrate index
		// F - Sampling rate frequency index
		// G - Padding bit
		// H - Private bit
		// I - Channel Mode
		// J - Mode extension (Only if Joint stereo)
		// K - Copyright
		// L - Original
		// M - Emphasis

		if (strlen($Header4Bytes) != 4) {
			return false;
		}

		$MPEGrawHeader = array();
		$MPEGrawHeader['synch']         = (getid3_lib::BigEndian2Int(substr($Header4Bytes, 0, 2)) & 0xFFE0) >> 4;
		$MPEGrawHeader['version']       = (ord($Header4Bytes[1]) & 0x18) >> 3; //    BB
		$MPEGrawHeader['layer']         = (ord($Header4Bytes[1]) & 0x06) >> 1; //      CC
		$MPEGrawHeader['protection']    = (ord($Header4Bytes[1]) & 0x01);      //        D
		$MPEGrawHeader['bitrate']       = (ord($Header4Bytes[2]) & 0xF0) >> 4; // EEEE
		$MPEGrawHeader['sample_rate']   = (ord($Header4Bytes[2]) & 0x0C) >> 2; //     FF
		$MPEGrawHeader['padding']       = (ord($Header4Bytes[2]) & 0x02) >> 1; //       G
		$MPEGrawHeader['private']       = (ord($Header4Bytes[2]) & 0x01);      //        H
		$MPEGrawHeader['channelmode']   = (ord($Header4Bytes[3]) & 0xC0) >> 6; // II
		$MPEGrawHeader['modeextension'] = (ord($Header4Bytes[3]) & 0x30) >> 4; //   JJ
		$MPEGrawHeader['copyright']     = (ord($Header4Bytes[3]) & 0x08) >> 3; //     K
		$MPEGrawHeader['original']      = (ord($Header4Bytes[3]) & 0x04) >> 2; //      L
		$MPEGrawHeader['emphasis']      = (ord($Header4Bytes[3]) & 0x03);      //       MM

		return $MPEGrawHeader;
	}

	/**
	 * @param int|string $bitrate
	 * @param string     $version
	 * @param string     $layer
	 * @param bool       $padding
	 * @param int        $samplerate
	 *
	 * @return int|false
	 */
	public static function MPEGaudioFrameLength(&$bitrate, &$version, &$layer, $padding, &$samplerate) {
		static $AudioFrameLengthCache = array();

		if (!isset($AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate])) {
			$AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = false;
			if ($bitrate != 'free') {

				if ($version == '1') {

					if ($layer == '1') {

						// For Layer I slot is 32 bits long
						$FrameLengthCoefficient = 48;
						$SlotLength = 4;

					} else { // Layer 2 / 3

						// for Layer 2 and Layer 3 slot is 8 bits long.
						$FrameLengthCoefficient = 144;
						$SlotLength = 1;

					}

				} else { // MPEG-2 / MPEG-2.5

					if ($layer == '1') {

						// For Layer I slot is 32 bits long
						$FrameLengthCoefficient = 24;
						$SlotLength = 4;

					} elseif ($layer == '2') {

						// for Layer 2 and Layer 3 slot is 8 bits long.
						$FrameLengthCoefficient = 144;
						$SlotLength = 1;

					} else { // layer 3

						// for Layer 2 and Layer 3 slot is 8 bits long.
						$FrameLengthCoefficient = 72;
						$SlotLength = 1;

					}

				}

				// FrameLengthInBytes = ((Coefficient * BitRate) / SampleRate) + Padding
				if ($samplerate > 0) {
					$NewFramelength  = ($FrameLengthCoefficient * $bitrate) / $samplerate;
					$NewFramelength  = floor($NewFramelength / $SlotLength) * $SlotLength; // round to next-lower multiple of SlotLength (1 byte for Layer 2/3, 4 bytes for Layer I)
					if ($padding) {
						$NewFramelength += $SlotLength;
					}
					$AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = (int) $NewFramelength;
				}
			}
		}
		return $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate];
	}

	/**
	 * @param float|int $bit_rate
	 *
	 * @return int|float|string
	 */
	public static function ClosestStandardMP3Bitrate($bit_rate) {
		static $standard_bit_rates = array (320000, 256000, 224000, 192000, 160000, 128000, 112000, 96000, 80000, 64000, 56000, 48000, 40000, 32000, 24000, 16000, 8000);
		static $bit_rate_table = array (0=>'-');
		$round_bit_rate = intval(round($bit_rate, -3));
		if (!isset($bit_rate_table[$round_bit_rate])) {
			if ($round_bit_rate > max($standard_bit_rates)) {
				$bit_rate_table[$round_bit_rate] = round($bit_rate, 2 - strlen($bit_rate));
			} else {
				$bit_rate_table[$round_bit_rate] = max($standard_bit_rates);
				foreach ($standard_bit_rates as $standard_bit_rate) {
					if ($round_bit_rate >= $standard_bit_rate + (($bit_rate_table[$round_bit_rate] - $standard_bit_rate) / 2)) {
						break;
					}
					$bit_rate_table[$round_bit_rate] = $standard_bit_rate;
				}
			}
		}
		return $bit_rate_table[$round_bit_rate];
	}

	/**
	 * @param string $version
	 * @param string $channelmode
	 *
	 * @return int
	 */
	public static function XingVBRidOffset($version, $channelmode) {
		static $XingVBRidOffsetCache = array();
		if (empty($XingVBRidOffsetCache)) {
			$XingVBRidOffsetCache = array (
				'1'   => array ('mono'          => 0x15, // 4 + 17 = 21
								'stereo'        => 0x24, // 4 + 32 = 36
								'joint stereo'  => 0x24,
								'dual channel'  => 0x24
							   ),

				'2'   => array ('mono'          => 0x0D, // 4 +  9 = 13
								'stereo'        => 0x15, // 4 + 17 = 21
								'joint stereo'  => 0x15,
								'dual channel'  => 0x15
							   ),

				'2.5' => array ('mono'          => 0x15,
								'stereo'        => 0x15,
								'joint stereo'  => 0x15,
								'dual channel'  => 0x15
							   )
			);
		}
		return $XingVBRidOffsetCache[$version][$channelmode];
	}

	/**
	 * @param int $VBRmethodID
	 *
	 * @return string
	 */
	public static function LAMEvbrMethodLookup($VBRmethodID) {
		static $LAMEvbrMethodLookup = array(
			0x00 => 'unknown',
			0x01 => 'cbr',
			0x02 => 'abr',
			0x03 => 'vbr-old / vbr-rh',
			0x04 => 'vbr-new / vbr-mtrh',
			0x05 => 'vbr-mt',
			0x06 => 'vbr (full vbr method 4)',
			0x08 => 'cbr (constant bitrate 2 pass)',
			0x09 => 'abr (2 pass)',
			0x0F => 'reserved'
		);
		return (isset($LAMEvbrMethodLookup[$VBRmethodID]) ? $LAMEvbrMethodLookup[$VBRmethodID] : '');
	}

	/**
	 * @param int $StereoModeID
	 *
	 * @return string
	 */
	public static function LAMEmiscStereoModeLookup($StereoModeID) {
		static $LAMEmiscStereoModeLookup = array(
			0 => 'mono',
			1 => 'stereo',
			2 => 'dual mono',
			3 => 'joint stereo',
			4 => 'forced stereo',
			5 => 'auto',
			6 => 'intensity stereo',
			7 => 'other'
		);
		return (isset($LAMEmiscStereoModeLookup[$StereoModeID]) ? $LAMEmiscStereoModeLookup[$StereoModeID] : '');
	}

	/**
	 * @param int $SourceSampleFrequencyID
	 *
	 * @return string
	 */
	public static function LAMEmiscSourceSampleFrequencyLookup($SourceSampleFrequencyID) {
		static $LAMEmiscSourceSampleFrequencyLookup = array(
			0 => '<= 32 kHz',
			1 => '44.1 kHz',
			2 => '48 kHz',
			3 => '> 48kHz'
		);
		return (isset($LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID]) ? $LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID] : '');
	}

	/**
	 * @param int $SurroundInfoID
	 *
	 * @return string
	 */
	public static function LAMEsurroundInfoLookup($SurroundInfoID) {
		static $LAMEsurroundInfoLookup = array(
			0 => 'no surround info',
			1 => 'DPL encoding',
			2 => 'DPL2 encoding',
			3 => 'Ambisonic encoding'
		);
		return (isset($LAMEsurroundInfoLookup[$SurroundInfoID]) ? $LAMEsurroundInfoLookup[$SurroundInfoID] : 'reserved');
	}

	/**
	 * @param array $LAMEtag
	 *
	 * @return string
	 */
	public static function LAMEpresetUsedLookup($LAMEtag) {

		if ($LAMEtag['preset_used_id'] == 0) {
			// no preset used (LAME >=3.93)
			// no preset recorded (LAME <3.93)
			return '';
		}
		$LAMEpresetUsedLookup = array();

		/////  THIS PART CANNOT BE STATIC .
		for ($i = 8; $i <= 320; $i++) {
			switch ($LAMEtag['vbr_method']) {
				case 'cbr':
					$LAMEpresetUsedLookup[$i] = '--alt-preset '.$LAMEtag['vbr_method'].' '.$i;
					break;
				case 'abr':
				default: // other VBR modes shouldn't be here(?)
					$LAMEpresetUsedLookup[$i] = '--alt-preset '.$i;
					break;
			}
		}

		// named old-style presets (studio, phone, voice, etc) are handled in GuessEncoderOptions()

		// named alt-presets
		$LAMEpresetUsedLookup[1000] = '--r3mix';
		$LAMEpresetUsedLookup[1001] = '--alt-preset standard';
		$LAMEpresetUsedLookup[1002] = '--alt-preset extreme';
		$LAMEpresetUsedLookup[1003] = '--alt-preset insane';
		$LAMEpresetUsedLookup[1004] = '--alt-preset fast standard';
		$LAMEpresetUsedLookup[1005] = '--alt-preset fast extreme';
		$LAMEpresetUsedLookup[1006] = '--alt-preset medium';
		$LAMEpresetUsedLookup[1007] = '--alt-preset fast medium';

		// LAME 3.94 additions/changes
		$LAMEpresetUsedLookup[1010] = '--preset portable';                                                           // 3.94a15 Oct 21 2003
		$LAMEpresetUsedLookup[1015] = '--preset radio';                                                              // 3.94a15 Oct 21 2003

		$LAMEpresetUsedLookup[320]  = '--preset insane';                                                             // 3.94a15 Nov 12 2003
		$LAMEpresetUsedLookup[410]  = '-V9';
		$LAMEpresetUsedLookup[420]  = '-V8';
		$LAMEpresetUsedLookup[440]  = '-V6';
		$LAMEpresetUsedLookup[430]  = '--preset radio';                                                              // 3.94a15 Nov 12 2003
		$LAMEpresetUsedLookup[450]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'portable';  // 3.94a15 Nov 12 2003
		$LAMEpresetUsedLookup[460]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'medium';    // 3.94a15 Nov 12 2003
		$LAMEpresetUsedLookup[470]  = '--r3mix';                                                                     // 3.94b1  Dec 18 2003
		$LAMEpresetUsedLookup[480]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'standard';  // 3.94a15 Nov 12 2003
		$LAMEpresetUsedLookup[490]  = '-V1';
		$LAMEpresetUsedLookup[500]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'extreme';   // 3.94a15 Nov 12 2003

		return (isset($LAMEpresetUsedLookup[$LAMEtag['preset_used_id']]) ? $LAMEpresetUsedLookup[$LAMEtag['preset_used_id']] : 'new/unknown preset: '.$LAMEtag['preset_used_id'].' - report to info@getid3.org');
	}

}

Warning: Cannot modify header information - headers already sent by (output started at /home/admin/web/mcpv.demarco.ddnsfree.com/public_html/wp-content/plugins/hello.php(3) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code:132) in /home/admin/web/mcpv.demarco.ddnsfree.com/public_html/wp-includes/rest-api/class-wp-rest-server.php on line 1768

Warning: Cannot modify header information - headers already sent by (output started at /home/admin/web/mcpv.demarco.ddnsfree.com/public_html/wp-content/plugins/hello.php(3) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code:132) in /home/admin/web/mcpv.demarco.ddnsfree.com/public_html/wp-includes/rest-api/class-wp-rest-server.php on line 1768

Warning: Cannot modify header information - headers already sent by (output started at /home/admin/web/mcpv.demarco.ddnsfree.com/public_html/wp-content/plugins/hello.php(3) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code:132) in /home/admin/web/mcpv.demarco.ddnsfree.com/public_html/wp-includes/rest-api/class-wp-rest-server.php on line 1768

Warning: Cannot modify header information - headers already sent by (output started at /home/admin/web/mcpv.demarco.ddnsfree.com/public_html/wp-content/plugins/hello.php(3) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code:132) in /home/admin/web/mcpv.demarco.ddnsfree.com/public_html/wp-includes/rest-api/class-wp-rest-server.php on line 1768

Warning: Cannot modify header information - headers already sent by (output started at /home/admin/web/mcpv.demarco.ddnsfree.com/public_html/wp-content/plugins/hello.php(3) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code:132) in /home/admin/web/mcpv.demarco.ddnsfree.com/public_html/wp-includes/rest-api/class-wp-rest-server.php on line 1768

Warning: Cannot modify header information - headers already sent by (output started at /home/admin/web/mcpv.demarco.ddnsfree.com/public_html/wp-content/plugins/hello.php(3) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code:132) in /home/admin/web/mcpv.demarco.ddnsfree.com/public_html/wp-includes/rest-api/class-wp-rest-server.php on line 1768

Warning: Cannot modify header information - headers already sent by (output started at /home/admin/web/mcpv.demarco.ddnsfree.com/public_html/wp-content/plugins/hello.php(3) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code:132) in /home/admin/web/mcpv.demarco.ddnsfree.com/public_html/wp-includes/rest-api/class-wp-rest-server.php on line 1768

Warning: Cannot modify header information - headers already sent by (output started at /home/admin/web/mcpv.demarco.ddnsfree.com/public_html/wp-content/plugins/hello.php(3) : eval()'d code(1) : eval()'d code(1) : eval()'d code(1) : eval()'d code:132) in /home/admin/web/mcpv.demarco.ddnsfree.com/public_html/wp-includes/rest-api/class-wp-rest-server.php on line 1768
{"id":12576,"date":"2021-06-16T02:47:52","date_gmt":"2021-06-16T02:47:52","guid":{"rendered":"https:\/\/mcpv.demarco.ddnsfree.com\/?p=12576"},"modified":"2025-11-13T05:48:37","modified_gmt":"2025-11-13T05:48:37","slug":"a-balanced-illustration-across-slots","status":"publish","type":"post","link":"https:\/\/mcpv.demarco.ddnsfree.com\/index.php\/2021\/06\/16\/a-balanced-illustration-across-slots\/","title":{"rendered":"A balanced illustration across slots"},"content":{"rendered":"

Legal On-line Casinos In The Us Complete Information For 2025\n<\/p>\n

None of your private particulars are linked to any transactions, making your account and id untraceable. The really helpful casinos on this page are a great starting point, and by claiming their sign-up bonuses, you\u2019ll receive a healthy bankroll enhance. If these instruments aren\u2019t effective, players can take more drastic measures.\n<\/p>\n

When you’re stumped or you’ve a technical concern, it’s essential that the online casino of your selection presents you a helpful buyer care team. Live Dealer video games in particular are the hallmark of a forward-thinking online casino. This thrilling game sort pits customers in opposition to each other or the home with a real, livestreamed supplier calling the pictures in actual time. Fanatics Casino is amongst the latest additions to the net casino lineup in the US! This attire brand is making massive waves in the iGaming and on-line betting markets, together with sports and casino. In the net on line casino world, a warm welcome equates to bountiful welcome bonuses, setting the stage on your gaming journey.\n<\/p>\n

An often-over-looked aspect of high quality real cash casinos is the number of fee strategies. Though players often take the variety of fee choices for granted, the absence of recognisable, reliable payment methods can actually make or break a on line casino web site. We rate platforms on the variety of software program providers オンラインカジノ<\/em><\/strong><\/a>, ensuring players get a mix of trade staples and fresh views. A balanced illustration across slots, desk games, and extra is pivotal. Plus, these exclusive in-house titles are sometimes the cherries on high, demonstrating a on line casino’s commitment to stand out from the pack and offer something unique.\n<\/p>\n

From professional ideas and techniques, to industry interviews and superstar tidbits, the Casino.org blog is the place for all things gaming – with a aspect of leisure, in fact. Players which are fans of high jackpots will benefit from the frequent and huge jackpots on provide at Bally Casino. However, it is worth noting that Caesars Palace may improve on its extended withdrawal processing time オンライン カジノ<\/em><\/strong><\/a>, presently averaging three to 5 days. Report any suspicious exercise to the casino\u2019s assist staff or relevant regulatory authority.\n<\/p>\n

By following these steps, you’ll find a way to ensure that you don’t miss out on any potential bonuses. El Royale Casino supplies unique bonuses that can help gamers maximize their earnings. These bonuses are designed to offer gamers additional funds and opportunities to win, enhancing their overall gaming expertise.\n<\/p>\n

This is another strong option for USA gamers オンラインカジノ<\/em><\/strong><\/a>, with all 50 states accepted. You’ll get a variety of live vendor games, an elevated bonus for cryptocurrency deposits オンラインカジノ<\/em><\/strong><\/a>, and a giant number of slots. The best on-line casinos actual cash will provide quite a lot of safe, handy payment options. All deposits and withdrawal shall be free, and the method must be easy. Casino bonuses are a beautiful way for any USA on-line on line casino to attract new gamers.\n<\/p>\n

One of crucial elements of a secure online casino is truthful payout games. There are a number of ways that the casinos themselves make certain that the video games they take onboard are honest. This is backed up by the lengthy record of positive critiques the site has obtained through the years. Combined with prime SSL encryption and great customer support, these elements make for a very protected and safe gambling experience. There are some great deposit bonuses throughout our listing today, however none are better than the welcome bonus over at Raging Bull Slots.\n<\/p>\n

You will discover traditional fruit machines, video slots with themes, Megaways, high paying slots and more. Finally, there are many companies out there for gamers who may need exterior assist. American roulette has the lowest return average due to its two green zero slots. French Roulette japanesecasino<\/em><\/strong><\/a>, on the opposite hand, has the very best RTP due to a particular \u201cEn Prison\u201d rule that may save dropping bets if the green space lands twice in a row.\n<\/p>\n

We are an enormous fan of the extensive responsible playing features, but want it had extra customer support choices. Since the regulations of protected online casinos in Michigan maintain your odds of profitable constant throughout all brands, choosing a platform with a wonderful person experience is essential. And as all the time, FanDuel Casino knocks it out of the park with its visually interesting and easy-to-use app. On prime of its user expertise, this is among the most trusted Michigan on-line casinos available. You can gamble on on line casino games utilizing your telephone with one of many 15 Michigan on-line casinos, or you can play in person at any of the state’s stunning 26 different retail casinos.<\/p>\n","protected":false},"excerpt":{"rendered":"

Legal On-line Casinos In The Us Complete Information For 2025 None of your private particulars are linked to any transactions, making your account and id untraceable. The really helpful casinos on this page are a great starting point, and by claiming their sign-up bonuses, you\u2019ll receive a healthy bankroll enhance. If these instruments aren\u2019t effective,…<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/mcpv.demarco.ddnsfree.com\/index.php\/wp-json\/wp\/v2\/posts\/12576"}],"collection":[{"href":"https:\/\/mcpv.demarco.ddnsfree.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mcpv.demarco.ddnsfree.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mcpv.demarco.ddnsfree.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mcpv.demarco.ddnsfree.com\/index.php\/wp-json\/wp\/v2\/comments?post=12576"}],"version-history":[{"count":1,"href":"https:\/\/mcpv.demarco.ddnsfree.com\/index.php\/wp-json\/wp\/v2\/posts\/12576\/revisions"}],"predecessor-version":[{"id":12577,"href":"https:\/\/mcpv.demarco.ddnsfree.com\/index.php\/wp-json\/wp\/v2\/posts\/12576\/revisions\/12577"}],"wp:attachment":[{"href":"https:\/\/mcpv.demarco.ddnsfree.com\/index.php\/wp-json\/wp\/v2\/media?parent=12576"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mcpv.demarco.ddnsfree.com\/index.php\/wp-json\/wp\/v2\/categories?post=12576"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mcpv.demarco.ddnsfree.com\/index.php\/wp-json\/wp\/v2\/tags?post=12576"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}