File indexing completed on 2024-12-22 05:33:12
0001 <?php 0002 ///////////////////////////////////////////////////////////////// 0003 /// getID3() by James Heinrich <info@getid3.org> // 0004 // available at http://getid3.sourceforge.net // 0005 // or http://www.getid3.org // 0006 // also https://github.com/JamesHeinrich/getID3 // 0007 ///////////////////////////////////////////////////////////////// 0008 // See readme.txt for more details // 0009 ///////////////////////////////////////////////////////////////// 0010 // // 0011 // module.audio.la.php // 0012 // module for analyzing BONK audio files // 0013 // dependencies: module.tag.id3v2.php (optional) // 0014 // /// 0015 ///////////////////////////////////////////////////////////////// 0016 0017 0018 class getid3_bonk extends getid3_handler 0019 { 0020 public function Analyze() { 0021 $info = &$this->getid3->info; 0022 0023 // shortcut 0024 $info['bonk'] = array(); 0025 $thisfile_bonk = &$info['bonk']; 0026 0027 $thisfile_bonk['dataoffset'] = $info['avdataoffset']; 0028 $thisfile_bonk['dataend'] = $info['avdataend']; 0029 0030 if (!getid3_lib::intValueSupported($thisfile_bonk['dataend'])) { 0031 0032 $info['warning'][] = 'Unable to parse BONK file from end (v0.6+ preferred method) because PHP filesystem functions only support up to '.round(PHP_INT_MAX / 1073741824).'GB'; 0033 0034 } else { 0035 0036 // scan-from-end method, for v0.6 and higher 0037 $this->fseek($thisfile_bonk['dataend'] - 8); 0038 $PossibleBonkTag = $this->fread(8); 0039 while ($this->BonkIsValidTagName(substr($PossibleBonkTag, 4, 4), true)) { 0040 $BonkTagSize = getid3_lib::LittleEndian2Int(substr($PossibleBonkTag, 0, 4)); 0041 $this->fseek(0 - $BonkTagSize, SEEK_CUR); 0042 $BonkTagOffset = $this->ftell(); 0043 $TagHeaderTest = $this->fread(5); 0044 if (($TagHeaderTest{0} != "\x00") || (substr($PossibleBonkTag, 4, 4) != strtolower(substr($PossibleBonkTag, 4, 4)))) { 0045 $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes("\x00".strtoupper(substr($PossibleBonkTag, 4, 4))).'" at offset '.$BonkTagOffset.', found "'.getid3_lib::PrintHexBytes($TagHeaderTest).'"'; 0046 return false; 0047 } 0048 $BonkTagName = substr($TagHeaderTest, 1, 4); 0049 0050 $thisfile_bonk[$BonkTagName]['size'] = $BonkTagSize; 0051 $thisfile_bonk[$BonkTagName]['offset'] = $BonkTagOffset; 0052 $this->HandleBonkTags($BonkTagName); 0053 $NextTagEndOffset = $BonkTagOffset - 8; 0054 if ($NextTagEndOffset < $thisfile_bonk['dataoffset']) { 0055 if (empty($info['audio']['encoder'])) { 0056 $info['audio']['encoder'] = 'Extended BONK v0.9+'; 0057 } 0058 return true; 0059 } 0060 $this->fseek($NextTagEndOffset); 0061 $PossibleBonkTag = $this->fread(8); 0062 } 0063 0064 } 0065 0066 // seek-from-beginning method for v0.4 and v0.5 0067 if (empty($thisfile_bonk['BONK'])) { 0068 $this->fseek($thisfile_bonk['dataoffset']); 0069 do { 0070 $TagHeaderTest = $this->fread(5); 0071 switch ($TagHeaderTest) { 0072 case "\x00".'BONK': 0073 if (empty($info['audio']['encoder'])) { 0074 $info['audio']['encoder'] = 'BONK v0.4'; 0075 } 0076 break; 0077 0078 case "\x00".'INFO': 0079 $info['audio']['encoder'] = 'Extended BONK v0.5'; 0080 break; 0081 0082 default: 0083 break 2; 0084 } 0085 $BonkTagName = substr($TagHeaderTest, 1, 4); 0086 $thisfile_bonk[$BonkTagName]['size'] = $thisfile_bonk['dataend'] - $thisfile_bonk['dataoffset']; 0087 $thisfile_bonk[$BonkTagName]['offset'] = $thisfile_bonk['dataoffset']; 0088 $this->HandleBonkTags($BonkTagName); 0089 0090 } while (true); 0091 } 0092 0093 // parse META block for v0.6 - v0.8 0094 if (empty($thisfile_bonk['INFO']) && isset($thisfile_bonk['META']['tags']['info'])) { 0095 $this->fseek($thisfile_bonk['META']['tags']['info']); 0096 $TagHeaderTest = $this->fread(5); 0097 if ($TagHeaderTest == "\x00".'INFO') { 0098 $info['audio']['encoder'] = 'Extended BONK v0.6 - v0.8'; 0099 0100 $BonkTagName = substr($TagHeaderTest, 1, 4); 0101 $thisfile_bonk[$BonkTagName]['size'] = $thisfile_bonk['dataend'] - $thisfile_bonk['dataoffset']; 0102 $thisfile_bonk[$BonkTagName]['offset'] = $thisfile_bonk['dataoffset']; 0103 $this->HandleBonkTags($BonkTagName); 0104 } 0105 } 0106 0107 if (empty($info['audio']['encoder'])) { 0108 $info['audio']['encoder'] = 'Extended BONK v0.9+'; 0109 } 0110 if (empty($thisfile_bonk['BONK'])) { 0111 unset($info['bonk']); 0112 } 0113 return true; 0114 0115 } 0116 0117 public function HandleBonkTags($BonkTagName) { 0118 $info = &$this->getid3->info; 0119 switch ($BonkTagName) { 0120 case 'BONK': 0121 // shortcut 0122 $thisfile_bonk_BONK = &$info['bonk']['BONK']; 0123 0124 $BonkData = "\x00".'BONK'.$this->fread(17); 0125 $thisfile_bonk_BONK['version'] = getid3_lib::LittleEndian2Int(substr($BonkData, 5, 1)); 0126 $thisfile_bonk_BONK['number_samples'] = getid3_lib::LittleEndian2Int(substr($BonkData, 6, 4)); 0127 $thisfile_bonk_BONK['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BonkData, 10, 4)); 0128 0129 $thisfile_bonk_BONK['channels'] = getid3_lib::LittleEndian2Int(substr($BonkData, 14, 1)); 0130 $thisfile_bonk_BONK['lossless'] = (bool) getid3_lib::LittleEndian2Int(substr($BonkData, 15, 1)); 0131 $thisfile_bonk_BONK['joint_stereo'] = (bool) getid3_lib::LittleEndian2Int(substr($BonkData, 16, 1)); 0132 $thisfile_bonk_BONK['number_taps'] = getid3_lib::LittleEndian2Int(substr($BonkData, 17, 2)); 0133 $thisfile_bonk_BONK['downsampling_ratio'] = getid3_lib::LittleEndian2Int(substr($BonkData, 19, 1)); 0134 $thisfile_bonk_BONK['samples_per_packet'] = getid3_lib::LittleEndian2Int(substr($BonkData, 20, 2)); 0135 0136 $info['avdataoffset'] = $thisfile_bonk_BONK['offset'] + 5 + 17; 0137 $info['avdataend'] = $thisfile_bonk_BONK['offset'] + $thisfile_bonk_BONK['size']; 0138 0139 $info['fileformat'] = 'bonk'; 0140 $info['audio']['dataformat'] = 'bonk'; 0141 $info['audio']['bitrate_mode'] = 'vbr'; // assumed 0142 $info['audio']['channels'] = $thisfile_bonk_BONK['channels']; 0143 $info['audio']['sample_rate'] = $thisfile_bonk_BONK['sample_rate']; 0144 $info['audio']['channelmode'] = ($thisfile_bonk_BONK['joint_stereo'] ? 'joint stereo' : 'stereo'); 0145 $info['audio']['lossless'] = $thisfile_bonk_BONK['lossless']; 0146 $info['audio']['codec'] = 'bonk'; 0147 0148 $info['playtime_seconds'] = $thisfile_bonk_BONK['number_samples'] / ($thisfile_bonk_BONK['sample_rate'] * $thisfile_bonk_BONK['channels']); 0149 if ($info['playtime_seconds'] > 0) { 0150 $info['audio']['bitrate'] = (($info['bonk']['dataend'] - $info['bonk']['dataoffset']) * 8) / $info['playtime_seconds']; 0151 } 0152 break; 0153 0154 case 'INFO': 0155 // shortcut 0156 $thisfile_bonk_INFO = &$info['bonk']['INFO']; 0157 0158 $thisfile_bonk_INFO['version'] = getid3_lib::LittleEndian2Int($this->fread(1)); 0159 $thisfile_bonk_INFO['entries_count'] = 0; 0160 $NextInfoDataPair = $this->fread(5); 0161 if (!$this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) { 0162 while (!feof($this->getid3->fp)) { 0163 //$CurrentSeekInfo['offset'] = getid3_lib::LittleEndian2Int(substr($NextInfoDataPair, 0, 4)); 0164 //$CurrentSeekInfo['nextbit'] = getid3_lib::LittleEndian2Int(substr($NextInfoDataPair, 4, 1)); 0165 //$thisfile_bonk_INFO[] = $CurrentSeekInfo; 0166 0167 $NextInfoDataPair = $this->fread(5); 0168 if ($this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) { 0169 $this->fseek(-5, SEEK_CUR); 0170 break; 0171 } 0172 $thisfile_bonk_INFO['entries_count']++; 0173 } 0174 } 0175 break; 0176 0177 case 'META': 0178 $BonkData = "\x00".'META'.$this->fread($info['bonk']['META']['size'] - 5); 0179 $info['bonk']['META']['version'] = getid3_lib::LittleEndian2Int(substr($BonkData, 5, 1)); 0180 0181 $MetaTagEntries = floor(((strlen($BonkData) - 8) - 6) / 8); // BonkData - xxxxmeta - ØMETA 0182 $offset = 6; 0183 for ($i = 0; $i < $MetaTagEntries; $i++) { 0184 $MetaEntryTagName = substr($BonkData, $offset, 4); 0185 $offset += 4; 0186 $MetaEntryTagOffset = getid3_lib::LittleEndian2Int(substr($BonkData, $offset, 4)); 0187 $offset += 4; 0188 $info['bonk']['META']['tags'][$MetaEntryTagName] = $MetaEntryTagOffset; 0189 } 0190 break; 0191 0192 case ' ID3': 0193 $info['audio']['encoder'] = 'Extended BONK v0.9+'; 0194 0195 // ID3v2 checking is optional 0196 if (class_exists('getid3_id3v2')) { 0197 $getid3_temp = new getID3(); 0198 $getid3_temp->openfile($this->getid3->filename); 0199 $getid3_id3v2 = new getid3_id3v2($getid3_temp); 0200 $getid3_id3v2->StartingOffset = $info['bonk'][' ID3']['offset'] + 2; 0201 $info['bonk'][' ID3']['valid'] = $getid3_id3v2->Analyze(); 0202 if ($info['bonk'][' ID3']['valid']) { 0203 $info['id3v2'] = $getid3_temp->info['id3v2']; 0204 } 0205 unset($getid3_temp, $getid3_id3v2); 0206 } 0207 break; 0208 0209 default: 0210 $info['warning'][] = 'Unexpected Bonk tag "'.$BonkTagName.'" at offset '.$info['bonk'][$BonkTagName]['offset']; 0211 break; 0212 0213 } 0214 } 0215 0216 public static function BonkIsValidTagName($PossibleBonkTag, $ignorecase=false) { 0217 static $BonkIsValidTagName = array('BONK', 'INFO', ' ID3', 'META'); 0218 foreach ($BonkIsValidTagName as $validtagname) { 0219 if ($validtagname == $PossibleBonkTag) { 0220 return true; 0221 } elseif ($ignorecase && (strtolower($validtagname) == strtolower($PossibleBonkTag))) { 0222 return true; 0223 } 0224 } 0225 return false; 0226 } 0227 0228 }