[SPIP] v3.2.1-->v3.2.2
[lhc/web/www.git] / www / plugins-dist / medias / lib / getid3 / module.audio.bonk.php
1 <?php
2 /////////////////////////////////////////////////////////////////
3 /// getID3() by James Heinrich <info@getid3.org> //
4 // available at http://getid3.sourceforge.net //
5 // or http://www.getid3.org //
6 // also https://github.com/JamesHeinrich/getID3 //
7 /////////////////////////////////////////////////////////////////
8 // See readme.txt for more details //
9 /////////////////////////////////////////////////////////////////
10 // //
11 // module.audio.la.php //
12 // module for analyzing BONK audio files //
13 // dependencies: module.tag.id3v2.php (optional) //
14 // ///
15 /////////////////////////////////////////////////////////////////
16
17
18 class getid3_bonk extends getid3_handler
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 }