Another attempt to fix the http blog redirects.
[WebKit-https.git] / Websites / webkit.org / blog / wp-includes / ID3 / module.audio-video.riff.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-video.riff.php                                 //
12 // module for analyzing RIFF files                             //
13 // multiple formats supported by this module:                  //
14 //    Wave, AVI, AIFF/AIFC, (MP3,AC3)/RIFF, Wavpack v3, 8SVX   //
15 // dependencies: module.audio.mp3.php                          //
16 //               module.audio.ac3.php                          //
17 //               module.audio.dts.php                          //
18 //                                                            ///
19 /////////////////////////////////////////////////////////////////
20
21 /**
22 * @todo Parse AC-3/DTS audio inside WAVE correctly
23 * @todo Rewrite RIFF parser totally
24 */
25
26 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
27 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, true);
28 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.dts.php', __FILE__, true);
29
30 class getid3_riff extends getid3_handler {
31
32         protected $container = 'riff'; // default
33
34         public function Analyze() {
35                 $info = &$this->getid3->info;
36
37                 // initialize these values to an empty array, otherwise they default to NULL
38                 // and you can't append array values to a NULL value
39                 $info['riff'] = array('raw'=>array());
40
41                 // Shortcuts
42                 $thisfile_riff             = &$info['riff'];
43                 $thisfile_riff_raw         = &$thisfile_riff['raw'];
44                 $thisfile_audio            = &$info['audio'];
45                 $thisfile_video            = &$info['video'];
46                 $thisfile_audio_dataformat = &$thisfile_audio['dataformat'];
47                 $thisfile_riff_audio       = &$thisfile_riff['audio'];
48                 $thisfile_riff_video       = &$thisfile_riff['video'];
49
50                 $Original['avdataoffset'] = $info['avdataoffset'];
51                 $Original['avdataend']    = $info['avdataend'];
52
53                 $this->fseek($info['avdataoffset']);
54                 $RIFFheader = $this->fread(12);
55                 $offset = $this->ftell();
56                 $RIFFtype    = substr($RIFFheader, 0, 4);
57                 $RIFFsize    = substr($RIFFheader, 4, 4);
58                 $RIFFsubtype = substr($RIFFheader, 8, 4);
59
60                 switch ($RIFFtype) {
61
62                         case 'FORM':  // AIFF, AIFC
63                                 //$info['fileformat']   = 'aiff';
64                                 $this->container = 'aiff';
65                                 $thisfile_riff['header_size'] = $this->EitherEndian2Int($RIFFsize);
66                                 $thisfile_riff[$RIFFsubtype]  = $this->ParseRIFF($offset, ($offset + $thisfile_riff['header_size'] - 4));
67                                 break;
68
69                         case 'RIFF':  // AVI, WAV, etc
70                         case 'SDSS':  // SDSS is identical to RIFF, just renamed. Used by SmartSound QuickTracks (www.smartsound.com)
71                         case 'RMP3':  // RMP3 is identical to RIFF, just renamed. Used by [unknown program] when creating RIFF-MP3s
72                                 //$info['fileformat']   = 'riff';
73                                 $this->container = 'riff';
74                                 $thisfile_riff['header_size'] = $this->EitherEndian2Int($RIFFsize);
75                                 if ($RIFFsubtype == 'RMP3') {
76                                         // RMP3 is identical to WAVE, just renamed. Used by [unknown program] when creating RIFF-MP3s
77                                         $RIFFsubtype = 'WAVE';
78                                 }
79                                 if ($RIFFsubtype != 'AMV ') {
80                                         // AMV files are RIFF-AVI files with parts of the spec deliberately broken, such as chunk size fields hardcoded to zero (because players known in hardware that these fields are always a certain size
81                                         // Handled separately in ParseRIFFAMV()
82                                         $thisfile_riff[$RIFFsubtype]  = $this->ParseRIFF($offset, ($offset + $thisfile_riff['header_size'] - 4));
83                                 }
84                                 if (($info['avdataend'] - $info['filesize']) == 1) {
85                                         // LiteWave appears to incorrectly *not* pad actual output file
86                                         // to nearest WORD boundary so may appear to be short by one
87                                         // byte, in which case - skip warning
88                                         $info['avdataend'] = $info['filesize'];
89                                 }
90
91                                 $nextRIFFoffset = $Original['avdataoffset'] + 8 + $thisfile_riff['header_size']; // 8 = "RIFF" + 32-bit offset
92                                 while ($nextRIFFoffset < min($info['filesize'], $info['avdataend'])) {
93                                         try {
94                                                 $this->fseek($nextRIFFoffset);
95                                         } catch (getid3_exception $e) {
96                                                 if ($e->getCode() == 10) {
97                                                         //$this->warning('RIFF parser: '.$e->getMessage());
98                                                         $this->error('AVI extends beyond '.round(PHP_INT_MAX / 1073741824).'GB and PHP filesystem functions cannot read that far, playtime may be wrong');
99                                                         $this->warning('[avdataend] value may be incorrect, multiple AVIX chunks may be present');
100                                                         break;
101                                                 } else {
102                                                         throw $e;
103                                                 }
104                                         }
105                                         $nextRIFFheader = $this->fread(12);
106                                         if ($nextRIFFoffset == ($info['avdataend'] - 1)) {
107                                                 if (substr($nextRIFFheader, 0, 1) == "\x00") {
108                                                         // RIFF padded to WORD boundary, we're actually already at the end
109                                                         break;
110                                                 }
111                                         }
112                                         $nextRIFFheaderID =                         substr($nextRIFFheader, 0, 4);
113                                         $nextRIFFsize     = $this->EitherEndian2Int(substr($nextRIFFheader, 4, 4));
114                                         $nextRIFFtype     =                         substr($nextRIFFheader, 8, 4);
115                                         $chunkdata = array();
116                                         $chunkdata['offset'] = $nextRIFFoffset + 8;
117                                         $chunkdata['size']   = $nextRIFFsize;
118                                         $nextRIFFoffset = $chunkdata['offset'] + $chunkdata['size'];
119
120                                         switch ($nextRIFFheaderID) {
121                                                 case 'RIFF':
122                                                         $chunkdata['chunks'] = $this->ParseRIFF($chunkdata['offset'] + 4, $nextRIFFoffset);
123                                                         if (!isset($thisfile_riff[$nextRIFFtype])) {
124                                                                 $thisfile_riff[$nextRIFFtype] = array();
125                                                         }
126                                                         $thisfile_riff[$nextRIFFtype][] = $chunkdata;
127                                                         break;
128
129                                                 case 'AMV ':
130                                                         unset($info['riff']);
131                                                         $info['amv'] = $this->ParseRIFFAMV($chunkdata['offset'] + 4, $nextRIFFoffset);
132                                                         break;
133
134                                                 case 'JUNK':
135                                                         // ignore
136                                                         $thisfile_riff[$nextRIFFheaderID][] = $chunkdata;
137                                                         break;
138
139                                                 case 'IDVX':
140                                                         $info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunkdata['size']));
141                                                         break;
142
143                                                 default:
144                                                         if ($info['filesize'] == ($chunkdata['offset'] - 8 + 128)) {
145                                                                 $DIVXTAG = $nextRIFFheader.$this->fread(128 - 12);
146                                                                 if (substr($DIVXTAG, -7) == 'DIVXTAG') {
147                                                                         // DIVXTAG is supposed to be inside an IDVX chunk in a LIST chunk, but some bad encoders just slap it on the end of a file
148                                                                         $this->warning('Found wrongly-structured DIVXTAG at offset '.($this->ftell() - 128).', parsing anyway');
149                                                                         $info['divxtag']['comments'] = self::ParseDIVXTAG($DIVXTAG);
150                                                                         break 2;
151                                                                 }
152                                                         }
153                                                         $this->warning('Expecting "RIFF|JUNK|IDVX" at '.$nextRIFFoffset.', found "'.$nextRIFFheaderID.'" ('.getid3_lib::PrintHexBytes($nextRIFFheaderID).') - skipping rest of file');
154                                                         break 2;
155
156                                         }
157
158                                 }
159                                 if ($RIFFsubtype == 'WAVE') {
160                                         $thisfile_riff_WAVE = &$thisfile_riff['WAVE'];
161                                 }
162                                 break;
163
164                         default:
165                                 $this->error('Cannot parse RIFF (this is maybe not a RIFF / WAV / AVI file?) - expecting "FORM|RIFF|SDSS|RMP3" found "'.$RIFFsubtype.'" instead');
166                                 //unset($info['fileformat']);
167                                 return false;
168                 }
169
170                 $streamindex = 0;
171                 switch ($RIFFsubtype) {
172
173                         // http://en.wikipedia.org/wiki/Wav
174                         case 'WAVE':
175                                 $info['fileformat'] = 'wav';
176
177                                 if (empty($thisfile_audio['bitrate_mode'])) {
178                                         $thisfile_audio['bitrate_mode'] = 'cbr';
179                                 }
180                                 if (empty($thisfile_audio_dataformat)) {
181                                         $thisfile_audio_dataformat = 'wav';
182                                 }
183
184                                 if (isset($thisfile_riff_WAVE['data'][0]['offset'])) {
185                                         $info['avdataoffset'] = $thisfile_riff_WAVE['data'][0]['offset'] + 8;
186                                         $info['avdataend']    = $info['avdataoffset'] + $thisfile_riff_WAVE['data'][0]['size'];
187                                 }
188                                 if (isset($thisfile_riff_WAVE['fmt '][0]['data'])) {
189
190                                         $thisfile_riff_audio[$streamindex] = self::parseWAVEFORMATex($thisfile_riff_WAVE['fmt '][0]['data']);
191                                         $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag'];
192                                         if (!isset($thisfile_riff_audio[$streamindex]['bitrate']) || ($thisfile_riff_audio[$streamindex]['bitrate'] == 0)) {
193                                                 $info['error'][] = 'Corrupt RIFF file: bitrate_audio == zero';
194                                                 return false;
195                                         }
196                                         $thisfile_riff_raw['fmt '] = $thisfile_riff_audio[$streamindex]['raw'];
197                                         unset($thisfile_riff_audio[$streamindex]['raw']);
198                                         $thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex];
199
200                                         $thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]);
201                                         if (substr($thisfile_audio['codec'], 0, strlen('unknown: 0x')) == 'unknown: 0x') {
202                                                 $info['warning'][] = 'Audio codec = '.$thisfile_audio['codec'];
203                                         }
204                                         $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate'];
205
206                                         if (empty($info['playtime_seconds'])) { // may already be set (e.g. DTS-WAV)
207                                                 $info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $thisfile_audio['bitrate']);
208                                         }
209
210                                         $thisfile_audio['lossless'] = false;
211                                         if (isset($thisfile_riff_WAVE['data'][0]['offset']) && isset($thisfile_riff_raw['fmt ']['wFormatTag'])) {
212                                                 switch ($thisfile_riff_raw['fmt ']['wFormatTag']) {
213
214                                                         case 0x0001:  // PCM
215                                                                 $thisfile_audio['lossless'] = true;
216                                                                 break;
217
218                                                         case 0x2000:  // AC-3
219                                                                 $thisfile_audio_dataformat = 'ac3';
220                                                                 break;
221
222                                                         default:
223                                                                 // do nothing
224                                                                 break;
225
226                                                 }
227                                         }
228                                         $thisfile_audio['streams'][$streamindex]['wformattag']   = $thisfile_audio['wformattag'];
229                                         $thisfile_audio['streams'][$streamindex]['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
230                                         $thisfile_audio['streams'][$streamindex]['lossless']     = $thisfile_audio['lossless'];
231                                         $thisfile_audio['streams'][$streamindex]['dataformat']   = $thisfile_audio_dataformat;
232                                 }
233
234                                 if (isset($thisfile_riff_WAVE['rgad'][0]['data'])) {
235
236                                         // shortcuts
237                                         $rgadData = &$thisfile_riff_WAVE['rgad'][0]['data'];
238                                         $thisfile_riff_raw['rgad']    = array('track'=>array(), 'album'=>array());
239                                         $thisfile_riff_raw_rgad       = &$thisfile_riff_raw['rgad'];
240                                         $thisfile_riff_raw_rgad_track = &$thisfile_riff_raw_rgad['track'];
241                                         $thisfile_riff_raw_rgad_album = &$thisfile_riff_raw_rgad['album'];
242
243                                         $thisfile_riff_raw_rgad['fPeakAmplitude']      = getid3_lib::LittleEndian2Float(substr($rgadData, 0, 4));
244                                         $thisfile_riff_raw_rgad['nRadioRgAdjust']      =        $this->EitherEndian2Int(substr($rgadData, 4, 2));
245                                         $thisfile_riff_raw_rgad['nAudiophileRgAdjust'] =        $this->EitherEndian2Int(substr($rgadData, 6, 2));
246
247                                         $nRadioRgAdjustBitstring      = str_pad(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nRadioRgAdjust']), 16, '0', STR_PAD_LEFT);
248                                         $nAudiophileRgAdjustBitstring = str_pad(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nAudiophileRgAdjust']), 16, '0', STR_PAD_LEFT);
249                                         $thisfile_riff_raw_rgad_track['name']       = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 0, 3));
250                                         $thisfile_riff_raw_rgad_track['originator'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 3, 3));
251                                         $thisfile_riff_raw_rgad_track['signbit']    = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 6, 1));
252                                         $thisfile_riff_raw_rgad_track['adjustment'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 7, 9));
253                                         $thisfile_riff_raw_rgad_album['name']       = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 0, 3));
254                                         $thisfile_riff_raw_rgad_album['originator'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 3, 3));
255                                         $thisfile_riff_raw_rgad_album['signbit']    = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 6, 1));
256                                         $thisfile_riff_raw_rgad_album['adjustment'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 7, 9));
257
258                                         $thisfile_riff['rgad']['peakamplitude'] = $thisfile_riff_raw_rgad['fPeakAmplitude'];
259                                         if (($thisfile_riff_raw_rgad_track['name'] != 0) && ($thisfile_riff_raw_rgad_track['originator'] != 0)) {
260                                                 $thisfile_riff['rgad']['track']['name']            = getid3_lib::RGADnameLookup($thisfile_riff_raw_rgad_track['name']);
261                                                 $thisfile_riff['rgad']['track']['originator']      = getid3_lib::RGADoriginatorLookup($thisfile_riff_raw_rgad_track['originator']);
262                                                 $thisfile_riff['rgad']['track']['adjustment']      = getid3_lib::RGADadjustmentLookup($thisfile_riff_raw_rgad_track['adjustment'], $thisfile_riff_raw_rgad_track['signbit']);
263                                         }
264                                         if (($thisfile_riff_raw_rgad_album['name'] != 0) && ($thisfile_riff_raw_rgad_album['originator'] != 0)) {
265                                                 $thisfile_riff['rgad']['album']['name']       = getid3_lib::RGADnameLookup($thisfile_riff_raw_rgad_album['name']);
266                                                 $thisfile_riff['rgad']['album']['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_riff_raw_rgad_album['originator']);
267                                                 $thisfile_riff['rgad']['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($thisfile_riff_raw_rgad_album['adjustment'], $thisfile_riff_raw_rgad_album['signbit']);
268                                         }
269                                 }
270
271                                 if (isset($thisfile_riff_WAVE['fact'][0]['data'])) {
272                                         $thisfile_riff_raw['fact']['NumberOfSamples'] = $this->EitherEndian2Int(substr($thisfile_riff_WAVE['fact'][0]['data'], 0, 4));
273
274                                         // This should be a good way of calculating exact playtime,
275                                         // but some sample files have had incorrect number of samples,
276                                         // so cannot use this method
277
278                                         // if (!empty($thisfile_riff_raw['fmt ']['nSamplesPerSec'])) {
279                                         //     $info['playtime_seconds'] = (float) $thisfile_riff_raw['fact']['NumberOfSamples'] / $thisfile_riff_raw['fmt ']['nSamplesPerSec'];
280                                         // }
281                                 }
282                                 if (!empty($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'])) {
283                                         $thisfile_audio['bitrate'] = getid3_lib::CastAsInt($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'] * 8);
284                                 }
285
286                                 if (isset($thisfile_riff_WAVE['bext'][0]['data'])) {
287                                         // shortcut
288                                         $thisfile_riff_WAVE_bext_0 = &$thisfile_riff_WAVE['bext'][0];
289
290                                         $thisfile_riff_WAVE_bext_0['title']          =                         trim(substr($thisfile_riff_WAVE_bext_0['data'],   0, 256));
291                                         $thisfile_riff_WAVE_bext_0['author']         =                         trim(substr($thisfile_riff_WAVE_bext_0['data'], 256,  32));
292                                         $thisfile_riff_WAVE_bext_0['reference']      =                         trim(substr($thisfile_riff_WAVE_bext_0['data'], 288,  32));
293                                         $thisfile_riff_WAVE_bext_0['origin_date']    =                              substr($thisfile_riff_WAVE_bext_0['data'], 320,  10);
294                                         $thisfile_riff_WAVE_bext_0['origin_time']    =                              substr($thisfile_riff_WAVE_bext_0['data'], 330,   8);
295                                         $thisfile_riff_WAVE_bext_0['time_reference'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 338,   8));
296                                         $thisfile_riff_WAVE_bext_0['bwf_version']    = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 346,   1));
297                                         $thisfile_riff_WAVE_bext_0['reserved']       =                              substr($thisfile_riff_WAVE_bext_0['data'], 347, 254);
298                                         $thisfile_riff_WAVE_bext_0['coding_history'] =         explode("\r\n", trim(substr($thisfile_riff_WAVE_bext_0['data'], 601)));
299                                         if (preg_match('#^([0-9]{4}).([0-9]{2}).([0-9]{2})$#', $thisfile_riff_WAVE_bext_0['origin_date'], $matches_bext_date)) {
300                                                 if (preg_match('#^([0-9]{2}).([0-9]{2}).([0-9]{2})$#', $thisfile_riff_WAVE_bext_0['origin_time'], $matches_bext_time)) {
301                                                         list($dummy, $bext_timestamp['year'], $bext_timestamp['month'],  $bext_timestamp['day'])    = $matches_bext_date;
302                                                         list($dummy, $bext_timestamp['hour'], $bext_timestamp['minute'], $bext_timestamp['second']) = $matches_bext_time;
303                                                         $thisfile_riff_WAVE_bext_0['origin_date_unix'] = gmmktime($bext_timestamp['hour'], $bext_timestamp['minute'], $bext_timestamp['second'], $bext_timestamp['month'], $bext_timestamp['day'], $bext_timestamp['year']);
304                                                 } else {
305                                                         $info['warning'][] = 'RIFF.WAVE.BEXT.origin_time is invalid';
306                                                 }
307                                         } else {
308                                                 $info['warning'][] = 'RIFF.WAVE.BEXT.origin_date is invalid';
309                                         }
310                                         $thisfile_riff['comments']['author'][] = $thisfile_riff_WAVE_bext_0['author'];
311                                         $thisfile_riff['comments']['title'][]  = $thisfile_riff_WAVE_bext_0['title'];
312                                 }
313
314                                 if (isset($thisfile_riff_WAVE['MEXT'][0]['data'])) {
315                                         // shortcut
316                                         $thisfile_riff_WAVE_MEXT_0 = &$thisfile_riff_WAVE['MEXT'][0];
317
318                                         $thisfile_riff_WAVE_MEXT_0['raw']['sound_information']      = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 0, 2));
319                                         $thisfile_riff_WAVE_MEXT_0['flags']['homogenous']           = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0001);
320                                         if ($thisfile_riff_WAVE_MEXT_0['flags']['homogenous']) {
321                                                 $thisfile_riff_WAVE_MEXT_0['flags']['padding']          = ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0002) ? false : true;
322                                                 $thisfile_riff_WAVE_MEXT_0['flags']['22_or_44']         =        (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0004);
323                                                 $thisfile_riff_WAVE_MEXT_0['flags']['free_format']      =        (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0008);
324
325                                                 $thisfile_riff_WAVE_MEXT_0['nominal_frame_size']        = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 2, 2));
326                                         }
327                                         $thisfile_riff_WAVE_MEXT_0['anciliary_data_length']         = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 6, 2));
328                                         $thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def']     = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 8, 2));
329                                         $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_left']  = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0001);
330                                         $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_free']  = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0002);
331                                         $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_right'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0004);
332                                 }
333
334                                 if (isset($thisfile_riff_WAVE['cart'][0]['data'])) {
335                                         // shortcut
336                                         $thisfile_riff_WAVE_cart_0 = &$thisfile_riff_WAVE['cart'][0];
337
338                                         $thisfile_riff_WAVE_cart_0['version']              =                              substr($thisfile_riff_WAVE_cart_0['data'],   0,  4);
339                                         $thisfile_riff_WAVE_cart_0['title']                =                         trim(substr($thisfile_riff_WAVE_cart_0['data'],   4, 64));
340                                         $thisfile_riff_WAVE_cart_0['artist']               =                         trim(substr($thisfile_riff_WAVE_cart_0['data'],  68, 64));
341                                         $thisfile_riff_WAVE_cart_0['cut_id']               =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 132, 64));
342                                         $thisfile_riff_WAVE_cart_0['client_id']            =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 196, 64));
343                                         $thisfile_riff_WAVE_cart_0['category']             =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 260, 64));
344                                         $thisfile_riff_WAVE_cart_0['classification']       =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 324, 64));
345                                         $thisfile_riff_WAVE_cart_0['out_cue']              =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 388, 64));
346                                         $thisfile_riff_WAVE_cart_0['start_date']           =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 452, 10));
347                                         $thisfile_riff_WAVE_cart_0['start_time']           =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 462,  8));
348                                         $thisfile_riff_WAVE_cart_0['end_date']             =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 470, 10));
349                                         $thisfile_riff_WAVE_cart_0['end_time']             =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 480,  8));
350                                         $thisfile_riff_WAVE_cart_0['producer_app_id']      =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 488, 64));
351                                         $thisfile_riff_WAVE_cart_0['producer_app_version'] =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 552, 64));
352                                         $thisfile_riff_WAVE_cart_0['user_defined_text']    =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 616, 64));
353                                         $thisfile_riff_WAVE_cart_0['zero_db_reference']    = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_cart_0['data'], 680,  4), true);
354                                         for ($i = 0; $i < 8; $i++) {
355                                                 $thisfile_riff_WAVE_cart_0['post_time'][$i]['usage_fourcc'] =                  substr($thisfile_riff_WAVE_cart_0['data'], 684 + ($i * 8), 4);
356                                                 $thisfile_riff_WAVE_cart_0['post_time'][$i]['timer_value']  = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_cart_0['data'], 684 + ($i * 8) + 4, 4));
357                                         }
358                                         $thisfile_riff_WAVE_cart_0['url']              =                 trim(substr($thisfile_riff_WAVE_cart_0['data'],  748, 1024));
359                                         $thisfile_riff_WAVE_cart_0['tag_text']         = explode("\r\n", trim(substr($thisfile_riff_WAVE_cart_0['data'], 1772)));
360
361                                         $thisfile_riff['comments']['artist'][] = $thisfile_riff_WAVE_cart_0['artist'];
362                                         $thisfile_riff['comments']['title'][]  = $thisfile_riff_WAVE_cart_0['title'];
363                                 }
364
365                                 if (isset($thisfile_riff_WAVE['SNDM'][0]['data'])) {
366                                         // SoundMiner metadata
367
368                                         // shortcuts
369                                         $thisfile_riff_WAVE_SNDM_0      = &$thisfile_riff_WAVE['SNDM'][0];
370                                         $thisfile_riff_WAVE_SNDM_0_data = &$thisfile_riff_WAVE_SNDM_0['data'];
371                                         $SNDM_startoffset = 0;
372                                         $SNDM_endoffset   = $thisfile_riff_WAVE_SNDM_0['size'];
373
374                                         while ($SNDM_startoffset < $SNDM_endoffset) {
375                                                 $SNDM_thisTagOffset = 0;
376                                                 $SNDM_thisTagSize      = getid3_lib::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 4));
377                                                 $SNDM_thisTagOffset += 4;
378                                                 $SNDM_thisTagKey       =                           substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 4);
379                                                 $SNDM_thisTagOffset += 4;
380                                                 $SNDM_thisTagDataSize  = getid3_lib::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 2));
381                                                 $SNDM_thisTagOffset += 2;
382                                                 $SNDM_thisTagDataFlags = getid3_lib::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 2));
383                                                 $SNDM_thisTagOffset += 2;
384                                                 $SNDM_thisTagDataText =                            substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, $SNDM_thisTagDataSize);
385                                                 $SNDM_thisTagOffset += $SNDM_thisTagDataSize;
386
387                                                 if ($SNDM_thisTagSize != (4 + 4 + 2 + 2 + $SNDM_thisTagDataSize)) {
388                                                         $info['warning'][] = 'RIFF.WAVE.SNDM.data contains tag not expected length (expected: '.$SNDM_thisTagSize.', found: '.(4 + 4 + 2 + 2 + $SNDM_thisTagDataSize).') at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')';
389                                                         break;
390                                                 } elseif ($SNDM_thisTagSize <= 0) {
391                                                         $info['warning'][] = 'RIFF.WAVE.SNDM.data contains zero-size tag at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')';
392                                                         break;
393                                                 }
394                                                 $SNDM_startoffset += $SNDM_thisTagSize;
395
396                                                 $thisfile_riff_WAVE_SNDM_0['parsed_raw'][$SNDM_thisTagKey] = $SNDM_thisTagDataText;
397                                                 if ($parsedkey = self::waveSNDMtagLookup($SNDM_thisTagKey)) {
398                                                         $thisfile_riff_WAVE_SNDM_0['parsed'][$parsedkey] = $SNDM_thisTagDataText;
399                                                 } else {
400                                                         $info['warning'][] = 'RIFF.WAVE.SNDM contains unknown tag "'.$SNDM_thisTagKey.'" at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')';
401                                                 }
402                                         }
403
404                                         $tagmapping = array(
405                                                 'tracktitle'=>'title',
406                                                 'category'  =>'genre',
407                                                 'cdtitle'   =>'album',
408                                                 'tracktitle'=>'title',
409                                         );
410                                         foreach ($tagmapping as $fromkey => $tokey) {
411                                                 if (isset($thisfile_riff_WAVE_SNDM_0['parsed'][$fromkey])) {
412                                                         $thisfile_riff['comments'][$tokey][] = $thisfile_riff_WAVE_SNDM_0['parsed'][$fromkey];
413                                                 }
414                                         }
415                                 }
416
417                                 if (isset($thisfile_riff_WAVE['iXML'][0]['data'])) {
418                                         // requires functions simplexml_load_string and get_object_vars
419                                         if ($parsedXML = getid3_lib::XML2array($thisfile_riff_WAVE['iXML'][0]['data'])) {
420                                                 $thisfile_riff_WAVE['iXML'][0]['parsed'] = $parsedXML;
421                                                 if (isset($parsedXML['SPEED']['MASTER_SPEED'])) {
422                                                         @list($numerator, $denominator) = explode('/', $parsedXML['SPEED']['MASTER_SPEED']);
423                                                         $thisfile_riff_WAVE['iXML'][0]['master_speed'] = $numerator / ($denominator ? $denominator : 1000);
424                                                 }
425                                                 if (isset($parsedXML['SPEED']['TIMECODE_RATE'])) {
426                                                         @list($numerator, $denominator) = explode('/', $parsedXML['SPEED']['TIMECODE_RATE']);
427                                                         $thisfile_riff_WAVE['iXML'][0]['timecode_rate'] = $numerator / ($denominator ? $denominator : 1000);
428                                                 }
429                                                 if (isset($parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO']) && !empty($parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE']) && !empty($thisfile_riff_WAVE['iXML'][0]['timecode_rate'])) {
430                                                         $samples_since_midnight = floatval(ltrim($parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_HI'].$parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO'], '0'));
431                                                         $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] = $samples_since_midnight / $parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE'];
432                                                         $h = floor( $thisfile_riff_WAVE['iXML'][0]['timecode_seconds']       / 3600);
433                                                         $m = floor(($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600))      / 60);
434                                                         $s = floor( $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600) - ($m * 60));
435                                                         $f =       ($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600) - ($m * 60) - $s) * $thisfile_riff_WAVE['iXML'][0]['timecode_rate'];
436                                                         $thisfile_riff_WAVE['iXML'][0]['timecode_string']       = sprintf('%02d:%02d:%02d:%05.2f', $h, $m, $s,       $f);
437                                                         $thisfile_riff_WAVE['iXML'][0]['timecode_string_round'] = sprintf('%02d:%02d:%02d:%02d',   $h, $m, $s, round($f));
438                                                 }
439                                                 unset($parsedXML);
440                                         }
441                                 }
442
443
444
445                                 if (!isset($thisfile_audio['bitrate']) && isset($thisfile_riff_audio[$streamindex]['bitrate'])) {
446                                         $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate'];
447                                         $info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $thisfile_audio['bitrate']);
448                                 }
449
450                                 if (!empty($info['wavpack'])) {
451                                         $thisfile_audio_dataformat = 'wavpack';
452                                         $thisfile_audio['bitrate_mode'] = 'vbr';
453                                         $thisfile_audio['encoder']      = 'WavPack v'.$info['wavpack']['version'];
454
455                                         // Reset to the way it was - RIFF parsing will have messed this up
456                                         $info['avdataend']        = $Original['avdataend'];
457                                         $thisfile_audio['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
458
459                                         $this->fseek($info['avdataoffset'] - 44);
460                                         $RIFFdata = $this->fread(44);
461                                         $OrignalRIFFheaderSize = getid3_lib::LittleEndian2Int(substr($RIFFdata,  4, 4)) +  8;
462                                         $OrignalRIFFdataSize   = getid3_lib::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44;
463
464                                         if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) {
465                                                 $info['avdataend'] -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
466                                                 $this->fseek($info['avdataend']);
467                                                 $RIFFdata .= $this->fread($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
468                                         }
469
470                                         // move the data chunk after all other chunks (if any)
471                                         // so that the RIFF parser doesn't see EOF when trying
472                                         // to skip over the data chunk
473                                         $RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8);
474                                         $getid3_riff = new getid3_riff($this->getid3);
475                                         $getid3_riff->ParseRIFFdata($RIFFdata);
476                                         unset($getid3_riff);
477                                 }
478
479                                 if (isset($thisfile_riff_raw['fmt ']['wFormatTag'])) {
480                                         switch ($thisfile_riff_raw['fmt ']['wFormatTag']) {
481                                                 case 0x0001: // PCM
482                                                         if (!empty($info['ac3'])) {
483                                                                 // Dolby Digital WAV files masquerade as PCM-WAV, but they're not
484                                                                 $thisfile_audio['wformattag']  = 0x2000;
485                                                                 $thisfile_audio['codec']       = self::wFormatTagLookup($thisfile_audio['wformattag']);
486                                                                 $thisfile_audio['lossless']    = false;
487                                                                 $thisfile_audio['bitrate']     = $info['ac3']['bitrate'];
488                                                                 $thisfile_audio['sample_rate'] = $info['ac3']['sample_rate'];
489                                                         }
490                                                         if (!empty($info['dts'])) {
491                                                                 // Dolby DTS files masquerade as PCM-WAV, but they're not
492                                                                 $thisfile_audio['wformattag']  = 0x2001;
493                                                                 $thisfile_audio['codec']       = self::wFormatTagLookup($thisfile_audio['wformattag']);
494                                                                 $thisfile_audio['lossless']    = false;
495                                                                 $thisfile_audio['bitrate']     = $info['dts']['bitrate'];
496                                                                 $thisfile_audio['sample_rate'] = $info['dts']['sample_rate'];
497                                                         }
498                                                         break;
499                                                 case 0x08AE: // ClearJump LiteWave
500                                                         $thisfile_audio['bitrate_mode'] = 'vbr';
501                                                         $thisfile_audio_dataformat   = 'litewave';
502
503                                                         //typedef struct tagSLwFormat {
504                                                         //  WORD    m_wCompFormat;     // low byte defines compression method, high byte is compression flags
505                                                         //  DWORD   m_dwScale;         // scale factor for lossy compression
506                                                         //  DWORD   m_dwBlockSize;     // number of samples in encoded blocks
507                                                         //  WORD    m_wQuality;        // alias for the scale factor
508                                                         //  WORD    m_wMarkDistance;   // distance between marks in bytes
509                                                         //  WORD    m_wReserved;
510                                                         //
511                                                         //  //following paramters are ignored if CF_FILESRC is not set
512                                                         //  DWORD   m_dwOrgSize;       // original file size in bytes
513                                                         //  WORD    m_bFactExists;     // indicates if 'fact' chunk exists in the original file
514                                                         //  DWORD   m_dwRiffChunkSize; // riff chunk size in the original file
515                                                         //
516                                                         //  PCMWAVEFORMAT m_OrgWf;     // original wave format
517                                                         // }SLwFormat, *PSLwFormat;
518
519                                                         // shortcut
520                                                         $thisfile_riff['litewave']['raw'] = array();
521                                                         $riff_litewave     = &$thisfile_riff['litewave'];
522                                                         $riff_litewave_raw = &$riff_litewave['raw'];
523
524                                                         $flags = array(
525                                                                 'compression_method' => 1,
526                                                                 'compression_flags'  => 1,
527                                                                 'm_dwScale'          => 4,
528                                                                 'm_dwBlockSize'      => 4,
529                                                                 'm_wQuality'         => 2,
530                                                                 'm_wMarkDistance'    => 2,
531                                                                 'm_wReserved'        => 2,
532                                                                 'm_dwOrgSize'        => 4,
533                                                                 'm_bFactExists'      => 2,
534                                                                 'm_dwRiffChunkSize'  => 4,
535                                                         );
536                                                         $litewave_offset = 18;
537                                                         foreach ($flags as $flag => $length) {
538                                                                 $riff_litewave_raw[$flag] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], $litewave_offset, $length));
539                                                                 $litewave_offset += $length;
540                                                         }
541
542                                                         //$riff_litewave['quality_factor'] = intval(round((2000 - $riff_litewave_raw['m_dwScale']) / 20));
543                                                         $riff_litewave['quality_factor'] = $riff_litewave_raw['m_wQuality'];
544
545                                                         $riff_litewave['flags']['raw_source']    = ($riff_litewave_raw['compression_flags'] & 0x01) ? false : true;
546                                                         $riff_litewave['flags']['vbr_blocksize'] = ($riff_litewave_raw['compression_flags'] & 0x02) ? false : true;
547                                                         $riff_litewave['flags']['seekpoints']    =        (bool) ($riff_litewave_raw['compression_flags'] & 0x04);
548
549                                                         $thisfile_audio['lossless']        = (($riff_litewave_raw['m_wQuality'] == 100) ? true : false);
550                                                         $thisfile_audio['encoder_options'] = '-q'.$riff_litewave['quality_factor'];
551                                                         break;
552
553                                                 default:
554                                                         break;
555                                         }
556                                 }
557                                 if ($info['avdataend'] > $info['filesize']) {
558                                         switch (!empty($thisfile_audio_dataformat) ? $thisfile_audio_dataformat : '') {
559                                                 case 'wavpack': // WavPack
560                                                 case 'lpac':    // LPAC
561                                                 case 'ofr':     // OptimFROG
562                                                 case 'ofs':     // OptimFROG DualStream
563                                                         // lossless compressed audio formats that keep original RIFF headers - skip warning
564                                                         break;
565
566                                                 case 'litewave':
567                                                         if (($info['avdataend'] - $info['filesize']) == 1) {
568                                                                 // LiteWave appears to incorrectly *not* pad actual output file
569                                                                 // to nearest WORD boundary so may appear to be short by one
570                                                                 // byte, in which case - skip warning
571                                                         } else {
572                                                                 // Short by more than one byte, throw warning
573                                                                 $info['warning'][] = 'Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' (short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($info['filesize'] - $info['avdataoffset'])).' bytes)';
574                                                                 $info['avdataend'] = $info['filesize'];
575                                                         }
576                                                         break;
577
578                                                 default:
579                                                         if ((($info['avdataend'] - $info['filesize']) == 1) && (($thisfile_riff[$RIFFsubtype]['data'][0]['size'] % 2) == 0) && ((($info['filesize'] - $info['avdataoffset']) % 2) == 1)) {
580                                                                 // output file appears to be incorrectly *not* padded to nearest WORD boundary
581                                                                 // Output less severe warning
582                                                                 $info['warning'][] = 'File should probably be padded to nearest WORD boundary, but it is not (expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' therefore short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($info['filesize'] - $info['avdataoffset'])).' bytes)';
583                                                                 $info['avdataend'] = $info['filesize'];
584                                                         } else {
585                                                                 // Short by more than one byte, throw warning
586                                                                 $info['warning'][] = 'Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' (short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($info['filesize'] - $info['avdataoffset'])).' bytes)';
587                                                                 $info['avdataend'] = $info['filesize'];
588                                                         }
589                                                         break;
590                                         }
591                                 }
592                                 if (!empty($info['mpeg']['audio']['LAME']['audio_bytes'])) {
593                                         if ((($info['avdataend'] - $info['avdataoffset']) - $info['mpeg']['audio']['LAME']['audio_bytes']) == 1) {
594                                                 $info['avdataend']--;
595                                                 $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
596                                         }
597                                 }
598                                 if (isset($thisfile_audio_dataformat) && ($thisfile_audio_dataformat == 'ac3')) {
599                                         unset($thisfile_audio['bits_per_sample']);
600                                         if (!empty($info['ac3']['bitrate']) && ($info['ac3']['bitrate'] != $thisfile_audio['bitrate'])) {
601                                                 $thisfile_audio['bitrate'] = $info['ac3']['bitrate'];
602                                         }
603                                 }
604                                 break;
605
606                         // http://en.wikipedia.org/wiki/Audio_Video_Interleave
607                         case 'AVI ':
608                                 $info['fileformat'] = 'avi';
609                                 $info['mime_type']  = 'video/avi';
610
611                                 $thisfile_video['bitrate_mode'] = 'vbr'; // maybe not, but probably
612                                 $thisfile_video['dataformat']   = 'avi';
613
614                                 if (isset($thisfile_riff[$RIFFsubtype]['movi']['offset'])) {
615                                         $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['movi']['offset'] + 8;
616                                         if (isset($thisfile_riff['AVIX'])) {
617                                                 $info['avdataend'] = $thisfile_riff['AVIX'][(count($thisfile_riff['AVIX']) - 1)]['chunks']['movi']['offset'] + $thisfile_riff['AVIX'][(count($thisfile_riff['AVIX']) - 1)]['chunks']['movi']['size'];
618                                         } else {
619                                                 $info['avdataend'] = $thisfile_riff['AVI ']['movi']['offset'] + $thisfile_riff['AVI ']['movi']['size'];
620                                         }
621                                         if ($info['avdataend'] > $info['filesize']) {
622                                                 $info['warning'][] = 'Probably truncated file - expecting '.($info['avdataend'] - $info['avdataoffset']).' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' (short by '.($info['avdataend'] - $info['filesize']).' bytes)';
623                                                 $info['avdataend'] = $info['filesize'];
624                                         }
625                                 }
626
627                                 if (isset($thisfile_riff['AVI ']['hdrl']['strl']['indx'])) {
628                                         //$bIndexType = array(
629                                         //      0x00 => 'AVI_INDEX_OF_INDEXES',
630                                         //      0x01 => 'AVI_INDEX_OF_CHUNKS',
631                                         //      0x80 => 'AVI_INDEX_IS_DATA',
632                                         //);
633                                         //$bIndexSubtype = array(
634                                         //      0x01 => array(
635                                         //              0x01 => 'AVI_INDEX_2FIELD',
636                                         //      ),
637                                         //);
638                                         foreach ($thisfile_riff['AVI ']['hdrl']['strl']['indx'] as $streamnumber => $steamdataarray) {
639                                                 $ahsisd = &$thisfile_riff['AVI ']['hdrl']['strl']['indx'][$streamnumber]['data'];
640
641                                                 $thisfile_riff_raw['indx'][$streamnumber]['wLongsPerEntry'] = $this->EitherEndian2Int(substr($ahsisd,  0, 2));
642                                                 $thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType']  = $this->EitherEndian2Int(substr($ahsisd,  2, 1));
643                                                 $thisfile_riff_raw['indx'][$streamnumber]['bIndexType']     = $this->EitherEndian2Int(substr($ahsisd,  3, 1));
644                                                 $thisfile_riff_raw['indx'][$streamnumber]['nEntriesInUse']  = $this->EitherEndian2Int(substr($ahsisd,  4, 4));
645                                                 $thisfile_riff_raw['indx'][$streamnumber]['dwChunkId']      =                         substr($ahsisd,  8, 4);
646                                                 $thisfile_riff_raw['indx'][$streamnumber]['dwReserved']     = $this->EitherEndian2Int(substr($ahsisd, 12, 4));
647
648                                                 //$thisfile_riff_raw['indx'][$streamnumber]['bIndexType_name']    =    $bIndexType[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']];
649                                                 //$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType_name'] = $bIndexSubtype[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']][$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType']];
650
651                                                 unset($ahsisd);
652                                         }
653                                 }
654                                 if (isset($thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data'])) {
655                                         $avihData = $thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data'];
656
657                                         // shortcut
658                                         $thisfile_riff_raw['avih'] = array();
659                                         $thisfile_riff_raw_avih = &$thisfile_riff_raw['avih'];
660
661                                         $thisfile_riff_raw_avih['dwMicroSecPerFrame']    = $this->EitherEndian2Int(substr($avihData,  0, 4)); // frame display rate (or 0L)
662                                         if ($thisfile_riff_raw_avih['dwMicroSecPerFrame'] == 0) {
663                                                 $info['error'][] = 'Corrupt RIFF file: avih.dwMicroSecPerFrame == zero';
664                                                 return false;
665                                         }
666
667                                         $flags = array(
668                                                 'dwMaxBytesPerSec',       // max. transfer rate
669                                                 'dwPaddingGranularity',   // pad to multiples of this size; normally 2K.
670                                                 'dwFlags',                // the ever-present flags
671                                                 'dwTotalFrames',          // # frames in file
672                                                 'dwInitialFrames',        //
673                                                 'dwStreams',              //
674                                                 'dwSuggestedBufferSize',  //
675                                                 'dwWidth',                //
676                                                 'dwHeight',               //
677                                                 'dwScale',                //
678                                                 'dwRate',                 //
679                                                 'dwStart',                //
680                                                 'dwLength',               //
681                                         );
682                                         $avih_offset = 4;
683                                         foreach ($flags as $flag) {
684                                                 $thisfile_riff_raw_avih[$flag] = $this->EitherEndian2Int(substr($avihData, $avih_offset, 4));
685                                                 $avih_offset += 4;
686                                         }
687
688                                         $flags = array(
689                                                 'hasindex'     => 0x00000010,
690                                                 'mustuseindex' => 0x00000020,
691                                                 'interleaved'  => 0x00000100,
692                                                 'trustcktype'  => 0x00000800,
693                                                 'capturedfile' => 0x00010000,
694                                                 'copyrighted'  => 0x00020010,
695                                         );
696                     foreach ($flags as $flag => $value) {
697                                                 $thisfile_riff_raw_avih['flags'][$flag] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & $value);
698                                         }
699
700                                         // shortcut
701                                         $thisfile_riff_video[$streamindex] = array();
702                                         $thisfile_riff_video_current = &$thisfile_riff_video[$streamindex];
703
704                                         if ($thisfile_riff_raw_avih['dwWidth'] > 0) {
705                                                 $thisfile_riff_video_current['frame_width'] = $thisfile_riff_raw_avih['dwWidth'];
706                                                 $thisfile_video['resolution_x']             = $thisfile_riff_video_current['frame_width'];
707                                         }
708                                         if ($thisfile_riff_raw_avih['dwHeight'] > 0) {
709                                                 $thisfile_riff_video_current['frame_height'] = $thisfile_riff_raw_avih['dwHeight'];
710                                                 $thisfile_video['resolution_y']              = $thisfile_riff_video_current['frame_height'];
711                                         }
712                                         if ($thisfile_riff_raw_avih['dwTotalFrames'] > 0) {
713                                                 $thisfile_riff_video_current['total_frames'] = $thisfile_riff_raw_avih['dwTotalFrames'];
714                                                 $thisfile_video['total_frames']              = $thisfile_riff_video_current['total_frames'];
715                                         }
716
717                                         $thisfile_riff_video_current['frame_rate'] = round(1000000 / $thisfile_riff_raw_avih['dwMicroSecPerFrame'], 3);
718                                         $thisfile_video['frame_rate'] = $thisfile_riff_video_current['frame_rate'];
719                                 }
720                                 if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][0]['data'])) {
721                                         if (is_array($thisfile_riff['AVI ']['hdrl']['strl']['strh'])) {
722                                                 for ($i = 0; $i < count($thisfile_riff['AVI ']['hdrl']['strl']['strh']); $i++) {
723                                                         if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data'])) {
724                                                                 $strhData = $thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data'];
725                                                                 $strhfccType = substr($strhData,  0, 4);
726
727                                                                 if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data'])) {
728                                                                         $strfData = $thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data'];
729
730                                                                         // shortcut
731                                                                         $thisfile_riff_raw_strf_strhfccType_streamindex = &$thisfile_riff_raw['strf'][$strhfccType][$streamindex];
732
733                                                                         switch ($strhfccType) {
734                                                                                 case 'auds':
735                                                                                         $thisfile_audio['bitrate_mode'] = 'cbr';
736                                                                                         $thisfile_audio_dataformat      = 'wav';
737                                                                                         if (isset($thisfile_riff_audio) && is_array($thisfile_riff_audio)) {
738                                                                                                 $streamindex = count($thisfile_riff_audio);
739                                                                                         }
740
741                                                                                         $thisfile_riff_audio[$streamindex] = self::parseWAVEFORMATex($strfData);
742                                                                                         $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag'];
743
744                                                                                         // shortcut
745                                                                                         $thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex];
746                                                                                         $thisfile_audio_streams_currentstream = &$thisfile_audio['streams'][$streamindex];
747
748                                                                                         if ($thisfile_audio_streams_currentstream['bits_per_sample'] == 0) {
749                                                                                                 unset($thisfile_audio_streams_currentstream['bits_per_sample']);
750                                                                                         }
751                                                                                         $thisfile_audio_streams_currentstream['wformattag'] = $thisfile_audio_streams_currentstream['raw']['wFormatTag'];
752                                                                                         unset($thisfile_audio_streams_currentstream['raw']);
753
754                                                                                         // shortcut
755                                                                                         $thisfile_riff_raw['strf'][$strhfccType][$streamindex] = $thisfile_riff_audio[$streamindex]['raw'];
756
757                                                                                         unset($thisfile_riff_audio[$streamindex]['raw']);
758                                                                                         $thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]);
759
760                                                                                         $thisfile_audio['lossless'] = false;
761                                                                                         switch ($thisfile_riff_raw_strf_strhfccType_streamindex['wFormatTag']) {
762                                                                                                 case 0x0001:  // PCM
763                                                                                                         $thisfile_audio_dataformat  = 'wav';
764                                                                                                         $thisfile_audio['lossless'] = true;
765                                                                                                         break;
766
767                                                                                                 case 0x0050: // MPEG Layer 2 or Layer 1
768                                                                                                         $thisfile_audio_dataformat = 'mp2'; // Assume Layer-2
769                                                                                                         break;
770
771                                                                                                 case 0x0055: // MPEG Layer 3
772                                                                                                         $thisfile_audio_dataformat = 'mp3';
773                                                                                                         break;
774
775                                                                                                 case 0x00FF: // AAC
776                                                                                                         $thisfile_audio_dataformat = 'aac';
777                                                                                                         break;
778
779                                                                                                 case 0x0161: // Windows Media v7 / v8 / v9
780                                                                                                 case 0x0162: // Windows Media Professional v9
781                                                                                                 case 0x0163: // Windows Media Lossess v9
782                                                                                                         $thisfile_audio_dataformat = 'wma';
783                                                                                                         break;
784
785                                                                                                 case 0x2000: // AC-3
786                                                                                                         $thisfile_audio_dataformat = 'ac3';
787                                                                                                         break;
788
789                                                                                                 case 0x2001: // DTS
790                                                                                                         $thisfile_audio_dataformat = 'dts';
791                                                                                                         break;
792
793                                                                                                 default:
794                                                                                                         $thisfile_audio_dataformat = 'wav';
795                                                                                                         break;
796                                                                                         }
797                                                                                         $thisfile_audio_streams_currentstream['dataformat']   = $thisfile_audio_dataformat;
798                                                                                         $thisfile_audio_streams_currentstream['lossless']     = $thisfile_audio['lossless'];
799                                                                                         $thisfile_audio_streams_currentstream['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
800                                                                                         break;
801
802
803                                                                                 case 'iavs':
804                                                                                 case 'vids':
805                                                                                         // shortcut
806                                                                                         $thisfile_riff_raw['strh'][$i]                  = array();
807                                                                                         $thisfile_riff_raw_strh_current                 = &$thisfile_riff_raw['strh'][$i];
808
809                                                                                         $thisfile_riff_raw_strh_current['fccType']               =                         substr($strhData,  0, 4);  // same as $strhfccType;
810                                                                                         $thisfile_riff_raw_strh_current['fccHandler']            =                         substr($strhData,  4, 4);
811                                                                                         $thisfile_riff_raw_strh_current['dwFlags']               = $this->EitherEndian2Int(substr($strhData,  8, 4)); // Contains AVITF_* flags
812                                                                                         $thisfile_riff_raw_strh_current['wPriority']             = $this->EitherEndian2Int(substr($strhData, 12, 2));
813                                                                                         $thisfile_riff_raw_strh_current['wLanguage']             = $this->EitherEndian2Int(substr($strhData, 14, 2));
814                                                                                         $thisfile_riff_raw_strh_current['dwInitialFrames']       = $this->EitherEndian2Int(substr($strhData, 16, 4));
815                                                                                         $thisfile_riff_raw_strh_current['dwScale']               = $this->EitherEndian2Int(substr($strhData, 20, 4));
816                                                                                         $thisfile_riff_raw_strh_current['dwRate']                = $this->EitherEndian2Int(substr($strhData, 24, 4));
817                                                                                         $thisfile_riff_raw_strh_current['dwStart']               = $this->EitherEndian2Int(substr($strhData, 28, 4));
818                                                                                         $thisfile_riff_raw_strh_current['dwLength']              = $this->EitherEndian2Int(substr($strhData, 32, 4));
819                                                                                         $thisfile_riff_raw_strh_current['dwSuggestedBufferSize'] = $this->EitherEndian2Int(substr($strhData, 36, 4));
820                                                                                         $thisfile_riff_raw_strh_current['dwQuality']             = $this->EitherEndian2Int(substr($strhData, 40, 4));
821                                                                                         $thisfile_riff_raw_strh_current['dwSampleSize']          = $this->EitherEndian2Int(substr($strhData, 44, 4));
822                                                                                         $thisfile_riff_raw_strh_current['rcFrame']               = $this->EitherEndian2Int(substr($strhData, 48, 4));
823
824                                                                                         $thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_riff_raw_strh_current['fccHandler']);
825                                                                                         $thisfile_video['fourcc']             = $thisfile_riff_raw_strh_current['fccHandler'];
826                                                                                         if (!$thisfile_riff_video_current['codec'] && isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) && self::fourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
827                                                                                                 $thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']);
828                                                                                                 $thisfile_video['fourcc']             = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'];
829                                                                                         }
830                                                                                         $thisfile_video['codec']              = $thisfile_riff_video_current['codec'];
831                                                                                         $thisfile_video['pixel_aspect_ratio'] = (float) 1;
832                                                                                         switch ($thisfile_riff_raw_strh_current['fccHandler']) {
833                                                                                                 case 'HFYU': // Huffman Lossless Codec
834                                                                                                 case 'IRAW': // Intel YUV Uncompressed
835                                                                                                 case 'YUY2': // Uncompressed YUV 4:2:2
836                                                                                                         $thisfile_video['lossless'] = true;
837                                                                                                         break;
838
839                                                                                                 default:
840                                                                                                         $thisfile_video['lossless'] = false;
841                                                                                                         break;
842                                                                                         }
843
844                                                                                         switch ($strhfccType) {
845                                                                                                 case 'vids':
846                                                                                                         $thisfile_riff_raw_strf_strhfccType_streamindex = self::ParseBITMAPINFOHEADER(substr($strfData, 0, 40), ($this->container == 'riff'));
847                                                                                                         $thisfile_video['bits_per_sample'] = $thisfile_riff_raw_strf_strhfccType_streamindex['biBitCount'];
848
849                                                                                                         if ($thisfile_riff_video_current['codec'] == 'DV') {
850                                                                                                                 $thisfile_riff_video_current['dv_type'] = 2;
851                                                                                                         }
852                                                                                                         break;
853
854                                                                                                 case 'iavs':
855                                                                                                         $thisfile_riff_video_current['dv_type'] = 1;
856                                                                                                         break;
857                                                                                         }
858                                                                                         break;
859
860                                                                                 default:
861                                                                                         $info['warning'][] = 'Unhandled fccType for stream ('.$i.'): "'.$strhfccType.'"';
862                                                                                         break;
863
864                                                                         }
865                                                                 }
866                                                         }
867
868                                                         if (isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
869
870                                                                 $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'];
871                                                                 if (self::fourccLookup($thisfile_video['fourcc'])) {
872                                                                         $thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_video['fourcc']);
873                                                                         $thisfile_video['codec']              = $thisfile_riff_video_current['codec'];
874                                                                 }
875
876                                                                 switch ($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) {
877                                                                         case 'HFYU': // Huffman Lossless Codec
878                                                                         case 'IRAW': // Intel YUV Uncompressed
879                                                                         case 'YUY2': // Uncompressed YUV 4:2:2
880                                                                                 $thisfile_video['lossless']        = true;
881                                                                                 //$thisfile_video['bits_per_sample'] = 24;
882                                                                                 break;
883
884                                                                         default:
885                                                                                 $thisfile_video['lossless']        = false;
886                                                                                 //$thisfile_video['bits_per_sample'] = 24;
887                                                                                 break;
888                                                                 }
889
890                                                         }
891                                                 }
892                                         }
893                                 }
894                                 break;
895
896
897                         case 'AMV ':
898                                 $info['fileformat'] = 'amv';
899                                 $info['mime_type']  = 'video/amv';
900
901                                 $thisfile_video['bitrate_mode']    = 'vbr'; // it's MJPEG, presumably contant-quality encoding, thereby VBR
902                                 $thisfile_video['dataformat']      = 'mjpeg';
903                                 $thisfile_video['codec']           = 'mjpeg';
904                                 $thisfile_video['lossless']        = false;
905                                 $thisfile_video['bits_per_sample'] = 24;
906
907                                 $thisfile_audio['dataformat']   = 'adpcm';
908                                 $thisfile_audio['lossless']     = false;
909                                 break;
910
911
912                         // http://en.wikipedia.org/wiki/CD-DA
913                         case 'CDDA':
914                                 $info['fileformat'] = 'cda';
915                             unset($info['mime_type']);
916
917                                 $thisfile_audio_dataformat      = 'cda';
918
919                                 $info['avdataoffset'] = 44;
920
921                                 if (isset($thisfile_riff['CDDA']['fmt '][0]['data'])) {
922                                         // shortcut
923                                         $thisfile_riff_CDDA_fmt_0 = &$thisfile_riff['CDDA']['fmt '][0];
924
925                                         $thisfile_riff_CDDA_fmt_0['unknown1']           = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'],  0, 2));
926                                         $thisfile_riff_CDDA_fmt_0['track_num']          = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'],  2, 2));
927                                         $thisfile_riff_CDDA_fmt_0['disc_id']            = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'],  4, 4));
928                                         $thisfile_riff_CDDA_fmt_0['start_offset_frame'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'],  8, 4));
929                                         $thisfile_riff_CDDA_fmt_0['playtime_frames']    = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 12, 4));
930                                         $thisfile_riff_CDDA_fmt_0['unknown6']           = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 16, 4));
931                                         $thisfile_riff_CDDA_fmt_0['unknown7']           = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 20, 4));
932
933                                         $thisfile_riff_CDDA_fmt_0['start_offset_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['start_offset_frame'] / 75;
934                                         $thisfile_riff_CDDA_fmt_0['playtime_seconds']     = (float) $thisfile_riff_CDDA_fmt_0['playtime_frames'] / 75;
935                                         $info['comments']['track']                = $thisfile_riff_CDDA_fmt_0['track_num'];
936                                         $info['playtime_seconds']                 = $thisfile_riff_CDDA_fmt_0['playtime_seconds'];
937
938                                         // hardcoded data for CD-audio
939                                         $thisfile_audio['lossless']        = true;
940                                         $thisfile_audio['sample_rate']     = 44100;
941                                         $thisfile_audio['channels']        = 2;
942                                         $thisfile_audio['bits_per_sample'] = 16;
943                                         $thisfile_audio['bitrate']         = $thisfile_audio['sample_rate'] * $thisfile_audio['channels'] * $thisfile_audio['bits_per_sample'];
944                                         $thisfile_audio['bitrate_mode']    = 'cbr';
945                                 }
946                                 break;
947
948             // http://en.wikipedia.org/wiki/AIFF
949                         case 'AIFF':
950                         case 'AIFC':
951                                 $info['fileformat'] = 'aiff';
952                                 $info['mime_type']  = 'audio/x-aiff';
953
954                                 $thisfile_audio['bitrate_mode'] = 'cbr';
955                                 $thisfile_audio_dataformat      = 'aiff';
956                                 $thisfile_audio['lossless']     = true;
957
958                                 if (isset($thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'])) {
959                                         $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'] + 8;
960                                         $info['avdataend']    = $info['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['SSND'][0]['size'];
961                                         if ($info['avdataend'] > $info['filesize']) {
962                                                 if (($info['avdataend'] == ($info['filesize'] + 1)) && (($info['filesize'] % 2) == 1)) {
963                                                         // structures rounded to 2-byte boundary, but dumb encoders
964                                                         // forget to pad end of file to make this actually work
965                                                 } else {
966                                                         $info['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['SSND'][0]['size'].' bytes of audio data, only '.($info['filesize'] - $info['avdataoffset']).' bytes found';
967                                                 }
968                                                 $info['avdataend'] = $info['filesize'];
969                                         }
970                                 }
971
972                                 if (isset($thisfile_riff[$RIFFsubtype]['COMM'][0]['data'])) {
973
974                                         // shortcut
975                                         $thisfile_riff_RIFFsubtype_COMM_0_data = &$thisfile_riff[$RIFFsubtype]['COMM'][0]['data'];
976
977                                         $thisfile_riff_audio['channels']         =         getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data,  0,  2), true);
978                                         $thisfile_riff_audio['total_samples']    =         getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data,  2,  4), false);
979                                         $thisfile_riff_audio['bits_per_sample']  =         getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data,  6,  2), true);
980                                         $thisfile_riff_audio['sample_rate']      = (int) getid3_lib::BigEndian2Float(substr($thisfile_riff_RIFFsubtype_COMM_0_data,  8, 10));
981
982                                         if ($thisfile_riff[$RIFFsubtype]['COMM'][0]['size'] > 18) {
983                                                 $thisfile_riff_audio['codec_fourcc'] =                                   substr($thisfile_riff_RIFFsubtype_COMM_0_data, 18,  4);
984                                                 $CodecNameSize                       =         getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 22,  1), false);
985                                                 $thisfile_riff_audio['codec_name']   =                                   substr($thisfile_riff_RIFFsubtype_COMM_0_data, 23,  $CodecNameSize);
986                                                 switch ($thisfile_riff_audio['codec_name']) {
987                                                         case 'NONE':
988                                                                 $thisfile_audio['codec']    = 'Pulse Code Modulation (PCM)';
989                                                                 $thisfile_audio['lossless'] = true;
990                                                                 break;
991
992                                                         case '':
993                                                                 switch ($thisfile_riff_audio['codec_fourcc']) {
994                                                                         // http://developer.apple.com/qa/snd/snd07.html
995                                                                         case 'sowt':
996                                                                                 $thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Little-Endian PCM';
997                                                                                 $thisfile_audio['lossless'] = true;
998                                                                                 break;
999
1000                                                                         case 'twos':
1001                                                                                 $thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Big-Endian PCM';
1002                                                                                 $thisfile_audio['lossless'] = true;
1003                                                                                 break;
1004
1005                                                                         default:
1006                                                                                 break;
1007                                                                 }
1008                                                                 break;
1009
1010                                                         default:
1011                                                                 $thisfile_audio['codec']    = $thisfile_riff_audio['codec_name'];
1012                                                                 $thisfile_audio['lossless'] = false;
1013                                                                 break;
1014                                                 }
1015                                         }
1016
1017                                         $thisfile_audio['channels']        = $thisfile_riff_audio['channels'];
1018                                         if ($thisfile_riff_audio['bits_per_sample'] > 0) {
1019                                                 $thisfile_audio['bits_per_sample'] = $thisfile_riff_audio['bits_per_sample'];
1020                                         }
1021                                         $thisfile_audio['sample_rate']     = $thisfile_riff_audio['sample_rate'];
1022                                         if ($thisfile_audio['sample_rate'] == 0) {
1023                                                 $info['error'][] = 'Corrupted AIFF file: sample_rate == zero';
1024                                                 return false;
1025                                         }
1026                                         $info['playtime_seconds'] = $thisfile_riff_audio['total_samples'] / $thisfile_audio['sample_rate'];
1027                                 }
1028
1029                                 if (isset($thisfile_riff[$RIFFsubtype]['COMT'])) {
1030                                         $offset = 0;
1031                                         $CommentCount                                   = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false);
1032                                         $offset += 2;
1033                                         for ($i = 0; $i < $CommentCount; $i++) {
1034                                                 $info['comments_raw'][$i]['timestamp']      = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 4), false);
1035                                                 $offset += 4;
1036                                                 $info['comments_raw'][$i]['marker_id']      = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), true);
1037                                                 $offset += 2;
1038                                                 $CommentLength                              = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false);
1039                                                 $offset += 2;
1040                                                 $info['comments_raw'][$i]['comment']        =                           substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, $CommentLength);
1041                                                 $offset += $CommentLength;
1042
1043                                                 $info['comments_raw'][$i]['timestamp_unix'] = getid3_lib::DateMac2Unix($info['comments_raw'][$i]['timestamp']);
1044                                                 $thisfile_riff['comments']['comment'][] = $info['comments_raw'][$i]['comment'];
1045                                         }
1046                                 }
1047
1048                                 $CommentsChunkNames = array('NAME'=>'title', 'author'=>'artist', '(c) '=>'copyright', 'ANNO'=>'comment');
1049                                 foreach ($CommentsChunkNames as $key => $value) {
1050                                         if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) {
1051                                                 $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data'];
1052                                         }
1053                                 }
1054 /*
1055                                 if (isset($thisfile_riff[$RIFFsubtype]['ID3 '])) {
1056                                         getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
1057                                         $getid3_temp = new getID3();
1058                                         $getid3_temp->openfile($this->getid3->filename);
1059                                         $getid3_id3v2 = new getid3_id3v2($getid3_temp);
1060                                         $getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['ID3 '][0]['offset'] + 8;
1061                                         if ($thisfile_riff[$RIFFsubtype]['ID3 '][0]['valid'] = $getid3_id3v2->Analyze()) {
1062                                                 $info['id3v2'] = $getid3_temp->info['id3v2'];
1063                                         }
1064                                         unset($getid3_temp, $getid3_id3v2);
1065                                 }
1066 */
1067                                 break;
1068
1069                         // http://en.wikipedia.org/wiki/8SVX
1070                         case '8SVX':
1071                                 $info['fileformat'] = '8svx';
1072                                 $info['mime_type']  = 'audio/8svx';
1073
1074                                 $thisfile_audio['bitrate_mode']    = 'cbr';
1075                                 $thisfile_audio_dataformat         = '8svx';
1076                                 $thisfile_audio['bits_per_sample'] = 8;
1077                                 $thisfile_audio['channels']        = 1; // overridden below, if need be
1078
1079                                 if (isset($thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'])) {
1080                                         $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'] + 8;
1081                                         $info['avdataend']    = $info['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['BODY'][0]['size'];
1082                                         if ($info['avdataend'] > $info['filesize']) {
1083                                                 $info['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['BODY'][0]['size'].' bytes of audio data, only '.($info['filesize'] - $info['avdataoffset']).' bytes found';
1084                                         }
1085                                 }
1086
1087                                 if (isset($thisfile_riff[$RIFFsubtype]['VHDR'][0]['offset'])) {
1088                                         // shortcut
1089                                         $thisfile_riff_RIFFsubtype_VHDR_0 = &$thisfile_riff[$RIFFsubtype]['VHDR'][0];
1090
1091                                         $thisfile_riff_RIFFsubtype_VHDR_0['oneShotHiSamples']  =   getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'],  0, 4));
1092                                         $thisfile_riff_RIFFsubtype_VHDR_0['repeatHiSamples']   =   getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'],  4, 4));
1093                                         $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerHiCycle'] =   getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'],  8, 4));
1094                                         $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec']     =   getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 12, 2));
1095                                         $thisfile_riff_RIFFsubtype_VHDR_0['ctOctave']          =   getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 14, 1));
1096                                         $thisfile_riff_RIFFsubtype_VHDR_0['sCompression']      =   getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 15, 1));
1097                                         $thisfile_riff_RIFFsubtype_VHDR_0['Volume']            = getid3_lib::FixedPoint16_16(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 16, 4));
1098
1099                                         $thisfile_audio['sample_rate'] = $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec'];
1100
1101                                         switch ($thisfile_riff_RIFFsubtype_VHDR_0['sCompression']) {
1102                                                 case 0:
1103                                                         $thisfile_audio['codec']    = 'Pulse Code Modulation (PCM)';
1104                                                         $thisfile_audio['lossless'] = true;
1105                                                         $ActualBitsPerSample        = 8;
1106                                                         break;
1107
1108                                                 case 1:
1109                                                         $thisfile_audio['codec']    = 'Fibonacci-delta encoding';
1110                                                         $thisfile_audio['lossless'] = false;
1111                                                         $ActualBitsPerSample        = 4;
1112                                                         break;
1113
1114                                                 default:
1115                                                         $info['warning'][] = 'Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "'.sCompression.'"';
1116                                                         break;
1117                                         }
1118                                 }
1119
1120                                 if (isset($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'])) {
1121                                         $ChannelsIndex = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'], 0, 4));
1122                                         switch ($ChannelsIndex) {
1123                                                 case 6: // Stereo
1124                                                         $thisfile_audio['channels'] = 2;
1125                                                         break;
1126
1127                                                 case 2: // Left channel only
1128                                                 case 4: // Right channel only
1129                                                         $thisfile_audio['channels'] = 1;
1130                                                         break;
1131
1132                                                 default:
1133                                                         $info['warning'][] = 'Unexpected value in 8SVX.CHAN chunk - expecting 2 or 4 or 6, found "'.$ChannelsIndex.'"';
1134                                                         break;
1135                                         }
1136
1137                                 }
1138
1139                                 $CommentsChunkNames = array('NAME'=>'title', 'author'=>'artist', '(c) '=>'copyright', 'ANNO'=>'comment');
1140                                 foreach ($CommentsChunkNames as $key => $value) {
1141                                         if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) {
1142                                                 $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data'];
1143                                         }
1144                                 }
1145
1146                                 $thisfile_audio['bitrate'] = $thisfile_audio['sample_rate'] * $ActualBitsPerSample * $thisfile_audio['channels'];
1147                                 if (!empty($thisfile_audio['bitrate'])) {
1148                                         $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) / ($thisfile_audio['bitrate'] / 8);
1149                                 }
1150                                 break;
1151
1152                         case 'CDXA':
1153                                 $info['fileformat'] = 'vcd'; // Asume Video CD
1154                                 $info['mime_type']  = 'video/mpeg';
1155
1156                                 if (!empty($thisfile_riff['CDXA']['data'][0]['size'])) {
1157                                         getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.mpeg.php', __FILE__, true);
1158
1159                                         $getid3_temp = new getID3();
1160                                         $getid3_temp->openfile($this->getid3->filename);
1161                                         $getid3_mpeg = new getid3_mpeg($getid3_temp);
1162                                         $getid3_mpeg->Analyze();
1163                                         if (empty($getid3_temp->info['error'])) {
1164                                                 $info['audio']   = $getid3_temp->info['audio'];
1165                                                 $info['video']   = $getid3_temp->info['video'];
1166                                                 $info['mpeg']    = $getid3_temp->info['mpeg'];
1167                                                 $info['warning'] = $getid3_temp->info['warning'];
1168                                         }
1169                                         unset($getid3_temp, $getid3_mpeg);
1170                                 }
1171                                 break;
1172
1173
1174                         default:
1175                                 $info['error'][] = 'Unknown RIFF type: expecting one of (WAVE|RMP3|AVI |CDDA|AIFF|AIFC|8SVX|CDXA), found "'.$RIFFsubtype.'" instead';
1176                                 //unset($info['fileformat']);
1177                 }
1178
1179                 switch ($RIFFsubtype) {
1180                         case 'WAVE':
1181                         case 'AIFF':
1182                         case 'AIFC':
1183                                 $ID3v2_key_good = 'id3 ';
1184                                 $ID3v2_keys_bad = array('ID3 ', 'tag ');
1185                                 foreach ($ID3v2_keys_bad as $ID3v2_key_bad) {
1186                                         if (isset($thisfile_riff[$RIFFsubtype][$ID3v2_key_bad]) && !array_key_exists($ID3v2_key_good, $thisfile_riff[$RIFFsubtype])) {
1187                                                 $thisfile_riff[$RIFFsubtype][$ID3v2_key_good] = $thisfile_riff[$RIFFsubtype][$ID3v2_key_bad];
1188                                                 $info['warning'][] = 'mapping "'.$ID3v2_key_bad.'" chunk to "'.$ID3v2_key_good.'"';
1189                                         }
1190                                 }
1191
1192                                 if (isset($thisfile_riff[$RIFFsubtype]['id3 '])) {
1193                                         getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
1194
1195                                         $getid3_temp = new getID3();
1196                                         $getid3_temp->openfile($this->getid3->filename);
1197                                         $getid3_id3v2 = new getid3_id3v2($getid3_temp);
1198                                         $getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['id3 '][0]['offset'] + 8;
1199                                         if ($thisfile_riff[$RIFFsubtype]['id3 '][0]['valid'] = $getid3_id3v2->Analyze()) {
1200                                                 $info['id3v2'] = $getid3_temp->info['id3v2'];
1201                                         }
1202                                         unset($getid3_temp, $getid3_id3v2);
1203                                 }
1204                                 break;
1205                 }
1206
1207                 if (isset($thisfile_riff_WAVE['DISP']) && is_array($thisfile_riff_WAVE['DISP'])) {
1208                         $thisfile_riff['comments']['title'][] = trim(substr($thisfile_riff_WAVE['DISP'][count($thisfile_riff_WAVE['DISP']) - 1]['data'], 4));
1209                 }
1210                 if (isset($thisfile_riff_WAVE['INFO']) && is_array($thisfile_riff_WAVE['INFO'])) {
1211                         self::parseComments($thisfile_riff_WAVE['INFO'], $thisfile_riff['comments']);
1212                 }
1213                 if (isset($thisfile_riff['AVI ']['INFO']) && is_array($thisfile_riff['AVI ']['INFO'])) {
1214                         self::parseComments($thisfile_riff['AVI ']['INFO'], $thisfile_riff['comments']);
1215                 }
1216
1217                 if (empty($thisfile_audio['encoder']) && !empty($info['mpeg']['audio']['LAME']['short_version'])) {
1218                         $thisfile_audio['encoder'] = $info['mpeg']['audio']['LAME']['short_version'];
1219                 }
1220
1221                 if (!isset($info['playtime_seconds'])) {
1222                         $info['playtime_seconds'] = 0;
1223                 }
1224                 if (isset($thisfile_riff_raw['strh'][0]['dwLength']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) {
1225                         // needed for >2GB AVIs where 'avih' chunk only lists number of frames in that chunk, not entire movie
1226                         $info['playtime_seconds'] = $thisfile_riff_raw['strh'][0]['dwLength'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000);
1227                 } elseif (isset($thisfile_riff_raw['avih']['dwTotalFrames']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) {
1228                         $info['playtime_seconds'] = $thisfile_riff_raw['avih']['dwTotalFrames'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000);
1229                 }
1230
1231                 if ($info['playtime_seconds'] > 0) {
1232                         if (isset($thisfile_riff_audio) && isset($thisfile_riff_video)) {
1233
1234                                 if (!isset($info['bitrate'])) {
1235                                         $info['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1236                                 }
1237
1238                         } elseif (isset($thisfile_riff_audio) && !isset($thisfile_riff_video)) {
1239
1240                                 if (!isset($thisfile_audio['bitrate'])) {
1241                                         $thisfile_audio['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1242                                 }
1243
1244                         } elseif (!isset($thisfile_riff_audio) && isset($thisfile_riff_video)) {
1245
1246                                 if (!isset($thisfile_video['bitrate'])) {
1247                                         $thisfile_video['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1248                                 }
1249
1250                         }
1251                 }
1252
1253
1254                 if (isset($thisfile_riff_video) && isset($thisfile_audio['bitrate']) && ($thisfile_audio['bitrate'] > 0) && ($info['playtime_seconds'] > 0)) {
1255
1256                         $info['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1257                         $thisfile_audio['bitrate'] = 0;
1258                         $thisfile_video['bitrate'] = $info['bitrate'];
1259                         foreach ($thisfile_riff_audio as $channelnumber => $audioinfoarray) {
1260                                 $thisfile_video['bitrate'] -= $audioinfoarray['bitrate'];
1261                                 $thisfile_audio['bitrate'] += $audioinfoarray['bitrate'];
1262                         }
1263                         if ($thisfile_video['bitrate'] <= 0) {
1264                                 unset($thisfile_video['bitrate']);
1265                         }
1266                         if ($thisfile_audio['bitrate'] <= 0) {
1267                                 unset($thisfile_audio['bitrate']);
1268                         }
1269                 }
1270
1271                 if (isset($info['mpeg']['audio'])) {
1272                         $thisfile_audio_dataformat      = 'mp'.$info['mpeg']['audio']['layer'];
1273                         $thisfile_audio['sample_rate']  = $info['mpeg']['audio']['sample_rate'];
1274                         $thisfile_audio['channels']     = $info['mpeg']['audio']['channels'];
1275                         $thisfile_audio['bitrate']      = $info['mpeg']['audio']['bitrate'];
1276                         $thisfile_audio['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
1277                         if (!empty($info['mpeg']['audio']['codec'])) {
1278                                 $thisfile_audio['codec'] = $info['mpeg']['audio']['codec'].' '.$thisfile_audio['codec'];
1279                         }
1280                         if (!empty($thisfile_audio['streams'])) {
1281                                 foreach ($thisfile_audio['streams'] as $streamnumber => $streamdata) {
1282                                         if ($streamdata['dataformat'] == $thisfile_audio_dataformat) {
1283                                                 $thisfile_audio['streams'][$streamnumber]['sample_rate']  = $thisfile_audio['sample_rate'];
1284                                                 $thisfile_audio['streams'][$streamnumber]['channels']     = $thisfile_audio['channels'];
1285                                                 $thisfile_audio['streams'][$streamnumber]['bitrate']      = $thisfile_audio['bitrate'];
1286                                                 $thisfile_audio['streams'][$streamnumber]['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
1287                                                 $thisfile_audio['streams'][$streamnumber]['codec']        = $thisfile_audio['codec'];
1288                                         }
1289                                 }
1290                         }
1291                         $getid3_mp3 = new getid3_mp3($this->getid3);
1292                         $thisfile_audio['encoder_options'] = $getid3_mp3->GuessEncoderOptions();
1293                         unset($getid3_mp3);
1294                 }
1295
1296
1297                 if (!empty($thisfile_riff_raw['fmt ']['wBitsPerSample']) && ($thisfile_riff_raw['fmt ']['wBitsPerSample'] > 0)) {
1298                         switch ($thisfile_audio_dataformat) {
1299                                 case 'ac3':
1300                                         // ignore bits_per_sample
1301                                         break;
1302
1303                                 default:
1304                                         $thisfile_audio['bits_per_sample'] = $thisfile_riff_raw['fmt ']['wBitsPerSample'];
1305                                         break;
1306                         }
1307                 }
1308
1309
1310                 if (empty($thisfile_riff_raw)) {
1311                         unset($thisfile_riff['raw']);
1312                 }
1313                 if (empty($thisfile_riff_audio)) {
1314                         unset($thisfile_riff['audio']);
1315                 }
1316                 if (empty($thisfile_riff_video)) {
1317                         unset($thisfile_riff['video']);
1318                 }
1319
1320                 return true;
1321         }
1322
1323         public function ParseRIFFAMV($startoffset, $maxoffset) {
1324                 // AMV files are RIFF-AVI files with parts of the spec deliberately broken, such as chunk size fields hardcoded to zero (because players known in hardware that these fields are always a certain size
1325
1326                 // https://code.google.com/p/amv-codec-tools/wiki/AmvDocumentation
1327                 //typedef struct _amvmainheader {
1328                 //FOURCC fcc; // 'amvh'
1329                 //DWORD cb;
1330                 //DWORD dwMicroSecPerFrame;
1331                 //BYTE reserve[28];
1332                 //DWORD dwWidth;
1333                 //DWORD dwHeight;
1334                 //DWORD dwSpeed;
1335                 //DWORD reserve0;
1336                 //DWORD reserve1;
1337                 //BYTE bTimeSec;
1338                 //BYTE bTimeMin;
1339                 //WORD wTimeHour;
1340                 //} AMVMAINHEADER;
1341
1342                 $info = &$this->getid3->info;
1343                 $RIFFchunk = false;
1344
1345                 try {
1346
1347                         $this->fseek($startoffset);
1348                         $maxoffset = min($maxoffset, $info['avdataend']);
1349                         $AMVheader = $this->fread(284);
1350                         if (substr($AMVheader,   0,  8) != 'hdrlamvh') {
1351                                 throw new Exception('expecting "hdrlamv" at offset '.($startoffset +   0).', found "'.substr($AMVheader,   0, 8).'"');
1352                         }
1353                         if (substr($AMVheader,   8,  4) != "\x38\x00\x00\x00") { // "amvh" chunk size, hardcoded to 0x38 = 56 bytes
1354                                 throw new Exception('expecting "0x38000000" at offset '.($startoffset +   8).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader,   8, 4)).'"');
1355                         }
1356                         $RIFFchunk = array();
1357                         $RIFFchunk['amvh']['us_per_frame']   = getid3_lib::LittleEndian2Int(substr($AMVheader,  12,  4));
1358                         $RIFFchunk['amvh']['reserved28']     =                              substr($AMVheader,  16, 28);  // null? reserved?
1359                         $RIFFchunk['amvh']['resolution_x']   = getid3_lib::LittleEndian2Int(substr($AMVheader,  44,  4));
1360                         $RIFFchunk['amvh']['resolution_y']   = getid3_lib::LittleEndian2Int(substr($AMVheader,  48,  4));
1361                         $RIFFchunk['amvh']['frame_rate_int'] = getid3_lib::LittleEndian2Int(substr($AMVheader,  52,  4));
1362                         $RIFFchunk['amvh']['reserved0']      = getid3_lib::LittleEndian2Int(substr($AMVheader,  56,  4)); // 1? reserved?
1363                         $RIFFchunk['amvh']['reserved1']      = getid3_lib::LittleEndian2Int(substr($AMVheader,  60,  4)); // 0? reserved?
1364                         $RIFFchunk['amvh']['runtime_sec']    = getid3_lib::LittleEndian2Int(substr($AMVheader,  64,  1));
1365                         $RIFFchunk['amvh']['runtime_min']    = getid3_lib::LittleEndian2Int(substr($AMVheader,  65,  1));
1366                         $RIFFchunk['amvh']['runtime_hrs']    = getid3_lib::LittleEndian2Int(substr($AMVheader,  66,  2));
1367
1368                         $info['video']['frame_rate']   = 1000000 / $RIFFchunk['amvh']['us_per_frame'];
1369                         $info['video']['resolution_x'] = $RIFFchunk['amvh']['resolution_x'];
1370                         $info['video']['resolution_y'] = $RIFFchunk['amvh']['resolution_y'];
1371                         $info['playtime_seconds']      = ($RIFFchunk['amvh']['runtime_hrs'] * 3600) + ($RIFFchunk['amvh']['runtime_min'] * 60) + $RIFFchunk['amvh']['runtime_sec'];
1372
1373                         // the rest is all hardcoded(?) and does not appear to be useful until you get to audio info at offset 256, even then everything is probably hardcoded
1374
1375                         if (substr($AMVheader,  68, 20) != 'LIST'."\x00\x00\x00\x00".'strlstrh'."\x38\x00\x00\x00") {
1376                                 throw new Exception('expecting "LIST<0x00000000>strlstrh<0x38000000>" at offset '.($startoffset +  68).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader,  68, 20)).'"');
1377                         }
1378                         // followed by 56 bytes of null: substr($AMVheader,  88, 56) -> 144
1379                         if (substr($AMVheader, 144,  8) != 'strf'."\x24\x00\x00\x00") {
1380                                 throw new Exception('expecting "strf<0x24000000>" at offset '.($startoffset + 144).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 144,  8)).'"');
1381                         }
1382                         // followed by 36 bytes of null: substr($AMVheader, 144, 36) -> 180
1383
1384                         if (substr($AMVheader, 188, 20) != 'LIST'."\x00\x00\x00\x00".'strlstrh'."\x30\x00\x00\x00") {
1385                                 throw new Exception('expecting "LIST<0x00000000>strlstrh<0x30000000>" at offset '.($startoffset + 188).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 188, 20)).'"');
1386                         }
1387                         // followed by 48 bytes of null: substr($AMVheader, 208, 48) -> 256
1388                         if (substr($AMVheader, 256,  8) != 'strf'."\x14\x00\x00\x00") {
1389                                 throw new Exception('expecting "strf<0x14000000>" at offset '.($startoffset + 256).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 256,  8)).'"');
1390                         }
1391                         // followed by 20 bytes of a modified WAVEFORMATEX:
1392                         // typedef struct {
1393                         // WORD wFormatTag;       //(Fixme: this is equal to PCM's 0x01 format code)
1394                         // WORD nChannels;        //(Fixme: this is always 1)
1395                         // DWORD nSamplesPerSec;  //(Fixme: for all known sample files this is equal to 22050)
1396                         // DWORD nAvgBytesPerSec; //(Fixme: for all known sample files this is equal to 44100)
1397                         // WORD nBlockAlign;      //(Fixme: this seems to be 2 in AMV files, is this correct ?)
1398                         // WORD wBitsPerSample;   //(Fixme: this seems to be 16 in AMV files instead of the expected 4)
1399                         // WORD cbSize;           //(Fixme: this seems to be 0 in AMV files)
1400                         // WORD reserved;
1401                         // } WAVEFORMATEX;
1402                         $RIFFchunk['strf']['wformattag']      = getid3_lib::LittleEndian2Int(substr($AMVheader,  264,  2));
1403                         $RIFFchunk['strf']['nchannels']       = getid3_lib::LittleEndian2Int(substr($AMVheader,  266,  2));
1404                         $RIFFchunk['strf']['nsamplespersec']  = getid3_lib::LittleEndian2Int(substr($AMVheader,  268,  4));
1405                         $RIFFchunk['strf']['navgbytespersec'] = getid3_lib::LittleEndian2Int(substr($AMVheader,  272,  4));
1406                         $RIFFchunk['strf']['nblockalign']     = getid3_lib::LittleEndian2Int(substr($AMVheader,  276,  2));
1407                         $RIFFchunk['strf']['wbitspersample']  = getid3_lib::LittleEndian2Int(substr($AMVheader,  278,  2));
1408                         $RIFFchunk['strf']['cbsize']          = getid3_lib::LittleEndian2Int(substr($AMVheader,  280,  2));
1409                         $RIFFchunk['strf']['reserved']        = getid3_lib::LittleEndian2Int(substr($AMVheader,  282,  2));
1410
1411
1412                         $info['audio']['lossless']        = false;
1413                         $info['audio']['sample_rate']     = $RIFFchunk['strf']['nsamplespersec'];
1414                         $info['audio']['channels']        = $RIFFchunk['strf']['nchannels'];
1415                         $info['audio']['bits_per_sample'] = $RIFFchunk['strf']['wbitspersample'];
1416                         $info['audio']['bitrate']         = $info['audio']['sample_rate'] * $info['audio']['channels'] * $info['audio']['bits_per_sample'];
1417                         $info['audio']['bitrate_mode']    = 'cbr';
1418
1419
1420                 } catch (getid3_exception $e) {
1421                         if ($e->getCode() == 10) {
1422                                 $this->warning('RIFFAMV parser: '.$e->getMessage());
1423                         } else {
1424                                 throw $e;
1425                         }
1426                 }
1427
1428                 return $RIFFchunk;
1429         }
1430
1431
1432         public function ParseRIFF($startoffset, $maxoffset) {
1433                 $info = &$this->getid3->info;
1434
1435                 $RIFFchunk = false;
1436                 $FoundAllChunksWeNeed = false;
1437
1438                 try {
1439                         $this->fseek($startoffset);
1440                         $maxoffset = min($maxoffset, $info['avdataend']);
1441                         while ($this->ftell() < $maxoffset) {
1442                                 $chunknamesize = $this->fread(8);
1443                                 //$chunkname =                          substr($chunknamesize, 0, 4);
1444                                 $chunkname = str_replace("\x00", '_', substr($chunknamesize, 0, 4));  // note: chunk names of 4 null bytes do appear to be legal (has been observed inside INFO and PRMI chunks, for example), but makes traversing array keys more difficult
1445                                 $chunksize =  $this->EitherEndian2Int(substr($chunknamesize, 4, 4));
1446                                 //if (strlen(trim($chunkname, "\x00")) < 4) {
1447                                 if (strlen($chunkname) < 4) {
1448                                         $this->error('Expecting chunk name at offset '.($this->ftell() - 8).' but found nothing. Aborting RIFF parsing.');
1449                                         break;
1450                                 }
1451                                 if (($chunksize == 0) && ($chunkname != 'JUNK')) {
1452                                         $this->warning('Chunk ('.$chunkname.') size at offset '.($this->ftell() - 4).' is zero. Aborting RIFF parsing.');
1453                                         break;
1454                                 }
1455                                 if (($chunksize % 2) != 0) {
1456                                         // all structures are packed on word boundaries
1457                                         $chunksize++;
1458                                 }
1459
1460                                 switch ($chunkname) {
1461                                         case 'LIST':
1462                                                 $listname = $this->fread(4);
1463                                                 if (preg_match('#^(movi|rec )$#i', $listname)) {
1464                                                         $RIFFchunk[$listname]['offset'] = $this->ftell() - 4;
1465                                                         $RIFFchunk[$listname]['size']   = $chunksize;
1466
1467                                                         if (!$FoundAllChunksWeNeed) {
1468                                                                 $WhereWeWere      = $this->ftell();
1469                                                                 $AudioChunkHeader = $this->fread(12);
1470                                                                 $AudioChunkStreamNum  =                              substr($AudioChunkHeader, 0, 2);
1471                                                                 $AudioChunkStreamType =                              substr($AudioChunkHeader, 2, 2);
1472                                                                 $AudioChunkSize       = getid3_lib::LittleEndian2Int(substr($AudioChunkHeader, 4, 4));
1473
1474                                                                 if ($AudioChunkStreamType == 'wb') {
1475                                                                         $FirstFourBytes = substr($AudioChunkHeader, 8, 4);
1476                                                                         if (preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', $FirstFourBytes)) {
1477                                                                                 // MP3
1478                                                                                 if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) {
1479                                                                                         $getid3_temp = new getID3();
1480                                                                                         $getid3_temp->openfile($this->getid3->filename);
1481                                                                                         $getid3_temp->info['avdataoffset'] = $this->ftell() - 4;
1482                                                                                         $getid3_temp->info['avdataend']    = $this->ftell() + $AudioChunkSize;
1483                                                                                         $getid3_mp3 = new getid3_mp3($getid3_temp, __CLASS__);
1484                                                                                         $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false);
1485                                                                                         if (isset($getid3_temp->info['mpeg']['audio'])) {
1486                                                                                                 $info['mpeg']['audio']         = $getid3_temp->info['mpeg']['audio'];
1487                                                                                                 $info['audio']                 = $getid3_temp->info['audio'];
1488                                                                                                 $info['audio']['dataformat']   = 'mp'.$info['mpeg']['audio']['layer'];
1489                                                                                                 $info['audio']['sample_rate']  = $info['mpeg']['audio']['sample_rate'];
1490                                                                                                 $info['audio']['channels']     = $info['mpeg']['audio']['channels'];
1491                                                                                                 $info['audio']['bitrate']      = $info['mpeg']['audio']['bitrate'];
1492                                                                                                 $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
1493                                                                                                 //$info['bitrate']               = $info['audio']['bitrate'];
1494                                                                                         }
1495                                                                                         unset($getid3_temp, $getid3_mp3);
1496                                                                                 }
1497
1498                                                                         } elseif (strpos($FirstFourBytes, getid3_ac3::syncword) === 0) {
1499
1500                                                                                 // AC3
1501                                                                                 $getid3_temp = new getID3();
1502                                                                                 $getid3_temp->openfile($this->getid3->filename);
1503                                                                                 $getid3_temp->info['avdataoffset'] = $this->ftell() - 4;
1504                                                                                 $getid3_temp->info['avdataend']    = $this->ftell() + $AudioChunkSize;
1505                                                                                 $getid3_ac3 = new getid3_ac3($getid3_temp);
1506                                                                                 $getid3_ac3->Analyze();
1507                                                                                 if (empty($getid3_temp->info['error'])) {
1508                                                                                         $info['audio']   = $getid3_temp->info['audio'];
1509                                                                                         $info['ac3']     = $getid3_temp->info['ac3'];
1510                                                                                         if (!empty($getid3_temp->info['warning'])) {
1511                                                                                                 foreach ($getid3_temp->info['warning'] as $key => $value) {
1512                                                                                                         $info['warning'][] = $value;
1513                                                                                                 }
1514                                                                                         }
1515                                                                                 }
1516                                                                                 unset($getid3_temp, $getid3_ac3);
1517                                                                         }
1518                                                                 }
1519                                                                 $FoundAllChunksWeNeed = true;
1520                                                                 $this->fseek($WhereWeWere);
1521                                                         }
1522                                                         $this->fseek($chunksize - 4, SEEK_CUR);
1523
1524                                                 } else {
1525
1526                                                         if (!isset($RIFFchunk[$listname])) {
1527                                                                 $RIFFchunk[$listname] = array();
1528                                                         }
1529                                                         $LISTchunkParent    = $listname;
1530                                                         $LISTchunkMaxOffset = $this->ftell() - 4 + $chunksize;
1531                                                         if ($parsedChunk = $this->ParseRIFF($this->ftell(), $LISTchunkMaxOffset)) {
1532                                                                 $RIFFchunk[$listname] = array_merge_recursive($RIFFchunk[$listname], $parsedChunk);
1533                                                         }
1534
1535                                                 }
1536                                                 break;
1537
1538                                         default:
1539                                                 if (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#', $chunkname)) {
1540                                                         $this->fseek($chunksize, SEEK_CUR);
1541                                                         break;
1542                                                 }
1543                                                 $thisindex = 0;
1544                                                 if (isset($RIFFchunk[$chunkname]) && is_array($RIFFchunk[$chunkname])) {
1545                                                         $thisindex = count($RIFFchunk[$chunkname]);
1546                                                 }
1547                                                 $RIFFchunk[$chunkname][$thisindex]['offset'] = $this->ftell() - 8;
1548                                                 $RIFFchunk[$chunkname][$thisindex]['size']   = $chunksize;
1549                                                 switch ($chunkname) {
1550                                                         case 'data':
1551                                                                 $info['avdataoffset'] = $this->ftell();
1552                                                                 $info['avdataend']    = $info['avdataoffset'] + $chunksize;
1553
1554                                                                 $testData = $this->fread(36);
1555                                                                 if ($testData === '') {
1556                                                                         break;
1557                                                                 }
1558                                                                 if (preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', substr($testData, 0, 4))) {
1559
1560                                                                         // Probably is MP3 data
1561                                                                         if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($testData, 0, 4))) {
1562                                                                                 $getid3_temp = new getID3();
1563                                                                                 $getid3_temp->openfile($this->getid3->filename);
1564                                                                                 $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
1565                                                                                 $getid3_temp->info['avdataend']    = $info['avdataend'];
1566                                                                                 $getid3_mp3 = new getid3_mp3($getid3_temp, __CLASS__);
1567                                                                                 $getid3_mp3->getOnlyMPEGaudioInfo($info['avdataoffset'], false);
1568                                                                                 if (empty($getid3_temp->info['error'])) {
1569                                                                                         $info['audio'] = $getid3_temp->info['audio'];
1570                                                                                         $info['mpeg']  = $getid3_temp->info['mpeg'];
1571                                                                                 }
1572                                                                                 unset($getid3_temp, $getid3_mp3);
1573                                                                         }
1574
1575                                                                 } elseif (($isRegularAC3 = (substr($testData, 0, 2) == getid3_ac3::syncword)) || substr($testData, 8, 2) == strrev(getid3_ac3::syncword)) {
1576
1577                                                                         // This is probably AC-3 data
1578                                                                         $getid3_temp = new getID3();
1579                                                                         if ($isRegularAC3) {
1580                                                                                 $getid3_temp->openfile($this->getid3->filename);
1581                                                                                 $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
1582                                                                                 $getid3_temp->info['avdataend']    = $info['avdataend'];
1583                                                                         }
1584                                                                         $getid3_ac3 = new getid3_ac3($getid3_temp);
1585                                                                         if ($isRegularAC3) {
1586                                                                                 $getid3_ac3->Analyze();
1587                                                                         } else {
1588                                                                                 // Dolby Digital WAV
1589                                                                                 // AC-3 content, but not encoded in same format as normal AC-3 file
1590                                                                                 // For one thing, byte order is swapped
1591                                                                                 $ac3_data = '';
1592                                                                                 for ($i = 0; $i < 28; $i += 2) {
1593                                                                                         $ac3_data .= substr($testData, 8 + $i + 1, 1);
1594                                                                                         $ac3_data .= substr($testData, 8 + $i + 0, 1);
1595                                                                                 }
1596                                                                                 $getid3_ac3->AnalyzeString($ac3_data);
1597                                                                         }
1598
1599                                                                         if (empty($getid3_temp->info['error'])) {
1600                                                                                 $info['audio'] = $getid3_temp->info['audio'];
1601                                                                                 $info['ac3']   = $getid3_temp->info['ac3'];
1602                                                                                 if (!empty($getid3_temp->info['warning'])) {
1603                                                                                         foreach ($getid3_temp->info['warning'] as $newerror) {
1604                                                                                                 $this->warning('getid3_ac3() says: ['.$newerror.']');
1605                                                                                         }
1606                                                                                 }
1607                                                                         }
1608                                                                         unset($getid3_temp, $getid3_ac3);
1609
1610                                                                 } elseif (preg_match('/^('.implode('|', array_map('preg_quote', getid3_dts::$syncwords)).')/', $testData)) {
1611
1612                                                                         // This is probably DTS data
1613                                                                         $getid3_temp = new getID3();
1614                                                                         $getid3_temp->openfile($this->getid3->filename);
1615                                                                         $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
1616                                                                         $getid3_dts = new getid3_dts($getid3_temp);
1617                                                                         $getid3_dts->Analyze();
1618                                                                         if (empty($getid3_temp->info['error'])) {
1619                                                                                 $info['audio']            = $getid3_temp->info['audio'];
1620                                                                                 $info['dts']              = $getid3_temp->info['dts'];
1621                                                                                 $info['playtime_seconds'] = $getid3_temp->info['playtime_seconds']; // may not match RIFF calculations since DTS-WAV often used 14/16 bit-word packing
1622                                                                                 if (!empty($getid3_temp->info['warning'])) {
1623                                                                                         foreach ($getid3_temp->info['warning'] as $newerror) {
1624                                                                                                 $this->warning('getid3_dts() says: ['.$newerror.']');
1625                                                                                         }
1626                                                                                 }
1627                                                                         }
1628
1629                                                                         unset($getid3_temp, $getid3_dts);
1630
1631                                                                 } elseif (substr($testData, 0, 4) == 'wvpk') {
1632
1633                                                                         // This is WavPack data
1634                                                                         $info['wavpack']['offset'] = $info['avdataoffset'];
1635                                                                         $info['wavpack']['size']   = getid3_lib::LittleEndian2Int(substr($testData, 4, 4));
1636                                                                         $this->parseWavPackHeader(substr($testData, 8, 28));
1637
1638                                                                 } else {
1639                                                                         // This is some other kind of data (quite possibly just PCM)
1640                                                                         // do nothing special, just skip it
1641                                                                 }
1642                                                                 $nextoffset = $info['avdataend'];
1643                                                                 $this->fseek($nextoffset);
1644                                                                 break;
1645
1646                                                         case 'iXML':
1647                                                         case 'bext':
1648                                                         case 'cart':
1649                                                         case 'fmt ':
1650                                                         case 'strh':
1651                                                         case 'strf':
1652                                                         case 'indx':
1653                                                         case 'MEXT':
1654                                                         case 'DISP':
1655                                                                 // always read data in
1656                                                         case 'JUNK':
1657                                                                 // should be: never read data in
1658                                                                 // but some programs write their version strings in a JUNK chunk (e.g. VirtualDub, AVIdemux, etc)
1659                                                                 if ($chunksize < 1048576) {
1660                                                                         if ($chunksize > 0) {
1661                                                                                 $RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize);
1662                                                                                 if ($chunkname == 'JUNK') {
1663                                                                                         if (preg_match('#^([\\x20-\\x7F]+)#', $RIFFchunk[$chunkname][$thisindex]['data'], $matches)) {
1664                                                                                                 // only keep text characters [chr(32)-chr(127)]
1665                                                                                                 $info['riff']['comments']['junk'][] = trim($matches[1]);
1666                                                                                         }
1667                                                                                         // but if nothing there, ignore
1668                                                                                         // remove the key in either case
1669                                                                                         unset($RIFFchunk[$chunkname][$thisindex]['data']);
1670                                                                                 }
1671                                                                         }
1672                                                                 } else {
1673                                                                         $this->warning('Chunk "'.$chunkname.'" at offset '.$this->ftell().' is unexpectedly larger than 1MB (claims to be '.number_format($chunksize).' bytes), skipping data');
1674                                                                         $this->fseek($chunksize, SEEK_CUR);
1675                                                                 }
1676                                                                 break;
1677
1678                                                         //case 'IDVX':
1679                                                         //      $info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunksize));
1680                                                         //      break;
1681
1682                                                         default:
1683                                                                 if (!empty($LISTchunkParent) && (($RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size']) <= $LISTchunkMaxOffset)) {
1684                                                                         $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
1685                                                                         $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['size']   = $RIFFchunk[$chunkname][$thisindex]['size'];
1686                                                                         unset($RIFFchunk[$chunkname][$thisindex]['offset']);
1687                                                                         unset($RIFFchunk[$chunkname][$thisindex]['size']);
1688                                                                         if (isset($RIFFchunk[$chunkname][$thisindex]) && empty($RIFFchunk[$chunkname][$thisindex])) {
1689                                                                                 unset($RIFFchunk[$chunkname][$thisindex]);
1690                                                                         }
1691                                                                         if (isset($RIFFchunk[$chunkname]) && empty($RIFFchunk[$chunkname])) {
1692                                                                                 unset($RIFFchunk[$chunkname]);
1693                                                                         }
1694                                                                         $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['data'] = $this->fread($chunksize);
1695                                                                 } elseif ($chunksize < 2048) {
1696                                                                         // only read data in if smaller than 2kB
1697                                                                         $RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize);
1698                                                                 } else {
1699                                                                         $this->fseek($chunksize, SEEK_CUR);
1700                                                                 }
1701                                                                 break;
1702                                                 }
1703                                                 break;
1704                                 }
1705                         }
1706
1707                 } catch (getid3_exception $e) {
1708                         if ($e->getCode() == 10) {
1709                                 $this->warning('RIFF parser: '.$e->getMessage());
1710                         } else {
1711                                 throw $e;
1712                         }
1713                 }
1714
1715                 return $RIFFchunk;
1716         }
1717
1718         public function ParseRIFFdata(&$RIFFdata) {
1719                 $info = &$this->getid3->info;
1720                 if ($RIFFdata) {
1721                         $tempfile = tempnam(GETID3_TEMP_DIR, 'getID3');
1722                         $fp_temp  = fopen($tempfile, 'wb');
1723                         $RIFFdataLength = strlen($RIFFdata);
1724                         $NewLengthString = getid3_lib::LittleEndian2String($RIFFdataLength, 4);
1725                         for ($i = 0; $i < 4; $i++) {
1726                                 $RIFFdata[($i + 4)] = $NewLengthString[$i];
1727                         }
1728                         fwrite($fp_temp, $RIFFdata);
1729                         fclose($fp_temp);
1730
1731                         $getid3_temp = new getID3();
1732                         $getid3_temp->openfile($tempfile);
1733                         $getid3_temp->info['filesize']     = $RIFFdataLength;
1734                         $getid3_temp->info['filenamepath'] = $info['filenamepath'];
1735                         $getid3_temp->info['tags']         = $info['tags'];
1736                         $getid3_temp->info['warning']      = $info['warning'];
1737                         $getid3_temp->info['error']        = $info['error'];
1738                         $getid3_temp->info['comments']     = $info['comments'];
1739                         $getid3_temp->info['audio']        = (isset($info['audio']) ? $info['audio'] : array());
1740                         $getid3_temp->info['video']        = (isset($info['video']) ? $info['video'] : array());
1741                         $getid3_riff = new getid3_riff($getid3_temp);
1742                         $getid3_riff->Analyze();
1743
1744                         $info['riff']     = $getid3_temp->info['riff'];
1745                         $info['warning']  = $getid3_temp->info['warning'];
1746                         $info['error']    = $getid3_temp->info['error'];
1747                         $info['tags']     = $getid3_temp->info['tags'];
1748                         $info['comments'] = $getid3_temp->info['comments'];
1749                         unset($getid3_riff, $getid3_temp);
1750                         unlink($tempfile);
1751                 }
1752                 return false;
1753         }
1754
1755         public static function parseComments(&$RIFFinfoArray, &$CommentsTargetArray) {
1756                 $RIFFinfoKeyLookup = array(
1757                         'IARL'=>'archivallocation',
1758                         'IART'=>'artist',
1759                         'ICDS'=>'costumedesigner',
1760                         'ICMS'=>'commissionedby',
1761                         'ICMT'=>'comment',
1762                         'ICNT'=>'country',
1763                         'ICOP'=>'copyright',
1764                         'ICRD'=>'creationdate',
1765                         'IDIM'=>'dimensions',
1766                         'IDIT'=>'digitizationdate',
1767                         'IDPI'=>'resolution',
1768                         'IDST'=>'distributor',
1769                         'IEDT'=>'editor',
1770                         'IENG'=>'engineers',
1771                         'IFRM'=>'accountofparts',
1772                         'IGNR'=>'genre',
1773                         'IKEY'=>'keywords',
1774                         'ILGT'=>'lightness',
1775                         'ILNG'=>'language',
1776                         'IMED'=>'orignalmedium',
1777                         'IMUS'=>'composer',
1778                         'INAM'=>'title',
1779                         'IPDS'=>'productiondesigner',
1780                         'IPLT'=>'palette',
1781                         'IPRD'=>'product',
1782                         'IPRO'=>'producer',
1783                         'IPRT'=>'part',
1784                         'IRTD'=>'rating',
1785                         'ISBJ'=>'subject',
1786                         'ISFT'=>'software',
1787                         'ISGN'=>'secondarygenre',
1788                         'ISHP'=>'sharpness',
1789                         'ISRC'=>'sourcesupplier',
1790                         'ISRF'=>'digitizationsource',
1791                         'ISTD'=>'productionstudio',
1792                         'ISTR'=>'starring',
1793                         'ITCH'=>'encoded_by',
1794                         'IWEB'=>'url',
1795                         'IWRI'=>'writer',
1796                         '____'=>'comment',
1797                 );
1798                 foreach ($RIFFinfoKeyLookup as $key => $value) {
1799                         if (isset($RIFFinfoArray[$key])) {
1800                                 foreach ($RIFFinfoArray[$key] as $commentid => $commentdata) {
1801                                         if (trim($commentdata['data']) != '') {
1802                                                 if (isset($CommentsTargetArray[$value])) {
1803                                                         $CommentsTargetArray[$value][] =     trim($commentdata['data']);
1804                                                 } else {
1805                                                         $CommentsTargetArray[$value] = array(trim($commentdata['data']));
1806                                                 }
1807                                         }
1808                                 }
1809                         }
1810                 }
1811                 return true;
1812         }
1813
1814         public static function parseWAVEFORMATex($WaveFormatExData) {
1815                 // shortcut
1816                 $WaveFormatEx['raw'] = array();
1817                 $WaveFormatEx_raw    = &$WaveFormatEx['raw'];
1818
1819                 $WaveFormatEx_raw['wFormatTag']      = substr($WaveFormatExData,  0, 2);
1820                 $WaveFormatEx_raw['nChannels']       = substr($WaveFormatExData,  2, 2);
1821                 $WaveFormatEx_raw['nSamplesPerSec']  = substr($WaveFormatExData,  4, 4);
1822                 $WaveFormatEx_raw['nAvgBytesPerSec'] = substr($WaveFormatExData,  8, 4);
1823                 $WaveFormatEx_raw['nBlockAlign']     = substr($WaveFormatExData, 12, 2);
1824                 $WaveFormatEx_raw['wBitsPerSample']  = substr($WaveFormatExData, 14, 2);
1825                 if (strlen($WaveFormatExData) > 16) {
1826                         $WaveFormatEx_raw['cbSize']      = substr($WaveFormatExData, 16, 2);
1827                 }
1828                 $WaveFormatEx_raw = array_map('getid3_lib::LittleEndian2Int', $WaveFormatEx_raw);
1829
1830                 $WaveFormatEx['codec']           = self::wFormatTagLookup($WaveFormatEx_raw['wFormatTag']);
1831                 $WaveFormatEx['channels']        = $WaveFormatEx_raw['nChannels'];
1832                 $WaveFormatEx['sample_rate']     = $WaveFormatEx_raw['nSamplesPerSec'];
1833                 $WaveFormatEx['bitrate']         = $WaveFormatEx_raw['nAvgBytesPerSec'] * 8;
1834                 $WaveFormatEx['bits_per_sample'] = $WaveFormatEx_raw['wBitsPerSample'];