balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

module.audio.ac3.php (39384B)


      1 <?php
      2 
      3 /////////////////////////////////////////////////////////////////
      4 /// getID3() by James Heinrich <info@getid3.org>               //
      5 //  available at https://github.com/JamesHeinrich/getID3       //
      6 //            or https://www.getid3.org                        //
      7 //            or http://getid3.sourceforge.net                 //
      8 //  see readme.txt for more details                            //
      9 /////////////////////////////////////////////////////////////////
     10 //                                                             //
     11 // module.audio.ac3.php                                        //
     12 // module for analyzing AC-3 (aka Dolby Digital) audio files   //
     13 // dependencies: NONE                                          //
     14 //                                                            ///
     15 /////////////////////////////////////////////////////////////////
     16 
     17 if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
     18 	exit;
     19 }
     20 
     21 class getid3_ac3 extends getid3_handler
     22 {
     23 	/**
     24 	 * @var array
     25 	 */
     26 	private $AC3header = array();
     27 
     28 	/**
     29 	 * @var int
     30 	 */
     31 	private $BSIoffset = 0;
     32 
     33 	const syncword = 0x0B77;
     34 
     35 	/**
     36 	 * @return bool
     37 	 */
     38 	public function Analyze() {
     39 		$info = &$this->getid3->info;
     40 
     41 		///AH
     42 		$info['ac3']['raw']['bsi'] = array();
     43 		$thisfile_ac3              = &$info['ac3'];
     44 		$thisfile_ac3_raw          = &$thisfile_ac3['raw'];
     45 		$thisfile_ac3_raw_bsi      = &$thisfile_ac3_raw['bsi'];
     46 
     47 
     48 		// http://www.atsc.org/standards/a_52a.pdf
     49 
     50 		$info['fileformat'] = 'ac3';
     51 
     52 		// An AC-3 serial coded audio bit stream is made up of a sequence of synchronization frames
     53 		// Each synchronization frame contains 6 coded audio blocks (AB), each of which represent 256
     54 		// new audio samples per channel. A synchronization information (SI) header at the beginning
     55 		// of each frame contains information needed to acquire and maintain synchronization. A
     56 		// bit stream information (BSI) header follows SI, and contains parameters describing the coded
     57 		// audio service. The coded audio blocks may be followed by an auxiliary data (Aux) field. At the
     58 		// end of each frame is an error check field that includes a CRC word for error detection. An
     59 		// additional CRC word is located in the SI header, the use of which, by a decoder, is optional.
     60 		//
     61 		// syncinfo() | bsi() | AB0 | AB1 | AB2 | AB3 | AB4 | AB5 | Aux | CRC
     62 
     63 		// syncinfo() {
     64 		// 	 syncword    16
     65 		// 	 crc1        16
     66 		// 	 fscod        2
     67 		// 	 frmsizecod   6
     68 		// } /* end of syncinfo */
     69 
     70 		$this->fseek($info['avdataoffset']);
     71 		$tempAC3header = $this->fread(100); // should be enough to cover all data, there are some variable-length fields...?
     72 		$this->AC3header['syncinfo']  =     getid3_lib::BigEndian2Int(substr($tempAC3header, 0, 2));
     73 		$this->AC3header['bsi']       =     getid3_lib::BigEndian2Bin(substr($tempAC3header, 2));
     74 		$thisfile_ac3_raw_bsi['bsid'] = (getid3_lib::LittleEndian2Int(substr($tempAC3header, 5, 1)) & 0xF8) >> 3; // AC3 and E-AC3 put the "bsid" version identifier in the same place, but unfortnately the 4 bytes between the syncword and the version identifier are interpreted differently, so grab it here so the following code structure can make sense
     75 		unset($tempAC3header);
     76 
     77 		if ($this->AC3header['syncinfo'] !== self::syncword) {
     78 			if (!$this->isDependencyFor('matroska')) {
     79 				unset($info['fileformat'], $info['ac3']);
     80 				return $this->error('Expecting "'.dechex(self::syncword).'" at offset '.$info['avdataoffset'].', found "'.dechex($this->AC3header['syncinfo']).'"');
     81 			}
     82 		}
     83 
     84 		$info['audio']['dataformat']   = 'ac3';
     85 		$info['audio']['bitrate_mode'] = 'cbr';
     86 		$info['audio']['lossless']     = false;
     87 
     88 		if ($thisfile_ac3_raw_bsi['bsid'] <= 8) {
     89 
     90 			$thisfile_ac3_raw_bsi['crc1']       = getid3_lib::Bin2Dec($this->readHeaderBSI(16));
     91 			$thisfile_ac3_raw_bsi['fscod']      =                     $this->readHeaderBSI(2);   // 5.4.1.3
     92 			$thisfile_ac3_raw_bsi['frmsizecod'] =                     $this->readHeaderBSI(6);   // 5.4.1.4
     93 			if ($thisfile_ac3_raw_bsi['frmsizecod'] > 37) { // binary: 100101 - see Table 5.18 Frame Size Code Table (1 word = 16 bits)
     94 				$this->warning('Unexpected ac3.bsi.frmsizecod value: '.$thisfile_ac3_raw_bsi['frmsizecod'].', bitrate not set correctly');
     95 			}
     96 
     97 			$thisfile_ac3_raw_bsi['bsid']  = $this->readHeaderBSI(5); // we already know this from pre-parsing the version identifier, but re-read it to let the bitstream flow as intended
     98 			$thisfile_ac3_raw_bsi['bsmod'] = $this->readHeaderBSI(3);
     99 			$thisfile_ac3_raw_bsi['acmod'] = $this->readHeaderBSI(3);
    100 
    101 			if ($thisfile_ac3_raw_bsi['acmod'] & 0x01) {
    102 				// If the lsb of acmod is a 1, center channel is in use and cmixlev follows in the bit stream.
    103 				$thisfile_ac3_raw_bsi['cmixlev'] = $this->readHeaderBSI(2);
    104 				$thisfile_ac3['center_mix_level'] = self::centerMixLevelLookup($thisfile_ac3_raw_bsi['cmixlev']);
    105 			}
    106 
    107 			if ($thisfile_ac3_raw_bsi['acmod'] & 0x04) {
    108 				// If the msb of acmod is a 1, surround channels are in use and surmixlev follows in the bit stream.
    109 				$thisfile_ac3_raw_bsi['surmixlev'] = $this->readHeaderBSI(2);
    110 				$thisfile_ac3['surround_mix_level'] = self::surroundMixLevelLookup($thisfile_ac3_raw_bsi['surmixlev']);
    111 			}
    112 
    113 			if ($thisfile_ac3_raw_bsi['acmod'] == 0x02) {
    114 				// When operating in the two channel mode, this 2-bit code indicates whether or not the program has been encoded in Dolby Surround.
    115 				$thisfile_ac3_raw_bsi['dsurmod'] = $this->readHeaderBSI(2);
    116 				$thisfile_ac3['dolby_surround_mode'] = self::dolbySurroundModeLookup($thisfile_ac3_raw_bsi['dsurmod']);
    117 			}
    118 
    119 			$thisfile_ac3_raw_bsi['flags']['lfeon'] = (bool) $this->readHeaderBSI(1);
    120 
    121 			// This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1-31.
    122 			// The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent.
    123 			$thisfile_ac3_raw_bsi['dialnorm'] = $this->readHeaderBSI(5);                 // 5.4.2.8 dialnorm: Dialogue Normalization, 5 Bits
    124 
    125 			$thisfile_ac3_raw_bsi['flags']['compr'] = (bool) $this->readHeaderBSI(1);       // 5.4.2.9 compre: Compression Gain Word Exists, 1 Bit
    126 			if ($thisfile_ac3_raw_bsi['flags']['compr']) {
    127 				$thisfile_ac3_raw_bsi['compr'] = $this->readHeaderBSI(8);                // 5.4.2.10 compr: Compression Gain Word, 8 Bits
    128 				$thisfile_ac3['heavy_compression'] = self::heavyCompression($thisfile_ac3_raw_bsi['compr']);
    129 			}
    130 
    131 			$thisfile_ac3_raw_bsi['flags']['langcod'] = (bool) $this->readHeaderBSI(1);     // 5.4.2.11 langcode: Language Code Exists, 1 Bit
    132 			if ($thisfile_ac3_raw_bsi['flags']['langcod']) {
    133 				$thisfile_ac3_raw_bsi['langcod'] = $this->readHeaderBSI(8);              // 5.4.2.12 langcod: Language Code, 8 Bits
    134 			}
    135 
    136 			$thisfile_ac3_raw_bsi['flags']['audprodinfo'] = (bool) $this->readHeaderBSI(1);  // 5.4.2.13 audprodie: Audio Production Information Exists, 1 Bit
    137 			if ($thisfile_ac3_raw_bsi['flags']['audprodinfo']) {
    138 				$thisfile_ac3_raw_bsi['mixlevel'] = $this->readHeaderBSI(5);             // 5.4.2.14 mixlevel: Mixing Level, 5 Bits
    139 				$thisfile_ac3_raw_bsi['roomtyp']  = $this->readHeaderBSI(2);             // 5.4.2.15 roomtyp: Room Type, 2 Bits
    140 
    141 				$thisfile_ac3['mixing_level'] = (80 + $thisfile_ac3_raw_bsi['mixlevel']).'dB';
    142 				$thisfile_ac3['room_type']    = self::roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp']);
    143 			}
    144 
    145 
    146 			$thisfile_ac3_raw_bsi['dialnorm2'] = $this->readHeaderBSI(5);                // 5.4.2.16 dialnorm2: Dialogue Normalization, ch2, 5 Bits
    147 			$thisfile_ac3['dialogue_normalization2'] = '-'.$thisfile_ac3_raw_bsi['dialnorm2'].'dB';  // This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1-31. The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent.
    148 
    149 			$thisfile_ac3_raw_bsi['flags']['compr2'] = (bool) $this->readHeaderBSI(1);       // 5.4.2.17 compr2e: Compression Gain Word Exists, ch2, 1 Bit
    150 			if ($thisfile_ac3_raw_bsi['flags']['compr2']) {
    151 				$thisfile_ac3_raw_bsi['compr2'] = $this->readHeaderBSI(8);               // 5.4.2.18 compr2: Compression Gain Word, ch2, 8 Bits
    152 				$thisfile_ac3['heavy_compression2'] = self::heavyCompression($thisfile_ac3_raw_bsi['compr2']);
    153 			}
    154 
    155 			$thisfile_ac3_raw_bsi['flags']['langcod2'] = (bool) $this->readHeaderBSI(1);    // 5.4.2.19 langcod2e: Language Code Exists, ch2, 1 Bit
    156 			if ($thisfile_ac3_raw_bsi['flags']['langcod2']) {
    157 				$thisfile_ac3_raw_bsi['langcod2'] = $this->readHeaderBSI(8);             // 5.4.2.20 langcod2: Language Code, ch2, 8 Bits
    158 			}
    159 
    160 			$thisfile_ac3_raw_bsi['flags']['audprodinfo2'] = (bool) $this->readHeaderBSI(1); // 5.4.2.21 audprodi2e: Audio Production Information Exists, ch2, 1 Bit
    161 			if ($thisfile_ac3_raw_bsi['flags']['audprodinfo2']) {
    162 				$thisfile_ac3_raw_bsi['mixlevel2'] = $this->readHeaderBSI(5);            // 5.4.2.22 mixlevel2: Mixing Level, ch2, 5 Bits
    163 				$thisfile_ac3_raw_bsi['roomtyp2']  = $this->readHeaderBSI(2);            // 5.4.2.23 roomtyp2: Room Type, ch2, 2 Bits
    164 
    165 				$thisfile_ac3['mixing_level2'] = (80 + $thisfile_ac3_raw_bsi['mixlevel2']).'dB';
    166 				$thisfile_ac3['room_type2']    = self::roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp2']);
    167 			}
    168 
    169 			$thisfile_ac3_raw_bsi['copyright'] = (bool) $this->readHeaderBSI(1);         // 5.4.2.24 copyrightb: Copyright Bit, 1 Bit
    170 
    171 			$thisfile_ac3_raw_bsi['original']  = (bool) $this->readHeaderBSI(1);         // 5.4.2.25 origbs: Original Bit Stream, 1 Bit
    172 
    173 			$thisfile_ac3_raw_bsi['flags']['timecod1'] = $this->readHeaderBSI(2);            // 5.4.2.26 timecod1e, timcode2e: Time Code (first and second) Halves Exist, 2 Bits
    174 			if ($thisfile_ac3_raw_bsi['flags']['timecod1'] & 0x01) {
    175 				$thisfile_ac3_raw_bsi['timecod1'] = $this->readHeaderBSI(14);            // 5.4.2.27 timecod1: Time code first half, 14 bits
    176 				$thisfile_ac3['timecode1'] = 0;
    177 				$thisfile_ac3['timecode1'] += (($thisfile_ac3_raw_bsi['timecod1'] & 0x3E00) >>  9) * 3600;  // The first 5 bits of this 14-bit field represent the time in hours, with valid values of 0�23
    178 				$thisfile_ac3['timecode1'] += (($thisfile_ac3_raw_bsi['timecod1'] & 0x01F8) >>  3) *   60;  // The next 6 bits represent the time in minutes, with valid values of 0�59
    179 				$thisfile_ac3['timecode1'] += (($thisfile_ac3_raw_bsi['timecod1'] & 0x0003) >>  0) *    8;  // The final 3 bits represents the time in 8 second increments, with valid values of 0�7 (representing 0, 8, 16, ... 56 seconds)
    180 			}
    181 			if ($thisfile_ac3_raw_bsi['flags']['timecod1'] & 0x02) {
    182 				$thisfile_ac3_raw_bsi['timecod2'] = $this->readHeaderBSI(14);            // 5.4.2.28 timecod2: Time code second half, 14 bits
    183 				$thisfile_ac3['timecode2'] = 0;
    184 				$thisfile_ac3['timecode2'] += (($thisfile_ac3_raw_bsi['timecod2'] & 0x3800) >> 11) *   1;              // The first 3 bits of this 14-bit field represent the time in seconds, with valid values from 0�7 (representing 0-7 seconds)
    185 				$thisfile_ac3['timecode2'] += (($thisfile_ac3_raw_bsi['timecod2'] & 0x07C0) >>  6) *  (1 / 30);        // The next 5 bits represents the time in frames, with valid values from 0�29 (one frame = 1/30th of a second)
    186 				$thisfile_ac3['timecode2'] += (($thisfile_ac3_raw_bsi['timecod2'] & 0x003F) >>  0) * ((1 / 30) / 60);  // The final 6 bits represents fractions of 1/64 of a frame, with valid values from 0�63
    187 			}
    188 
    189 			$thisfile_ac3_raw_bsi['flags']['addbsi'] = (bool) $this->readHeaderBSI(1);
    190 			if ($thisfile_ac3_raw_bsi['flags']['addbsi']) {
    191 				$thisfile_ac3_raw_bsi['addbsi_length'] = $this->readHeaderBSI(6) + 1; // This 6-bit code, which exists only if addbside is a 1, indicates the length in bytes of additional bit stream information. The valid range of addbsil is 0�63, indicating 1�64 additional bytes, respectively.
    192 
    193 				$this->AC3header['bsi'] .= getid3_lib::BigEndian2Bin($this->fread($thisfile_ac3_raw_bsi['addbsi_length']));
    194 
    195 				$thisfile_ac3_raw_bsi['addbsi_data'] = substr($this->AC3header['bsi'], $this->BSIoffset, $thisfile_ac3_raw_bsi['addbsi_length'] * 8);
    196 				$this->BSIoffset += $thisfile_ac3_raw_bsi['addbsi_length'] * 8;
    197 			}
    198 
    199 
    200 		} elseif ($thisfile_ac3_raw_bsi['bsid'] <= 16) { // E-AC3
    201 
    202 
    203 			$this->error('E-AC3 parsing is incomplete and experimental in this version of getID3 ('.$this->getid3->version().'). Notably the bitrate calculations are wrong -- value might (or not) be correct, but it is not calculated correctly. Email info@getid3.org if you know how to calculate EAC3 bitrate correctly.');
    204 			$info['audio']['dataformat'] = 'eac3';
    205 
    206 			$thisfile_ac3_raw_bsi['strmtyp']          =        $this->readHeaderBSI(2);
    207 			$thisfile_ac3_raw_bsi['substreamid']      =        $this->readHeaderBSI(3);
    208 			$thisfile_ac3_raw_bsi['frmsiz']           =        $this->readHeaderBSI(11);
    209 			$thisfile_ac3_raw_bsi['fscod']            =        $this->readHeaderBSI(2);
    210 			if ($thisfile_ac3_raw_bsi['fscod'] == 3) {
    211 				$thisfile_ac3_raw_bsi['fscod2']       =        $this->readHeaderBSI(2);
    212 				$thisfile_ac3_raw_bsi['numblkscod'] = 3; // six blocks per syncframe
    213 			} else {
    214 				$thisfile_ac3_raw_bsi['numblkscod']   =        $this->readHeaderBSI(2);
    215 			}
    216 			$thisfile_ac3['bsi']['blocks_per_sync_frame'] = self::blocksPerSyncFrame($thisfile_ac3_raw_bsi['numblkscod']);
    217 			$thisfile_ac3_raw_bsi['acmod']            =        $this->readHeaderBSI(3);
    218 			$thisfile_ac3_raw_bsi['flags']['lfeon']   = (bool) $this->readHeaderBSI(1);
    219 			$thisfile_ac3_raw_bsi['bsid']             =        $this->readHeaderBSI(5); // we already know this from pre-parsing the version identifier, but re-read it to let the bitstream flow as intended
    220 			$thisfile_ac3_raw_bsi['dialnorm']         =        $this->readHeaderBSI(5);
    221 			$thisfile_ac3_raw_bsi['flags']['compr']       = (bool) $this->readHeaderBSI(1);
    222 			if ($thisfile_ac3_raw_bsi['flags']['compr']) {
    223 				$thisfile_ac3_raw_bsi['compr']        =        $this->readHeaderBSI(8);
    224 			}
    225 			if ($thisfile_ac3_raw_bsi['acmod'] == 0) { // if 1+1 mode (dual mono, so some items need a second value)
    226 				$thisfile_ac3_raw_bsi['dialnorm2']    =        $this->readHeaderBSI(5);
    227 				$thisfile_ac3_raw_bsi['flags']['compr2']  = (bool) $this->readHeaderBSI(1);
    228 				if ($thisfile_ac3_raw_bsi['flags']['compr2']) {
    229 					$thisfile_ac3_raw_bsi['compr2']   =        $this->readHeaderBSI(8);
    230 				}
    231 			}
    232 			if ($thisfile_ac3_raw_bsi['strmtyp'] == 1) { // if dependent stream
    233 				$thisfile_ac3_raw_bsi['flags']['chanmap'] = (bool) $this->readHeaderBSI(1);
    234 				if ($thisfile_ac3_raw_bsi['flags']['chanmap']) {
    235 					$thisfile_ac3_raw_bsi['chanmap']  =        $this->readHeaderBSI(8);
    236 				}
    237 			}
    238 			$thisfile_ac3_raw_bsi['flags']['mixmdat']     = (bool) $this->readHeaderBSI(1);
    239 			if ($thisfile_ac3_raw_bsi['flags']['mixmdat']) { // Mixing metadata
    240 				if ($thisfile_ac3_raw_bsi['acmod'] > 2) { // if more than 2 channels
    241 					$thisfile_ac3_raw_bsi['dmixmod']  =        $this->readHeaderBSI(2);
    242 				}
    243 				if (($thisfile_ac3_raw_bsi['acmod'] & 0x01) && ($thisfile_ac3_raw_bsi['acmod'] > 2)) { // if three front channels exist
    244 					$thisfile_ac3_raw_bsi['ltrtcmixlev'] =        $this->readHeaderBSI(3);
    245 					$thisfile_ac3_raw_bsi['lorocmixlev'] =        $this->readHeaderBSI(3);
    246 				}
    247 				if ($thisfile_ac3_raw_bsi['acmod'] & 0x04) { // if a surround channel exists
    248 					$thisfile_ac3_raw_bsi['ltrtsurmixlev'] =        $this->readHeaderBSI(3);
    249 					$thisfile_ac3_raw_bsi['lorosurmixlev'] =        $this->readHeaderBSI(3);
    250 				}
    251 				if ($thisfile_ac3_raw_bsi['flags']['lfeon']) { // if the LFE channel exists
    252 					$thisfile_ac3_raw_bsi['flags']['lfemixlevcod'] = (bool) $this->readHeaderBSI(1);
    253 					if ($thisfile_ac3_raw_bsi['flags']['lfemixlevcod']) {
    254 						$thisfile_ac3_raw_bsi['lfemixlevcod']  =        $this->readHeaderBSI(5);
    255 					}
    256 				}
    257 				if ($thisfile_ac3_raw_bsi['strmtyp'] == 0) { // if independent stream
    258 					$thisfile_ac3_raw_bsi['flags']['pgmscl'] = (bool) $this->readHeaderBSI(1);
    259 					if ($thisfile_ac3_raw_bsi['flags']['pgmscl']) {
    260 						$thisfile_ac3_raw_bsi['pgmscl']  =        $this->readHeaderBSI(6);
    261 					}
    262 					if ($thisfile_ac3_raw_bsi['acmod'] == 0) { // if 1+1 mode (dual mono, so some items need a second value)
    263 						$thisfile_ac3_raw_bsi['flags']['pgmscl2'] = (bool) $this->readHeaderBSI(1);
    264 						if ($thisfile_ac3_raw_bsi['flags']['pgmscl2']) {
    265 							$thisfile_ac3_raw_bsi['pgmscl2']  =        $this->readHeaderBSI(6);
    266 						}
    267 					}
    268 					$thisfile_ac3_raw_bsi['flags']['extpgmscl'] = (bool) $this->readHeaderBSI(1);
    269 					if ($thisfile_ac3_raw_bsi['flags']['extpgmscl']) {
    270 						$thisfile_ac3_raw_bsi['extpgmscl']  =        $this->readHeaderBSI(6);
    271 					}
    272 					$thisfile_ac3_raw_bsi['mixdef']  =        $this->readHeaderBSI(2);
    273 					if ($thisfile_ac3_raw_bsi['mixdef'] == 1) { // mixing option 2
    274 						$thisfile_ac3_raw_bsi['premixcmpsel']  = (bool) $this->readHeaderBSI(1);
    275 						$thisfile_ac3_raw_bsi['drcsrc']        = (bool) $this->readHeaderBSI(1);
    276 						$thisfile_ac3_raw_bsi['premixcmpscl']  =        $this->readHeaderBSI(3);
    277 					} elseif ($thisfile_ac3_raw_bsi['mixdef'] == 2) { // mixing option 3
    278 						$thisfile_ac3_raw_bsi['mixdata']       =        $this->readHeaderBSI(12);
    279 					} elseif ($thisfile_ac3_raw_bsi['mixdef'] == 3) { // mixing option 4
    280 						$mixdefbitsread = 0;
    281 						$thisfile_ac3_raw_bsi['mixdeflen']     =        $this->readHeaderBSI(5); $mixdefbitsread += 5;
    282 						$thisfile_ac3_raw_bsi['flags']['mixdata2'] = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
    283 						if ($thisfile_ac3_raw_bsi['flags']['mixdata2']) {
    284 							$thisfile_ac3_raw_bsi['premixcmpsel']  = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
    285 							$thisfile_ac3_raw_bsi['drcsrc']        = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
    286 							$thisfile_ac3_raw_bsi['premixcmpscl']  =        $this->readHeaderBSI(3); $mixdefbitsread += 3;
    287 							$thisfile_ac3_raw_bsi['flags']['extpgmlscl']   = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
    288 							if ($thisfile_ac3_raw_bsi['flags']['extpgmlscl']) {
    289 								$thisfile_ac3_raw_bsi['extpgmlscl']    =        $this->readHeaderBSI(4); $mixdefbitsread += 4;
    290 							}
    291 							$thisfile_ac3_raw_bsi['flags']['extpgmcscl']   = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
    292 							if ($thisfile_ac3_raw_bsi['flags']['extpgmcscl']) {
    293 								$thisfile_ac3_raw_bsi['extpgmcscl']    =        $this->readHeaderBSI(4); $mixdefbitsread += 4;
    294 							}
    295 							$thisfile_ac3_raw_bsi['flags']['extpgmrscl']   = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
    296 							if ($thisfile_ac3_raw_bsi['flags']['extpgmrscl']) {
    297 								$thisfile_ac3_raw_bsi['extpgmrscl']    =        $this->readHeaderBSI(4);
    298 							}
    299 							$thisfile_ac3_raw_bsi['flags']['extpgmlsscl']  = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
    300 							if ($thisfile_ac3_raw_bsi['flags']['extpgmlsscl']) {
    301 								$thisfile_ac3_raw_bsi['extpgmlsscl']   =        $this->readHeaderBSI(4); $mixdefbitsread += 4;
    302 							}
    303 							$thisfile_ac3_raw_bsi['flags']['extpgmrsscl']  = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
    304 							if ($thisfile_ac3_raw_bsi['flags']['extpgmrsscl']) {
    305 								$thisfile_ac3_raw_bsi['extpgmrsscl']   =        $this->readHeaderBSI(4); $mixdefbitsread += 4;
    306 							}
    307 							$thisfile_ac3_raw_bsi['flags']['extpgmlfescl'] = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
    308 							if ($thisfile_ac3_raw_bsi['flags']['extpgmlfescl']) {
    309 								$thisfile_ac3_raw_bsi['extpgmlfescl']  =        $this->readHeaderBSI(4); $mixdefbitsread += 4;
    310 							}
    311 							$thisfile_ac3_raw_bsi['flags']['dmixscl']      = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
    312 							if ($thisfile_ac3_raw_bsi['flags']['dmixscl']) {
    313 								$thisfile_ac3_raw_bsi['dmixscl']       =        $this->readHeaderBSI(4); $mixdefbitsread += 4;
    314 							}
    315 							$thisfile_ac3_raw_bsi['flags']['addch']        = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
    316 							if ($thisfile_ac3_raw_bsi['flags']['addch']) {
    317 								$thisfile_ac3_raw_bsi['flags']['extpgmaux1scl']   = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
    318 								if ($thisfile_ac3_raw_bsi['flags']['extpgmaux1scl']) {
    319 									$thisfile_ac3_raw_bsi['extpgmaux1scl']    =        $this->readHeaderBSI(4); $mixdefbitsread += 4;
    320 								}
    321 								$thisfile_ac3_raw_bsi['flags']['extpgmaux2scl']   = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
    322 								if ($thisfile_ac3_raw_bsi['flags']['extpgmaux2scl']) {
    323 									$thisfile_ac3_raw_bsi['extpgmaux2scl']    =        $this->readHeaderBSI(4); $mixdefbitsread += 4;
    324 								}
    325 							}
    326 						}
    327 						$thisfile_ac3_raw_bsi['flags']['mixdata3'] = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
    328 						if ($thisfile_ac3_raw_bsi['flags']['mixdata3']) {
    329 							$thisfile_ac3_raw_bsi['spchdat']   =        $this->readHeaderBSI(5); $mixdefbitsread += 5;
    330 							$thisfile_ac3_raw_bsi['flags']['addspchdat'] = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
    331 							if ($thisfile_ac3_raw_bsi['flags']['addspchdat']) {
    332 								$thisfile_ac3_raw_bsi['spchdat1']   =         $this->readHeaderBSI(5); $mixdefbitsread += 5;
    333 								$thisfile_ac3_raw_bsi['spchan1att'] =         $this->readHeaderBSI(2); $mixdefbitsread += 2;
    334 								$thisfile_ac3_raw_bsi['flags']['addspchdat1'] = (bool) $this->readHeaderBSI(1); $mixdefbitsread += 1;
    335 								if ($thisfile_ac3_raw_bsi['flags']['addspchdat1']) {
    336 									$thisfile_ac3_raw_bsi['spchdat2']   =         $this->readHeaderBSI(5); $mixdefbitsread += 5;
    337 									$thisfile_ac3_raw_bsi['spchan2att'] =         $this->readHeaderBSI(3); $mixdefbitsread += 3;
    338 								}
    339 							}
    340 						}
    341 						$mixdata_bits = (8 * ($thisfile_ac3_raw_bsi['mixdeflen'] + 2)) - $mixdefbitsread;
    342 						$mixdata_fill = (($mixdata_bits % 8) ? 8 - ($mixdata_bits % 8) : 0);
    343 						$thisfile_ac3_raw_bsi['mixdata']     =        $this->readHeaderBSI($mixdata_bits);
    344 						$thisfile_ac3_raw_bsi['mixdatafill'] =        $this->readHeaderBSI($mixdata_fill);
    345 						unset($mixdefbitsread, $mixdata_bits, $mixdata_fill);
    346 					}
    347 					if ($thisfile_ac3_raw_bsi['acmod'] < 2) { // if mono or dual mono source
    348 						$thisfile_ac3_raw_bsi['flags']['paninfo'] = (bool) $this->readHeaderBSI(1);
    349 						if ($thisfile_ac3_raw_bsi['flags']['paninfo']) {
    350 							$thisfile_ac3_raw_bsi['panmean']   =        $this->readHeaderBSI(8);
    351 							$thisfile_ac3_raw_bsi['paninfo']   =        $this->readHeaderBSI(6);
    352 						}
    353 						if ($thisfile_ac3_raw_bsi['acmod'] == 0) { // if 1+1 mode (dual mono, so some items need a second value)
    354 							$thisfile_ac3_raw_bsi['flags']['paninfo2'] = (bool) $this->readHeaderBSI(1);
    355 							if ($thisfile_ac3_raw_bsi['flags']['paninfo2']) {
    356 								$thisfile_ac3_raw_bsi['panmean2']   =        $this->readHeaderBSI(8);
    357 								$thisfile_ac3_raw_bsi['paninfo2']   =        $this->readHeaderBSI(6);
    358 							}
    359 						}
    360 					}
    361 					$thisfile_ac3_raw_bsi['flags']['frmmixcfginfo'] = (bool) $this->readHeaderBSI(1);
    362 					if ($thisfile_ac3_raw_bsi['flags']['frmmixcfginfo']) { // mixing configuration information
    363 						if ($thisfile_ac3_raw_bsi['numblkscod'] == 0) {
    364 							$thisfile_ac3_raw_bsi['blkmixcfginfo'][0]  =        $this->readHeaderBSI(5);
    365 						} else {
    366 							for ($blk = 0; $blk < $thisfile_ac3_raw_bsi['numblkscod']; $blk++) {
    367 								$thisfile_ac3_raw_bsi['flags']['blkmixcfginfo'.$blk] = (bool) $this->readHeaderBSI(1);
    368 								if ($thisfile_ac3_raw_bsi['flags']['blkmixcfginfo'.$blk]) { // mixing configuration information
    369 									$thisfile_ac3_raw_bsi['blkmixcfginfo'][$blk]  =        $this->readHeaderBSI(5);
    370 								}
    371 							}
    372 						}
    373 					}
    374 				}
    375 			}
    376 			$thisfile_ac3_raw_bsi['flags']['infomdat']          = (bool) $this->readHeaderBSI(1);
    377 			if ($thisfile_ac3_raw_bsi['flags']['infomdat']) { // Informational metadata
    378 				$thisfile_ac3_raw_bsi['bsmod']                  =        $this->readHeaderBSI(3);
    379 				$thisfile_ac3_raw_bsi['flags']['copyrightb']    = (bool) $this->readHeaderBSI(1);
    380 				$thisfile_ac3_raw_bsi['flags']['origbs']        = (bool) $this->readHeaderBSI(1);
    381 				if ($thisfile_ac3_raw_bsi['acmod'] == 2) { //  if in 2/0 mode
    382 					$thisfile_ac3_raw_bsi['dsurmod']            =        $this->readHeaderBSI(2);
    383 					$thisfile_ac3_raw_bsi['dheadphonmod']       =        $this->readHeaderBSI(2);
    384 				}
    385 				if ($thisfile_ac3_raw_bsi['acmod'] >= 6) { //  if both surround channels exist
    386 					$thisfile_ac3_raw_bsi['dsurexmod']          =        $this->readHeaderBSI(2);
    387 				}
    388 				$thisfile_ac3_raw_bsi['flags']['audprodi']      = (bool) $this->readHeaderBSI(1);
    389 				if ($thisfile_ac3_raw_bsi['flags']['audprodi']) {
    390 					$thisfile_ac3_raw_bsi['mixlevel']           =        $this->readHeaderBSI(5);
    391 					$thisfile_ac3_raw_bsi['roomtyp']            =        $this->readHeaderBSI(2);
    392 					$thisfile_ac3_raw_bsi['flags']['adconvtyp'] = (bool) $this->readHeaderBSI(1);
    393 				}
    394 				if ($thisfile_ac3_raw_bsi['acmod'] == 0) { //  if 1+1 mode (dual mono, so some items need a second value)
    395 					$thisfile_ac3_raw_bsi['flags']['audprodi2']      = (bool) $this->readHeaderBSI(1);
    396 					if ($thisfile_ac3_raw_bsi['flags']['audprodi2']) {
    397 						$thisfile_ac3_raw_bsi['mixlevel2']           =        $this->readHeaderBSI(5);
    398 						$thisfile_ac3_raw_bsi['roomtyp2']            =        $this->readHeaderBSI(2);
    399 						$thisfile_ac3_raw_bsi['flags']['adconvtyp2'] = (bool) $this->readHeaderBSI(1);
    400 					}
    401 				}
    402 				if ($thisfile_ac3_raw_bsi['fscod'] < 3) { // if not half sample rate
    403 					$thisfile_ac3_raw_bsi['flags']['sourcefscod'] = (bool) $this->readHeaderBSI(1);
    404 				}
    405 			}
    406 			if (($thisfile_ac3_raw_bsi['strmtyp'] == 0) && ($thisfile_ac3_raw_bsi['numblkscod'] != 3)) { //  if both surround channels exist
    407 				$thisfile_ac3_raw_bsi['flags']['convsync'] = (bool) $this->readHeaderBSI(1);
    408 			}
    409 			if ($thisfile_ac3_raw_bsi['strmtyp'] == 2) { //  if bit stream converted from AC-3
    410 				if ($thisfile_ac3_raw_bsi['numblkscod'] != 3) { // 6 blocks per syncframe
    411 					$thisfile_ac3_raw_bsi['flags']['blkid']  = 1;
    412 				} else {
    413 					$thisfile_ac3_raw_bsi['flags']['blkid']  = (bool) $this->readHeaderBSI(1);
    414 				}
    415 				if ($thisfile_ac3_raw_bsi['flags']['blkid']) {
    416 					$thisfile_ac3_raw_bsi['frmsizecod']  =        $this->readHeaderBSI(6);
    417 				}
    418 			}
    419 			$thisfile_ac3_raw_bsi['flags']['addbsi']  = (bool) $this->readHeaderBSI(1);
    420 			if ($thisfile_ac3_raw_bsi['flags']['addbsi']) {
    421 				$thisfile_ac3_raw_bsi['addbsil']  =        $this->readHeaderBSI(6);
    422 				$thisfile_ac3_raw_bsi['addbsi']   =        $this->readHeaderBSI(($thisfile_ac3_raw_bsi['addbsil'] + 1) * 8);
    423 			}
    424 
    425 		} else {
    426 
    427 			$this->error('Bit stream identification is version '.$thisfile_ac3_raw_bsi['bsid'].', but getID3() only understands up to version 16. Please submit a support ticket with a sample file.');
    428 			unset($info['ac3']);
    429 			return false;
    430 
    431 		}
    432 
    433 		if (isset($thisfile_ac3_raw_bsi['fscod2'])) {
    434 			$thisfile_ac3['sample_rate'] = self::sampleRateCodeLookup2($thisfile_ac3_raw_bsi['fscod2']);
    435 		} else {
    436 			$thisfile_ac3['sample_rate'] = self::sampleRateCodeLookup($thisfile_ac3_raw_bsi['fscod']);
    437 		}
    438 		if ($thisfile_ac3_raw_bsi['fscod'] <= 3) {
    439 			$info['audio']['sample_rate'] = $thisfile_ac3['sample_rate'];
    440 		} else {
    441 			$this->warning('Unexpected ac3.bsi.fscod value: '.$thisfile_ac3_raw_bsi['fscod']);
    442 		}
    443 		if (isset($thisfile_ac3_raw_bsi['frmsizecod'])) {
    444 			$thisfile_ac3['frame_length'] = self::frameSizeLookup($thisfile_ac3_raw_bsi['frmsizecod'], $thisfile_ac3_raw_bsi['fscod']);
    445 			$thisfile_ac3['bitrate']      = self::bitrateLookup($thisfile_ac3_raw_bsi['frmsizecod']);
    446 		} elseif (!empty($thisfile_ac3_raw_bsi['frmsiz'])) {
    447 			// this isn't right, but it's (usually) close, roughly 5% less than it should be.
    448 			// but WHERE is the actual bitrate value stored in EAC3?? email info@getid3.org if you know!
    449 			$thisfile_ac3['bitrate']      = ($thisfile_ac3_raw_bsi['frmsiz'] + 1) * 16 * 30; // The frmsiz field shall contain a value one less than the overall size of the coded syncframe in 16-bit words. That is, this field may assume a value ranging from 0 to 2047, and these values correspond to syncframe sizes ranging from 1 to 2048.
    450 			// kludge-fix to make it approximately the expected value, still not "right":
    451 			$thisfile_ac3['bitrate'] = round(($thisfile_ac3['bitrate'] * 1.05) / 16000) * 16000;
    452 		}
    453 		$info['audio']['bitrate'] = $thisfile_ac3['bitrate'];
    454 
    455 		if (isset($thisfile_ac3_raw_bsi['bsmod']) && isset($thisfile_ac3_raw_bsi['acmod'])) {
    456 			$thisfile_ac3['service_type'] = self::serviceTypeLookup($thisfile_ac3_raw_bsi['bsmod'], $thisfile_ac3_raw_bsi['acmod']);
    457 		}
    458 		$ac3_coding_mode = self::audioCodingModeLookup($thisfile_ac3_raw_bsi['acmod']);
    459 		foreach($ac3_coding_mode as $key => $value) {
    460 			$thisfile_ac3[$key] = $value;
    461 		}
    462 		switch ($thisfile_ac3_raw_bsi['acmod']) {
    463 			case 0:
    464 			case 1:
    465 				$info['audio']['channelmode'] = 'mono';
    466 				break;
    467 			case 3:
    468 			case 4:
    469 				$info['audio']['channelmode'] = 'stereo';
    470 				break;
    471 			default:
    472 				$info['audio']['channelmode'] = 'surround';
    473 				break;
    474 		}
    475 		$info['audio']['channels'] = $thisfile_ac3['num_channels'];
    476 
    477 		$thisfile_ac3['lfe_enabled'] = $thisfile_ac3_raw_bsi['flags']['lfeon'];
    478 		if ($thisfile_ac3_raw_bsi['flags']['lfeon']) {
    479 			$info['audio']['channels'] .= '.1';
    480 		}
    481 
    482 		$thisfile_ac3['channels_enabled'] = self::channelsEnabledLookup($thisfile_ac3_raw_bsi['acmod'], $thisfile_ac3_raw_bsi['flags']['lfeon']);
    483 		$thisfile_ac3['dialogue_normalization'] = '-'.$thisfile_ac3_raw_bsi['dialnorm'].'dB';
    484 
    485 		return true;
    486 	}
    487 
    488 	/**
    489 	 * @param int $length
    490 	 *
    491 	 * @return int
    492 	 */
    493 	private function readHeaderBSI($length) {
    494 		$data = substr($this->AC3header['bsi'], $this->BSIoffset, $length);
    495 		$this->BSIoffset += $length;
    496 
    497 		return bindec($data);
    498 	}
    499 
    500 	/**
    501 	 * @param int $fscod
    502 	 *
    503 	 * @return int|string|false
    504 	 */
    505 	public static function sampleRateCodeLookup($fscod) {
    506 		static $sampleRateCodeLookup = array(
    507 			0 => 48000,
    508 			1 => 44100,
    509 			2 => 32000,
    510 			3 => 'reserved' // If the reserved code is indicated, the decoder should not attempt to decode audio and should mute.
    511 		);
    512 		return (isset($sampleRateCodeLookup[$fscod]) ? $sampleRateCodeLookup[$fscod] : false);
    513 	}
    514 
    515 	/**
    516 	 * @param int $fscod2
    517 	 *
    518 	 * @return int|string|false
    519 	 */
    520 	public static function sampleRateCodeLookup2($fscod2) {
    521 		static $sampleRateCodeLookup2 = array(
    522 			0 => 24000,
    523 			1 => 22050,
    524 			2 => 16000,
    525 			3 => 'reserved' // If the reserved code is indicated, the decoder should not attempt to decode audio and should mute.
    526 		);
    527 		return (isset($sampleRateCodeLookup2[$fscod2]) ? $sampleRateCodeLookup2[$fscod2] : false);
    528 	}
    529 
    530 	/**
    531 	 * @param int $bsmod
    532 	 * @param int $acmod
    533 	 *
    534 	 * @return string|false
    535 	 */
    536 	public static function serviceTypeLookup($bsmod, $acmod) {
    537 		static $serviceTypeLookup = array();
    538 		if (empty($serviceTypeLookup)) {
    539 			for ($i = 0; $i <= 7; $i++) {
    540 				$serviceTypeLookup[0][$i] = 'main audio service: complete main (CM)';
    541 				$serviceTypeLookup[1][$i] = 'main audio service: music and effects (ME)';
    542 				$serviceTypeLookup[2][$i] = 'associated service: visually impaired (VI)';
    543 				$serviceTypeLookup[3][$i] = 'associated service: hearing impaired (HI)';
    544 				$serviceTypeLookup[4][$i] = 'associated service: dialogue (D)';
    545 				$serviceTypeLookup[5][$i] = 'associated service: commentary (C)';
    546 				$serviceTypeLookup[6][$i] = 'associated service: emergency (E)';
    547 			}
    548 
    549 			$serviceTypeLookup[7][1]      = 'associated service: voice over (VO)';
    550 			for ($i = 2; $i <= 7; $i++) {
    551 				$serviceTypeLookup[7][$i] = 'main audio service: karaoke';
    552 			}
    553 		}
    554 		return (isset($serviceTypeLookup[$bsmod][$acmod]) ? $serviceTypeLookup[$bsmod][$acmod] : false);
    555 	}
    556 
    557 	/**
    558 	 * @param int $acmod
    559 	 *
    560 	 * @return array|false
    561 	 */
    562 	public static function audioCodingModeLookup($acmod) {
    563 		// array(channel configuration, # channels (not incl LFE), channel order)
    564 		static $audioCodingModeLookup = array (
    565 			0 => array('channel_config'=>'1+1', 'num_channels'=>2, 'channel_order'=>'Ch1,Ch2'),
    566 			1 => array('channel_config'=>'1/0', 'num_channels'=>1, 'channel_order'=>'C'),
    567 			2 => array('channel_config'=>'2/0', 'num_channels'=>2, 'channel_order'=>'L,R'),
    568 			3 => array('channel_config'=>'3/0', 'num_channels'=>3, 'channel_order'=>'L,C,R'),
    569 			4 => array('channel_config'=>'2/1', 'num_channels'=>3, 'channel_order'=>'L,R,S'),
    570 			5 => array('channel_config'=>'3/1', 'num_channels'=>4, 'channel_order'=>'L,C,R,S'),
    571 			6 => array('channel_config'=>'2/2', 'num_channels'=>4, 'channel_order'=>'L,R,SL,SR'),
    572 			7 => array('channel_config'=>'3/2', 'num_channels'=>5, 'channel_order'=>'L,C,R,SL,SR'),
    573 		);
    574 		return (isset($audioCodingModeLookup[$acmod]) ? $audioCodingModeLookup[$acmod] : false);
    575 	}
    576 
    577 	/**
    578 	 * @param int $cmixlev
    579 	 *
    580 	 * @return int|float|string|false
    581 	 */
    582 	public static function centerMixLevelLookup($cmixlev) {
    583 		static $centerMixLevelLookup;
    584 		if (empty($centerMixLevelLookup)) {
    585 			$centerMixLevelLookup = array(
    586 				0 => pow(2, -3.0 / 6), // 0.707 (-3.0 dB)
    587 				1 => pow(2, -4.5 / 6), // 0.595 (-4.5 dB)
    588 				2 => pow(2, -6.0 / 6), // 0.500 (-6.0 dB)
    589 				3 => 'reserved'
    590 			);
    591 		}
    592 		return (isset($centerMixLevelLookup[$cmixlev]) ? $centerMixLevelLookup[$cmixlev] : false);
    593 	}
    594 
    595 	/**
    596 	 * @param int $surmixlev
    597 	 *
    598 	 * @return int|float|string|false
    599 	 */
    600 	public static function surroundMixLevelLookup($surmixlev) {
    601 		static $surroundMixLevelLookup;
    602 		if (empty($surroundMixLevelLookup)) {
    603 			$surroundMixLevelLookup = array(
    604 				0 => pow(2, -3.0 / 6),
    605 				1 => pow(2, -6.0 / 6),
    606 				2 => 0,
    607 				3 => 'reserved'
    608 			);
    609 		}
    610 		return (isset($surroundMixLevelLookup[$surmixlev]) ? $surroundMixLevelLookup[$surmixlev] : false);
    611 	}
    612 
    613 	/**
    614 	 * @param int $dsurmod
    615 	 *
    616 	 * @return string|false
    617 	 */
    618 	public static function dolbySurroundModeLookup($dsurmod) {
    619 		static $dolbySurroundModeLookup = array(
    620 			0 => 'not indicated',
    621 			1 => 'Not Dolby Surround encoded',
    622 			2 => 'Dolby Surround encoded',
    623 			3 => 'reserved'
    624 		);
    625 		return (isset($dolbySurroundModeLookup[$dsurmod]) ? $dolbySurroundModeLookup[$dsurmod] : false);
    626 	}
    627 
    628 	/**
    629 	 * @param int  $acmod
    630 	 * @param bool $lfeon
    631 	 *
    632 	 * @return array
    633 	 */
    634 	public static function channelsEnabledLookup($acmod, $lfeon) {
    635 		$lookup = array(
    636 			'ch1'=>($acmod == 0),
    637 			'ch2'=>($acmod == 0),
    638 			'left'=>($acmod > 1),
    639 			'right'=>($acmod > 1),
    640 			'center'=>(bool) ($acmod & 0x01),
    641 			'surround_mono'=>false,
    642 			'surround_left'=>false,
    643 			'surround_right'=>false,
    644 			'lfe'=>$lfeon);
    645 		switch ($acmod) {
    646 			case 4:
    647 			case 5:
    648 				$lookup['surround_mono']  = true;
    649 				break;
    650 			case 6:
    651 			case 7:
    652 				$lookup['surround_left']  = true;
    653 				$lookup['surround_right'] = true;
    654 				break;
    655 		}
    656 		return $lookup;
    657 	}
    658 
    659 	/**
    660 	 * @param int $compre
    661 	 *
    662 	 * @return float|int
    663 	 */
    664 	public static function heavyCompression($compre) {
    665 		// The first four bits indicate gain changes in 6.02dB increments which can be
    666 		// implemented with an arithmetic shift operation. The following four bits
    667 		// indicate linear gain changes, and require a 5-bit multiply.
    668 		// We will represent the two 4-bit fields of compr as follows:
    669 		//   X0 X1 X2 X3 . Y4 Y5 Y6 Y7
    670 		// The meaning of the X values is most simply described by considering X to represent a 4-bit
    671 		// signed integer with values from -8 to +7. The gain indicated by X is then (X + 1) * 6.02 dB. The
    672 		// following table shows this in detail.
    673 
    674 		// Meaning of 4 msb of compr
    675 		//  7    +48.16 dB
    676 		//  6    +42.14 dB
    677 		//  5    +36.12 dB
    678 		//  4    +30.10 dB
    679 		//  3    +24.08 dB
    680 		//  2    +18.06 dB
    681 		//  1    +12.04 dB
    682 		//  0     +6.02 dB
    683 		// -1         0 dB
    684 		// -2     -6.02 dB
    685 		// -3    -12.04 dB
    686 		// -4    -18.06 dB
    687 		// -5    -24.08 dB
    688 		// -6    -30.10 dB
    689 		// -7    -36.12 dB
    690 		// -8    -42.14 dB
    691 
    692 		$fourbit = str_pad(decbin(($compre & 0xF0) >> 4), 4, '0', STR_PAD_LEFT);
    693 		if ($fourbit[0] == '1') {
    694 			$log_gain = -8 + bindec(substr($fourbit, 1));
    695 		} else {
    696 			$log_gain = bindec(substr($fourbit, 1));
    697 		}
    698 		$log_gain = ($log_gain + 1) * getid3_lib::RGADamplitude2dB(2);
    699 
    700 		// The value of Y is a linear representation of a gain change of up to -6 dB. Y is considered to
    701 		// be an unsigned fractional integer, with a leading value of 1, or: 0.1 Y4 Y5 Y6 Y7 (base 2). Y can
    702 		// represent values between 0.111112 (or 31/32) and 0.100002 (or 1/2). Thus, Y can represent gain
    703 		// changes from -0.28 dB to -6.02 dB.
    704 
    705 		$lin_gain = (16 + ($compre & 0x0F)) / 32;
    706 
    707 		// The combination of X and Y values allows compr to indicate gain changes from
    708 		//  48.16 - 0.28 = +47.89 dB, to
    709 		// -42.14 - 6.02 = -48.16 dB.
    710 
    711 		return $log_gain - $lin_gain;
    712 	}
    713 
    714 	/**
    715 	 * @param int $roomtyp
    716 	 *
    717 	 * @return string|false
    718 	 */
    719 	public static function roomTypeLookup($roomtyp) {
    720 		static $roomTypeLookup = array(
    721 			0 => 'not indicated',
    722 			1 => 'large room, X curve monitor',
    723 			2 => 'small room, flat monitor',
    724 			3 => 'reserved'
    725 		);
    726 		return (isset($roomTypeLookup[$roomtyp]) ? $roomTypeLookup[$roomtyp] : false);
    727 	}
    728 
    729 	/**
    730 	 * @param int $frmsizecod
    731 	 * @param int $fscod
    732 	 *
    733 	 * @return int|false
    734 	 */
    735 	public static function frameSizeLookup($frmsizecod, $fscod) {
    736 		// LSB is whether padding is used or not
    737 		$padding     = (bool) ($frmsizecod & 0x01);
    738 		$framesizeid =        ($frmsizecod & 0x3E) >> 1;
    739 
    740 		static $frameSizeLookup = array();
    741 		if (empty($frameSizeLookup)) {
    742 			$frameSizeLookup = array (
    743 				0  => array( 128,  138,  192),  //  32 kbps
    744 				1  => array( 160,  174,  240),  //  40 kbps
    745 				2  => array( 192,  208,  288),  //  48 kbps
    746 				3  => array( 224,  242,  336),  //  56 kbps
    747 				4  => array( 256,  278,  384),  //  64 kbps
    748 				5  => array( 320,  348,  480),  //  80 kbps
    749 				6  => array( 384,  416,  576),  //  96 kbps
    750 				7  => array( 448,  486,  672),  // 112 kbps
    751 				8  => array( 512,  556,  768),  // 128 kbps
    752 				9  => array( 640,  696,  960),  // 160 kbps
    753 				10 => array( 768,  834, 1152),  // 192 kbps
    754 				11 => array( 896,  974, 1344),  // 224 kbps
    755 				12 => array(1024, 1114, 1536),  // 256 kbps
    756 				13 => array(1280, 1392, 1920),  // 320 kbps
    757 				14 => array(1536, 1670, 2304),  // 384 kbps
    758 				15 => array(1792, 1950, 2688),  // 448 kbps
    759 				16 => array(2048, 2228, 3072),  // 512 kbps
    760 				17 => array(2304, 2506, 3456),  // 576 kbps
    761 				18 => array(2560, 2786, 3840)   // 640 kbps
    762 			);
    763 		}
    764 		$paddingBytes = 0;
    765 		if (($fscod == 1) && $padding) {
    766 			// frame lengths are padded by 1 word (16 bits) at 44100
    767 			// (fscode==1) means 44100Hz (see sampleRateCodeLookup)
    768 			$paddingBytes = 2;
    769 		}
    770 		return (isset($frameSizeLookup[$framesizeid][$fscod]) ? $frameSizeLookup[$framesizeid][$fscod] + $paddingBytes : false);
    771 	}
    772 
    773 	/**
    774 	 * @param int $frmsizecod
    775 	 *
    776 	 * @return int|false
    777 	 */
    778 	public static function bitrateLookup($frmsizecod) {
    779 		// LSB is whether padding is used or not
    780 		$padding     = (bool) ($frmsizecod & 0x01);
    781 		$framesizeid =        ($frmsizecod & 0x3E) >> 1;
    782 
    783 		static $bitrateLookup = array(
    784 			 0 =>  32000,
    785 			 1 =>  40000,
    786 			 2 =>  48000,
    787 			 3 =>  56000,
    788 			 4 =>  64000,
    789 			 5 =>  80000,
    790 			 6 =>  96000,
    791 			 7 => 112000,
    792 			 8 => 128000,
    793 			 9 => 160000,
    794 			10 => 192000,
    795 			11 => 224000,
    796 			12 => 256000,
    797 			13 => 320000,
    798 			14 => 384000,
    799 			15 => 448000,
    800 			16 => 512000,
    801 			17 => 576000,
    802 			18 => 640000,
    803 		);
    804 		return (isset($bitrateLookup[$framesizeid]) ? $bitrateLookup[$framesizeid] : false);
    805 	}
    806 
    807 	/**
    808 	 * @param int $numblkscod
    809 	 *
    810 	 * @return int|false
    811 	 */
    812 	public static function blocksPerSyncFrame($numblkscod) {
    813 		static $blocksPerSyncFrameLookup = array(
    814 			0 => 1,
    815 			1 => 2,
    816 			2 => 3,
    817 			3 => 6,
    818 		);
    819 		return (isset($blocksPerSyncFrameLookup[$numblkscod]) ? $blocksPerSyncFrameLookup[$numblkscod] : false);
    820 	}
    821 
    822 
    823 }