ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
Bonk.php
Go to the documentation of this file.
1 <?php
2 
3 namespace GetId3\Module\Audio;
4 
9 
12 // available at http://getid3.sourceforge.net //
13 // or http://www.getid3.org //
15 // See readme.txt for more details //
17 // //
18 // module.audio.la.php //
19 // module for analyzing BONK audio files //
20 // dependencies: module.tag.id3v2.php (optional) //
21 // ///
23 
31 class Bonk extends BaseHandler
32 {
37  public function analyze()
38  {
39  $info = &$this->getid3->info;
40 
41  // shortcut
42  $info['bonk'] = array();
43  $thisfile_bonk = &$info['bonk'];
44 
45  $thisfile_bonk['dataoffset'] = $info['avdataoffset'];
46  $thisfile_bonk['dataend'] = $info['avdataend'];
47 
48  if (!Helper::intValueSupported($thisfile_bonk['dataend'])) {
49 
50  $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';
51 
52  } else {
53 
54  // scan-from-end method, for v0.6 and higher
55  fseek($this->getid3->fp, $thisfile_bonk['dataend'] - 8, SEEK_SET);
56  $PossibleBonkTag = fread($this->getid3->fp, 8);
57  while ($this->BonkIsValidTagName(substr($PossibleBonkTag, 4, 4), true)) {
58  $BonkTagSize = Helper::LittleEndian2Int(substr($PossibleBonkTag, 0, 4));
59  fseek($this->getid3->fp, 0 - $BonkTagSize, SEEK_CUR);
60  $BonkTagOffset = ftell($this->getid3->fp);
61  $TagHeaderTest = fread($this->getid3->fp, 5);
62  if (($TagHeaderTest{0} != "\x00") || (substr($PossibleBonkTag, 4, 4) != strtolower(substr($PossibleBonkTag, 4, 4)))) {
63  $info['error'][] = 'Expecting "'.Helper::PrintHexBytes("\x00".strtoupper(substr($PossibleBonkTag, 4, 4))).'" at offset '.$BonkTagOffset.', found "'.Helper::PrintHexBytes($TagHeaderTest).'"';
64 
65  return false;
66  }
67  $BonkTagName = substr($TagHeaderTest, 1, 4);
68 
69  $thisfile_bonk[$BonkTagName]['size'] = $BonkTagSize;
70  $thisfile_bonk[$BonkTagName]['offset'] = $BonkTagOffset;
71  $this->HandleBonkTags($BonkTagName);
72  $NextTagEndOffset = $BonkTagOffset - 8;
73  if ($NextTagEndOffset < $thisfile_bonk['dataoffset']) {
74  if (empty($info['audio']['encoder'])) {
75  $info['audio']['encoder'] = 'Extended BONK v0.9+';
76  }
77 
78  return true;
79  }
80  fseek($this->getid3->fp, $NextTagEndOffset, SEEK_SET);
81  $PossibleBonkTag = fread($this->getid3->fp, 8);
82  }
83 
84  }
85 
86  // seek-from-beginning method for v0.4 and v0.5
87  if (empty($thisfile_bonk['BONK'])) {
88  fseek($this->getid3->fp, $thisfile_bonk['dataoffset'], SEEK_SET);
89  do {
90  $TagHeaderTest = fread($this->getid3->fp, 5);
91  switch ($TagHeaderTest) {
92  case "\x00".'BONK':
93  if (empty($info['audio']['encoder'])) {
94  $info['audio']['encoder'] = 'BONK v0.4';
95  }
96  break;
97 
98  case "\x00".'INFO':
99  $info['audio']['encoder'] = 'Extended BONK v0.5';
100  break;
101 
102  default:
103  break 2;
104  }
105  $BonkTagName = substr($TagHeaderTest, 1, 4);
106  $thisfile_bonk[$BonkTagName]['size'] = $thisfile_bonk['dataend'] - $thisfile_bonk['dataoffset'];
107  $thisfile_bonk[$BonkTagName]['offset'] = $thisfile_bonk['dataoffset'];
108  $this->HandleBonkTags($BonkTagName);
109 
110  } while (true);
111  }
112 
113  // parse META block for v0.6 - v0.8
114  if (empty($thisfile_bonk['INFO']) && isset($thisfile_bonk['META']['tags']['info'])) {
115  fseek($this->getid3->fp, $thisfile_bonk['META']['tags']['info'], SEEK_SET);
116  $TagHeaderTest = fread($this->getid3->fp, 5);
117  if ($TagHeaderTest == "\x00".'INFO') {
118  $info['audio']['encoder'] = 'Extended BONK v0.6 - v0.8';
119 
120  $BonkTagName = substr($TagHeaderTest, 1, 4);
121  $thisfile_bonk[$BonkTagName]['size'] = $thisfile_bonk['dataend'] - $thisfile_bonk['dataoffset'];
122  $thisfile_bonk[$BonkTagName]['offset'] = $thisfile_bonk['dataoffset'];
123  $this->HandleBonkTags($BonkTagName);
124  }
125  }
126 
127  if (empty($info['audio']['encoder'])) {
128  $info['audio']['encoder'] = 'Extended BONK v0.9+';
129  }
130  if (empty($thisfile_bonk['BONK'])) {
131  unset($info['bonk']);
132  }
133 
134  return true;
135 
136  }
137 
142  public function HandleBonkTags($BonkTagName)
143  {
144  $info = &$this->getid3->info;
145  switch ($BonkTagName) {
146  case 'BONK':
147  // shortcut
148  $thisfile_bonk_BONK = &$info['bonk']['BONK'];
149 
150  $BonkData = "\x00".'BONK'.fread($this->getid3->fp, 17);
151  $thisfile_bonk_BONK['version'] = Helper::LittleEndian2Int(substr($BonkData, 5, 1));
152  $thisfile_bonk_BONK['number_samples'] = Helper::LittleEndian2Int(substr($BonkData, 6, 4));
153  $thisfile_bonk_BONK['sample_rate'] = Helper::LittleEndian2Int(substr($BonkData, 10, 4));
154 
155  $thisfile_bonk_BONK['channels'] = Helper::LittleEndian2Int(substr($BonkData, 14, 1));
156  $thisfile_bonk_BONK['lossless'] = (bool) Helper::LittleEndian2Int(substr($BonkData, 15, 1));
157  $thisfile_bonk_BONK['joint_stereo'] = (bool) Helper::LittleEndian2Int(substr($BonkData, 16, 1));
158  $thisfile_bonk_BONK['number_taps'] = Helper::LittleEndian2Int(substr($BonkData, 17, 2));
159  $thisfile_bonk_BONK['downsampling_ratio'] = Helper::LittleEndian2Int(substr($BonkData, 19, 1));
160  $thisfile_bonk_BONK['samples_per_packet'] = Helper::LittleEndian2Int(substr($BonkData, 20, 2));
161 
162  $info['avdataoffset'] = $thisfile_bonk_BONK['offset'] + 5 + 17;
163  $info['avdataend'] = $thisfile_bonk_BONK['offset'] + $thisfile_bonk_BONK['size'];
164 
165  $info['fileformat'] = 'bonk';
166  $info['audio']['dataformat'] = 'bonk';
167  $info['audio']['bitrate_mode'] = 'vbr'; // assumed
168  $info['audio']['channels'] = $thisfile_bonk_BONK['channels'];
169  $info['audio']['sample_rate'] = $thisfile_bonk_BONK['sample_rate'];
170  $info['audio']['channelmode'] = ($thisfile_bonk_BONK['joint_stereo'] ? 'joint stereo' : 'stereo');
171  $info['audio']['lossless'] = $thisfile_bonk_BONK['lossless'];
172  $info['audio']['codec'] = 'bonk';
173 
174  $info['playtime_seconds'] = $thisfile_bonk_BONK['number_samples'] / ($thisfile_bonk_BONK['sample_rate'] * $thisfile_bonk_BONK['channels']);
175  if ($info['playtime_seconds'] > 0) {
176  $info['audio']['bitrate'] = (($info['bonk']['dataend'] - $info['bonk']['dataoffset']) * 8) / $info['playtime_seconds'];
177  }
178  break;
179 
180  case 'INFO':
181  // shortcut
182  $thisfile_bonk_INFO = &$info['bonk']['INFO'];
183 
184  $thisfile_bonk_INFO['version'] = Helper::LittleEndian2Int(fread($this->getid3->fp, 1));
185  $thisfile_bonk_INFO['entries_count'] = 0;
186  $NextInfoDataPair = fread($this->getid3->fp, 5);
187  if (!$this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) {
188  while (!feof($this->getid3->fp)) {
189  //$CurrentSeekInfo['offset'] = GetId3_lib::LittleEndian2Int(substr($NextInfoDataPair, 0, 4));
190  //$CurrentSeekInfo['nextbit'] = GetId3_lib::LittleEndian2Int(substr($NextInfoDataPair, 4, 1));
191  //$thisfile_bonk_INFO[] = $CurrentSeekInfo;
192 
193  $NextInfoDataPair = fread($this->getid3->fp, 5);
194  if ($this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) {
195  fseek($this->getid3->fp, -5, SEEK_CUR);
196  break;
197  }
198  $thisfile_bonk_INFO['entries_count']++;
199  }
200  }
201  break;
202 
203  case 'META':
204  $BonkData = "\x00".'META'.fread($this->getid3->fp, $info['bonk']['META']['size'] - 5);
205  $info['bonk']['META']['version'] = Helper::LittleEndian2Int(substr($BonkData, 5, 1));
206 
207  $MetaTagEntries = floor(((strlen($BonkData) - 8) - 6) / 8); // BonkData - xxxxmeta - ØMETA
208  $offset = 6;
209  for ($i = 0; $i < $MetaTagEntries; $i++) {
210  $MetaEntryTagName = substr($BonkData, $offset, 4);
211  $offset += 4;
212  $MetaEntryTagOffset = Helper::LittleEndian2Int(substr($BonkData, $offset, 4));
213  $offset += 4;
214  $info['bonk']['META']['tags'][$MetaEntryTagName] = $MetaEntryTagOffset;
215  }
216  break;
217 
218  case ' ID3':
219  $info['audio']['encoder'] = 'Extended BONK v0.9+';
220 
221  // ID3v2 checking is optional
222  if (class_exists('GetId3\\Module\\Tag\\Id3v2')) {
223  $getid3_temp = new GetId3Core();
224  $getid3_temp->openfile($this->getid3->filename);
225  $getid3_id3v2 = new Id3v2($getid3_temp);
226  $getid3_id3v2->StartingOffset = $info['bonk'][' ID3']['offset'] + 2;
227  $info['bonk'][' ID3']['valid'] = $getid3_id3v2->analyze();
228  if ($info['bonk'][' ID3']['valid']) {
229  $info['id3v2'] = $getid3_temp->info['id3v2'];
230  }
231  unset($getid3_temp, $getid3_id3v2);
232  }
233  break;
234 
235  default:
236  $info['warning'][] = 'Unexpected Bonk tag "'.$BonkTagName.'" at offset '.$info['bonk'][$BonkTagName]['offset'];
237  break;
238 
239  }
240  }
241 
249  public static function BonkIsValidTagName($PossibleBonkTag, $ignorecase=false)
250  {
251  static $BonkIsValidTagName = array('BONK', 'INFO', ' ID3', 'META');
252  foreach ($BonkIsValidTagName as $validtagname) {
253  if ($validtagname == $PossibleBonkTag) {
254  return true;
255  } elseif ($ignorecase && (strtolower($validtagname) == strtolower($PossibleBonkTag))) {
256  return true;
257  }
258  }
259 
260  return false;
261  }
262 
263 }
static PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8')
Definition: Helper.php:36
HandleBonkTags($BonkTagName)
Definition: Bonk.php:142
GetId3() by James Heinrich info@getid3.org //.
Definition: BaseHandler.php:25
GetId3() by James Heinrich info@getid3.org //.
Definition: Bonk.php:31
GetId3() by James Heinrich info@getid3.org //.
Definition: Id3v2.php:30
fseek($bytes, $whence=SEEK_SET)
$info
Definition: example_052.php:80
GetId3() by James Heinrich info@getid3.org //.
Definition: GetId3Core.php:25
Create styles array
The data for the language used.
static BonkIsValidTagName($PossibleBonkTag, $ignorecase=false)
array $BonkIsValidTagName
Definition: Bonk.php:249
static LittleEndian2Int($byteword, $signed=false)
Definition: Helper.php:413
static intValueSupported($num)
null $hasINT64
Definition: Helper.php:130