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