WordPress 4.1.4-scripts
[autoinstalls/wordpress.git] / 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'];
1835
1836                 return $WaveFormatEx;
1837         }
1838
1839         public function parseWavPackHeader($WavPackChunkData) {
1840                 // typedef struct {
1841                 //     char ckID [4];
1842                 //     long ckSize;
1843                 //     short version;
1844                 //     short bits;                // added for version 2.00
1845                 //     short flags, shift;        // added for version 3.00
1846                 //     long total_samples, crc, crc2;
1847                 //     char extension [4], extra_bc, extras [3];
1848                 // } WavpackHeader;
1849
1850                 // shortcut
1851                 $info = &$this->getid3->info;
1852                 $info['wavpack']  = array();
1853                 $thisfile_wavpack = &$info['wavpack'];
1854
1855                 $thisfile_wavpack['version']           = getid3_lib::LittleEndian2Int(substr($WavPackChunkData,  0, 2));
1856                 if ($thisfile_wavpack['version'] >= 2) {
1857                         $thisfile_wavpack['bits']          = getid3_lib::LittleEndian2Int(substr($WavPackChunkData,  2, 2));
1858                 }
1859                 if ($thisfile_wavpack['version'] >= 3) {
1860                         $thisfile_wavpack['flags_raw']     = getid3_lib::LittleEndian2Int(substr($WavPackChunkData,  4, 2));
1861                         $thisfile_wavpack['shift']         = getid3_lib::LittleEndian2Int(substr($WavPackChunkData,  6, 2));
1862                         $thisfile_wavpack['total_samples'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData,  8, 4));
1863                         $thisfile_wavpack['crc1']          = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 12, 4));
1864                         $thisfile_wavpack['crc2']          = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 16, 4));
1865                         $thisfile_wavpack['extension']     =                              substr($WavPackChunkData, 20, 4);
1866                         $thisfile_wavpack['extra_bc']      = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 24, 1));
1867                         for ($i = 0; $i <= 2; $i++) {
1868                                 $thisfile_wavpack['extras'][]  = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 25 + $i, 1));
1869                         }
1870
1871                         // shortcut
1872                         $thisfile_wavpack['flags'] = array();
1873                         $thisfile_wavpack_flags = &$thisfile_wavpack['flags'];
1874
1875                         $thisfile_wavpack_flags['mono']                 = (bool) ($thisfile_wavpack['flags_raw'] & 0x000001);
1876                         $thisfile_wavpack_flags['fast_mode']            = (bool) ($thisfile_wavpack['flags_raw'] & 0x000002);
1877                         $thisfile_wavpack_flags['raw_mode']             = (bool) ($thisfile_wavpack['flags_raw'] & 0x000004);
1878                         $thisfile_wavpack_flags['calc_noise']           = (bool) ($thisfile_wavpack['flags_raw'] & 0x000008);
1879                         $thisfile_wavpack_flags['high_quality']         = (bool) ($thisfile_wavpack['flags_raw'] & 0x000010);
1880                         $thisfile_wavpack_flags['3_byte_samples']       = (bool) ($thisfile_wavpack['flags_raw'] & 0x000020);
1881                         $thisfile_wavpack_flags['over_20_bits']         = (bool) ($thisfile_wavpack['flags_raw'] & 0x000040);
1882                         $thisfile_wavpack_flags['use_wvc']              = (bool) ($thisfile_wavpack['flags_raw'] & 0x000080);
1883                         $thisfile_wavpack_flags['noiseshaping']         = (bool) ($thisfile_wavpack['flags_raw'] & 0x000100);
1884                         $thisfile_wavpack_flags['very_fast_mode']       = (bool) ($thisfile_wavpack['flags_raw'] & 0x000200);
1885                         $thisfile_wavpack_flags['new_high_quality']     = (bool) ($thisfile_wavpack['flags_raw'] & 0x000400);
1886                         $thisfile_wavpack_flags['cancel_extreme']       = (bool) ($thisfile_wavpack['flags_raw'] & 0x000800);
1887                         $thisfile_wavpack_flags['cross_decorrelation']  = (bool) ($thisfile_wavpack['flags_raw'] & 0x001000);
1888                         $thisfile_wavpack_flags['new_decorrelation']    = (bool) ($thisfile_wavpack['flags_raw'] & 0x002000);
1889                         $thisfile_wavpack_flags['joint_stereo']         = (bool) ($thisfile_wavpack['flags_raw'] & 0x004000);
1890                         $thisfile_wavpack_flags['extra_decorrelation']  = (bool) ($thisfile_wavpack['flags_raw'] & 0x008000);
1891                         $thisfile_wavpack_flags['override_noiseshape']  = (bool) ($thisfile_wavpack['flags_raw'] & 0x010000);
1892                         $thisfile_wavpack_flags['override_jointstereo'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x020000);
1893                         $thisfile_wavpack_flags['copy_source_filetime'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x040000);
1894                         $thisfile_wavpack_flags['create_exe']           = (bool) ($thisfile_wavpack['flags_raw'] & 0x080000);
1895                 }
1896
1897                 return true;
1898         }
1899
1900         public static function ParseBITMAPINFOHEADER($BITMAPINFOHEADER, $littleEndian=true) {
1901
1902                 $parsed['biSize']          = substr($BITMAPINFOHEADER,  0, 4); // number of bytes required by the BITMAPINFOHEADER structure
1903                 $parsed['biWidth']         = substr($BITMAPINFOHEADER,  4, 4); // width of the bitmap in pixels
1904                 $parsed['biHeight']        = substr($BITMAPINFOHEADER,  8, 4); // height of the bitmap in pixels. If biHeight is positive, the bitmap is a 'bottom-up' DIB and its origin is the lower left corner. If biHeight is negative, the bitmap is a 'top-down' DIB and its origin is the upper left corner
1905                 $parsed['biPlanes']        = substr($BITMAPINFOHEADER, 12, 2); // number of color planes on the target device. In most cases this value must be set to 1
1906                 $parsed['biBitCount']      = substr($BITMAPINFOHEADER, 14, 2); // Specifies the number of bits per pixels
1907                 $parsed['biSizeImage']     = substr($BITMAPINFOHEADER, 20, 4); // size of the bitmap data section of the image (the actual pixel data, excluding BITMAPINFOHEADER and RGBQUAD structures)
1908                 $parsed['biXPelsPerMeter'] = substr($BITMAPINFOHEADER, 24, 4); // horizontal resolution, in pixels per metre, of the target device
1909                 $parsed['biYPelsPerMeter'] = substr($BITMAPINFOHEADER, 28, 4); // vertical resolution, in pixels per metre, of the target device
1910                 $parsed['biClrUsed']       = substr($BITMAPINFOHEADER, 32, 4); // actual number of color indices in the color table used by the bitmap. If this value is zero, the bitmap uses the maximum number of colors corresponding to the value of the biBitCount member for the compression mode specified by biCompression
1911                 $parsed['biClrImportant']  = substr($BITMAPINFOHEADER, 36, 4); // number of color indices that are considered important for displaying the bitmap. If this value is zero, all colors are important
1912                 $parsed = array_map('getid3_lib::'.($littleEndian ? 'Little' : 'Big').'Endian2Int', $parsed);
1913
1914                 $parsed['fourcc']          = substr($BITMAPINFOHEADER, 16, 4);  // compression identifier
1915
1916                 return $parsed;
1917         }
1918
1919         public static function ParseDIVXTAG($DIVXTAG, $raw=false) {
1920                 // structure from "IDivX" source, Form1.frm, by "Greg Frazier of Daemonic Software Group", email: gfrazier@icestorm.net, web: http://dsg.cjb.net/
1921                 // source available at http://files.divx-digest.com/download/c663efe7ef8ad2e90bf4af4d3ea6188a/on0SWN2r/edit/IDivX.zip
1922                 // 'Byte Layout:                   '1111111111111111
1923                 // '32 for Movie - 1               '1111111111111111
1924                 // '28 for Author - 6              '6666666666666666
1925                 // '4  for year - 2                '6666666666662222
1926                 // '3  for genre - 3               '7777777777777777
1927                 // '48 for Comments - 7            '7777777777777777
1928                 // '1  for Rating - 4              '7777777777777777
1929                 // '5  for Future Additions - 0    '333400000DIVXTAG
1930                 // '128 bytes total
1931
1932                 static $DIVXTAGgenre  = array(
1933                          0 => 'Action',
1934                          1 => 'Action/Adventure',
1935                          2 => 'Adventure',
1936                          3 => 'Adult',
1937                          4 => 'Anime',
1938                          5 => 'Cartoon',
1939                          6 => 'Claymation',
1940                          7 => 'Comedy',
1941                          8 => 'Commercial',
1942                          9 => 'Documentary',
1943                         10 => 'Drama',
1944                         11 => 'Home Video',
1945                         12 => 'Horror',
1946                         13 => 'Infomercial',
1947                         14 => 'Interactive',
1948                         15 => 'Mystery',
1949                         16 => 'Music Video',
1950                         17 => 'Other',
1951                         18 => 'Religion',
1952                         19 => 'Sci Fi',
1953                         20 => 'Thriller',
1954                         21 => 'Western',
1955                 ),
1956                 $DIVXTAGrating = array(
1957                          0 => 'Unrated',
1958                          1 => 'G',
1959                          2 => 'PG',
1960                          3 => 'PG-13',
1961                          4 => 'R',
1962                          5 => 'NC-17',
1963                 );
1964
1965                 $parsed['title']     =        trim(substr($DIVXTAG,   0, 32));
1966                 $parsed['artist']    =        trim(substr($DIVXTAG,  32, 28));
1967                 $parsed['year']      = intval(trim(substr($DIVXTAG,  60,  4)));
1968                 $parsed['comment']   =        trim(substr($DIVXTAG,  64, 48));
1969                 $parsed['genre_id']  = intval(trim(substr($DIVXTAG, 112,  3)));
1970                 $parsed['rating_id'] =         ord(substr($DIVXTAG, 115,  1));
1971                 //$parsed['padding'] =             substr($DIVXTAG, 116,  5);  // 5-byte null
1972                 //$parsed['magic']   =             substr($DIVXTAG, 121,  7);  // "DIVXTAG"
1973
1974                 $parsed['genre']  = (isset($DIVXTAGgenre[$parsed['genre_id']])   ? $DIVXTAGgenre[$parsed['genre_id']]   : $parsed['genre_id']);
1975                 $parsed['rating'] = (isset($DIVXTAGrating[$parsed['rating_id']]) ? $DIVXTAGrating[$parsed['rating_id']] : $parsed['rating_id']);
1976
1977                 if (!$raw) {
1978                         unset($parsed['genre_id'], $parsed['rating_id']);
1979                         foreach ($parsed as $key => $value) {
1980                                 if (!$value === '') {
1981                                         unset($parsed['key']);
1982                                 }
1983                         }
1984                 }
1985
1986                 foreach ($parsed as $tag => $value) {
1987                         $parsed[$tag] = array($value);
1988                 }
1989
1990                 return $parsed;
1991         }
1992
1993         public static function waveSNDMtagLookup($tagshortname) {
1994                 $begin = __LINE__;
1995
1996                 /** This is not a comment!
1997
1998                         ©kwd   keywords
1999                         ©BPM   bpm
2000                         ©trt   tracktitle
2001                         ©des   description
2002                         ©gen   category
2003                         ©fin   featuredinstrument
2004                         ©LID   longid
2005                         ©bex   bwdescription
2006                         ©pub   publisher
2007                         ©cdt   cdtitle
2008                         ©alb   library
2009                         ©com   composer
2010
2011                 */
2012
2013                 return getid3_lib::EmbeddedLookup($tagshortname, $begin, __LINE__, __FILE__, 'riff-sndm');
2014         }
2015
2016         public static function wFormatTagLookup($wFormatTag) {
2017
2018                 $begin = __LINE__;
2019
2020                 /** This is not a comment!
2021
2022                         0x0000  Microsoft Unknown Wave Format
2023                         0x0001  Pulse Code Modulation (PCM)
2024                         0x0002  Microsoft ADPCM
2025                         0x0003  IEEE Float
2026                         0x0004  Compaq Computer VSELP
2027                         0x0005  IBM CVSD
2028                         0x0006  Microsoft A-Law
2029                         0x0007  Microsoft mu-Law
2030                         0x0008  Microsoft DTS
2031                         0x0010  OKI ADPCM
2032                         0x0011  Intel DVI/IMA ADPCM
2033                         0x0012  Videologic MediaSpace ADPCM
2034                         0x0013  Sierra Semiconductor ADPCM
2035                         0x0014  Antex Electronics G.723 ADPCM
2036                         0x0015  DSP Solutions DigiSTD
2037                         0x0016  DSP Solutions DigiFIX
2038                         0x0017  Dialogic OKI ADPCM
2039                         0x0018  MediaVision ADPCM
2040                         0x0019  Hewlett-Packard CU
2041                         0x0020  Yamaha ADPCM
2042                         0x0021  Speech Compression Sonarc
2043                         0x0022  DSP Group TrueSpeech
2044                         0x0023  Echo Speech EchoSC1
2045                         0x0024  Audiofile AF36
2046                         0x0025  Audio Processing Technology APTX
2047                         0x0026  AudioFile AF10
2048                         0x0027  Prosody 1612
2049                         0x0028  LRC
2050                         0x0030  Dolby AC2
2051                         0x0031  Microsoft GSM 6.10
2052                         0x0032  MSNAudio
2053                         0x0033  Antex Electronics ADPCME
2054                         0x0034  Control Resources VQLPC
2055                         0x0035  DSP Solutions DigiREAL
2056                         0x0036  DSP Solutions DigiADPCM
2057                         0x0037  Control Resources CR10
2058                         0x0038  Natural MicroSystems VBXADPCM
2059                         0x0039  Crystal Semiconductor IMA ADPCM
2060                         0x003A  EchoSC3
2061                         0x003B  Rockwell ADPCM
2062                         0x003C  Rockwell Digit LK
2063                         0x003D  Xebec
2064                         0x0040  Antex Electronics G.721 ADPCM
2065                         0x0041  G.728 CELP
2066                         0x0042  MSG723
2067                         0x0050  MPEG Layer-2 or Layer-1
2068                         0x0052  RT24
2069                         0x0053  PAC
2070                         0x0055  MPEG Layer-3
2071                         0x0059  Lucent G.723
2072                         0x0060  Cirrus
2073                         0x0061  ESPCM
2074                         0x0062  Voxware
2075                         0x0063  Canopus Atrac
2076                         0x0064  G.726 ADPCM
2077                         0x0065  G.722 ADPCM
2078                         0x0066  DSAT
2079                         0x0067  DSAT Display
2080                         0x0069  Voxware Byte Aligned
2081                         0x0070  Voxware AC8
2082                         0x0071  Voxware AC10
2083                         0x0072  Voxware AC16
2084                         0x0073  Voxware AC20
2085                         0x0074  Voxware MetaVoice
2086                         0x0075  Voxware MetaSound
2087                         0x0076  Voxware RT29HW
2088                         0x0077  Voxware VR12
2089                         0x0078  Voxware VR18
2090                         0x0079  Voxware TQ40
2091                         0x0080  Softsound
2092                         0x0081  Voxware TQ60
2093                         0x0082  MSRT24
2094                         0x0083  G.729A
2095                         0x0084  MVI MV12
2096                         0x0085  DF G.726
2097                         0x0086  DF GSM610
2098                         0x0088  ISIAudio
2099                         0x0089  Onlive
2100                         0x0091  SBC24
2101                         0x0092  Dolby AC3 SPDIF
2102                         0x0093  MediaSonic G.723
2103                         0x0094  Aculab PLC    Prosody 8kbps
2104                         0x0097  ZyXEL ADPCM
2105                         0x0098  Philips LPCBB
2106                         0x0099  Packed
2107                         0x00FF  AAC
2108                         0x0100  Rhetorex ADPCM
2109                         0x0101  IBM mu-law
2110                         0x0102  IBM A-law
2111                         0x0103  IBM AVC Adaptive Differential Pulse Code Modulation (ADPCM)
2112                         0x0111  Vivo G.723
2113                         0x0112  Vivo Siren
2114                         0x0123  Digital G.723
2115                         0x0125  Sanyo LD ADPCM
2116                         0x0130  Sipro Lab Telecom ACELP NET
2117                         0x0131  Sipro Lab Telecom ACELP 4800
2118                         0x0132  Sipro Lab Telecom ACELP 8V3
2119                         0x0133  Sipro Lab Telecom G.729
2120                         0x0134  Sipro Lab Telecom G.729A
2121                         0x0135  Sipro Lab Telecom Kelvin
2122                         0x0140  Windows Media Video V8
2123