Wordpress 3.6
[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 /////////////////////////////////////////////////////////////////
7 // See readme.txt for more details                             //
8 /////////////////////////////////////////////////////////////////
9 //                                                             //
10 // module.audio-video.riff.php                                 //
11 // module for analyzing RIFF files                             //
12 // multiple formats supported by this module:                  //
13 //    Wave, AVI, AIFF/AIFC, (MP3,AC3)/RIFF, Wavpack v3, 8SVX   //
14 // dependencies: module.audio.mp3.php                          //
15 //               module.audio.ac3.php                          //
16 //               module.audio.dts.php                          //
17 //                                                            ///
18 /////////////////////////////////////////////////////////////////
19
20 /**
21 * @todo Parse AC-3/DTS audio inside WAVE correctly
22 * @todo Rewrite RIFF parser totally
23 */
24
25 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
26 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, true);
27 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.dts.php', __FILE__, true);
28
29 class getid3_riff extends getid3_handler
30 {
31
32         public function Analyze() {
33                 $info = &$this->getid3->info;
34
35                 // initialize these values to an empty array, otherwise they default to NULL
36                 // and you can't append array values to a NULL value
37                 $info['riff'] = array('raw'=>array());
38
39                 // Shortcuts
40                 $thisfile_riff             = &$info['riff'];
41                 $thisfile_riff_raw         = &$thisfile_riff['raw'];
42                 $thisfile_audio            = &$info['audio'];
43                 $thisfile_video            = &$info['video'];
44                 $thisfile_audio_dataformat = &$thisfile_audio['dataformat'];
45                 $thisfile_riff_audio       = &$thisfile_riff['audio'];
46                 $thisfile_riff_video       = &$thisfile_riff['video'];
47
48                 $Original['avdataoffset'] = $info['avdataoffset'];
49                 $Original['avdataend']    = $info['avdataend'];
50
51                 $this->fseek($info['avdataoffset']);
52                 $RIFFheader = $this->fread(12);
53                 $offset = $this->ftell();
54                 $RIFFtype    = substr($RIFFheader, 0, 4);
55                 $RIFFsize    = substr($RIFFheader, 4, 4);
56                 $RIFFsubtype = substr($RIFFheader, 8, 4);
57
58                 switch ($RIFFtype) {
59
60                         case 'FORM':  // AIFF, AIFC
61                                 $info['fileformat']   = 'aiff';
62                                 $thisfile_riff['header_size'] = $this->EitherEndian2Int($RIFFsize);
63                                 $thisfile_riff[$RIFFsubtype]  = $this->ParseRIFF($offset, ($offset + $thisfile_riff['header_size'] - 4));
64                                 break;
65
66                         case 'RIFF':  // AVI, WAV, etc
67                         case 'SDSS':  // SDSS is identical to RIFF, just renamed. Used by SmartSound QuickTracks (www.smartsound.com)
68                         case 'RMP3':  // RMP3 is identical to RIFF, just renamed. Used by [unknown program] when creating RIFF-MP3s
69                                 $info['fileformat']   = 'riff';
70                                 $thisfile_riff['header_size'] = $this->EitherEndian2Int($RIFFsize);
71                                 if ($RIFFsubtype == 'RMP3') {
72                                         // RMP3 is identical to WAVE, just renamed. Used by [unknown program] when creating RIFF-MP3s
73                                         $RIFFsubtype = 'WAVE';
74                                 }
75                                 $thisfile_riff[$RIFFsubtype]  = $this->ParseRIFF($offset, ($offset + $thisfile_riff['header_size'] - 4));
76                                 if (($info['avdataend'] - $info['filesize']) == 1) {
77                                         // LiteWave appears to incorrectly *not* pad actual output file
78                                         // to nearest WORD boundary so may appear to be short by one
79                                         // byte, in which case - skip warning
80                                         $info['avdataend'] = $info['filesize'];
81                                 }
82
83                                 $nextRIFFoffset = $Original['avdataoffset'] + 8 + $thisfile_riff['header_size']; // 8 = "RIFF" + 32-bit offset
84                                 while ($nextRIFFoffset < min($info['filesize'], $info['avdataend'])) {
85                                         try {
86                                                 $this->fseek($nextRIFFoffset);
87                                         } catch (getid3_exception $e) {
88                                                 if ($e->getCode() == 10) {
89                                                         //$this->warning('RIFF parser: '.$e->getMessage());
90                                                         $this->error('AVI extends beyond '.round(PHP_INT_MAX / 1073741824).'GB and PHP filesystem functions cannot read that far, playtime may be wrong');
91                                                         $this->warning('[avdataend] value may be incorrect, multiple AVIX chunks may be present');
92                                                         break;
93                                                 } else {
94                                                         throw $e;
95                                                 }
96                                         }
97                                         $nextRIFFheader = $this->fread(12);
98                                         if ($nextRIFFoffset == ($info['avdataend'] - 1)) {
99                                                 if (substr($nextRIFFheader, 0, 1) == "\x00") {
100                                                         // RIFF padded to WORD boundary, we're actually already at the end
101                                                         break;
102                                                 }
103                                         }
104                                         $nextRIFFheaderID =                         substr($nextRIFFheader, 0, 4);
105                                         $nextRIFFsize     = $this->EitherEndian2Int(substr($nextRIFFheader, 4, 4));
106                                         $nextRIFFtype     =                         substr($nextRIFFheader, 8, 4);
107                                         $chunkdata = array();
108                                         $chunkdata['offset'] = $nextRIFFoffset + 8;
109                                         $chunkdata['size']   = $nextRIFFsize;
110                                         $nextRIFFoffset = $chunkdata['offset'] + $chunkdata['size'];
111
112                                         switch ($nextRIFFheaderID) {
113
114                                                 case 'RIFF':
115                                                         $chunkdata['chunks'] = $this->ParseRIFF($chunkdata['offset'] + 4, $nextRIFFoffset);
116
117                                                         if (!isset($thisfile_riff[$nextRIFFtype])) {
118                                                                 $thisfile_riff[$nextRIFFtype] = array();
119                                                         }
120                                                         $thisfile_riff[$nextRIFFtype][] = $chunkdata;
121                                                         break;
122
123                                                 case 'JUNK':
124                                                         // ignore
125                                                         $thisfile_riff[$nextRIFFheaderID][] = $chunkdata;
126                                                         break;
127
128                                                 case 'IDVX':
129                                                         $info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunkdata['size']));
130                                                         break;
131
132                                                 default:
133                                                         if ($info['filesize'] == ($chunkdata['offset'] - 8 + 128)) {
134                                                                 $DIVXTAG = $nextRIFFheader.$this->fread(128 - 12);
135                                                                 if (substr($DIVXTAG, -7) == 'DIVXTAG') {
136                                                                         // 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
137                                                                         $this->warning('Found wrongly-structured DIVXTAG at offset '.($this->ftell() - 128).', parsing anyway');
138                                                                         $info['divxtag']['comments'] = self::ParseDIVXTAG($DIVXTAG);
139                                                                         break 2;
140                                                                 }
141                                                         }
142                                                         $this->warning('Expecting "RIFF|JUNK|IDVX" at '.$nextRIFFoffset.', found "'.$nextRIFFheaderID.'" ('.getid3_lib::PrintHexBytes($nextRIFFheaderID).') - skipping rest of file');
143                                                         break 2;
144
145                                         }
146
147                                 }
148                                 if ($RIFFsubtype == 'WAVE') {
149                                         $thisfile_riff_WAVE = &$thisfile_riff['WAVE'];
150                                 }
151                                 break;
152
153                         default:
154                                 $this->error('Cannot parse RIFF (this is maybe not a RIFF / WAV / AVI file?) - expecting "FORM|RIFF|SDSS|RMP3" found "'.$RIFFsubtype.'" instead');
155                                 unset($info['fileformat']);
156                                 return false;
157                 }
158
159                 $streamindex = 0;
160                 switch ($RIFFsubtype) {
161                         case 'WAVE':
162                                 if (empty($thisfile_audio['bitrate_mode'])) {
163                                         $thisfile_audio['bitrate_mode'] = 'cbr';
164                                 }
165                                 if (empty($thisfile_audio_dataformat)) {
166                                         $thisfile_audio_dataformat = 'wav';
167                                 }
168
169                                 if (isset($thisfile_riff_WAVE['data'][0]['offset'])) {
170                                         $info['avdataoffset'] = $thisfile_riff_WAVE['data'][0]['offset'] + 8;
171                                         $info['avdataend']    = $info['avdataoffset'] + $thisfile_riff_WAVE['data'][0]['size'];
172                                 }
173                                 if (isset($thisfile_riff_WAVE['fmt '][0]['data'])) {
174
175                                         $thisfile_riff_audio[$streamindex] = self::parseWAVEFORMATex($thisfile_riff_WAVE['fmt '][0]['data']);
176                                         $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag'];
177                                         if (!isset($thisfile_riff_audio[$streamindex]['bitrate']) || ($thisfile_riff_audio[$streamindex]['bitrate'] == 0)) {
178                                                 $info['error'][] = 'Corrupt RIFF file: bitrate_audio == zero';
179                                                 return false;
180                                         }
181                                         $thisfile_riff_raw['fmt '] = $thisfile_riff_audio[$streamindex]['raw'];
182                                         unset($thisfile_riff_audio[$streamindex]['raw']);
183                                         $thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex];
184
185                                         $thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]);
186                                         if (substr($thisfile_audio['codec'], 0, strlen('unknown: 0x')) == 'unknown: 0x') {
187                                                 $info['warning'][] = 'Audio codec = '.$thisfile_audio['codec'];
188                                         }
189                                         $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate'];
190
191                                         if (empty($info['playtime_seconds'])) { // may already be set (e.g. DTS-WAV)
192                                                 $info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $thisfile_audio['bitrate']);
193                                         }
194
195                                         $thisfile_audio['lossless'] = false;
196                                         if (isset($thisfile_riff_WAVE['data'][0]['offset']) && isset($thisfile_riff_raw['fmt ']['wFormatTag'])) {
197                                                 switch ($thisfile_riff_raw['fmt ']['wFormatTag']) {
198
199                                                         case 0x0001:  // PCM
200                                                                 $thisfile_audio['lossless'] = true;
201                                                                 break;
202
203                                                         case 0x2000:  // AC-3
204                                                                 $thisfile_audio_dataformat = 'ac3';
205                                                                 break;
206
207                                                         default:
208                                                                 // do nothing
209                                                                 break;
210
211                                                 }
212                                         }
213                                         $thisfile_audio['streams'][$streamindex]['wformattag']   = $thisfile_audio['wformattag'];
214                                         $thisfile_audio['streams'][$streamindex]['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
215                                         $thisfile_audio['streams'][$streamindex]['lossless']     = $thisfile_audio['lossless'];
216                                         $thisfile_audio['streams'][$streamindex]['dataformat']   = $thisfile_audio_dataformat;
217                                 }
218
219                                 if (isset($thisfile_riff_WAVE['rgad'][0]['data'])) {
220
221                                         // shortcuts
222                                         $rgadData = &$thisfile_riff_WAVE['rgad'][0]['data'];
223                                         $thisfile_riff_raw['rgad']    = array('track'=>array(), 'album'=>array());
224                                         $thisfile_riff_raw_rgad       = &$thisfile_riff_raw['rgad'];
225                                         $thisfile_riff_raw_rgad_track = &$thisfile_riff_raw_rgad['track'];
226                                         $thisfile_riff_raw_rgad_album = &$thisfile_riff_raw_rgad['album'];
227
228                                         $thisfile_riff_raw_rgad['fPeakAmplitude']      = getid3_lib::LittleEndian2Float(substr($rgadData, 0, 4));
229                                         $thisfile_riff_raw_rgad['nRadioRgAdjust']      =        $this->EitherEndian2Int(substr($rgadData, 4, 2));
230                                         $thisfile_riff_raw_rgad['nAudiophileRgAdjust'] =        $this->EitherEndian2Int(substr($rgadData, 6, 2));
231
232                                         $nRadioRgAdjustBitstring      = str_pad(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nRadioRgAdjust']), 16, '0', STR_PAD_LEFT);
233                                         $nAudiophileRgAdjustBitstring = str_pad(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nAudiophileRgAdjust']), 16, '0', STR_PAD_LEFT);
234                                         $thisfile_riff_raw_rgad_track['name']       = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 0, 3));
235                                         $thisfile_riff_raw_rgad_track['originator'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 3, 3));
236                                         $thisfile_riff_raw_rgad_track['signbit']    = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 6, 1));
237                                         $thisfile_riff_raw_rgad_track['adjustment'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 7, 9));
238                                         $thisfile_riff_raw_rgad_album['name']       = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 0, 3));
239                                         $thisfile_riff_raw_rgad_album['originator'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 3, 3));
240                                         $thisfile_riff_raw_rgad_album['signbit']    = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 6, 1));
241                                         $thisfile_riff_raw_rgad_album['adjustment'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 7, 9));
242
243                                         $thisfile_riff['rgad']['peakamplitude'] = $thisfile_riff_raw_rgad['fPeakAmplitude'];
244                                         if (($thisfile_riff_raw_rgad_track['name'] != 0) && ($thisfile_riff_raw_rgad_track['originator'] != 0)) {
245                                                 $thisfile_riff['rgad']['track']['name']            = getid3_lib::RGADnameLookup($thisfile_riff_raw_rgad_track['name']);
246                                                 $thisfile_riff['rgad']['track']['originator']      = getid3_lib::RGADoriginatorLookup($thisfile_riff_raw_rgad_track['originator']);
247                                                 $thisfile_riff['rgad']['track']['adjustment']      = getid3_lib::RGADadjustmentLookup($thisfile_riff_raw_rgad_track['adjustment'], $thisfile_riff_raw_rgad_track['signbit']);
248                                         }
249                                         if (($thisfile_riff_raw_rgad_album['name'] != 0) && ($thisfile_riff_raw_rgad_album['originator'] != 0)) {
250                                                 $thisfile_riff['rgad']['album']['name']       = getid3_lib::RGADnameLookup($thisfile_riff_raw_rgad_album['name']);
251                                                 $thisfile_riff['rgad']['album']['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_riff_raw_rgad_album['originator']);
252                                                 $thisfile_riff['rgad']['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($thisfile_riff_raw_rgad_album['adjustment'], $thisfile_riff_raw_rgad_album['signbit']);
253                                         }
254                                 }
255
256                                 if (isset($thisfile_riff_WAVE['fact'][0]['data'])) {
257                                         $thisfile_riff_raw['fact']['NumberOfSamples'] = $this->EitherEndian2Int(substr($thisfile_riff_WAVE['fact'][0]['data'], 0, 4));
258
259                                         // This should be a good way of calculating exact playtime,
260                                         // but some sample files have had incorrect number of samples,
261                                         // so cannot use this method
262
263                                         // if (!empty($thisfile_riff_raw['fmt ']['nSamplesPerSec'])) {
264                                         //     $info['playtime_seconds'] = (float) $thisfile_riff_raw['fact']['NumberOfSamples'] / $thisfile_riff_raw['fmt ']['nSamplesPerSec'];
265                                         // }
266                                 }
267                                 if (!empty($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'])) {
268                                         $thisfile_audio['bitrate'] = getid3_lib::CastAsInt($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'] * 8);
269                                 }
270
271                                 if (isset($thisfile_riff_WAVE['bext'][0]['data'])) {
272                                         // shortcut
273                                         $thisfile_riff_WAVE_bext_0 = &$thisfile_riff_WAVE['bext'][0];
274
275                                         $thisfile_riff_WAVE_bext_0['title']          =                         trim(substr($thisfile_riff_WAVE_bext_0['data'],   0, 256));
276                                         $thisfile_riff_WAVE_bext_0['author']         =                         trim(substr($thisfile_riff_WAVE_bext_0['data'], 256,  32));
277                                         $thisfile_riff_WAVE_bext_0['reference']      =                         trim(substr($thisfile_riff_WAVE_bext_0['data'], 288,  32));
278                                         $thisfile_riff_WAVE_bext_0['origin_date']    =                              substr($thisfile_riff_WAVE_bext_0['data'], 320,  10);
279                                         $thisfile_riff_WAVE_bext_0['origin_time']    =                              substr($thisfile_riff_WAVE_bext_0['data'], 330,   8);
280                                         $thisfile_riff_WAVE_bext_0['time_reference'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 338,   8));
281                                         $thisfile_riff_WAVE_bext_0['bwf_version']    = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 346,   1));
282                                         $thisfile_riff_WAVE_bext_0['reserved']       =                              substr($thisfile_riff_WAVE_bext_0['data'], 347, 254);
283                                         $thisfile_riff_WAVE_bext_0['coding_history'] =         explode("\r\n", trim(substr($thisfile_riff_WAVE_bext_0['data'], 601)));
284                                         if (preg_match('#^([0-9]{4}).([0-9]{2}).([0-9]{2})$#', $thisfile_riff_WAVE_bext_0['origin_date'], $matches_bext_date)) {
285                                                 if (preg_match('#^([0-9]{2}).([0-9]{2}).([0-9]{2})$#', $thisfile_riff_WAVE_bext_0['origin_time'], $matches_bext_time)) {
286                                                         list($dummy, $bext_timestamp['year'], $bext_timestamp['month'],  $bext_timestamp['day'])    = $matches_bext_date;
287                                                         list($dummy, $bext_timestamp['hour'], $bext_timestamp['minute'], $bext_timestamp['second']) = $matches_bext_time;
288                                                         $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']);
289                                                 } else {
290                                                         $info['warning'][] = 'RIFF.WAVE.BEXT.origin_time is invalid';
291                                                 }
292                                         } else {
293                                                 $info['warning'][] = 'RIFF.WAVE.BEXT.origin_date is invalid';
294                                         }
295                                         $thisfile_riff['comments']['author'][] = $thisfile_riff_WAVE_bext_0['author'];
296                                         $thisfile_riff['comments']['title'][]  = $thisfile_riff_WAVE_bext_0['title'];
297                                 }
298
299                                 if (isset($thisfile_riff_WAVE['MEXT'][0]['data'])) {
300                                         // shortcut
301                                         $thisfile_riff_WAVE_MEXT_0 = &$thisfile_riff_WAVE['MEXT'][0];
302
303                                         $thisfile_riff_WAVE_MEXT_0['raw']['sound_information']      = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 0, 2));
304                                         $thisfile_riff_WAVE_MEXT_0['flags']['homogenous']           = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0001);
305                                         if ($thisfile_riff_WAVE_MEXT_0['flags']['homogenous']) {
306                                                 $thisfile_riff_WAVE_MEXT_0['flags']['padding']          = ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0002) ? false : true;
307                                                 $thisfile_riff_WAVE_MEXT_0['flags']['22_or_44']         =        (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0004);
308                                                 $thisfile_riff_WAVE_MEXT_0['flags']['free_format']      =        (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0008);
309
310                                                 $thisfile_riff_WAVE_MEXT_0['nominal_frame_size']        = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 2, 2));
311                                         }
312                                         $thisfile_riff_WAVE_MEXT_0['anciliary_data_length']         = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 6, 2));
313                                         $thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def']     = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 8, 2));
314                                         $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_left']  = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0001);
315                                         $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_free']  = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0002);
316                                         $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_right'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0004);
317                                 }
318
319                                 if (isset($thisfile_riff_WAVE['cart'][0]['data'])) {
320                                         // shortcut
321                                         $thisfile_riff_WAVE_cart_0 = &$thisfile_riff_WAVE['cart'][0];
322
323                                         $thisfile_riff_WAVE_cart_0['version']              =                              substr($thisfile_riff_WAVE_cart_0['data'],   0,  4);
324                                         $thisfile_riff_WAVE_cart_0['title']                =                         trim(substr($thisfile_riff_WAVE_cart_0['data'],   4, 64));
325                                         $thisfile_riff_WAVE_cart_0['artist']               =                         trim(substr($thisfile_riff_WAVE_cart_0['data'],  68, 64));
326                                         $thisfile_riff_WAVE_cart_0['cut_id']               =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 132, 64));
327                                         $thisfile_riff_WAVE_cart_0['client_id']            =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 196, 64));
328                                         $thisfile_riff_WAVE_cart_0['category']             =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 260, 64));
329                                         $thisfile_riff_WAVE_cart_0['classification']       =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 324, 64));
330                                         $thisfile_riff_WAVE_cart_0['out_cue']              =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 388, 64));
331                                         $thisfile_riff_WAVE_cart_0['start_date']           =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 452, 10));
332                                         $thisfile_riff_WAVE_cart_0['start_time']           =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 462,  8));
333                                         $thisfile_riff_WAVE_cart_0['end_date']             =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 470, 10));
334                                         $thisfile_riff_WAVE_cart_0['end_time']             =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 480,  8));
335                                         $thisfile_riff_WAVE_cart_0['producer_app_id']      =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 488, 64));
336                                         $thisfile_riff_WAVE_cart_0['producer_app_version'] =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 552, 64));
337                                         $thisfile_riff_WAVE_cart_0['user_defined_text']    =                         trim(substr($thisfile_riff_WAVE_cart_0['data'], 616, 64));
338                                         $thisfile_riff_WAVE_cart_0['zero_db_reference']    = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_cart_0['data'], 680,  4), true);
339                                         for ($i = 0; $i < 8; $i++) {
340                                                 $thisfile_riff_WAVE_cart_0['post_time'][$i]['usage_fourcc'] =                  substr($thisfile_riff_WAVE_cart_0['data'], 684 + ($i * 8), 4);
341                                                 $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));
342                                         }
343                                         $thisfile_riff_WAVE_cart_0['url']              =                 trim(substr($thisfile_riff_WAVE_cart_0['data'],  748, 1024));
344                                         $thisfile_riff_WAVE_cart_0['tag_text']         = explode("\r\n", trim(substr($thisfile_riff_WAVE_cart_0['data'], 1772)));
345
346                                         $thisfile_riff['comments']['artist'][] = $thisfile_riff_WAVE_cart_0['artist'];
347                                         $thisfile_riff['comments']['title'][]  = $thisfile_riff_WAVE_cart_0['title'];
348                                 }
349
350                                 if (isset($thisfile_riff_WAVE['SNDM'][0]['data'])) {
351                                         // SoundMiner metadata
352
353                                         // shortcuts
354                                         $thisfile_riff_WAVE_SNDM_0      = &$thisfile_riff_WAVE['SNDM'][0];
355                                         $thisfile_riff_WAVE_SNDM_0_data = &$thisfile_riff_WAVE_SNDM_0['data'];
356                                         $SNDM_startoffset = 0;
357                                         $SNDM_endoffset   = $thisfile_riff_WAVE_SNDM_0['size'];
358
359                                         while ($SNDM_startoffset < $SNDM_endoffset) {
360                                                 $SNDM_thisTagOffset = 0;
361                                                 $SNDM_thisTagSize      = getid3_lib::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 4));
362                                                 $SNDM_thisTagOffset += 4;
363                                                 $SNDM_thisTagKey       =                           substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 4);
364                                                 $SNDM_thisTagOffset += 4;
365                                                 $SNDM_thisTagDataSize  = getid3_lib::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 2));
366                                                 $SNDM_thisTagOffset += 2;
367                                                 $SNDM_thisTagDataFlags = getid3_lib::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 2));
368                                                 $SNDM_thisTagOffset += 2;
369                                                 $SNDM_thisTagDataText =                            substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, $SNDM_thisTagDataSize);
370                                                 $SNDM_thisTagOffset += $SNDM_thisTagDataSize;
371
372                                                 if ($SNDM_thisTagSize != (4 + 4 + 2 + 2 + $SNDM_thisTagDataSize)) {
373                                                         $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).')';
374                                                         break;
375                                                 } elseif ($SNDM_thisTagSize <= 0) {
376                                                         $info['warning'][] = 'RIFF.WAVE.SNDM.data contains zero-size tag at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')';
377                                                         break;
378                                                 }
379                                                 $SNDM_startoffset += $SNDM_thisTagSize;
380
381                                                 $thisfile_riff_WAVE_SNDM_0['parsed_raw'][$SNDM_thisTagKey] = $SNDM_thisTagDataText;
382                                                 if ($parsedkey = self::waveSNDMtagLookup($SNDM_thisTagKey)) {
383                                                         $thisfile_riff_WAVE_SNDM_0['parsed'][$parsedkey] = $SNDM_thisTagDataText;
384                                                 } else {
385                                                         $info['warning'][] = 'RIFF.WAVE.SNDM contains unknown tag "'.$SNDM_thisTagKey.'" at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')';
386                                                 }
387                                         }
388
389                                         $tagmapping = array(
390                                                 'tracktitle'=>'title',
391                                                 'category'  =>'genre',
392                                                 'cdtitle'   =>'album',
393                                                 'tracktitle'=>'title',
394                                         );
395                                         foreach ($tagmapping as $fromkey => $tokey) {
396                                                 if (isset($thisfile_riff_WAVE_SNDM_0['parsed'][$fromkey])) {
397                                                         $thisfile_riff['comments'][$tokey][] = $thisfile_riff_WAVE_SNDM_0['parsed'][$fromkey];
398                                                 }
399                                         }
400                                 }
401
402                                 if (isset($thisfile_riff_WAVE['iXML'][0]['data'])) {
403                                         // requires functions simplexml_load_string and get_object_vars
404                                         if ($parsedXML = getid3_lib::XML2array($thisfile_riff_WAVE['iXML'][0]['data'])) {
405                                                 $thisfile_riff_WAVE['iXML'][0]['parsed'] = $parsedXML;
406                                                 if (isset($parsedXML['SPEED']['MASTER_SPEED'])) {
407                                                         @list($numerator, $denominator) = explode('/', $parsedXML['SPEED']['MASTER_SPEED']);
408                                                         $thisfile_riff_WAVE['iXML'][0]['master_speed'] = $numerator / ($denominator ? $denominator : 1000);
409                                                 }
410                                                 if (isset($parsedXML['SPEED']['TIMECODE_RATE'])) {
411                                                         @list($numerator, $denominator) = explode('/', $parsedXML['SPEED']['TIMECODE_RATE']);
412                                                         $thisfile_riff_WAVE['iXML'][0]['timecode_rate'] = $numerator / ($denominator ? $denominator : 1000);
413                                                 }
414                                                 if (isset($parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO']) && !empty($parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE']) && !empty($thisfile_riff_WAVE['iXML'][0]['timecode_rate'])) {
415                                                         $samples_since_midnight = floatval(ltrim($parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_HI'].$parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO'], '0'));
416                                                         $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] = $samples_since_midnight / $parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE'];
417                                                         $h = floor( $thisfile_riff_WAVE['iXML'][0]['timecode_seconds']       / 3600);
418                                                         $m = floor(($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600))      / 60);
419                                                         $s = floor( $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600) - ($m * 60));
420                                                         $f =       ($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600) - ($m * 60) - $s) * $thisfile_riff_WAVE['iXML'][0]['timecode_rate'];
421                                                         $thisfile_riff_WAVE['iXML'][0]['timecode_string']       = sprintf('%02d:%02d:%02d:%05.2f', $h, $m, $s,       $f);
422                                                         $thisfile_riff_WAVE['iXML'][0]['timecode_string_round'] = sprintf('%02d:%02d:%02d:%02d',   $h, $m, $s, round($f));
423                                                 }
424                                                 unset($parsedXML);
425                                         }
426                                 }
427
428
429
430                                 if (!isset($thisfile_audio['bitrate']) && isset($thisfile_riff_audio[$streamindex]['bitrate'])) {
431                                         $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate'];
432                                         $info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $thisfile_audio['bitrate']);
433                                 }
434
435                                 if (!empty($info['wavpack'])) {
436                                         $thisfile_audio_dataformat = 'wavpack';
437                                         $thisfile_audio['bitrate_mode'] = 'vbr';
438                                         $thisfile_audio['encoder']      = 'WavPack v'.$info['wavpack']['version'];
439
440                                         // Reset to the way it was - RIFF parsing will have messed this up
441                                         $info['avdataend']        = $Original['avdataend'];
442                                         $thisfile_audio['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
443
444                                         $this->fseek($info['avdataoffset'] - 44);
445                                         $RIFFdata = $this->fread(44);
446                                         $OrignalRIFFheaderSize = getid3_lib::LittleEndian2Int(substr($RIFFdata,  4, 4)) +  8;
447                                         $OrignalRIFFdataSize   = getid3_lib::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44;
448
449                                         if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) {
450                                                 $info['avdataend'] -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
451                                                 $this->fseek($info['avdataend']);
452                                                 $RIFFdata .= $this->fread($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
453                                         }
454
455                                         // move the data chunk after all other chunks (if any)
456                                         // so that the RIFF parser doesn't see EOF when trying
457                                         // to skip over the data chunk
458                                         $RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8);
459                                         $getid3_riff = new getid3_riff($this->getid3);
460                                         $getid3_riff->ParseRIFFdata($RIFFdata);
461                                         unset($getid3_riff);
462                                 }
463
464                                 if (isset($thisfile_riff_raw['fmt ']['wFormatTag'])) {
465                                         switch ($thisfile_riff_raw['fmt ']['wFormatTag']) {
466                                                 case 0x0001: // PCM
467                                                         if (!empty($info['ac3'])) {
468                                                                 // Dolby Digital WAV files masquerade as PCM-WAV, but they're not
469                                                                 $thisfile_audio['wformattag']  = 0x2000;
470                                                                 $thisfile_audio['codec']       = self::wFormatTagLookup($thisfile_audio['wformattag']);
471                                                                 $thisfile_audio['lossless']    = false;
472                                                                 $thisfile_audio['bitrate']     = $info['ac3']['bitrate'];
473                                                                 $thisfile_audio['sample_rate'] = $info['ac3']['sample_rate'];
474                                                         }
475                                                         if (!empty($info['dts'])) {
476                                                                 // Dolby DTS files masquerade as PCM-WAV, but they're not
477                                                                 $thisfile_audio['wformattag']  = 0x2001;
478                                                                 $thisfile_audio['codec']       = self::wFormatTagLookup($thisfile_audio['wformattag']);
479                                                                 $thisfile_audio['lossless']    = false;
480                                                                 $thisfile_audio['bitrate']     = $info['dts']['bitrate'];
481                                                                 $thisfile_audio['sample_rate'] = $info['dts']['sample_rate'];
482                                                         }
483                                                         break;
484                                                 case 0x08AE: // ClearJump LiteWave
485                                                         $thisfile_audio['bitrate_mode'] = 'vbr';
486                                                         $thisfile_audio_dataformat   = 'litewave';
487
488                                                         //typedef struct tagSLwFormat {
489                                                         //  WORD    m_wCompFormat;     // low byte defines compression method, high byte is compression flags
490                                                         //  DWORD   m_dwScale;         // scale factor for lossy compression
491                                                         //  DWORD   m_dwBlockSize;     // number of samples in encoded blocks
492                                                         //  WORD    m_wQuality;        // alias for the scale factor
493                                                         //  WORD    m_wMarkDistance;   // distance between marks in bytes
494                                                         //  WORD    m_wReserved;
495                                                         //
496                                                         //  //following paramters are ignored if CF_FILESRC is not set
497                                                         //  DWORD   m_dwOrgSize;       // original file size in bytes
498                                                         //  WORD    m_bFactExists;     // indicates if 'fact' chunk exists in the original file
499                                                         //  DWORD   m_dwRiffChunkSize; // riff chunk size in the original file
500                                                         //
501                                                         //  PCMWAVEFORMAT m_OrgWf;     // original wave format
502                                                         // }SLwFormat, *PSLwFormat;
503
504                                                         // shortcut
505                                                         $thisfile_riff['litewave']['raw'] = array();
506                                                         $riff_litewave     = &$thisfile_riff['litewave'];
507                                                         $riff_litewave_raw = &$riff_litewave['raw'];
508
509                                                         $flags = array(
510                                                                 'compression_method' => 1,
511                                                                 'compression_flags'  => 1,
512                                                                 'm_dwScale'          => 4,
513                                                                 'm_dwBlockSize'      => 4,
514                                                                 'm_wQuality'         => 2,
515                                                                 'm_wMarkDistance'    => 2,
516                                                                 'm_wReserved'        => 2,
517                                                                 'm_dwOrgSize'        => 4,
518                                                                 'm_bFactExists'      => 2,
519                                                                 'm_dwRiffChunkSize'  => 4,
520                                                         );
521                                                         $litewave_offset = 18;
522                                                         foreach ($flags as $flag => $length) {
523                                                                 $riff_litewave_raw[$flag] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], $litewave_offset, $length));
524                                                                 $litewave_offset += $length;
525                                                         }
526
527                                                         //$riff_litewave['quality_factor'] = intval(round((2000 - $riff_litewave_raw['m_dwScale']) / 20));
528                                                         $riff_litewave['quality_factor'] = $riff_litewave_raw['m_wQuality'];
529
530                                                         $riff_litewave['flags']['raw_source']    = ($riff_litewave_raw['compression_flags'] & 0x01) ? false : true;
531                                                         $riff_litewave['flags']['vbr_blocksize'] = ($riff_litewave_raw['compression_flags'] & 0x02) ? false : true;
532                                                         $riff_litewave['flags']['seekpoints']    =        (bool) ($riff_litewave_raw['compression_flags'] & 0x04);
533
534                                                         $thisfile_audio['lossless']        = (($riff_litewave_raw['m_wQuality'] == 100) ? true : false);
535                                                         $thisfile_audio['encoder_options'] = '-q'.$riff_litewave['quality_factor'];
536                                                         break;
537
538                                                 default:
539                                                         break;
540                                         }
541                                 }
542                                 if ($info['avdataend'] > $info['filesize']) {
543                                         switch (!empty($thisfile_audio_dataformat) ? $thisfile_audio_dataformat : '') {
544                                                 case 'wavpack': // WavPack
545                                                 case 'lpac':    // LPAC
546                                                 case 'ofr':     // OptimFROG
547                                                 case 'ofs':     // OptimFROG DualStream
548                                                         // lossless compressed audio formats that keep original RIFF headers - skip warning
549                                                         break;
550
551                                                 case 'litewave':
552                                                         if (($info['avdataend'] - $info['filesize']) == 1) {
553                                                                 // LiteWave appears to incorrectly *not* pad actual output file
554                                                                 // to nearest WORD boundary so may appear to be short by one
555                                                                 // byte, in which case - skip warning
556                                                         } else {
557                                                                 // Short by more than one byte, throw warning
558                                                                 $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)';
559                                                                 $info['avdataend'] = $info['filesize'];
560                                                         }
561                                                         break;
562
563                                                 default:
564                                                         if ((($info['avdataend'] - $info['filesize']) == 1) && (($thisfile_riff[$RIFFsubtype]['data'][0]['size'] % 2) == 0) && ((($info['filesize'] - $info['avdataoffset']) % 2) == 1)) {
565                                                                 // output file appears to be incorrectly *not* padded to nearest WORD boundary
566                                                                 // Output less severe warning
567                                                                 $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)';
568                                                                 $info['avdataend'] = $info['filesize'];
569                                                         } else {
570                                                                 // Short by more than one byte, throw warning
571                                                                 $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)';
572                                                                 $info['avdataend'] = $info['filesize'];
573                                                         }
574                                                         break;
575                                         }
576                                 }
577                                 if (!empty($info['mpeg']['audio']['LAME']['audio_bytes'])) {
578                                         if ((($info['avdataend'] - $info['avdataoffset']) - $info['mpeg']['audio']['LAME']['audio_bytes']) == 1) {
579                                                 $info['avdataend']--;
580                                                 $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
581                                         }
582                                 }
583                                 if (isset($thisfile_audio_dataformat) && ($thisfile_audio_dataformat == 'ac3')) {
584                                         unset($thisfile_audio['bits_per_sample']);
585                                         if (!empty($info['ac3']['bitrate']) && ($info['ac3']['bitrate'] != $thisfile_audio['bitrate'])) {
586                                                 $thisfile_audio['bitrate'] = $info['ac3']['bitrate'];
587                                         }
588                                 }
589                                 break;
590
591                         case 'AVI ':
592                                 $thisfile_video['bitrate_mode'] = 'vbr'; // maybe not, but probably
593                                 $thisfile_video['dataformat']   = 'avi';
594                                 $info['mime_type']      = 'video/avi';
595
596                                 if (isset($thisfile_riff[$RIFFsubtype]['movi']['offset'])) {
597                                         $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['movi']['offset'] + 8;
598                                         if (isset($thisfile_riff['AVIX'])) {
599                                                 $info['avdataend'] = $thisfile_riff['AVIX'][(count($thisfile_riff['AVIX']) - 1)]['chunks']['movi']['offset'] + $thisfile_riff['AVIX'][(count($thisfile_riff['AVIX']) - 1)]['chunks']['movi']['size'];
600                                         } else {
601                                                 $info['avdataend'] = $thisfile_riff['AVI ']['movi']['offset'] + $thisfile_riff['AVI ']['movi']['size'];
602                                         }
603                                         if ($info['avdataend'] > $info['filesize']) {
604                                                 $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)';
605                                                 $info['avdataend'] = $info['filesize'];
606                                         }
607                                 }
608
609                                 if (isset($thisfile_riff['AVI ']['hdrl']['strl']['indx'])) {
610                                         //$bIndexType = array(
611                                         //      0x00 => 'AVI_INDEX_OF_INDEXES',
612                                         //      0x01 => 'AVI_INDEX_OF_CHUNKS',
613                                         //      0x80 => 'AVI_INDEX_IS_DATA',
614                                         //);
615                                         //$bIndexSubtype = array(
616                                         //      0x01 => array(
617                                         //              0x01 => 'AVI_INDEX_2FIELD',
618                                         //      ),
619                                         //);
620                                         foreach ($thisfile_riff['AVI ']['hdrl']['strl']['indx'] as $streamnumber => $steamdataarray) {
621                                                 $ahsisd = &$thisfile_riff['AVI ']['hdrl']['strl']['indx'][$streamnumber]['data'];
622
623                                                 $thisfile_riff_raw['indx'][$streamnumber]['wLongsPerEntry'] = $this->EitherEndian2Int(substr($ahsisd,  0, 2));
624                                                 $thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType']  = $this->EitherEndian2Int(substr($ahsisd,  2, 1));
625                                                 $thisfile_riff_raw['indx'][$streamnumber]['bIndexType']     = $this->EitherEndian2Int(substr($ahsisd,  3, 1));
626                                                 $thisfile_riff_raw['indx'][$streamnumber]['nEntriesInUse']  = $this->EitherEndian2Int(substr($ahsisd,  4, 4));
627                                                 $thisfile_riff_raw['indx'][$streamnumber]['dwChunkId']      =                         substr($ahsisd,  8, 4);
628                                                 $thisfile_riff_raw['indx'][$streamnumber]['dwReserved']     = $this->EitherEndian2Int(substr($ahsisd, 12, 4));
629
630                                                 //$thisfile_riff_raw['indx'][$streamnumber]['bIndexType_name']    =    $bIndexType[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']];
631                                                 //$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType_name'] = $bIndexSubtype[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']][$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType']];
632
633                                                 unset($ahsisd);
634                                         }
635                                 }
636                                 if (isset($thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data'])) {
637                                         $avihData = $thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data'];
638
639                                         // shortcut
640                                         $thisfile_riff_raw['avih'] = array();
641                                         $thisfile_riff_raw_avih = &$thisfile_riff_raw['avih'];
642
643                                         $thisfile_riff_raw_avih['dwMicroSecPerFrame']    = $this->EitherEndian2Int(substr($avihData,  0, 4)); // frame display rate (or 0L)
644                                         if ($thisfile_riff_raw_avih['dwMicroSecPerFrame'] == 0) {
645                                                 $info['error'][] = 'Corrupt RIFF file: avih.dwMicroSecPerFrame == zero';
646                                                 return false;
647                                         }
648
649                                         $flags = array(
650                                                 'dwMaxBytesPerSec',       // max. transfer rate
651                                                 'dwPaddingGranularity',   // pad to multiples of this size; normally 2K.
652                                                 'dwFlags',                // the ever-present flags
653                                                 'dwTotalFrames',          // # frames in file
654                                                 'dwInitialFrames',        //
655                                                 'dwStreams',              //
656                                                 'dwSuggestedBufferSize',  //
657                                                 'dwWidth',                //
658                                                 'dwHeight',               //
659                                                 'dwScale',                //
660                                                 'dwRate',                 //
661                                                 'dwStart',                //
662                                                 'dwLength',               //
663                                         );
664                                         $avih_offset = 4;
665                                         foreach ($flags as $flag) {
666                                                 $thisfile_riff_raw_avih[$flag] = $this->EitherEndian2Int(substr($avihData, $avih_offset, 4));
667                                                 $avih_offset += 4;
668                                         }
669
670                                         $flags = array(
671                                                 'hasindex'     => 0x00000010,
672                                                 'mustuseindex' => 0x00000020,
673                                                 'interleaved'  => 0x00000100,
674                                                 'trustcktype'  => 0x00000800,
675                                                 'capturedfile' => 0x00010000,
676                                                 'copyrighted'  => 0x00020010,
677                                         );
678                     foreach ($flags as $flag => $value) {
679                                                 $thisfile_riff_raw_avih['flags'][$flag] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & $value);
680                                         }
681
682                                         // shortcut
683                                         $thisfile_riff_video[$streamindex] = array();
684                                         $thisfile_riff_video_current = &$thisfile_riff_video[$streamindex];
685
686                                         if ($thisfile_riff_raw_avih['dwWidth'] > 0) {
687                                                 $thisfile_riff_video_current['frame_width'] = $thisfile_riff_raw_avih['dwWidth'];
688                                                 $thisfile_video['resolution_x']             = $thisfile_riff_video_current['frame_width'];
689                                         }
690                                         if ($thisfile_riff_raw_avih['dwHeight'] > 0) {
691                                                 $thisfile_riff_video_current['frame_height'] = $thisfile_riff_raw_avih['dwHeight'];
692                                                 $thisfile_video['resolution_y']              = $thisfile_riff_video_current['frame_height'];
693                                         }
694                                         if ($thisfile_riff_raw_avih['dwTotalFrames'] > 0) {
695                                                 $thisfile_riff_video_current['total_frames'] = $thisfile_riff_raw_avih['dwTotalFrames'];
696                                                 $thisfile_video['total_frames']              = $thisfile_riff_video_current['total_frames'];
697                                         }
698
699                                         $thisfile_riff_video_current['frame_rate'] = round(1000000 / $thisfile_riff_raw_avih['dwMicroSecPerFrame'], 3);
700                                         $thisfile_video['frame_rate'] = $thisfile_riff_video_current['frame_rate'];
701                                 }
702                                 if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][0]['data'])) {
703                                         if (is_array($thisfile_riff['AVI ']['hdrl']['strl']['strh'])) {
704                                                 for ($i = 0; $i < count($thisfile_riff['AVI ']['hdrl']['strl']['strh']); $i++) {
705                                                         if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data'])) {
706                                                                 $strhData = $thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data'];
707                                                                 $strhfccType = substr($strhData,  0, 4);
708
709                                                                 if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data'])) {
710                                                                         $strfData = $thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data'];
711
712                                                                         // shortcut
713                                                                         $thisfile_riff_raw_strf_strhfccType_streamindex = &$thisfile_riff_raw['strf'][$strhfccType][$streamindex];
714
715                                                                         switch ($strhfccType) {
716                                                                                 case 'auds':
717                                                                                         $thisfile_audio['bitrate_mode'] = 'cbr';
718                                                                                         $thisfile_audio_dataformat      = 'wav';
719                                                                                         if (isset($thisfile_riff_audio) && is_array($thisfile_riff_audio)) {
720                                                                                                 $streamindex = count($thisfile_riff_audio);
721                                                                                         }
722
723                                                                                         $thisfile_riff_audio[$streamindex] = self::parseWAVEFORMATex($strfData);
724                                                                                         $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag'];
725
726                                                                                         // shortcut
727                                                                                         $thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex];
728                                                                                         $thisfile_audio_streams_currentstream = &$thisfile_audio['streams'][$streamindex];
729
730                                                                                         if ($thisfile_audio_streams_currentstream['bits_per_sample'] == 0) {
731                                                                                                 unset($thisfile_audio_streams_currentstream['bits_per_sample']);
732                                                                                         }
733                                                                                         $thisfile_audio_streams_currentstream['wformattag'] = $thisfile_audio_streams_currentstream['raw']['wFormatTag'];
734                                                                                         unset($thisfile_audio_streams_currentstream['raw']);
735
736                                                                                         // shortcut
737                                                                                         $thisfile_riff_raw['strf'][$strhfccType][$streamindex] = $thisfile_riff_audio[$streamindex]['raw'];
738
739                                                                                         unset($thisfile_riff_audio[$streamindex]['raw']);
740                                                                                         $thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]);
741
742                                                                                         $thisfile_audio['lossless'] = false;
743                                                                                         switch ($thisfile_riff_raw_strf_strhfccType_streamindex['wFormatTag']) {
744                                                                                                 case 0x0001:  // PCM
745                                                                                                         $thisfile_audio_dataformat  = 'wav';
746                                                                                                         $thisfile_audio['lossless'] = true;
747                                                                                                         break;
748
749                                                                                                 case 0x0050: // MPEG Layer 2 or Layer 1
750                                                                                                         $thisfile_audio_dataformat = 'mp2'; // Assume Layer-2
751                                                                                                         break;
752
753                                                                                                 case 0x0055: // MPEG Layer 3
754                                                                                                         $thisfile_audio_dataformat = 'mp3';
755                                                                                                         break;
756
757                                                                                                 case 0x00FF: // AAC
758                                                                                                         $thisfile_audio_dataformat = 'aac';
759                                                                                                         break;
760
761                                                                                                 case 0x0161: // Windows Media v7 / v8 / v9
762                                                                                                 case 0x0162: // Windows Media Professional v9
763                                                                                                 case 0x0163: // Windows Media Lossess v9
764                                                                                                         $thisfile_audio_dataformat = 'wma';
765                                                                                                         break;
766
767                                                                                                 case 0x2000: // AC-3
768                                                                                                         $thisfile_audio_dataformat = 'ac3';
769                                                                                                         break;
770
771                                                                                                 case 0x2001: // DTS
772                                                                                                         $thisfile_audio_dataformat = 'dts';
773                                                                                                         break;
774
775                                                                                                 default:
776                                                                                                         $thisfile_audio_dataformat = 'wav';
777                                                                                                         break;
778                                                                                         }
779                                                                                         $thisfile_audio_streams_currentstream['dataformat']   = $thisfile_audio_dataformat;
780                                                                                         $thisfile_audio_streams_currentstream['lossless']     = $thisfile_audio['lossless'];
781                                                                                         $thisfile_audio_streams_currentstream['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
782                                                                                         break;
783
784
785                                                                                 case 'iavs':
786                                                                                 case 'vids':
787                                                                                         // shortcut
788                                                                                         $thisfile_riff_raw['strh'][$i]                  = array();
789                                                                                         $thisfile_riff_raw_strh_current                 = &$thisfile_riff_raw['strh'][$i];
790
791                                                                                         $thisfile_riff_raw_strh_current['fccType']               =                         substr($strhData,  0, 4);  // same as $strhfccType;
792                                                                                         $thisfile_riff_raw_strh_current['fccHandler']            =                         substr($strhData,  4, 4);
793                                                                                         $thisfile_riff_raw_strh_current['dwFlags']               = $this->EitherEndian2Int(substr($strhData,  8, 4)); // Contains AVITF_* flags
794                                                                                         $thisfile_riff_raw_strh_current['wPriority']             = $this->EitherEndian2Int(substr($strhData, 12, 2));
795                                                                                         $thisfile_riff_raw_strh_current['wLanguage']             = $this->EitherEndian2Int(substr($strhData, 14, 2));
796                                                                                         $thisfile_riff_raw_strh_current['dwInitialFrames']       = $this->EitherEndian2Int(substr($strhData, 16, 4));
797                                                                                         $thisfile_riff_raw_strh_current['dwScale']               = $this->EitherEndian2Int(substr($strhData, 20, 4));
798                                                                                         $thisfile_riff_raw_strh_current['dwRate']                = $this->EitherEndian2Int(substr($strhData, 24, 4));
799                                                                                         $thisfile_riff_raw_strh_current['dwStart']               = $this->EitherEndian2Int(substr($strhData, 28, 4));
800                                                                                         $thisfile_riff_raw_strh_current['dwLength']              = $this->EitherEndian2Int(substr($strhData, 32, 4));
801                                                                                         $thisfile_riff_raw_strh_current['dwSuggestedBufferSize'] = $this->EitherEndian2Int(substr($strhData, 36, 4));
802                                                                                         $thisfile_riff_raw_strh_current['dwQuality']             = $this->EitherEndian2Int(substr($strhData, 40, 4));
803                                                                                         $thisfile_riff_raw_strh_current['dwSampleSize']          = $this->EitherEndian2Int(substr($strhData, 44, 4));
804                                                                                         $thisfile_riff_raw_strh_current['rcFrame']               = $this->EitherEndian2Int(substr($strhData, 48, 4));
805
806                                                                                         $thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_riff_raw_strh_current['fccHandler']);
807                                                                                         $thisfile_video['fourcc']             = $thisfile_riff_raw_strh_current['fccHandler'];
808                                                                                         if (!$thisfile_riff_video_current['codec'] && isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) && self::fourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
809                                                                                                 $thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']);
810                                                                                                 $thisfile_video['fourcc']             = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'];
811                                                                                         }
812                                                                                         $thisfile_video['codec']              = $thisfile_riff_video_current['codec'];
813                                                                                         $thisfile_video['pixel_aspect_ratio'] = (float) 1;
814                                                                                         switch ($thisfile_riff_raw_strh_current['fccHandler']) {
815                                                                                                 case 'HFYU': // Huffman Lossless Codec
816                                                                                                 case 'IRAW': // Intel YUV Uncompressed
817                                                                                                 case 'YUY2': // Uncompressed YUV 4:2:2
818                                                                                                         $thisfile_video['lossless'] = true;
819                                                                                                         break;
820
821                                                                                                 default:
822                                                                                                         $thisfile_video['lossless'] = false;
823                                                                                                         break;
824                                                                                         }
825
826                                                                                         switch ($strhfccType) {
827                                                                                                 case 'vids':
828                                                                                                         $thisfile_riff_raw_strf_strhfccType_streamindex = self::ParseBITMAPINFOHEADER(substr($strfData, 0, 40), ($info['fileformat'] == 'riff'));
829                                                                                                         $thisfile_video['bits_per_sample'] = $thisfile_riff_raw_strf_strhfccType_streamindex['biBitCount'];
830
831                                                                                                         if ($thisfile_riff_video_current['codec'] == 'DV') {
832                                                                                                                 $thisfile_riff_video_current['dv_type'] = 2;
833                                                                                                         }
834                                                                                                         break;
835
836                                                                                                 case 'iavs':
837                                                                                                         $thisfile_riff_video_current['dv_type'] = 1;
838                                                                                                         break;
839                                                                                         }
840                                                                                         break;
841
842                                                                                 default:
843                                                                                         $info['warning'][] = 'Unhandled fccType for stream ('.$i.'): "'.$strhfccType.'"';
844                                                                                         break;
845
846                                                                         }
847                                                                 }
848                                                         }
849
850                                                         if (isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
851
852                                                                 $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'];
853                                                                 if (self::fourccLookup($thisfile_video['fourcc'])) {
854                                                                         $thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_video['fourcc']);
855                                                                         $thisfile_video['codec']              = $thisfile_riff_video_current['codec'];
856                                                                 }
857
858                                                                 switch ($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) {
859                                                                         case 'HFYU': // Huffman Lossless Codec
860                                                                         case 'IRAW': // Intel YUV Uncompressed
861                                                                         case 'YUY2': // Uncompressed YUV 4:2:2
862                                                                                 $thisfile_video['lossless']        = true;
863                                                                                 //$thisfile_video['bits_per_sample'] = 24;
864                                                                                 break;
865
866                                                                         default:
867                                                                                 $thisfile_video['lossless']        = false;
868                                                                                 //$thisfile_video['bits_per_sample'] = 24;
869                                                                                 break;
870                                                                 }
871
872                                                         }
873                                                 }
874                                         }
875                                 }
876                                 break;
877
878                         case 'CDDA':
879                                 $thisfile_audio['bitrate_mode'] = 'cbr';
880                                 $thisfile_audio_dataformat      = 'cda';
881                                 $thisfile_audio['lossless']     = true;
882                                 unset($info['mime_type']);
883
884                                 $info['avdataoffset'] = 44;
885
886                                 if (isset($thisfile_riff['CDDA']['fmt '][0]['data'])) {
887                                         // shortcut
888                                         $thisfile_riff_CDDA_fmt_0 = &$thisfile_riff['CDDA']['fmt '][0];
889
890                                         $thisfile_riff_CDDA_fmt_0['unknown1']           = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'],  0, 2));
891                                         $thisfile_riff_CDDA_fmt_0['track_num']          = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'],  2, 2));
892                                         $thisfile_riff_CDDA_fmt_0['disc_id']            = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'],  4, 4));
893                                         $thisfile_riff_CDDA_fmt_0['start_offset_frame'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'],  8, 4));
894                                         $thisfile_riff_CDDA_fmt_0['playtime_frames']    = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 12, 4));
895                                         $thisfile_riff_CDDA_fmt_0['unknown6']           = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 16, 4));
896                                         $thisfile_riff_CDDA_fmt_0['unknown7']           = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 20, 4));
897
898                                         $thisfile_riff_CDDA_fmt_0['start_offset_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['start_offset_frame'] / 75;
899                                         $thisfile_riff_CDDA_fmt_0['playtime_seconds']     = (float) $thisfile_riff_CDDA_fmt_0['playtime_frames'] / 75;
900                                         $info['comments']['track']                = $thisfile_riff_CDDA_fmt_0['track_num'];
901                                         $info['playtime_seconds']                 = $thisfile_riff_CDDA_fmt_0['playtime_seconds'];
902
903                                         // hardcoded data for CD-audio
904                                         $thisfile_audio['sample_rate']     = 44100;
905                                         $thisfile_audio['channels']        = 2;
906                                         $thisfile_audio['bits_per_sample'] = 16;
907                                         $thisfile_audio['bitrate']         = $thisfile_audio['sample_rate'] * $thisfile_audio['channels'] * $thisfile_audio['bits_per_sample'];
908                                         $thisfile_audio['bitrate_mode']    = 'cbr';
909                                 }
910                                 break;
911
912
913                         case 'AIFF':
914                         case 'AIFC':
915                                 $thisfile_audio['bitrate_mode'] = 'cbr';
916                                 $thisfile_audio_dataformat      = 'aiff';
917                                 $thisfile_audio['lossless']     = true;
918                                 $info['mime_type']      = 'audio/x-aiff';
919
920                                 if (isset($thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'])) {
921                                         $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'] + 8;
922                                         $info['avdataend']    = $info['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['SSND'][0]['size'];
923                                         if ($info['avdataend'] > $info['filesize']) {
924                                                 if (($info['avdataend'] == ($info['filesize'] + 1)) && (($info['filesize'] % 2) == 1)) {
925                                                         // structures rounded to 2-byte boundary, but dumb encoders
926                                                         // forget to pad end of file to make this actually work
927                                                 } else {
928                                                         $info['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['SSND'][0]['size'].' bytes of audio data, only '.($info['filesize'] - $info['avdataoffset']).' bytes found';
929                                                 }
930                                                 $info['avdataend'] = $info['filesize'];
931                                         }
932                                 }
933
934                                 if (isset($thisfile_riff[$RIFFsubtype]['COMM'][0]['data'])) {
935
936                                         // shortcut
937                                         $thisfile_riff_RIFFsubtype_COMM_0_data = &$thisfile_riff[$RIFFsubtype]['COMM'][0]['data'];
938
939                                         $thisfile_riff_audio['channels']         =         getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data,  0,  2), true);
940                                         $thisfile_riff_audio['total_samples']    =         getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data,  2,  4), false);
941                                         $thisfile_riff_audio['bits_per_sample']  =         getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data,  6,  2), true);
942                                         $thisfile_riff_audio['sample_rate']      = (int) getid3_lib::BigEndian2Float(substr($thisfile_riff_RIFFsubtype_COMM_0_data,  8, 10));
943
944                                         if ($thisfile_riff[$RIFFsubtype]['COMM'][0]['size'] > 18) {
945                                                 $thisfile_riff_audio['codec_fourcc'] =                                   substr($thisfile_riff_RIFFsubtype_COMM_0_data, 18,  4);
946                                                 $CodecNameSize                       =         getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 22,  1), false);
947                                                 $thisfile_riff_audio['codec_name']   =                                   substr($thisfile_riff_RIFFsubtype_COMM_0_data, 23,  $CodecNameSize);
948                                                 switch ($thisfile_riff_audio['codec_name']) {
949                                                         case 'NONE':
950                                                                 $thisfile_audio['codec']    = 'Pulse Code Modulation (PCM)';
951                                                                 $thisfile_audio['lossless'] = true;
952                                                                 break;
953
954                                                         case '':
955                                                                 switch ($thisfile_riff_audio['codec_fourcc']) {
956                                                                         // http://developer.apple.com/qa/snd/snd07.html
957                                                                         case 'sowt':
958                                                                                 $thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Little-Endian PCM';
959                                                                                 $thisfile_audio['lossless'] = true;
960                                                                                 break;
961
962                                                                         case 'twos':
963                                                                                 $thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Big-Endian PCM';
964                                                                                 $thisfile_audio['lossless'] = true;
965                                                                                 break;
966
967                                                                         default:
968                                                                                 break;
969                                                                 }
970                                                                 break;
971
972                                                         default:
973                                                                 $thisfile_audio['codec']    = $thisfile_riff_audio['codec_name'];
974                                                                 $thisfile_audio['lossless'] = false;
975                                                                 break;
976                                                 }
977                                         }
978
979                                         $thisfile_audio['channels']        = $thisfile_riff_audio['channels'];
980                                         if ($thisfile_riff_audio['bits_per_sample'] > 0) {
981                                                 $thisfile_audio['bits_per_sample'] = $thisfile_riff_audio['bits_per_sample'];
982                                         }
983                                         $thisfile_audio['sample_rate']     = $thisfile_riff_audio['sample_rate'];
984                                         if ($thisfile_audio['sample_rate'] == 0) {
985                                                 $info['error'][] = 'Corrupted AIFF file: sample_rate == zero';
986                                                 return false;
987                                         }
988                                         $info['playtime_seconds'] = $thisfile_riff_audio['total_samples'] / $thisfile_audio['sample_rate'];
989                                 }
990
991                                 if (isset($thisfile_riff[$RIFFsubtype]['COMT'])) {
992                                         $offset = 0;
993                                         $CommentCount                                   = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false);
994                                         $offset += 2;
995                                         for ($i = 0; $i < $CommentCount; $i++) {
996                                                 $info['comments_raw'][$i]['timestamp']      = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 4), false);
997                                                 $offset += 4;
998                                                 $info['comments_raw'][$i]['marker_id']      = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), true);
999                                                 $offset += 2;
1000                                                 $CommentLength                              = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false);
1001                                                 $offset += 2;
1002                                                 $info['comments_raw'][$i]['comment']        =                           substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, $CommentLength);
1003                                                 $offset += $CommentLength;
1004
1005                                                 $info['comments_raw'][$i]['timestamp_unix'] = getid3_lib::DateMac2Unix($info['comments_raw'][$i]['timestamp']);
1006                                                 $thisfile_riff['comments']['comment'][] = $info['comments_raw'][$i]['comment'];
1007                                         }
1008                                 }
1009
1010                                 $CommentsChunkNames = array('NAME'=>'title', 'author'=>'artist', '(c) '=>'copyright', 'ANNO'=>'comment');
1011                                 foreach ($CommentsChunkNames as $key => $value) {
1012                                         if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) {
1013                                                 $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data'];
1014                                         }
1015                                 }
1016 /*
1017                                 if (isset($thisfile_riff[$RIFFsubtype]['ID3 '])) {
1018                                         getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
1019                                         $getid3_temp = new getID3();
1020                                         $getid3_temp->openfile($this->getid3->filename);
1021                                         $getid3_id3v2 = new getid3_id3v2($getid3_temp);
1022                                         $getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['ID3 '][0]['offset'] + 8;
1023                                         if ($thisfile_riff[$RIFFsubtype]['ID3 '][0]['valid'] = $getid3_id3v2->Analyze()) {
1024                                                 $info['id3v2'] = $getid3_temp->info['id3v2'];
1025                                         }
1026                                         unset($getid3_temp, $getid3_id3v2);
1027                                 }
1028 */
1029                                 break;
1030
1031                         case '8SVX':
1032                                 $thisfile_audio['bitrate_mode']    = 'cbr';
1033                                 $thisfile_audio_dataformat         = '8svx';
1034                                 $thisfile_audio['bits_per_sample'] = 8;
1035                                 $thisfile_audio['channels']        = 1; // overridden below, if need be
1036                                 $info['mime_type']                = 'audio/x-aiff';
1037
1038                                 if (isset($thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'])) {
1039                                         $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'] + 8;
1040                                         $info['avdataend']    = $info['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['BODY'][0]['size'];
1041                                         if ($info['avdataend'] > $info['filesize']) {
1042                                                 $info['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['BODY'][0]['size'].' bytes of audio data, only '.($info['filesize'] - $info['avdataoffset']).' bytes found';
1043                                         }
1044                                 }
1045
1046                                 if (isset($thisfile_riff[$RIFFsubtype]['VHDR'][0]['offset'])) {
1047                                         // shortcut
1048                                         $thisfile_riff_RIFFsubtype_VHDR_0 = &$thisfile_riff[$RIFFsubtype]['VHDR'][0];
1049
1050                                         $thisfile_riff_RIFFsubtype_VHDR_0['oneShotHiSamples']  =   getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'],  0, 4));
1051                                         $thisfile_riff_RIFFsubtype_VHDR_0['repeatHiSamples']   =   getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'],  4, 4));
1052                                         $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerHiCycle'] =   getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'],  8, 4));
1053                                         $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec']     =   getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 12, 2));
1054                                         $thisfile_riff_RIFFsubtype_VHDR_0['ctOctave']          =   getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 14, 1));
1055                                         $thisfile_riff_RIFFsubtype_VHDR_0['sCompression']      =   getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 15, 1));
1056                                         $thisfile_riff_RIFFsubtype_VHDR_0['Volume']            = getid3_lib::FixedPoint16_16(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 16, 4));
1057
1058                                         $thisfile_audio['sample_rate'] = $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec'];
1059
1060                                         switch ($thisfile_riff_RIFFsubtype_VHDR_0['sCompression']) {
1061                                                 case 0:
1062                                                         $thisfile_audio['codec']    = 'Pulse Code Modulation (PCM)';
1063                                                         $thisfile_audio['lossless'] = true;
1064                                                         $ActualBitsPerSample        = 8;
1065                                                         break;
1066
1067                                                 case 1:
1068                                                         $thisfile_audio['codec']    = 'Fibonacci-delta encoding';
1069                                                         $thisfile_audio['lossless'] = false;
1070                                                         $ActualBitsPerSample        = 4;
1071                                                         break;
1072
1073                                                 default:
1074                                                         $info['warning'][] = 'Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "'.sCompression.'"';
1075                                                         break;
1076                                         }
1077                                 }
1078
1079                                 if (isset($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'])) {
1080                                         $ChannelsIndex = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'], 0, 4));
1081                                         switch ($ChannelsIndex) {
1082                                                 case 6: // Stereo
1083                                                         $thisfile_audio['channels'] = 2;
1084                                                         break;
1085
1086                                                 case 2: // Left channel only
1087                                                 case 4: // Right channel only
1088                                                         $thisfile_audio['channels'] = 1;
1089                                                         break;
1090
1091                                                 default:
1092                                                         $info['warning'][] = 'Unexpected value in 8SVX.CHAN chunk - expecting 2 or 4 or 6, found "'.$ChannelsIndex.'"';
1093                                                         break;
1094                                         }
1095
1096                                 }
1097
1098                                 $CommentsChunkNames = array('NAME'=>'title', 'author'=>'artist', '(c) '=>'copyright', 'ANNO'=>'comment');
1099                                 foreach ($CommentsChunkNames as $key => $value) {
1100                                         if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) {
1101                                                 $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data'];
1102                                         }
1103                                 }
1104
1105                                 $thisfile_audio['bitrate'] = $thisfile_audio['sample_rate'] * $ActualBitsPerSample * $thisfile_audio['channels'];
1106                                 if (!empty($thisfile_audio['bitrate'])) {
1107                                         $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) / ($thisfile_audio['bitrate'] / 8);
1108                                 }
1109                                 break;
1110
1111
1112                         case 'CDXA':
1113                                 $info['mime_type'] = 'video/mpeg';
1114                                 if (!empty($thisfile_riff['CDXA']['data'][0]['size'])) {
1115                                         if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.mpeg.php', __FILE__, false)) {
1116                                                 $getid3_temp = new getID3();
1117                                                 $getid3_temp->openfile($this->getid3->filename);
1118                                                 $getid3_mpeg = new getid3_mpeg($getid3_temp);
1119                                                 $getid3_mpeg->Analyze();
1120                                                 if (empty($getid3_temp->info['error'])) {
1121                                                         $info['audio']   = $getid3_temp->info['audio'];
1122                                                         $info['video']   = $getid3_temp->info['video'];
1123                                                         $info['mpeg']    = $getid3_temp->info['mpeg'];
1124                                                         $info['warning'] = $getid3_temp->info['warning'];
1125                                                 }
1126                                                 unset($getid3_temp, $getid3_mpeg);
1127                                         }
1128                                 }
1129                                 break;
1130
1131
1132                         default:
1133                                 $info['error'][] = 'Unknown RIFF type: expecting one of (WAVE|RMP3|AVI |CDDA|AIFF|AIFC|8SVX|CDXA), found "'.$RIFFsubtype.'" instead';
1134                                 unset($info['fileformat']);
1135                                 break;
1136                 }
1137
1138                 switch ($RIFFsubtype) {
1139                         case 'WAVE':
1140                         case 'AIFF':
1141                         case 'AIFC':
1142                                 $ID3v2_key_good = 'id3 ';
1143                                 $ID3v2_keys_bad = array('ID3 ', 'tag ');
1144                                 foreach ($ID3v2_keys_bad as $ID3v2_key_bad) {
1145                                         if (isset($thisfile_riff[$RIFFsubtype][$ID3v2_key_bad]) && !array_key_exists($ID3v2_key_good, $thisfile_riff[$RIFFsubtype])) {
1146                                                 $thisfile_riff[$RIFFsubtype][$ID3v2_key_good] = $thisfile_riff[$RIFFsubtype][$ID3v2_key_bad];
1147                                                 $info['warning'][] = 'mapping "'.$ID3v2_key_bad.'" chunk to "'.$ID3v2_key_good.'"';
1148                                         }
1149                                 }
1150
1151                                 if (isset($thisfile_riff[$RIFFsubtype]['id3 '])) {
1152                                         getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
1153                                         $getid3_temp = new getID3();
1154                                         $getid3_temp->openfile($this->getid3->filename);
1155                                         $getid3_id3v2 = new getid3_id3v2($getid3_temp);
1156                                         $getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['id3 '][0]['offset'] + 8;
1157                                         if ($thisfile_riff[$RIFFsubtype]['id3 '][0]['valid'] = $getid3_id3v2->Analyze()) {
1158                                                 $info['id3v2'] = $getid3_temp->info['id3v2'];
1159                                         }
1160                                         unset($getid3_temp, $getid3_id3v2);
1161                                 }
1162                                 break;
1163                 }
1164
1165                 if (isset($thisfile_riff_WAVE['DISP']) && is_array($thisfile_riff_WAVE['DISP'])) {
1166                         $thisfile_riff['comments']['title'][] = trim(substr($thisfile_riff_WAVE['DISP'][count($thisfile_riff_WAVE['DISP']) - 1]['data'], 4));
1167                 }
1168                 if (isset($thisfile_riff_WAVE['INFO']) && is_array($thisfile_riff_WAVE['INFO'])) {
1169                         self::parseComments($thisfile_riff_WAVE['INFO'], $thisfile_riff['comments']);
1170                 }
1171                 if (isset($thisfile_riff['AVI ']['INFO']) && is_array($thisfile_riff['AVI ']['INFO'])) {
1172                         self::parseComments($thisfile_riff['AVI ']['INFO'], $thisfile_riff['comments']);
1173                 }
1174
1175                 if (empty($thisfile_audio['encoder']) && !empty($info['mpeg']['audio']['LAME']['short_version'])) {
1176                         $thisfile_audio['encoder'] = $info['mpeg']['audio']['LAME']['short_version'];
1177                 }
1178
1179                 if (!isset($info['playtime_seconds'])) {
1180                         $info['playtime_seconds'] = 0;
1181                 }
1182                 if (isset($thisfile_riff_raw['strh'][0]['dwLength']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) {
1183                         // needed for >2GB AVIs where 'avih' chunk only lists number of frames in that chunk, not entire movie
1184                         $info['playtime_seconds'] = $thisfile_riff_raw['strh'][0]['dwLength'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000);
1185                 } elseif (isset($thisfile_riff_raw['avih']['dwTotalFrames']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) {
1186                         $info['playtime_seconds'] = $thisfile_riff_raw['avih']['dwTotalFrames'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000);
1187                 }
1188
1189                 if ($info['playtime_seconds'] > 0) {
1190                         if (isset($thisfile_riff_audio) && isset($thisfile_riff_video)) {
1191
1192                                 if (!isset($info['bitrate'])) {
1193                                         $info['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1194                                 }
1195
1196                         } elseif (isset($thisfile_riff_audio) && !isset($thisfile_riff_video)) {
1197
1198                                 if (!isset($thisfile_audio['bitrate'])) {
1199                                         $thisfile_audio['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1200                                 }
1201
1202                         } elseif (!isset($thisfile_riff_audio) && isset($thisfile_riff_video)) {
1203
1204                                 if (!isset($thisfile_video['bitrate'])) {
1205                                         $thisfile_video['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1206                                 }
1207
1208                         }
1209                 }
1210
1211
1212                 if (isset($thisfile_riff_video) && isset($thisfile_audio['bitrate']) && ($thisfile_audio['bitrate'] > 0) && ($info['playtime_seconds'] > 0)) {
1213
1214                         $info['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8);
1215                         $thisfile_audio['bitrate'] = 0;
1216                         $thisfile_video['bitrate'] = $info['bitrate'];
1217                         foreach ($thisfile_riff_audio as $channelnumber => $audioinfoarray) {
1218                                 $thisfile_video['bitrate'] -= $audioinfoarray['bitrate'];
1219                                 $thisfile_audio['bitrate'] += $audioinfoarray['bitrate'];
1220                         }
1221                         if ($thisfile_video['bitrate'] <= 0) {
1222                                 unset($thisfile_video['bitrate']);
1223                         }
1224                         if ($thisfile_audio['bitrate'] <= 0) {
1225                                 unset($thisfile_audio['bitrate']);
1226                         }
1227                 }
1228
1229                 if (isset($info['mpeg']['audio'])) {
1230                         $thisfile_audio_dataformat      = 'mp'.$info['mpeg']['audio']['layer'];
1231                         $thisfile_audio['sample_rate']  = $info['mpeg']['audio']['sample_rate'];
1232                         $thisfile_audio['channels']     = $info['mpeg']['audio']['channels'];
1233                         $thisfile_audio['bitrate']      = $info['mpeg']['audio']['bitrate'];
1234                         $thisfile_audio['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
1235                         if (!empty($info['mpeg']['audio']['codec'])) {
1236                                 $thisfile_audio['codec'] = $info['mpeg']['audio']['codec'].' '.$thisfile_audio['codec'];
1237                         }
1238                         if (!empty($thisfile_audio['streams'])) {
1239                                 foreach ($thisfile_audio['streams'] as $streamnumber => $streamdata) {
1240                                         if ($streamdata['dataformat'] == $thisfile_audio_dataformat) {
1241                                                 $thisfile_audio['streams'][$streamnumber]['sample_rate']  = $thisfile_audio['sample_rate'];
1242                                                 $thisfile_audio['streams'][$streamnumber]['channels']     = $thisfile_audio['channels'];
1243                                                 $thisfile_audio['streams'][$streamnumber]['bitrate']      = $thisfile_audio['bitrate'];
1244                                                 $thisfile_audio['streams'][$streamnumber]['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
1245                                                 $thisfile_audio['streams'][$streamnumber]['codec']        = $thisfile_audio['codec'];
1246                                         }
1247                                 }
1248                         }
1249                         $getid3_mp3 = new getid3_mp3($this->getid3);
1250                         $thisfile_audio['encoder_options'] = $getid3_mp3->GuessEncoderOptions();
1251                         unset($getid3_mp3);
1252                 }
1253
1254
1255                 if (!empty($thisfile_riff_raw['fmt ']['wBitsPerSample']) && ($thisfile_riff_raw['fmt ']['wBitsPerSample'] > 0)) {
1256                         switch ($thisfile_audio_dataformat) {
1257                                 case 'ac3':
1258                                         // ignore bits_per_sample
1259                                         break;
1260
1261                                 default:
1262                                         $thisfile_audio['bits_per_sample'] = $thisfile_riff_raw['fmt ']['wBitsPerSample'];
1263                                         break;
1264                         }
1265                 }
1266
1267
1268                 if (empty($thisfile_riff_raw)) {
1269                         unset($thisfile_riff['raw']);
1270                 }
1271                 if (empty($thisfile_riff_audio)) {
1272                         unset($thisfile_riff['audio']);
1273                 }
1274                 if (empty($thisfile_riff_video)) {
1275                         unset($thisfile_riff['video']);
1276                 }
1277
1278                 return true;
1279         }
1280
1281         public function ParseRIFF($startoffset, $maxoffset) {
1282                 $info = &$this->getid3->info;
1283
1284                 $RIFFchunk = false;
1285                 $FoundAllChunksWeNeed = false;
1286
1287                 try {
1288                         $this->fseek($startoffset);
1289                         $maxoffset = min($maxoffset, $info['avdataend']);
1290                         while ($this->ftell() < $maxoffset) {
1291                                 $chunknamesize = $this->fread(8);
1292                                 //$chunkname =                          substr($chunknamesize, 0, 4);
1293                                 $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
1294                                 $chunksize =  $this->EitherEndian2Int(substr($chunknamesize, 4, 4));
1295                                 //if (strlen(trim($chunkname, "\x00")) < 4) {
1296                                 if (strlen($chunkname) < 4) {
1297                                         $this->error('Expecting chunk name at offset '.($this->ftell() - 8).' but found nothing. Aborting RIFF parsing.');
1298                                         break;
1299                                 }
1300                                 if (($chunksize == 0) && ($chunkname != 'JUNK')) {
1301                                         $this->warning('Chunk ('.$chunkname.') size at offset '.($this->ftell() - 4).' is zero. Aborting RIFF parsing.');
1302                                         break;
1303                                 }
1304                                 if (($chunksize % 2) != 0) {
1305                                         // all structures are packed on word boundaries
1306                                         $chunksize++;
1307                                 }
1308
1309                                 switch ($chunkname) {
1310                                         case 'LIST':
1311                                                 $listname = $this->fread(4);
1312                                                 if (preg_match('#^(movi|rec )$#i', $listname)) {
1313                                                         $RIFFchunk[$listname]['offset'] = $this->ftell() - 4;
1314                                                         $RIFFchunk[$listname]['size']   = $chunksize;
1315
1316                                                         if (!$FoundAllChunksWeNeed) {
1317                                                                 $WhereWeWere      = $this->ftell();
1318                                                                 $AudioChunkHeader = $this->fread(12);
1319                                                                 $AudioChunkStreamNum  =                              substr($AudioChunkHeader, 0, 2);
1320                                                                 $AudioChunkStreamType =                              substr($AudioChunkHeader, 2, 2);
1321                                                                 $AudioChunkSize       = getid3_lib::LittleEndian2Int(substr($AudioChunkHeader, 4, 4));
1322
1323                                                                 if ($AudioChunkStreamType == 'wb') {
1324                                                                         $FirstFourBytes = substr($AudioChunkHeader, 8, 4);
1325                                                                         if (preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', $FirstFourBytes)) {
1326                                                                                 // MP3
1327                                                                                 if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) {
1328                                                                                         $getid3_temp = new getID3();
1329                                                                                         $getid3_temp->openfile($this->getid3->filename);
1330                                                                                         $getid3_temp->info['avdataoffset'] = $this->ftell() - 4;
1331                                                                                         $getid3_temp->info['avdataend']    = $this->ftell() + $AudioChunkSize;
1332                                                                                         $getid3_mp3 = new getid3_mp3($getid3_temp);
1333                                                                                         $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false);
1334                                                                                         if (isset($getid3_temp->info['mpeg']['audio'])) {
1335                                                                                                 $info['mpeg']['audio']         = $getid3_temp->info['mpeg']['audio'];
1336                                                                                                 $info['audio']                 = $getid3_temp->info['audio'];
1337                                                                                                 $info['audio']['dataformat']   = 'mp'.$info['mpeg']['audio']['layer'];
1338                                                                                                 $info['audio']['sample_rate']  = $info['mpeg']['audio']['sample_rate'];
1339                                                                                                 $info['audio']['channels']     = $info['mpeg']['audio']['channels'];
1340                                                                                                 $info['audio']['bitrate']      = $info['mpeg']['audio']['bitrate'];
1341                                                                                                 $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
1342                                                                                                 //$info['bitrate']               = $info['audio']['bitrate'];
1343                                                                                         }
1344                                                                                         unset($getid3_temp, $getid3_mp3);
1345                                                                                 }
1346
1347                                                                         } elseif (strpos($FirstFourBytes, getid3_ac3::syncword) === 0) {
1348
1349                                                                                 // AC3
1350                                                                                 $getid3_temp = new getID3();
1351                                                                                 $getid3_temp->openfile($this->getid3->filename);
1352                                                                                 $getid3_temp->info['avdataoffset'] = $this->ftell() - 4;
1353                                                                                 $getid3_temp->info['avdataend']    = $this->ftell() + $AudioChunkSize;
1354                                                                                 $getid3_ac3 = new getid3_ac3($getid3_temp);
1355                                                                                 $getid3_ac3->Analyze();
1356                                                                                 if (empty($getid3_temp->info['error'])) {
1357                                                                                         $info['audio']   = $getid3_temp->info['audio'];
1358                                                                                         $info['ac3']     = $getid3_temp->info['ac3'];
1359                                                                                         if (!empty($getid3_temp->info['warning'])) {
1360                                                                                                 foreach ($getid3_temp->info['warning'] as $key => $value) {
1361                                                                                                         $info['warning'][] = $value;
1362                                                                                                 }
1363                                                                                         }
1364                                                                                 }
1365                                                                                 unset($getid3_temp, $getid3_ac3);
1366                                                                         }
1367                                                                 }
1368                                                                 $FoundAllChunksWeNeed = true;
1369                                                                 $this->fseek($WhereWeWere);
1370                                                         }
1371                                                         $this->fseek($chunksize - 4, SEEK_CUR);
1372
1373                                                 } else {
1374
1375                                                         if (!isset($RIFFchunk[$listname])) {
1376                                                                 $RIFFchunk[$listname] = array();
1377                                                         }
1378                                                         $LISTchunkParent    = $listname;
1379                                                         $LISTchunkMaxOffset = $this->ftell() - 4 + $chunksize;
1380                                                         if ($parsedChunk = $this->ParseRIFF($this->ftell(), $LISTchunkMaxOffset)) {
1381                                                                 $RIFFchunk[$listname] = array_merge_recursive($RIFFchunk[$listname], $parsedChunk);
1382                                                         }
1383
1384                                                 }
1385                                                 break;
1386
1387                                         default:
1388                                                 if (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#', $chunkname)) {
1389                                                         $this->fseek($chunksize, SEEK_CUR);
1390                                                         break;
1391                                                 }
1392                                                 $thisindex = 0;
1393                                                 if (isset($RIFFchunk[$chunkname]) && is_array($RIFFchunk[$chunkname])) {
1394                                                         $thisindex = count($RIFFchunk[$chunkname]);
1395                                                 }
1396                                                 $RIFFchunk[$chunkname][$thisindex]['offset'] = $this->ftell() - 8;
1397                                                 $RIFFchunk[$chunkname][$thisindex]['size']   = $chunksize;
1398                                                 switch ($chunkname) {
1399                                                         case 'data':
1400                                                                 $info['avdataoffset'] = $this->ftell();
1401                                                                 $info['avdataend']    = $info['avdataoffset'] + $chunksize;
1402
1403                                                                 $testData = $this->fread(36);
1404                                                                 if ($testData === '') {
1405                                                                         break;
1406                                                                 }
1407                                                                 if (preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', substr($testData, 0, 4))) {
1408
1409                                                                         // Probably is MP3 data
1410                                                                         if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($testData, 0, 4))) {
1411                                                                                 $getid3_temp = new getID3();
1412                                                                                 $getid3_temp->openfile($this->getid3->filename);
1413                                                                                 $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
1414                                                                                 $getid3_temp->info['avdataend']    = $info['avdataend'];
1415                                                                                 $getid3_mp3 = new getid3_mp3($getid3_temp);
1416                                                                                 $getid3_mp3->getOnlyMPEGaudioInfo($info['avdataoffset'], false);
1417                                                                                 if (empty($getid3_temp->info['error'])) {
1418                                                                                         $info['audio'] = $getid3_temp->info['audio'];
1419                                                                                         $info['mpeg']  = $getid3_temp->info['mpeg'];
1420                                                                                 }
1421                                                                                 unset($getid3_temp, $getid3_mp3);
1422                                                                         }
1423
1424                                                                 } elseif (($isRegularAC3 = (substr($testData, 0, 2) == getid3_ac3::syncword)) || substr($testData, 8, 2) == strrev(getid3_ac3::syncword)) {
1425
1426                                                                         // This is probably AC-3 data
1427                                                                         $getid3_temp = new getID3();
1428                                                                         if ($isRegularAC3) {
1429                                                                                 $getid3_temp->openfile($this->getid3->filename);
1430                                                                                 $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
1431                                                                                 $getid3_temp->info['avdataend']    = $info['avdataend'];
1432                                                                         }
1433                                                                         $getid3_ac3 = new getid3_ac3($getid3_temp);
1434                                                                         if ($isRegularAC3) {
1435                                                                                 $getid3_ac3->Analyze();
1436                                                                         } else {
1437                                                                                 // Dolby Digital WAV
1438                                                                                 // AC-3 content, but not encoded in same format as normal AC-3 file
1439                                                                                 // For one thing, byte order is swapped
1440                                                                                 $ac3_data = '';
1441                                                                                 for ($i = 0; $i < 28; $i += 2) {
1442                                                                                         $ac3_data .= substr($testData, 8 + $i + 1, 1);
1443                                                                                         $ac3_data .= substr($testData, 8 + $i + 0, 1);
1444                                                                                 }
1445                                                                                 $getid3_ac3->AnalyzeString($ac3_data);
1446                                                                         }
1447
1448                                                                         if (empty($getid3_temp->info['error'])) {
1449                                                                                 $info['audio'] = $getid3_temp->info['audio'];
1450                                                                                 $info['ac3']   = $getid3_temp->info['ac3'];
1451                                                                                 if (!empty($getid3_temp->info['warning'])) {
1452                                                                                         foreach ($getid3_temp->info['warning'] as $newerror) {
1453                                                                                                 $this->warning('getid3_ac3() says: ['.$newerror.']');
1454                                                                                         }
1455                                                                                 }
1456                                                                         }
1457                                                                         unset($getid3_temp, $getid3_ac3);
1458
1459                                                                 } elseif (preg_match('/^('.implode('|', array_map('preg_quote', getid3_dts::$syncwords)).')/', $testData)) {
1460
1461                                                                         // This is probably DTS data
1462                                                                         $getid3_temp = new getID3();
1463                                                                         $getid3_temp->openfile($this->getid3->filename);
1464                                                                         $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
1465                                                                         $getid3_dts = new getid3_dts($getid3_temp);
1466                                                                         $getid3_dts->Analyze();
1467                                                                         if (empty($getid3_temp->info['error'])) {
1468                                                                                 $info['audio']            = $getid3_temp->info['audio'];
1469                                                                                 $info['dts']              = $getid3_temp->info['dts'];
1470                                                                                 $info['playtime_seconds'] = $getid3_temp->info['playtime_seconds']; // may not match RIFF calculations since DTS-WAV often used 14/16 bit-word packing
1471                                                                                 if (!empty($getid3_temp->info['warning'])) {
1472                                                                                         foreach ($getid3_temp->info['warning'] as $newerror) {
1473                                                                                                 $this->warning('getid3_dts() says: ['.$newerror.']');
1474                                                                                         }
1475                                                                                 }
1476                                                                         }
1477
1478                                                                         unset($getid3_temp, $getid3_dts);
1479
1480                                                                 } elseif (substr($testData, 0, 4) == 'wvpk') {
1481
1482                                                                         // This is WavPack data
1483                                                                         $info['wavpack']['offset'] = $info['avdataoffset'];
1484                                                                         $info['wavpack']['size']   = getid3_lib::LittleEndian2Int(substr($testData, 4, 4));
1485                                                                         $this->parseWavPackHeader(substr($testData, 8, 28));
1486
1487                                                                 } else {
1488                                                                         // This is some other kind of data (quite possibly just PCM)
1489                                                                         // do nothing special, just skip it
1490                                                                 }
1491                                                                 $nextoffset = $info['avdataend'];
1492                                                                 $this->fseek($nextoffset);
1493                                                                 break;
1494
1495                                                         case 'iXML':
1496                                                         case 'bext':
1497                                                         case 'cart':
1498                                                         case 'fmt ':
1499                                                         case 'strh':
1500                                                         case 'strf':
1501                                                         case 'indx':
1502                                                         case 'MEXT':
1503                                                         case 'DISP':
1504                                                                 // always read data in
1505                                                         case 'JUNK':
1506                                                                 // should be: never read data in
1507                                                                 // but some programs write their version strings in a JUNK chunk (e.g. VirtualDub, AVIdemux, etc)
1508                                                                 if ($chunksize < 1048576) {
1509                                                                         if ($chunksize > 0) {
1510                                                                                 $RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize);
1511                                                                                 if ($chunkname == 'JUNK') {
1512                                                                                         if (preg_match('#^([\\x20-\\x7F]+)#', $RIFFchunk[$chunkname][$thisindex]['data'], $matches)) {
1513                                                                                                 // only keep text characters [chr(32)-chr(127)]
1514                                                                                                 $info['riff']['comments']['junk'][] = trim($matches[1]);
1515                                                                                         }
1516                                                                                         // but if nothing there, ignore
1517                                                                                         // remove the key in either case
1518                                                                                         unset($RIFFchunk[$chunkname][$thisindex]['data']);
1519                                                                                 }
1520                                                                         }
1521                                                                 } else {
1522                                                                         $this->warning('Chunk "'.$chunkname.'" at offset '.$this->ftell().' is unexpectedly larger than 1MB (claims to be '.number_format($chunksize).' bytes), skipping data');
1523                                                                         $this->fseek($chunksize, SEEK_CUR);
1524                                                                 }
1525                                                                 break;
1526
1527                                                         //case 'IDVX':
1528                                                         //      $info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunksize));
1529                                                         //      break;
1530
1531                                                         default:
1532                                                                 if (!empty($LISTchunkParent) && (($RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size']) <= $LISTchunkMaxOffset)) {
1533                                                                         $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
1534                                                                         $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['size']   = $RIFFchunk[$chunkname][$thisindex]['size'];
1535                                                                         unset($RIFFchunk[$chunkname][$thisindex]['offset']);
1536                                                                         unset($RIFFchunk[$chunkname][$thisindex]['size']);
1537                                                                         if (isset($RIFFchunk[$chunkname][$thisindex]) && empty($RIFFchunk[$chunkname][$thisindex])) {
1538                                                                                 unset($RIFFchunk[$chunkname][$thisindex]);
1539                                                                         }
1540                                                                         if (isset($RIFFchunk[$chunkname]) && empty($RIFFchunk[$chunkname])) {
1541                                                                                 unset($RIFFchunk[$chunkname]);
1542                                                                         }
1543                                                                         $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['data'] = $this->fread($chunksize);
1544                                                                 } elseif ($chunksize < 2048) {
1545                                                                         // only read data in if smaller than 2kB
1546                                                                         $RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize);
1547                                                                 } else {
1548                                                                         $this->fseek($chunksize, SEEK_CUR);
1549                                                                 }
1550                                                                 break;
1551                                                 }
1552                                                 break;
1553                                 }
1554                         }
1555
1556                 } catch (getid3_exception $e) {
1557                         if ($e->getCode() == 10) {
1558                                 $this->warning('RIFF parser: '.$e->getMessage());
1559                         } else {
1560                                 throw $e;
1561                         }
1562                 }
1563
1564                 return $RIFFchunk;
1565         }
1566
1567         public function ParseRIFFdata(&$RIFFdata) {
1568                 $info = &$this->getid3->info;
1569                 if ($RIFFdata) {
1570                         $tempfile = tempnam(GETID3_TEMP_DIR, 'getID3');
1571                         $fp_temp  = fopen($tempfile, 'wb');
1572                         $RIFFdataLength = strlen($RIFFdata);
1573                         $NewLengthString = getid3_lib::LittleEndian2String($RIFFdataLength, 4);
1574                         for ($i = 0; $i < 4; $i++) {
1575                                 $RIFFdata[($i + 4)] = $NewLengthString[$i];
1576                         }
1577                         fwrite($fp_temp, $RIFFdata);
1578                         fclose($fp_temp);
1579
1580                         $getid3_temp = new getID3();
1581                         $getid3_temp->openfile($tempfile);
1582                         $getid3_temp->info['filesize']     = $RIFFdataLength;
1583                         $getid3_temp->info['filenamepath'] = $info['filenamepath'];
1584                         $getid3_temp->info['tags']         = $info['tags'];
1585                         $getid3_temp->info['warning']      = $info['warning'];
1586                         $getid3_temp->info['error']        = $info['error'];
1587                         $getid3_temp->info['comments']     = $info['comments'];
1588                         $getid3_temp->info['audio']        = (isset($info['audio']) ? $info['audio'] : array());
1589                         $getid3_temp->info['video']        = (isset($info['video']) ? $info['video'] : array());
1590                         $getid3_riff = new getid3_riff($getid3_temp);
1591                         $getid3_riff->Analyze();
1592
1593                         $info['riff']     = $getid3_temp->info['riff'];
1594                         $info['warning']  = $getid3_temp->info['warning'];
1595                         $info['error']    = $getid3_temp->info['error'];
1596                         $info['tags']     = $getid3_temp->info['tags'];
1597                         $info['comments'] = $getid3_temp->info['comments'];
1598                         unset($getid3_riff, $getid3_temp);
1599                         unlink($tempfile);
1600                 }
1601                 return false;
1602         }
1603
1604         public static function parseComments(&$RIFFinfoArray, &$CommentsTargetArray) {
1605                 $RIFFinfoKeyLookup = array(
1606                         'IARL'=>'archivallocation',
1607                         'IART'=>'artist',
1608                         'ICDS'=>'costumedesigner',
1609                         'ICMS'=>'commissionedby',
1610                         'ICMT'=>'comment',
1611                         'ICNT'=>'country',
1612                         'ICOP'=>'copyright',
1613                         'ICRD'=>'creationdate',
1614                         'IDIM'=>'dimensions',
1615                         'IDIT'=>'digitizationdate',
1616                         'IDPI'=>'resolution',
1617                         'IDST'=>'distributor',
1618                         'IEDT'=>'editor',
1619                         'IENG'=>'engineers',
1620                         'IFRM'=>'accountofparts',
1621                         'IGNR'=>'genre',
1622                         'IKEY'=>'keywords',
1623                         'ILGT'=>'lightness',
1624                         'ILNG'=>'language',
1625                         'IMED'=>'orignalmedium',
1626                         'IMUS'=>'composer',
1627                         'INAM'=>'title',
1628                         'IPDS'=>'productiondesigner',
1629                         'IPLT'=>'palette',
1630                         'IPRD'=>'product',
1631                         'IPRO'=>'producer',
1632                         'IPRT'=>'part',
1633                         'IRTD'=>'rating',
1634                         'ISBJ'=>'subject',
1635                         'ISFT'=>'software',
1636                         'ISGN'=>'secondarygenre',
1637                         'ISHP'=>'sharpness',
1638                         'ISRC'=>'sourcesupplier',
1639                         'ISRF'=>'digitizationsource',
1640                         'ISTD'=>'productionstudio',
1641                         'ISTR'=>'starring',
1642                         'ITCH'=>'encoded_by',
1643                         'IWEB'=>'url',
1644                         'IWRI'=>'writer',
1645                         '____'=>'comment',
1646                 );
1647                 foreach ($RIFFinfoKeyLookup as $key => $value) {
1648                         if (isset($RIFFinfoArray[$key])) {
1649                                 foreach ($RIFFinfoArray[$key] as $commentid => $commentdata) {
1650                                         if (trim($commentdata['data']) != '') {
1651                                                 if (isset($CommentsTargetArray[$value])) {
1652                                                         $CommentsTargetArray[$value][] =     trim($commentdata['data']);
1653                                                 } else {
1654                                                         $CommentsTargetArray[$value] = array(trim($commentdata['data']));
1655                                                 }
1656                                         }
1657                                 }
1658                         }
1659                 }
1660                 return true;
1661         }
1662
1663         public static function parseWAVEFORMATex($WaveFormatExData) {
1664                 // shortcut
1665                 $WaveFormatEx['raw'] = array();
1666                 $WaveFormatEx_raw    = &$WaveFormatEx['raw'];
1667
1668                 $WaveFormatEx_raw['wFormatTag']      = substr($WaveFormatExData,  0, 2);
1669                 $WaveFormatEx_raw['nChannels']       = substr($WaveFormatExData,  2, 2);
1670                 $WaveFormatEx_raw['nSamplesPerSec']  = substr($WaveFormatExData,  4, 4);
1671                 $WaveFormatEx_raw['nAvgBytesPerSec'] = substr($WaveFormatExData,  8, 4);
1672                 $WaveFormatEx_raw['nBlockAlign']     = substr($WaveFormatExData, 12, 2);
1673                 $WaveFormatEx_raw['wBitsPerSample']  = substr($WaveFormatExData, 14, 2);
1674                 if (strlen($WaveFormatExData) > 16) {
1675                         $WaveFormatEx_raw['cbSize']      = substr($WaveFormatExData, 16, 2);
1676                 }
1677                 $WaveFormatEx_raw = array_map('getid3_lib::LittleEndian2Int', $WaveFormatEx_raw);
1678
1679                 $WaveFormatEx['codec']           = self::wFormatTagLookup($WaveFormatEx_raw['wFormatTag']);
1680                 $WaveFormatEx['channels']        = $WaveFormatEx_raw['nChannels'];
1681                 $WaveFormatEx['sample_rate']     = $WaveFormatEx_raw['nSamplesPerSec'];
1682                 $WaveFormatEx['bitrate']         = $WaveFormatEx_raw['nAvgBytesPerSec'] * 8;
1683                 $WaveFormatEx['bits_per_sample'] = $WaveFormatEx_raw['wBitsPerSample'];
1684
1685                 return $WaveFormatEx;
1686         }
1687
1688         public function parseWavPackHeader($WavPackChunkData) {
1689                 // typedef struct {
1690                 //     char ckID [4];
1691                 //     long ckSize;
1692                 //     short version;
1693                 //     short bits;                // added for version 2.00
1694                 //     short flags, shift;        // added for version 3.00
1695                 //     long total_samples, crc, crc2;
1696                 //     char extension [4], extra_bc, extras [3];
1697                 // } WavpackHeader;
1698
1699                 // shortcut
1700                 $info = &$this->getid3->info;
1701                 $info['wavpack']  = array();
1702                 $thisfile_wavpack = &$info['wavpack'];
1703
1704                 $thisfile_wavpack['version']           = getid3_lib::LittleEndian2Int(substr($WavPackChunkData,  0, 2));
1705                 if ($thisfile_wavpack['version'] >= 2) {
1706                         $thisfile_wavpack['bits']          = getid3_lib::LittleEndian2Int(substr($WavPackChunkData,  2, 2));
1707                 }
1708                 if ($thisfile_wavpack['version'] >= 3) {
1709                         $thisfile_wavpack['flags_raw']     = getid3_lib::LittleEndian2Int(substr($WavPackChunkData,  4, 2));
1710                         $thisfile_wavpack['shift']         = getid3_lib::LittleEndian2Int(substr($WavPackChunkData,  6, 2));
1711                         $thisfile_wavpack['total_samples'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData,  8, 4));
1712                         $thisfile_wavpack['crc1']          = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 12, 4));
1713                         $thisfile_wavpack['crc2']          = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 16, 4));
1714                         $thisfile_wavpack['extension']     =                              substr($WavPackChunkData, 20, 4);
1715                         $thisfile_wavpack['extra_bc']      = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 24, 1));
1716                         for ($i = 0; $i <= 2; $i++) {
1717                                 $thisfile_wavpack['extras'][]  = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 25 + $i, 1));
1718                         }
1719
1720                         // shortcut
1721                         $thisfile_wavpack['flags'] = array();
1722                         $thisfile_wavpack_flags = &$thisfile_wavpack['flags'];
1723
1724                         $thisfile_wavpack_flags['mono']                 = (bool) ($thisfile_wavpack['flags_raw'] & 0x000001);
1725                         $thisfile_wavpack_flags['fast_mode']            = (bool) ($thisfile_wavpack['flags_raw'] & 0x000002);
1726                         $thisfile_wavpack_flags['raw_mode']             = (bool) ($thisfile_wavpack['flags_raw'] & 0x000004);
1727                         $thisfile_wavpack_flags['calc_noise']           = (bool) ($thisfile_wavpack['flags_raw'] & 0x000008);
1728                         $thisfile_wavpack_flags['high_quality']         = (bool) ($thisfile_wavpack['flags_raw'] & 0x000010);
1729                         $thisfile_wavpack_flags['3_byte_samples']       = (bool) ($thisfile_wavpack['flags_raw'] & 0x000020);
1730                         $thisfile_wavpack_flags['over_20_bits']         = (bool) ($thisfile_wavpack['flags_raw'] & 0x000040);
1731                         $thisfile_wavpack_flags['use_wvc']              = (bool) ($thisfile_wavpack['flags_raw'] & 0x000080);
1732                         $thisfile_wavpack_flags['noiseshaping']         = (bool) ($thisfile_wavpack['flags_raw'] & 0x000100);
1733                         $thisfile_wavpack_flags['very_fast_mode']       = (bool) ($thisfile_wavpack['flags_raw'] & 0x000200);
1734                         $thisfile_wavpack_flags['new_high_quality']     = (bool) ($thisfile_wavpack['flags_raw'] & 0x000400);
1735                         $thisfile_wavpack_flags['cancel_extreme']       = (bool) ($thisfile_wavpack['flags_raw'] & 0x000800);
1736                         $thisfile_wavpack_flags['cross_decorrelation']  = (bool) ($thisfile_wavpack['flags_raw'] & 0x001000);
1737                         $thisfile_wavpack_flags['new_decorrelation']    = (bool) ($thisfile_wavpack['flags_raw'] & 0x002000);
1738                         $thisfile_wavpack_flags['joint_stereo']         = (bool) ($thisfile_wavpack['flags_raw'] & 0x004000);
1739                         $thisfile_wavpack_flags['extra_decorrelation']  = (bool) ($thisfile_wavpack['flags_raw'] & 0x008000);
1740                         $thisfile_wavpack_flags['override_noiseshape']  = (bool) ($thisfile_wavpack['flags_raw'] & 0x010000);
1741                         $thisfile_wavpack_flags['override_jointstereo'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x020000);
1742                         $thisfile_wavpack_flags['copy_source_filetime'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x040000);
1743                         $thisfile_wavpack_flags['create_exe']           = (bool) ($thisfile_wavpack['flags_raw'] & 0x080000);
1744                 }
1745
1746                 return true;
1747         }
1748
1749         public static function ParseBITMAPINFOHEADER($BITMAPINFOHEADER, $littleEndian=true) {
1750
1751                 $parsed['biSize']          = substr($BITMAPINFOHEADER,  0, 4); // number of bytes required by the BITMAPINFOHEADER structure
1752                 $parsed['biWidth']         = substr($BITMAPINFOHEADER,  4, 4); // width of the bitmap in pixels
1753                 $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
1754                 $parsed['biPlanes']        = substr($BITMAPINFOHEADER, 12, 2); // number of color planes on the target device. In most cases this value must be set to 1
1755                 $parsed['biBitCount']      = substr($BITMAPINFOHEADER, 14, 2); // Specifies the number of bits per pixels
1756                 $parsed['biSizeImage']     = substr($BITMAPINFOHEADER, 20, 4); // size of the bitmap data section of the image (the actual pixel data, excluding BITMAPINFOHEADER and RGBQUAD structures)
1757                 $parsed['biXPelsPerMeter'] = substr($BITMAPINFOHEADER, 24, 4); // horizontal resolution, in pixels per metre, of the target device
1758                 $parsed['biYPelsPerMeter'] = substr($BITMAPINFOHEADER, 28, 4); // vertical resolution, in pixels per metre, of the target device
1759                 $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
1760                 $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
1761                 $parsed = array_map('getid3_lib::'.($littleEndian ? 'Little' : 'Big').'Endian2Int', $parsed);
1762
1763                 $parsed['fourcc']          = substr($BITMAPINFOHEADER, 16, 4);  // compression identifier
1764
1765                 return $parsed;
1766         }
1767
1768         public static function ParseDIVXTAG($DIVXTAG, $raw=false) {
1769                 // structure from "IDivX" source, Form1.frm, by "Greg Frazier of Daemonic Software Group", email: gfrazier@icestorm.net, web: http://dsg.cjb.net/
1770                 // source available at http://files.divx-digest.com/download/c663efe7ef8ad2e90bf4af4d3ea6188a/on0SWN2r/edit/IDivX.zip
1771                 // 'Byte Layout:                   '1111111111111111
1772                 // '32 for Movie - 1               '1111111111111111
1773                 // '28 for Author - 6              '6666666666666666
1774                 // '4  for year - 2                '6666666666662222
1775                 // '3  for genre - 3               '7777777777777777
1776                 // '48 for Comments - 7            '7777777777777777
1777                 // '1  for Rating - 4              '7777777777777777
1778                 // '5  for Future Additions - 0    '333400000DIVXTAG
1779                 // '128 bytes total
1780
1781                 static $DIVXTAGgenre  = array(
1782                          0 => 'Action',
1783                          1 => 'Action/Adventure',
1784                          2 => 'Adventure',
1785                          3 => 'Adult',
1786                          4 => 'Anime',
1787                          5 => 'Cartoon',
1788                          6 => 'Claymation',
1789                          7 => 'Comedy',
1790                          8 => 'Commercial',
1791                          9 => 'Documentary',
1792                         10 => 'Drama',
1793                         11 => 'Home Video',
1794                         12 => 'Horror',
1795                         13 => 'Infomercial',
1796                         14 => 'Interactive',
1797                         15 => 'Mystery',
1798                         16 => 'Music Video',
1799                         17 => 'Other',
1800                         18 => 'Religion',
1801                         19 => 'Sci Fi',
1802                         20 => 'Thriller',
1803                         21 => 'Western',
1804                 ),
1805                 $DIVXTAGrating = array(
1806                          0 => 'Unrated',
1807                          1 => 'G',
1808                          2 => 'PG',
1809                          3 => 'PG-13',
1810                          4 => 'R',
1811                          5 => 'NC-17',
1812                 );
1813
1814                 $parsed['title']     =        trim(substr($DIVXTAG,   0, 32));
1815                 $parsed['artist']    =        trim(substr($DIVXTAG,  32, 28));
1816                 $parsed['year']      = intval(trim(substr($DIVXTAG,  60,  4)));
1817                 $parsed['comment']   =        trim(substr($DIVXTAG,  64, 48));
1818                 $parsed['genre_id']  = intval(trim(substr($DIVXTAG, 112,  3)));
1819                 $parsed['rating_id'] =         ord(substr($DIVXTAG, 115,  1));
1820                 //$parsed['padding'] =             substr($DIVXTAG, 116,  5);  // 5-byte null
1821                 //$parsed['magic']   =             substr($DIVXTAG, 121,  7);  // "DIVXTAG"
1822
1823                 $parsed['genre']  = (isset($DIVXTAGgenre[$parsed['genre_id']])   ? $DIVXTAGgenre[$parsed['genre_id']]   : $parsed['genre_id']);
1824                 $parsed['rating'] = (isset($DIVXTAGrating[$parsed['rating_id']]) ? $DIVXTAGrating[$parsed['rating_id']] : $parsed['rating_id']);
1825
1826                 if (!$raw) {
1827                         unset($parsed['genre_id'], $parsed['rating_id']);
1828                         foreach ($parsed as $key => $value) {
1829                                 if (!$value === '') {
1830                                         unset($parsed['key']);
1831                                 }
1832                         }
1833                 }
1834
1835                 foreach ($parsed as $tag => $value) {
1836                         $parsed[$tag] = array($value);
1837                 }
1838
1839                 return $parsed;
1840         }
1841
1842         public static function waveSNDMtagLookup($tagshortname) {
1843                 $begin = __LINE__;
1844
1845                 /** This is not a comment!
1846
1847                         ©kwd   keywords
1848                         ©BPM   bpm
1849                         ©trt   tracktitle
1850                         ©des   description
1851                         ©gen   category
1852                         ©fin   featuredinstrument
1853                         ©LID   longid
1854                         ©bex   bwdescription
1855                         ©pub   publisher
1856                         ©cdt   cdtitle
1857                         ©alb   library
1858                         ©com   composer
1859
1860                 */
1861
1862                 return getid3_lib::EmbeddedLookup($tagshortname, $begin, __LINE__, __FILE__, 'riff-sndm');
1863         }
1864
1865         public static function wFormatTagLookup($wFormatTag) {
1866
1867                 $begin = __LINE__;
1868
1869                 /** This is not a comment!
1870
1871                         0x0000  Microsoft Unknown Wave Format
1872                         0x0001  Pulse Code Modulation (PCM)
1873                         0x0002  Microsoft ADPCM
1874                         0x0003  IEEE Float
1875                         0x0004  Compaq Computer VSELP
1876                         0x0005  IBM CVSD
1877                         0x0006  Microsoft A-Law
1878                         0x0007  Microsoft mu-Law
1879                         0x0008  Microsoft DTS
1880                         0x0010  OKI ADPCM
1881                         0x0011  Intel DVI/IMA ADPCM
1882                         0x0012  Videologic MediaSpace ADPCM
1883                         0x0013  Sierra Semiconductor ADPCM
1884                         0x0014  Antex Electronics G.723 ADPCM
1885                         0x0015  DSP Solutions DigiSTD
1886                         0x0016  DSP Solutions DigiFIX
1887                         0x0017  Dialogic OKI ADPCM
1888                         0x0018  MediaVision ADPCM
1889                         0x0019  Hewlett-Packard CU
1890                         0x0020  Yamaha ADPCM
1891                         0x0021  Speech Compression Sonarc
1892                         0x0022  DSP Group TrueSpeech
1893                         0x0023  Echo Speech EchoSC1
1894                         0x0024  Audiofile AF36
1895                         0x0025  Audio Processing Technology APTX
1896                         0x0026  AudioFile AF10
1897                         0x0027  Prosody 1612
1898                         0x0028  LRC
1899                         0x0030  Dolby AC2
1900                         0x0031  Microsoft GSM 6.10
1901                         0x0032  MSNAudio
1902                         0x0033  Antex Electronics ADPCME
1903                         0x0034  Control Resources VQLPC
1904                         0x0035  DSP Solutions DigiREAL
1905                         0x0036  DSP Solutions DigiADPCM
1906                         0x0037  Control Resources CR10
1907                         0x0038  Natural MicroSystems VBXADPCM
1908                         0x0039  Crystal Semiconductor IMA ADPCM
1909                         0x003A  EchoSC3
1910                         0x003B  Rockwell ADPCM
1911                         0x003C  Rockwell Digit LK
1912                         0x003D  Xebec
1913                         0x0040  Antex Electronics G.721 ADPCM
1914                         0x0041  G.728 CELP
1915                         0x0042  MSG723
1916                         0x0050  MPEG Layer-2 or Layer-1
1917                         0x0052  RT24
1918                         0x0053  PAC
1919                         0x0055  MPEG Layer-3
1920                         0x0059  Lucent G.723
1921                         0x0060  Cirrus
1922                         0x0061  ESPCM
1923                         0x0062  Voxware
1924                         0x0063  Canopus Atrac
1925                         0x0064  G.726 ADPCM
1926                         0x0065  G.722 ADPCM
1927                         0x0066  DSAT
1928                         0x0067  DSAT Display
1929                         0x0069  Voxware Byte Aligned
1930                         0x0070  Voxware AC8
1931                         0x0071  Voxware AC10
1932                         0x0072  Voxware AC16
1933                         0x0073  Voxware AC20
1934                         0x0074  Voxware MetaVoice
1935                         0x0075  Voxware MetaSound
1936                         0x0076  Voxware RT29HW
1937                         0x0077  Voxware VR12
1938                         0x0078  Voxware VR18
1939                         0x0079  Voxware TQ40
1940                         0x0080  Softsound
1941                         0x0081  Voxware TQ60
1942                         0x0082  MSRT24
1943                         0x0083  G.729A
1944                         0x0084  MVI MV12
1945                         0x0085  DF G.726
1946                         0x0086  DF GSM610
1947                         0x0088  ISIAudio
1948                         0x0089  Onlive
1949                         0x0091  SBC24
1950                         0x0092  Dolby AC3 SPDIF
1951                         0x0093  MediaSonic G.723
1952                         0x0094  Aculab PLC    Prosody 8kbps
1953                         0x0097  ZyXEL ADPCM
1954                         0x0098  Philips LPCBB
1955                         0x0099  Packed
1956                         0x00FF  AAC
1957                         0x0100  Rhetorex ADPCM
1958                         0x0101  IBM mu-law
1959                         0x0102  IBM A-law
1960                         0x0103  IBM AVC Adaptive Differential Pulse Code Modulation (ADPCM)
1961                         0x0111  Vivo G.723
1962                         0x0112  Vivo Siren
1963                         0x0123  Digital G.723
1964                         0x0125  Sanyo LD ADPCM
1965                         0x0130  Sipro Lab Telecom ACELP NET
1966                         0x0131  Sipro Lab Telecom ACELP 4800
1967                         0x0132  Sipro Lab Telecom ACELP 8V3
1968                         0x0133  Sipro Lab Telecom G.729
1969                         0x0134  Sipro Lab Telecom G.729A
1970                         0x0135  Sipro Lab Telecom Kelvin
1971                         0x0140  Windows Media Video V8
1972                         0x0150  Qualcomm PureVoice
1973                         0x0151  Qualcomm HalfRate
1974                         0x0155  Ring Zero Systems TUB GSM
1975                         0x0160  Microsoft Audio 1
1976                         0x0161  Windows Media Audio V7 / V8 / V9
1977                         0x0162  Windows Media Audio Professional V9
1978                         0x0163  Windows Media Audio Lossless V9
1979                         0x0200  Creative Labs ADPCM
1980                         0x0202  Creative Labs Fastspeech8
1981                         0x0203  Creative Labs Fastspeech10
1982                         0x0210  UHER Informatic GmbH ADPCM
1983                         0x0220  Quarterdeck
1984                         0x0230  I-link Worldwide VC
1985                         0x0240  Aureal RAW Sport
1986                         0x0250  Interactive Products HSX
1987                         0x0251  Interactive Products RPELP
1988                         0x0260  Consistent Software CS2
1989                         0x0270  Sony SCX
1990                         0x0300  Fujitsu FM Towns Snd
1991                         0x0400  BTV Digital
1992                         0x0401  Intel Music Coder
1993                         0x0450  QDesign Music
1994                         0x0680  VME VMPCM
1995                         0x0681  AT&T Labs TPC
1996                         0x08AE  ClearJump LiteWave
1997                         0x1000  Olivetti GSM
1998                         0x1001  Olivetti ADPCM
1999                         0x1002  Olivetti CELP
2000                         0x1003  Olivetti SBC
2001                         0x1004  Olivetti OPR
2002                         0x1100  Lernout & Hauspie Codec (0x1100)
2003                         0x1101  Lernout & Hauspie CELP Codec (0x1101)
2004                         0x1102  Lernout & Hauspie SBC Codec (0x1102)
2005                         0x1103  Lernout & Hauspie SBC Codec (0x1103)
2006                         0x1104  Lernout & Hauspie SBC Codec (0x1104)
2007                         0x1400  Norris
2008                         0x1401  AT&T ISIAudio
2009                         0x1500  Soundspace Music Compression
2010                         0x181C  VoxWare RT24 Speech
2011                         0x1FC4  NCT Soft ALF2CD (www.nctsoft.com)
2012                         0x2000  Dolby AC3
2013                         0x2001  Dolby DTS
2014                         0x2002  WAVE_FORMAT_14_4
2015                         0x2003  WAVE_FORMAT_28_8
2016                         0x2004  WAVE_FORMAT_COOK
2017                         0x2005  WAVE_FORMAT_DNET
2018                         0x674F  Ogg Vorbis 1
2019                         0x6750  Ogg Vorbis 2
2020                         0x6751  Ogg Vorbis 3
2021                         0x676F  Ogg Vorbis 1+
2022                         0x6770  Ogg Vorbis 2+
2023                         0x6771  Ogg Vorbis 3+
2024                         0x7A21  GSM-AMR (CBR, no SID)
2025                         0x7A22  GSM-AMR (VBR, including SID)
2026                         0xFFFE  WAVE_FORMAT_EXTENSIBLE
2027                         0xFFFF  WAVE_FORMAT_DEVELOPMENT
2028
2029                 */
2030
2031                 return getid3_lib::EmbeddedLookup('0x'.str_pad(strtoupper(dechex($wFormatTag)), 4, '0', STR_PAD_LEFT), $begin, __LINE__, __FILE__, 'riff-wFormatTag');
2032         }
2033
2034         public static function fourccLookup($fourcc) {
2035
2036                 $begin = __LINE__;
2037
2038                 /** This is not a comment!
2039
2040                         swot    http://developer.apple.com/qa/snd/snd07.html
2041                         ____    No Codec (____)
2042                         _BIT    BI_BITFIELDS (Raw RGB)
2043                         _JPG    JPEG compressed
2044                         _PNG    PNG compressed W3C/ISO/IEC (RFC-2083)
2045                         _RAW    Full Frames (Uncompressed)
2046                         _RGB    Raw RGB Bitmap
2047                         _RL4    RLE 4bpp RGB
2048                         _RL8    RLE 8bpp RGB
2049                         3IV1    3ivx MPEG-4 v1
2050                         3IV2    3ivx MPEG-4 v2
2051                         3IVX    3ivx MPEG-4
2052                         AASC    Autodesk Animator
2053                         ABYR    Kensington ?ABYR?
2054                         AEMI    Array Microsystems VideoONE MPEG1-I Capture
2055                         AFLC    Autodesk Animator FLC
2056                         AFLI    Autodesk Animator FLI
2057                         AMPG    Array Microsystems VideoONE MPEG
2058                         ANIM    Intel RDX (ANIM)
2059                         AP41    AngelPotion Definitive
2060                         ASV1    Asus Video v1
2061                         ASV2    Asus Video v2
2062                         ASVX    Asus Video 2.0 (audio)
2063                         AUR2    AuraVision Aura 2 Codec - YUV 4:2:2
2064                         AURA    AuraVision Aura 1 Codec - YUV 4:1:1
2065                         AVDJ    Independent JPEG Group\'s codec (AVDJ)
2066                         AVRN    Independent JPEG Group\'s codec (AVRN)
2067                         AYUV    4:4:4 YUV (AYUV)
2068                         AZPR    Quicktime Apple Video (AZPR)
2069                         BGR     Raw RGB32
2070                         BLZ0    Blizzard DivX MPEG-4
2071                         BTVC    Conexant Composite Video
2072                         BINK    RAD Game Tools Bink Video
2073                         BT20    Conexant Prosumer Video
2074                         BTCV    Conexant Composite Video Codec
2075                         BW10    Data Translation Broadway MPEG Capture
2076                         CC12    Intel YUV12
2077                         CDVC    Canopus DV
2078                         CFCC    Digital Processing Systems DPS Perception
2079                         CGDI    Microsoft Office 97 Camcorder Video
2080                         CHAM    Winnov Caviara Champagne
2081                         CJPG    Creative WebCam JPEG
2082                         CLJR    Cirrus Logic YUV 4:1:1
2083                         CMYK    Common Data Format in Printing (Colorgraph)
2084                         CPLA    Weitek 4:2:0 YUV Planar
2085                         CRAM    Microsoft Video 1 (CRAM)
2086                         cvid    Radius Cinepak
2087                         CVID    Radius Cinepak
2088                         CWLT    Microsoft Color WLT DIB
2089                         CYUV    Creative Labs YUV
2090                         CYUY    ATI YUV
2091                         D261    H.261
2092                         D263    H.263
2093                         DIB     Device Independent Bitmap
2094                         DIV1    FFmpeg OpenDivX
2095                         DIV2    Microsoft MPEG-4 v1/v2
2096                         DIV3    DivX ;-) MPEG-4 v3.x Low-Motion
2097                         DIV4    DivX ;-) MPEG-4 v3.x Fast-Motion
2098                         DIV5    DivX MPEG-4 v5.x
2099                         DIV6    DivX ;-) (MS MPEG-4 v3.x)
2100                         DIVX    DivX MPEG-4 v4 (OpenDivX / Project Mayo)
2101                         divx    DivX MPEG-4
2102                         DMB1    Matrox Rainbow Runner hardware MJPEG
2103                         DMB2    Paradigm MJPEG
2104                         DSVD    ?DSVD?
2105                         DUCK    Duck TrueMotion 1.0
2106                         DPS0    DPS/Leitch Reality Motion JPEG
2107                         DPSC    DPS/Leitch PAR Motion JPEG
2108                         DV25    Matrox DVCPRO codec
2109                         DV50    Matrox DVCPRO50 codec
2110                         DVC     IEC 61834 and SMPTE 314M (DVC/DV Video)
2111                         DVCP    IEC 61834 and SMPTE 314M (DVC/DV Video)
2112                         DVHD    IEC Standard DV 1125 lines @ 30fps / 1250 lines @ 25fps
2113                         DVMA    Darim Vision DVMPEG (dummy for MPEG compressor) (www.darvision.com)
2114                         DVSL    IEC Standard DV compressed in SD (SDL)
2115                         DVAN    ?DVAN?
2116                         DVE2    InSoft DVE-2 Videoconferencing
2117                         dvsd    IEC 61834 and SMPTE 314M DVC/DV Video
2118                         DVSD    IEC 61834 and SMPTE 314M DVC/DV Video
2119                         DVX1    Lucent DVX1000SP Video Decoder
2120                         DVX2    Lucent DVX2000S Video Decoder
2121                         DVX3    Lucent DVX3000S Video Decoder
2122                         DX50    DivX v5
2123                         DXT1    Microsoft DirectX Compressed Texture (DXT1)
2124                         DXT2    Microsoft DirectX Compressed Texture (DXT2)