Example #1
0
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     // initialize these values to an empty array, otherwise they default to NULL
     // and you can't append array values to a NULL value
     $info['riff'] = array('raw' => array());
     // Shortcuts
     $thisfile_riff =& $info['riff'];
     $thisfile_riff_raw =& $thisfile_riff['raw'];
     $thisfile_audio =& $info['audio'];
     $thisfile_video =& $info['video'];
     $thisfile_audio_dataformat =& $thisfile_audio['dataformat'];
     $thisfile_riff_audio =& $thisfile_riff['audio'];
     $thisfile_riff_video =& $thisfile_riff['video'];
     $Original['avdataoffset'] = $info['avdataoffset'];
     $Original['avdataend'] = $info['avdataend'];
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $RIFFheader = fread($this->getid3->fp, 12);
     $RIFFsubtype = substr($RIFFheader, 8, 4);
     switch (substr($RIFFheader, 0, 4)) {
         case 'FORM':
             $info['fileformat'] = 'aiff';
             $thisfile_riff['header_size'] = $this->EitherEndian2Int(substr($RIFFheader, 4, 4));
             $thisfile_riff[$RIFFsubtype] = $this->ParseRIFF($info['avdataoffset'] + 12, $info['avdataoffset'] + $thisfile_riff['header_size']);
             break;
         case 'RIFF':
             // AVI, WAV, etc
         // AVI, WAV, etc
         case 'SDSS':
             // SDSS is identical to RIFF, just renamed. Used by SmartSound QuickTracks (www.smartsound.com)
         // SDSS is identical to RIFF, just renamed. Used by SmartSound QuickTracks (www.smartsound.com)
         case 'RMP3':
             // RMP3 is identical to RIFF, just renamed. Used by [unknown program] when creating RIFF-MP3s
             $info['fileformat'] = 'riff';
             $thisfile_riff['header_size'] = $this->EitherEndian2Int(substr($RIFFheader, 4, 4));
             if ($RIFFsubtype == 'RMP3') {
                 // RMP3 is identical to WAVE, just renamed. Used by [unknown program] when creating RIFF-MP3s
                 $RIFFsubtype = 'WAVE';
             }
             $thisfile_riff[$RIFFsubtype] = $this->ParseRIFF($info['avdataoffset'] + 12, $info['avdataoffset'] + $thisfile_riff['header_size']);
             if ($info['avdataend'] - $info['filesize'] == 1) {
                 // LiteWave appears to incorrectly *not* pad actual output file
                 // to nearest WORD boundary so may appear to be short by one
                 // byte, in which case - skip warning
                 $info['avdataend'] = $info['filesize'];
             }
             $nextRIFFoffset = $Original['avdataoffset'] + 8 + $thisfile_riff['header_size'];
             // 8 = "RIFF" + 32-bit offset
             while ($nextRIFFoffset < min($info['filesize'], $info['avdataend'])) {
                 if (!Helper::intValueSupported($nextRIFFoffset + 1024)) {
                     $info['error'][] = 'AVI extends beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB and PHP filesystem functions cannot read that far, playtime is probably wrong';
                     $info['warning'][] = '[avdataend] value may be incorrect, multiple AVIX chunks may be present';
                     break;
                 } else {
                     fseek($this->getid3->fp, $nextRIFFoffset, SEEK_SET);
                     $nextRIFFheader = fread($this->getid3->fp, 12);
                     if ($nextRIFFoffset == $info['avdataend'] - 1) {
                         if (substr($nextRIFFheader, 0, 1) == "") {
                             // RIFF padded to WORD boundary, we're actually already at the end
                             break;
                         }
                     }
                     $nextRIFFheaderID = substr($nextRIFFheader, 0, 4);
                     $nextRIFFsize = $this->EitherEndian2Int(substr($nextRIFFheader, 4, 4));
                     $nextRIFFtype = substr($nextRIFFheader, 8, 4);
                     $chunkdata = array();
                     $chunkdata['offset'] = $nextRIFFoffset + 8;
                     $chunkdata['size'] = $nextRIFFsize;
                     $nextRIFFoffset = $chunkdata['offset'] + $chunkdata['size'];
                     switch ($nextRIFFheaderID) {
                         case 'RIFF':
                             $info['avdataend'] = $nextRIFFoffset;
                             if (!Helper::intValueSupported($info['avdataend'])) {
                                 $info['error'][] = 'AVI extends beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB and PHP filesystem functions cannot read that far, playtime is probably wrong';
                                 $info['warning'][] = '[avdataend] value may be incorrect, multiple AVIX chunks may be present';
                             }
                             $chunkdata['chunks'] = $this->ParseRIFF($chunkdata['offset'] + 4, $chunkdata['offset'] + $chunkdata['size']);
                             if (!isset($thisfile_riff[$nextRIFFtype])) {
                                 $thisfile_riff[$nextRIFFtype] = array();
                             }
                             $thisfile_riff[$nextRIFFtype][] = $chunkdata;
                             break;
                         case 'JUNK':
                             // ignore
                             $thisfile_riff[$nextRIFFheaderID][] = $chunkdata;
                             break;
                         default:
                             if ($info['filesize'] == $chunkdata['offset'] - 8 + 128) {
                                 $DIVXTAG = $nextRIFFheader . fread($this->getid3->fp, 128 - 12);
                                 if (substr($DIVXTAG, -7) == 'DIVXTAG') {
                                     // 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
                                     $info['warning'][] = 'Found wrongly-structured DIVXTAG at offset ' . (ftell($this->getid3->fp) - 128 + 12) . ', parsing anyway';
                                     $thisfile_riff['DIVXTAG'] = $this->ParseDIVXTAG($DIVXTAG);
                                     foreach ($thisfile_riff['DIVXTAG'] as $key => $value) {
                                         if ($value && !preg_match('#_id$#', $key)) {
                                             $thisfile_riff['comments'][$key][] = $value;
                                         }
                                     }
                                     break 2;
                                 }
                             }
                             $info['warning'][] = 'expecting "RIFF" or "JUNK" at ' . $nextRIFFoffset . ', found ' . Helper::PrintHexBytes(substr($nextRIFFheader, 0, 4)) . ' - skipping rest of file';
                             break 2;
                     }
                 }
             }
             if ($RIFFsubtype == 'WAVE') {
                 $thisfile_riff_WAVE =& $thisfile_riff['WAVE'];
             }
             break;
         default:
             $info['error'][] = 'Cannot parse RIFF (this is maybe not a RIFF / WAV / AVI file?) - expecting "FORM|RIFF|SDSS|RMP3" found "' . $RIFFsubtype . '" instead';
             unset($info['fileformat']);
             return false;
             break;
     }
     $streamindex = 0;
     switch ($RIFFsubtype) {
         case 'WAVE':
             if (empty($thisfile_audio['bitrate_mode'])) {
                 $thisfile_audio['bitrate_mode'] = 'cbr';
             }
             if (empty($thisfile_audio_dataformat)) {
                 $thisfile_audio_dataformat = 'wav';
             }
             if (isset($thisfile_riff_WAVE['data'][0]['offset'])) {
                 $info['avdataoffset'] = $thisfile_riff_WAVE['data'][0]['offset'] + 8;
                 $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff_WAVE['data'][0]['size'];
             }
             if (isset($thisfile_riff_WAVE['fmt '][0]['data'])) {
                 $thisfile_riff_audio[$streamindex] = self::RIFFparseWAVEFORMATex($thisfile_riff_WAVE['fmt '][0]['data']);
                 $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag'];
                 if (!isset($thisfile_riff_audio[$streamindex]['bitrate']) || $thisfile_riff_audio[$streamindex]['bitrate'] == 0) {
                     $info['error'][] = 'Corrupt RIFF file: bitrate_audio == zero';
                     return false;
                 }
                 $thisfile_riff_raw['fmt '] = $thisfile_riff_audio[$streamindex]['raw'];
                 unset($thisfile_riff_audio[$streamindex]['raw']);
                 $thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex];
                 $thisfile_audio = Helper::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]);
                 if (substr($thisfile_audio['codec'], 0, strlen('unknown: 0x')) == 'unknown: 0x') {
                     $info['warning'][] = 'Audio codec = ' . $thisfile_audio['codec'];
                 }
                 $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate'];
                 $info['playtime_seconds'] = (double) (($info['avdataend'] - $info['avdataoffset']) * 8 / $thisfile_audio['bitrate']);
                 $thisfile_audio['lossless'] = false;
                 if (isset($thisfile_riff_WAVE['data'][0]['offset']) && isset($thisfile_riff_raw['fmt ']['wFormatTag'])) {
                     switch ($thisfile_riff_raw['fmt ']['wFormatTag']) {
                         case 0x1:
                             // PCM
                             $thisfile_audio['lossless'] = true;
                             break;
                         case 0x2000:
                             // AC-3
                             $thisfile_audio_dataformat = 'ac3';
                             break;
                         default:
                             // do nothing
                             break;
                     }
                 }
                 $thisfile_audio['streams'][$streamindex]['wformattag'] = $thisfile_audio['wformattag'];
                 $thisfile_audio['streams'][$streamindex]['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
                 $thisfile_audio['streams'][$streamindex]['lossless'] = $thisfile_audio['lossless'];
                 $thisfile_audio['streams'][$streamindex]['dataformat'] = $thisfile_audio_dataformat;
             }
             if (isset($thisfile_riff_WAVE['rgad'][0]['data'])) {
                 // shortcuts
                 $rgadData =& $thisfile_riff_WAVE['rgad'][0]['data'];
                 $thisfile_riff_raw['rgad'] = array('track' => array(), 'album' => array());
                 $thisfile_riff_raw_rgad =& $thisfile_riff_raw['rgad'];
                 $thisfile_riff_raw_rgad_track =& $thisfile_riff_raw_rgad['track'];
                 $thisfile_riff_raw_rgad_album =& $thisfile_riff_raw_rgad['album'];
                 $thisfile_riff_raw_rgad['fPeakAmplitude'] = Helper::LittleEndian2Float(substr($rgadData, 0, 4));
                 $thisfile_riff_raw_rgad['nRadioRgAdjust'] = $this->EitherEndian2Int(substr($rgadData, 4, 2));
                 $thisfile_riff_raw_rgad['nAudiophileRgAdjust'] = $this->EitherEndian2Int(substr($rgadData, 6, 2));
                 $nRadioRgAdjustBitstring = str_pad(Helper::Dec2Bin($thisfile_riff_raw_rgad['nRadioRgAdjust']), 16, '0', STR_PAD_LEFT);
                 $nAudiophileRgAdjustBitstring = str_pad(Helper::Dec2Bin($thisfile_riff_raw_rgad['nAudiophileRgAdjust']), 16, '0', STR_PAD_LEFT);
                 $thisfile_riff_raw_rgad_track['name'] = Helper::Bin2Dec(substr($nRadioRgAdjustBitstring, 0, 3));
                 $thisfile_riff_raw_rgad_track['originator'] = Helper::Bin2Dec(substr($nRadioRgAdjustBitstring, 3, 3));
                 $thisfile_riff_raw_rgad_track['signbit'] = Helper::Bin2Dec(substr($nRadioRgAdjustBitstring, 6, 1));
                 $thisfile_riff_raw_rgad_track['adjustment'] = Helper::Bin2Dec(substr($nRadioRgAdjustBitstring, 7, 9));
                 $thisfile_riff_raw_rgad_album['name'] = Helper::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 0, 3));
                 $thisfile_riff_raw_rgad_album['originator'] = Helper::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 3, 3));
                 $thisfile_riff_raw_rgad_album['signbit'] = Helper::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 6, 1));
                 $thisfile_riff_raw_rgad_album['adjustment'] = Helper::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 7, 9));
                 $thisfile_riff['rgad']['peakamplitude'] = $thisfile_riff_raw_rgad['fPeakAmplitude'];
                 if ($thisfile_riff_raw_rgad_track['name'] != 0 && $thisfile_riff_raw_rgad_track['originator'] != 0) {
                     $thisfile_riff['rgad']['track']['name'] = Helper::RGADnameLookup($thisfile_riff_raw_rgad_track['name']);
                     $thisfile_riff['rgad']['track']['originator'] = Helper::RGADoriginatorLookup($thisfile_riff_raw_rgad_track['originator']);
                     $thisfile_riff['rgad']['track']['adjustment'] = Helper::RGADadjustmentLookup($thisfile_riff_raw_rgad_track['adjustment'], $thisfile_riff_raw_rgad_track['signbit']);
                 }
                 if ($thisfile_riff_raw_rgad_album['name'] != 0 && $thisfile_riff_raw_rgad_album['originator'] != 0) {
                     $thisfile_riff['rgad']['album']['name'] = Helper::RGADnameLookup($thisfile_riff_raw_rgad_album['name']);
                     $thisfile_riff['rgad']['album']['originator'] = Helper::RGADoriginatorLookup($thisfile_riff_raw_rgad_album['originator']);
                     $thisfile_riff['rgad']['album']['adjustment'] = Helper::RGADadjustmentLookup($thisfile_riff_raw_rgad_album['adjustment'], $thisfile_riff_raw_rgad_album['signbit']);
                 }
             }
             if (isset($thisfile_riff_WAVE['fact'][0]['data'])) {
                 $thisfile_riff_raw['fact']['NumberOfSamples'] = $this->EitherEndian2Int(substr($thisfile_riff_WAVE['fact'][0]['data'], 0, 4));
                 // This should be a good way of calculating exact playtime,
                 // but some sample files have had incorrect number of samples,
                 // so cannot use this method
                 // if (!empty($thisfile_riff_raw['fmt ']['nSamplesPerSec'])) {
                 //     $info['playtime_seconds'] = (float) $thisfile_riff_raw['fact']['NumberOfSamples'] / $thisfile_riff_raw['fmt ']['nSamplesPerSec'];
                 // }
             }
             if (!empty($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'])) {
                 $thisfile_audio['bitrate'] = Helper::CastAsInt($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'] * 8);
             }
             if (isset($thisfile_riff_WAVE['bext'][0]['data'])) {
                 // shortcut
                 $thisfile_riff_WAVE_bext_0 =& $thisfile_riff_WAVE['bext'][0];
                 $thisfile_riff_WAVE_bext_0['title'] = trim(substr($thisfile_riff_WAVE_bext_0['data'], 0, 256));
                 $thisfile_riff_WAVE_bext_0['author'] = trim(substr($thisfile_riff_WAVE_bext_0['data'], 256, 32));
                 $thisfile_riff_WAVE_bext_0['reference'] = trim(substr($thisfile_riff_WAVE_bext_0['data'], 288, 32));
                 $thisfile_riff_WAVE_bext_0['origin_date'] = substr($thisfile_riff_WAVE_bext_0['data'], 320, 10);
                 $thisfile_riff_WAVE_bext_0['origin_time'] = substr($thisfile_riff_WAVE_bext_0['data'], 330, 8);
                 $thisfile_riff_WAVE_bext_0['time_reference'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 338, 8));
                 $thisfile_riff_WAVE_bext_0['bwf_version'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 346, 1));
                 $thisfile_riff_WAVE_bext_0['reserved'] = substr($thisfile_riff_WAVE_bext_0['data'], 347, 254);
                 $thisfile_riff_WAVE_bext_0['coding_history'] = explode("\r\n", trim(substr($thisfile_riff_WAVE_bext_0['data'], 601)));
                 if (preg_match('#^([0-9]{4}).([0-9]{2}).([0-9]{2})$#', $thisfile_riff_WAVE_bext_0['origin_date'], $matches_bext_date)) {
                     if (preg_match('#^([0-9]{2}).([0-9]{2}).([0-9]{2})$#', $thisfile_riff_WAVE_bext_0['origin_time'], $matches_bext_time)) {
                         list($dummy, $bext_timestamp['year'], $bext_timestamp['month'], $bext_timestamp['day']) = $matches_bext_date;
                         list($dummy, $bext_timestamp['hour'], $bext_timestamp['minute'], $bext_timestamp['second']) = $matches_bext_time;
                         $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']);
                     } else {
                         $info['warning'][] = 'RIFF.WAVE.BEXT.origin_time is invalid';
                     }
                 } else {
                     $info['warning'][] = 'RIFF.WAVE.BEXT.origin_date is invalid';
                 }
                 $thisfile_riff['comments']['author'][] = $thisfile_riff_WAVE_bext_0['author'];
                 $thisfile_riff['comments']['title'][] = $thisfile_riff_WAVE_bext_0['title'];
             }
             if (isset($thisfile_riff_WAVE['MEXT'][0]['data'])) {
                 // shortcut
                 $thisfile_riff_WAVE_MEXT_0 =& $thisfile_riff_WAVE['MEXT'][0];
                 $thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 0, 2));
                 $thisfile_riff_WAVE_MEXT_0['flags']['homogenous'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x1);
                 if ($thisfile_riff_WAVE_MEXT_0['flags']['homogenous']) {
                     $thisfile_riff_WAVE_MEXT_0['flags']['padding'] = $thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x2 ? false : true;
                     $thisfile_riff_WAVE_MEXT_0['flags']['22_or_44'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x4);
                     $thisfile_riff_WAVE_MEXT_0['flags']['free_format'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x8);
                     $thisfile_riff_WAVE_MEXT_0['nominal_frame_size'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 2, 2));
                 }
                 $thisfile_riff_WAVE_MEXT_0['anciliary_data_length'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 6, 2));
                 $thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 8, 2));
                 $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_left'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x1);
                 $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_free'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x2);
                 $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_right'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x4);
             }
             if (isset($thisfile_riff_WAVE['cart'][0]['data'])) {
                 // shortcut
                 $thisfile_riff_WAVE_cart_0 =& $thisfile_riff_WAVE['cart'][0];
                 $thisfile_riff_WAVE_cart_0['version'] = substr($thisfile_riff_WAVE_cart_0['data'], 0, 4);
                 $thisfile_riff_WAVE_cart_0['title'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 4, 64));
                 $thisfile_riff_WAVE_cart_0['artist'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 68, 64));
                 $thisfile_riff_WAVE_cart_0['cut_id'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 132, 64));
                 $thisfile_riff_WAVE_cart_0['client_id'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 196, 64));
                 $thisfile_riff_WAVE_cart_0['category'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 260, 64));
                 $thisfile_riff_WAVE_cart_0['classification'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 324, 64));
                 $thisfile_riff_WAVE_cart_0['out_cue'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 388, 64));
                 $thisfile_riff_WAVE_cart_0['start_date'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 452, 10));
                 $thisfile_riff_WAVE_cart_0['start_time'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 462, 8));
                 $thisfile_riff_WAVE_cart_0['end_date'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 470, 10));
                 $thisfile_riff_WAVE_cart_0['end_time'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 480, 8));
                 $thisfile_riff_WAVE_cart_0['producer_app_id'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 488, 64));
                 $thisfile_riff_WAVE_cart_0['producer_app_version'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 552, 64));
                 $thisfile_riff_WAVE_cart_0['user_defined_text'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 616, 64));
                 $thisfile_riff_WAVE_cart_0['zero_db_reference'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE_cart_0['data'], 680, 4), true);
                 for ($i = 0; $i < 8; ++$i) {
                     $thisfile_riff_WAVE_cart_0['post_time'][$i]['usage_fourcc'] = substr($thisfile_riff_WAVE_cart_0['data'], 684 + $i * 8, 4);
                     $thisfile_riff_WAVE_cart_0['post_time'][$i]['timer_value'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE_cart_0['data'], 684 + $i * 8 + 4, 4));
                 }
                 $thisfile_riff_WAVE_cart_0['url'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 748, 1024));
                 $thisfile_riff_WAVE_cart_0['tag_text'] = explode("\r\n", trim(substr($thisfile_riff_WAVE_cart_0['data'], 1772)));
                 $thisfile_riff['comments']['artist'][] = $thisfile_riff_WAVE_cart_0['artist'];
                 $thisfile_riff['comments']['title'][] = $thisfile_riff_WAVE_cart_0['title'];
             }
             if (isset($thisfile_riff_WAVE['SNDM'][0]['data'])) {
                 // SoundMiner metadata
                 // shortcuts
                 $thisfile_riff_WAVE_SNDM_0 =& $thisfile_riff_WAVE['SNDM'][0];
                 $thisfile_riff_WAVE_SNDM_0_data =& $thisfile_riff_WAVE_SNDM_0['data'];
                 $SNDM_startoffset = 0;
                 $SNDM_endoffset = $thisfile_riff_WAVE_SNDM_0['size'];
                 while ($SNDM_startoffset < $SNDM_endoffset) {
                     $SNDM_thisTagOffset = 0;
                     $SNDM_thisTagSize = Helper::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 4));
                     $SNDM_thisTagOffset += 4;
                     $SNDM_thisTagKey = substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 4);
                     $SNDM_thisTagOffset += 4;
                     $SNDM_thisTagDataSize = Helper::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 2));
                     $SNDM_thisTagOffset += 2;
                     $SNDM_thisTagDataFlags = Helper::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 2));
                     $SNDM_thisTagOffset += 2;
                     $SNDM_thisTagDataText = substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, $SNDM_thisTagDataSize);
                     $SNDM_thisTagOffset += $SNDM_thisTagDataSize;
                     if ($SNDM_thisTagSize != 4 + 4 + 2 + 2 + $SNDM_thisTagDataSize) {
                         $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) . ')';
                         break;
                     } elseif ($SNDM_thisTagSize <= 0) {
                         $info['warning'][] = 'RIFF.WAVE.SNDM.data contains zero-size tag at offset ' . $SNDM_startoffset . ' (file offset ' . ($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset) . ')';
                         break;
                     }
                     $SNDM_startoffset += $SNDM_thisTagSize;
                     $thisfile_riff_WAVE_SNDM_0['parsed_raw'][$SNDM_thisTagKey] = $SNDM_thisTagDataText;
                     if ($parsedkey = $this->RIFFwaveSNDMtagLookup($SNDM_thisTagKey)) {
                         $thisfile_riff_WAVE_SNDM_0['parsed'][$parsedkey] = $SNDM_thisTagDataText;
                     } else {
                         $info['warning'][] = 'RIFF.WAVE.SNDM contains unknown tag "' . $SNDM_thisTagKey . '" at offset ' . $SNDM_startoffset . ' (file offset ' . ($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset) . ')';
                     }
                 }
                 $tagmapping = array('tracktitle' => 'title', 'category' => 'genre', 'cdtitle' => 'album', 'tracktitle' => 'title');
                 foreach ($tagmapping as $fromkey => $tokey) {
                     if (isset($thisfile_riff_WAVE_SNDM_0['parsed'][$fromkey])) {
                         $thisfile_riff['comments'][$tokey][] = $thisfile_riff_WAVE_SNDM_0['parsed'][$fromkey];
                     }
                 }
             }
             if (isset($thisfile_riff_WAVE['iXML'][0]['data'])) {
                 // requires functions simplexml_load_string and get_object_vars
                 if ($parsedXML = Helper::XML2array($thisfile_riff_WAVE['iXML'][0]['data'])) {
                     $thisfile_riff_WAVE['iXML'][0]['parsed'] = $parsedXML;
                     if (isset($parsedXML['SPEED']['MASTER_SPEED'])) {
                         @(list($numerator, $denominator) = explode('/', $parsedXML['SPEED']['MASTER_SPEED']));
                         $thisfile_riff_WAVE['iXML'][0]['master_speed'] = $numerator / ($denominator ? $denominator : 1000);
                     }
                     if (isset($parsedXML['SPEED']['TIMECODE_RATE'])) {
                         @(list($numerator, $denominator) = explode('/', $parsedXML['SPEED']['TIMECODE_RATE']));
                         $thisfile_riff_WAVE['iXML'][0]['timecode_rate'] = $numerator / ($denominator ? $denominator : 1000);
                     }
                     if (isset($parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO']) && !empty($parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE']) && !empty($thisfile_riff_WAVE['iXML'][0]['timecode_rate'])) {
                         $samples_since_midnight = floatval(ltrim($parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_HI'] . $parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO'], '0'));
                         $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] = $samples_since_midnight / $parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE'];
                         $h = floor($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] / 3600);
                         $m = floor(($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - $h * 3600) / 60);
                         $s = floor($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - $h * 3600 - $m * 60);
                         $f = ($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - $h * 3600 - $m * 60 - $s) * $thisfile_riff_WAVE['iXML'][0]['timecode_rate'];
                         $thisfile_riff_WAVE['iXML'][0]['timecode_string'] = sprintf('%02d:%02d:%02d:%05.2f', $h, $m, $s, $f);
                         $thisfile_riff_WAVE['iXML'][0]['timecode_string_round'] = sprintf('%02d:%02d:%02d:%02d', $h, $m, $s, round($f));
                     }
                     unset($parsedXML);
                 }
             }
             if (!isset($thisfile_audio['bitrate']) && isset($thisfile_riff_audio[$streamindex]['bitrate'])) {
                 $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate'];
                 $info['playtime_seconds'] = (double) (($info['avdataend'] - $info['avdataoffset']) * 8 / $thisfile_audio['bitrate']);
             }
             if (!empty($info['wavpack'])) {
                 $thisfile_audio_dataformat = 'wavpack';
                 $thisfile_audio['bitrate_mode'] = 'vbr';
                 $thisfile_audio['encoder'] = 'WavPack v' . $info['wavpack']['version'];
                 // Reset to the way it was - RIFF parsing will have messed this up
                 $info['avdataend'] = $Original['avdataend'];
                 $thisfile_audio['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
                 fseek($this->getid3->fp, $info['avdataoffset'] - 44, SEEK_SET);
                 $RIFFdata = fread($this->getid3->fp, 44);
                 $OrignalRIFFheaderSize = Helper::LittleEndian2Int(substr($RIFFdata, 4, 4)) + 8;
                 $OrignalRIFFdataSize = Helper::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44;
                 if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) {
                     $info['avdataend'] -= $OrignalRIFFheaderSize - $OrignalRIFFdataSize;
                     fseek($this->getid3->fp, $info['avdataend'], SEEK_SET);
                     $RIFFdata .= fread($this->getid3->fp, $OrignalRIFFheaderSize - $OrignalRIFFdataSize);
                 }
                 // move the data chunk after all other chunks (if any)
                 // so that the RIFF parser doesn't see EOF when trying
                 // to skip over the data chunk
                 $RIFFdata = substr($RIFFdata, 0, 36) . substr($RIFFdata, 44) . substr($RIFFdata, 36, 8);
                 $getid3_riff = new self($this->getid3);
                 $getid3_riff->ParseRIFFdata($RIFFdata);
                 unset($getid3_riff);
             }
             if (isset($thisfile_riff_raw['fmt ']['wFormatTag'])) {
                 switch ($thisfile_riff_raw['fmt ']['wFormatTag']) {
                     case 0x1:
                         // PCM
                         if (!empty($info['ac3'])) {
                             // Dolby Digital WAV files masquerade as PCM-WAV, but they're not
                             $thisfile_audio['wformattag'] = 0x2000;
                             $thisfile_audio['codec'] = $this->RIFFwFormatTagLookup($thisfile_audio['wformattag']);
                             $thisfile_audio['lossless'] = false;
                             $thisfile_audio['bitrate'] = $info['ac3']['bitrate'];
                             $thisfile_audio['sample_rate'] = $info['ac3']['sample_rate'];
                         }
                         break;
                     case 0x8ae:
                         // ClearJump LiteWave
                         $thisfile_audio['bitrate_mode'] = 'vbr';
                         $thisfile_audio_dataformat = 'litewave';
                         //typedef struct tagSLwFormat {
                         //  WORD    m_wCompFormat;     // low byte defines compression method, high byte is compression flags
                         //  DWORD   m_dwScale;         // scale factor for lossy compression
                         //  DWORD   m_dwBlockSize;     // number of samples in encoded blocks
                         //  WORD    m_wQuality;        // alias for the scale factor
                         //  WORD    m_wMarkDistance;   // distance between marks in bytes
                         //  WORD    m_wReserved;
                         //
                         //  //following paramters are ignored if CF_FILESRC is not set
                         //  DWORD   m_dwOrgSize;       // original file size in bytes
                         //  WORD    m_bFactExists;     // indicates if 'fact' chunk exists in the original file
                         //  DWORD   m_dwRiffChunkSize; // riff chunk size in the original file
                         //
                         //  PCMWAVEFORMAT m_OrgWf;     // original wave format
                         // }SLwFormat, *PSLwFormat;
                         // shortcut
                         $thisfile_riff['litewave']['raw'] = array();
                         $thisfile_riff_litewave =& $thisfile_riff['litewave'];
                         $thisfile_riff_litewave_raw =& $thisfile_riff_litewave['raw'];
                         $thisfile_riff_litewave_raw['compression_method'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 18, 1));
                         $thisfile_riff_litewave_raw['compression_flags'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 19, 1));
                         $thisfile_riff_litewave_raw['m_dwScale'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 20, 4));
                         $thisfile_riff_litewave_raw['m_dwBlockSize'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 24, 4));
                         $thisfile_riff_litewave_raw['m_wQuality'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 28, 2));
                         $thisfile_riff_litewave_raw['m_wMarkDistance'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 30, 2));
                         $thisfile_riff_litewave_raw['m_wReserved'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 32, 2));
                         $thisfile_riff_litewave_raw['m_dwOrgSize'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 34, 4));
                         $thisfile_riff_litewave_raw['m_bFactExists'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 38, 2));
                         $thisfile_riff_litewave_raw['m_dwRiffChunkSize'] = Helper::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 40, 4));
                         //$thisfile_riff_litewave['quality_factor'] = intval(round((2000 - $thisfile_riff_litewave_raw['m_dwScale']) / 20));
                         $thisfile_riff_litewave['quality_factor'] = $thisfile_riff_litewave_raw['m_wQuality'];
                         $thisfile_riff_litewave['flags']['raw_source'] = $thisfile_riff_litewave_raw['compression_flags'] & 0x1 ? false : true;
                         $thisfile_riff_litewave['flags']['vbr_blocksize'] = $thisfile_riff_litewave_raw['compression_flags'] & 0x2 ? false : true;
                         $thisfile_riff_litewave['flags']['seekpoints'] = (bool) ($thisfile_riff_litewave_raw['compression_flags'] & 0x4);
                         $thisfile_audio['lossless'] = $thisfile_riff_litewave_raw['m_wQuality'] == 100 ? true : false;
                         $thisfile_audio['encoder_options'] = '-q' . $thisfile_riff_litewave['quality_factor'];
                         break;
                     default:
                         break;
                 }
             }
             if ($info['avdataend'] > $info['filesize']) {
                 switch (!empty($thisfile_audio_dataformat) ? $thisfile_audio_dataformat : '') {
                     case 'wavpack':
                         // WavPack
                     // WavPack
                     case 'lpac':
                         // LPAC
                     // LPAC
                     case 'ofr':
                         // OptimFROG
                     // OptimFROG
                     case 'ofs':
                         // OptimFROG DualStream
                         // lossless compressed audio formats that keep original RIFF headers - skip warning
                         break;
                     case 'litewave':
                         if ($info['avdataend'] - $info['filesize'] == 1) {
                             // LiteWave appears to incorrectly *not* pad actual output file
                             // to nearest WORD boundary so may appear to be short by one
                             // byte, in which case - skip warning
                         } else {
                             // Short by more than one byte, throw warning
                             $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)';
                             $info['avdataend'] = $info['filesize'];
                         }
                         break;
                     default:
                         if ($info['avdataend'] - $info['filesize'] == 1 && $thisfile_riff[$RIFFsubtype]['data'][0]['size'] % 2 == 0 && ($info['filesize'] - $info['avdataoffset']) % 2 == 1) {
                             // output file appears to be incorrectly *not* padded to nearest WORD boundary
                             // Output less severe warning
                             $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)';
                             $info['avdataend'] = $info['filesize'];
                         } else {
                             // Short by more than one byte, throw warning
                             $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)';
                             $info['avdataend'] = $info['filesize'];
                         }
                         break;
                 }
             }
             if (!empty($info['mpeg']['audio']['LAME']['audio_bytes'])) {
                 if ($info['avdataend'] - $info['avdataoffset'] - $info['mpeg']['audio']['LAME']['audio_bytes'] == 1) {
                     --$info['avdataend'];
                     $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
                 }
             }
             if (isset($thisfile_audio_dataformat) && $thisfile_audio_dataformat == 'ac3') {
                 unset($thisfile_audio['bits_per_sample']);
                 if (!empty($info['ac3']['bitrate']) && $info['ac3']['bitrate'] != $thisfile_audio['bitrate']) {
                     $thisfile_audio['bitrate'] = $info['ac3']['bitrate'];
                 }
             }
             break;
         case 'AVI ':
             $thisfile_video['bitrate_mode'] = 'vbr';
             // maybe not, but probably
             $thisfile_video['dataformat'] = 'avi';
             $info['mime_type'] = 'video/avi';
             if (isset($thisfile_riff[$RIFFsubtype]['movi']['offset'])) {
                 $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['movi']['offset'] + 8;
                 if (isset($thisfile_riff['AVIX'])) {
                     $info['avdataend'] = $thisfile_riff['AVIX'][count($thisfile_riff['AVIX']) - 1]['chunks']['movi']['offset'] + $thisfile_riff['AVIX'][count($thisfile_riff['AVIX']) - 1]['chunks']['movi']['size'];
                 } else {
                     $info['avdataend'] = $thisfile_riff['AVI ']['movi']['offset'] + $thisfile_riff['AVI ']['movi']['size'];
                 }
                 if ($info['avdataend'] > $info['filesize']) {
                     $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)';
                     $info['avdataend'] = $info['filesize'];
                 }
             }
             if (isset($thisfile_riff['AVI ']['hdrl']['strl']['indx'])) {
                 //$bIndexType = array(
                 //    0x00 => 'AVI_INDEX_OF_INDEXES',
                 //	0x01 => 'AVI_INDEX_OF_CHUNKS',
                 //	0x80 => 'AVI_INDEX_IS_DATA',
                 //);
                 //$bIndexSubtype = array(
                 //	0x01 => array(
                 //		0x01 => 'AVI_INDEX_2FIELD',
                 //	),
                 //);
                 foreach ($thisfile_riff['AVI ']['hdrl']['strl']['indx'] as $streamnumber => $steamdataarray) {
                     $thisfile_riff_avi_hdrl_strl_indx_stream_data =& $thisfile_riff['AVI ']['hdrl']['strl']['indx'][$streamnumber]['data'];
                     $thisfile_riff_raw['indx'][$streamnumber]['wLongsPerEntry'] = $this->EitherEndian2Int(substr($thisfile_riff_avi_hdrl_strl_indx_stream_data, 0, 2));
                     $thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType'] = $this->EitherEndian2Int(substr($thisfile_riff_avi_hdrl_strl_indx_stream_data, 2, 1));
                     $thisfile_riff_raw['indx'][$streamnumber]['bIndexType'] = $this->EitherEndian2Int(substr($thisfile_riff_avi_hdrl_strl_indx_stream_data, 3, 1));
                     $thisfile_riff_raw['indx'][$streamnumber]['nEntriesInUse'] = $this->EitherEndian2Int(substr($thisfile_riff_avi_hdrl_strl_indx_stream_data, 4, 4));
                     $thisfile_riff_raw['indx'][$streamnumber]['dwChunkId'] = substr($thisfile_riff_avi_hdrl_strl_indx_stream_data, 8, 4);
                     $thisfile_riff_raw['indx'][$streamnumber]['dwReserved'] = $this->EitherEndian2Int(substr($thisfile_riff_avi_hdrl_strl_indx_stream_data, 12, 4));
                     //$thisfile_riff_raw['indx'][$streamnumber]['bIndexType_name']    =    $bIndexType[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']];
                     //$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType_name'] = $bIndexSubtype[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']][$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType']];
                     unset($thisfile_riff_avi_hdrl_strl_indx_stream_data);
                 }
             }
             if (isset($thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data'])) {
                 $avihData = $thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data'];
                 // shortcut
                 $thisfile_riff_raw['avih'] = array();
                 $thisfile_riff_raw_avih =& $thisfile_riff_raw['avih'];
                 $thisfile_riff_raw_avih['dwMicroSecPerFrame'] = $this->EitherEndian2Int(substr($avihData, 0, 4));
                 // frame display rate (or 0L)
                 if ($thisfile_riff_raw_avih['dwMicroSecPerFrame'] == 0) {
                     $info['error'][] = 'Corrupt RIFF file: avih.dwMicroSecPerFrame == zero';
                     return false;
                 }
                 $thisfile_riff_raw_avih['dwMaxBytesPerSec'] = $this->EitherEndian2Int(substr($avihData, 4, 4));
                 // max. transfer rate
                 $thisfile_riff_raw_avih['dwPaddingGranularity'] = $this->EitherEndian2Int(substr($avihData, 8, 4));
                 // pad to multiples of this size; normally 2K.
                 $thisfile_riff_raw_avih['dwFlags'] = $this->EitherEndian2Int(substr($avihData, 12, 4));
                 // the ever-present flags
                 $thisfile_riff_raw_avih['dwTotalFrames'] = $this->EitherEndian2Int(substr($avihData, 16, 4));
                 // # frames in file
                 $thisfile_riff_raw_avih['dwInitialFrames'] = $this->EitherEndian2Int(substr($avihData, 20, 4));
                 $thisfile_riff_raw_avih['dwStreams'] = $this->EitherEndian2Int(substr($avihData, 24, 4));
                 $thisfile_riff_raw_avih['dwSuggestedBufferSize'] = $this->EitherEndian2Int(substr($avihData, 28, 4));
                 $thisfile_riff_raw_avih['dwWidth'] = $this->EitherEndian2Int(substr($avihData, 32, 4));
                 $thisfile_riff_raw_avih['dwHeight'] = $this->EitherEndian2Int(substr($avihData, 36, 4));
                 $thisfile_riff_raw_avih['dwScale'] = $this->EitherEndian2Int(substr($avihData, 40, 4));
                 $thisfile_riff_raw_avih['dwRate'] = $this->EitherEndian2Int(substr($avihData, 44, 4));
                 $thisfile_riff_raw_avih['dwStart'] = $this->EitherEndian2Int(substr($avihData, 48, 4));
                 $thisfile_riff_raw_avih['dwLength'] = $this->EitherEndian2Int(substr($avihData, 52, 4));
                 $thisfile_riff_raw_avih['flags']['hasindex'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x10);
                 $thisfile_riff_raw_avih['flags']['mustuseindex'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x20);
                 $thisfile_riff_raw_avih['flags']['interleaved'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x100);
                 $thisfile_riff_raw_avih['flags']['trustcktype'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x800);
                 $thisfile_riff_raw_avih['flags']['capturedfile'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x10000);
                 $thisfile_riff_raw_avih['flags']['copyrighted'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x20010);
                 // shortcut
                 $thisfile_riff_video[$streamindex] = array();
                 $thisfile_riff_video_current =& $thisfile_riff_video[$streamindex];
                 if ($thisfile_riff_raw_avih['dwWidth'] > 0) {
                     $thisfile_riff_video_current['frame_width'] = $thisfile_riff_raw_avih['dwWidth'];
                     $thisfile_video['resolution_x'] = $thisfile_riff_video_current['frame_width'];
                 }
                 if ($thisfile_riff_raw_avih['dwHeight'] > 0) {
                     $thisfile_riff_video_current['frame_height'] = $thisfile_riff_raw_avih['dwHeight'];
                     $thisfile_video['resolution_y'] = $thisfile_riff_video_current['frame_height'];
                 }
                 if ($thisfile_riff_raw_avih['dwTotalFrames'] > 0) {
                     $thisfile_riff_video_current['total_frames'] = $thisfile_riff_raw_avih['dwTotalFrames'];
                     $thisfile_video['total_frames'] = $thisfile_riff_video_current['total_frames'];
                 }
                 $thisfile_riff_video_current['frame_rate'] = round(1000000 / $thisfile_riff_raw_avih['dwMicroSecPerFrame'], 3);
                 $thisfile_video['frame_rate'] = $thisfile_riff_video_current['frame_rate'];
             }
             if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][0]['data'])) {
                 if (is_array($thisfile_riff['AVI ']['hdrl']['strl']['strh'])) {
                     for ($i = 0; $i < count($thisfile_riff['AVI ']['hdrl']['strl']['strh']); ++$i) {
                         if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data'])) {
                             $strhData = $thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data'];
                             $strhfccType = substr($strhData, 0, 4);
                             if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data'])) {
                                 $strfData = $thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data'];
                                 // shortcut
                                 $thisfile_riff_raw_strf_strhfccType_streamindex =& $thisfile_riff_raw['strf'][$strhfccType][$streamindex];
                                 switch ($strhfccType) {
                                     case 'auds':
                                         $thisfile_audio['bitrate_mode'] = 'cbr';
                                         $thisfile_audio_dataformat = 'wav';
                                         if (isset($thisfile_riff_audio) && is_array($thisfile_riff_audio)) {
                                             $streamindex = count($thisfile_riff_audio);
                                         }
                                         $thisfile_riff_audio[$streamindex] = self::RIFFparseWAVEFORMATex($strfData);
                                         $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag'];
                                         // shortcut
                                         $thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex];
                                         $thisfile_audio_streams_currentstream =& $thisfile_audio['streams'][$streamindex];
                                         if ($thisfile_audio_streams_currentstream['bits_per_sample'] == 0) {
                                             unset($thisfile_audio_streams_currentstream['bits_per_sample']);
                                         }
                                         $thisfile_audio_streams_currentstream['wformattag'] = $thisfile_audio_streams_currentstream['raw']['wFormatTag'];
                                         unset($thisfile_audio_streams_currentstream['raw']);
                                         // shortcut
                                         $thisfile_riff_raw['strf'][$strhfccType][$streamindex] = $thisfile_riff_audio[$streamindex]['raw'];
                                         unset($thisfile_riff_audio[$streamindex]['raw']);
                                         $thisfile_audio = Helper::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]);
                                         $thisfile_audio['lossless'] = false;
                                         switch ($thisfile_riff_raw_strf_strhfccType_streamindex['wFormatTag']) {
                                             case 0x1:
                                                 // PCM
                                                 $thisfile_audio_dataformat = 'wav';
                                                 $thisfile_audio['lossless'] = true;
                                                 break;
                                             case 0x50:
                                                 // MPEG Layer 2 or Layer 1
                                                 $thisfile_audio_dataformat = 'mp2';
                                                 // Assume Layer-2
                                                 break;
                                             case 0x55:
                                                 // MPEG Layer 3
                                                 $thisfile_audio_dataformat = 'mp3';
                                                 break;
                                             case 0xff:
                                                 // AAC
                                                 $thisfile_audio_dataformat = 'aac';
                                                 break;
                                             case 0x161:
                                                 // Windows Media v7 / v8 / v9
                                             // Windows Media v7 / v8 / v9
                                             case 0x162:
                                                 // Windows Media Professional v9
                                             // Windows Media Professional v9
                                             case 0x163:
                                                 // Windows Media Lossess v9
                                                 $thisfile_audio_dataformat = 'wma';
                                                 break;
                                             case 0x2000:
                                                 // AC-3
                                                 $thisfile_audio_dataformat = 'ac3';
                                                 break;
                                             case 0x2001:
                                                 // DTS
                                                 $thisfile_audio_dataformat = 'dts';
                                                 break;
                                             default:
                                                 $thisfile_audio_dataformat = 'wav';
                                                 break;
                                         }
                                         $thisfile_audio_streams_currentstream['dataformat'] = $thisfile_audio_dataformat;
                                         $thisfile_audio_streams_currentstream['lossless'] = $thisfile_audio['lossless'];
                                         $thisfile_audio_streams_currentstream['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
                                         break;
                                     case 'iavs':
                                     case 'vids':
                                         // shortcut
                                         $thisfile_riff_raw['strh'][$i] = array();
                                         $thisfile_riff_raw_strh_current =& $thisfile_riff_raw['strh'][$i];
                                         $thisfile_riff_raw_strh_current['fccType'] = substr($strhData, 0, 4);
                                         // same as $strhfccType;
                                         $thisfile_riff_raw_strh_current['fccHandler'] = substr($strhData, 4, 4);
                                         $thisfile_riff_raw_strh_current['dwFlags'] = $this->EitherEndian2Int(substr($strhData, 8, 4));
                                         // Contains AVITF_* flags
                                         $thisfile_riff_raw_strh_current['wPriority'] = $this->EitherEndian2Int(substr($strhData, 12, 2));
                                         $thisfile_riff_raw_strh_current['wLanguage'] = $this->EitherEndian2Int(substr($strhData, 14, 2));
                                         $thisfile_riff_raw_strh_current['dwInitialFrames'] = $this->EitherEndian2Int(substr($strhData, 16, 4));
                                         $thisfile_riff_raw_strh_current['dwScale'] = $this->EitherEndian2Int(substr($strhData, 20, 4));
                                         $thisfile_riff_raw_strh_current['dwRate'] = $this->EitherEndian2Int(substr($strhData, 24, 4));
                                         $thisfile_riff_raw_strh_current['dwStart'] = $this->EitherEndian2Int(substr($strhData, 28, 4));
                                         $thisfile_riff_raw_strh_current['dwLength'] = $this->EitherEndian2Int(substr($strhData, 32, 4));
                                         $thisfile_riff_raw_strh_current['dwSuggestedBufferSize'] = $this->EitherEndian2Int(substr($strhData, 36, 4));
                                         $thisfile_riff_raw_strh_current['dwQuality'] = $this->EitherEndian2Int(substr($strhData, 40, 4));
                                         $thisfile_riff_raw_strh_current['dwSampleSize'] = $this->EitherEndian2Int(substr($strhData, 44, 4));
                                         $thisfile_riff_raw_strh_current['rcFrame'] = $this->EitherEndian2Int(substr($strhData, 48, 4));
                                         $thisfile_riff_video_current['codec'] = self::RIFFfourccLookup($thisfile_riff_raw_strh_current['fccHandler']);
                                         $thisfile_video['fourcc'] = $thisfile_riff_raw_strh_current['fccHandler'];
                                         if (!$thisfile_riff_video_current['codec'] && isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) && self::RIFFfourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
                                             $thisfile_riff_video_current['codec'] = self::RIFFfourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']);
                                             $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'];
                                         }
                                         $thisfile_video['codec'] = $thisfile_riff_video_current['codec'];
                                         $thisfile_video['pixel_aspect_ratio'] = (double) 1;
                                         switch ($thisfile_riff_raw_strh_current['fccHandler']) {
                                             case 'HFYU':
                                                 // Huffman Lossless Codec
                                             // Huffman Lossless Codec
                                             case 'IRAW':
                                                 // Intel YUV Uncompressed
                                             // Intel YUV Uncompressed
                                             case 'YUY2':
                                                 // Uncompressed YUV 4:2:2
                                                 $thisfile_video['lossless'] = true;
                                                 break;
                                             default:
                                                 $thisfile_video['lossless'] = false;
                                                 break;
                                         }
                                         switch ($strhfccType) {
                                             case 'vids':
                                                 $thisfile_riff_raw_strf_strhfccType_streamindex = self::ParseBITMAPINFOHEADER(substr($strfData, 0, 40), $info['fileformat'] == 'riff');
                                                 //echo '<pre>'.print_r($thisfile_riff_raw_strf_strhfccType_streamindex, true).'</pre>';
                                                 $thisfile_video['bits_per_sample'] = $thisfile_riff_raw_strf_strhfccType_streamindex['biBitCount'];
                                                 if ($thisfile_riff_video_current['codec'] == 'DV') {
                                                     $thisfile_riff_video_current['dv_type'] = 2;
                                                 }
                                                 break;
                                             case 'iavs':
                                                 $thisfile_riff_video_current['dv_type'] = 1;
                                                 break;
                                         }
                                         break;
                                     default:
                                         $info['warning'][] = 'Unhandled fccType for stream (' . $i . '): "' . $strhfccType . '"';
                                         break;
                                 }
                             }
                         }
                         if (isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
                             $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'];
                             if (self::RIFFfourccLookup($thisfile_video['fourcc'])) {
                                 $thisfile_riff_video_current['codec'] = self::RIFFfourccLookup($thisfile_video['fourcc']);
                                 $thisfile_video['codec'] = $thisfile_riff_video_current['codec'];
                             }
                             switch ($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) {
                                 case 'HFYU':
                                     // Huffman Lossless Codec
                                 // Huffman Lossless Codec
                                 case 'IRAW':
                                     // Intel YUV Uncompressed
                                 // Intel YUV Uncompressed
                                 case 'YUY2':
                                     // Uncompressed YUV 4:2:2
                                     $thisfile_video['lossless'] = true;
                                     //$thisfile_video['bits_per_sample'] = 24;
                                     break;
                                 default:
                                     $thisfile_video['lossless'] = false;
                                     //$thisfile_video['bits_per_sample'] = 24;
                                     break;
                             }
                         }
                     }
                 }
             }
             break;
         case 'CDDA':
             $thisfile_audio['bitrate_mode'] = 'cbr';
             $thisfile_audio_dataformat = 'cda';
             $thisfile_audio['lossless'] = true;
             unset($info['mime_type']);
             $info['avdataoffset'] = 44;
             if (isset($thisfile_riff['CDDA']['fmt '][0]['data'])) {
                 // shortcut
                 $thisfile_riff_CDDA_fmt_0 =& $thisfile_riff['CDDA']['fmt '][0];
                 $thisfile_riff_CDDA_fmt_0['unknown1'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 0, 2));
                 $thisfile_riff_CDDA_fmt_0['track_num'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 2, 2));
                 $thisfile_riff_CDDA_fmt_0['disc_id'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 4, 4));
                 $thisfile_riff_CDDA_fmt_0['start_offset_frame'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 8, 4));
                 $thisfile_riff_CDDA_fmt_0['playtime_frames'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 12, 4));
                 $thisfile_riff_CDDA_fmt_0['unknown6'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 16, 4));
                 $thisfile_riff_CDDA_fmt_0['unknown7'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 20, 4));
                 $thisfile_riff_CDDA_fmt_0['start_offset_seconds'] = (double) $thisfile_riff_CDDA_fmt_0['start_offset_frame'] / 75;
                 $thisfile_riff_CDDA_fmt_0['playtime_seconds'] = (double) $thisfile_riff_CDDA_fmt_0['playtime_frames'] / 75;
                 $info['comments']['track'] = $thisfile_riff_CDDA_fmt_0['track_num'];
                 $info['playtime_seconds'] = $thisfile_riff_CDDA_fmt_0['playtime_seconds'];
                 // hardcoded data for CD-audio
                 $thisfile_audio['sample_rate'] = 44100;
                 $thisfile_audio['channels'] = 2;
                 $thisfile_audio['bits_per_sample'] = 16;
                 $thisfile_audio['bitrate'] = $thisfile_audio['sample_rate'] * $thisfile_audio['channels'] * $thisfile_audio['bits_per_sample'];
                 $thisfile_audio['bitrate_mode'] = 'cbr';
             }
             break;
         case 'AIFF':
         case 'AIFC':
             $thisfile_audio['bitrate_mode'] = 'cbr';
             $thisfile_audio_dataformat = 'aiff';
             $thisfile_audio['lossless'] = true;
             $info['mime_type'] = 'audio/x-aiff';
             if (isset($thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'])) {
                 $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'] + 8;
                 $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['SSND'][0]['size'];
                 if ($info['avdataend'] > $info['filesize']) {
                     if ($info['avdataend'] == $info['filesize'] + 1 && $info['filesize'] % 2 == 1) {
                         // structures rounded to 2-byte boundary, but dumb encoders
                         // forget to pad end of file to make this actually work
                     } else {
                         $info['warning'][] = 'Probable truncated AIFF file: expecting ' . $thisfile_riff[$RIFFsubtype]['SSND'][0]['size'] . ' bytes of audio data, only ' . ($info['filesize'] - $info['avdataoffset']) . ' bytes found';
                     }
                     $info['avdataend'] = $info['filesize'];
                 }
             }
             if (isset($thisfile_riff[$RIFFsubtype]['COMM'][0]['data'])) {
                 // shortcut
                 $thisfile_riff_RIFFsubtype_COMM_0_data =& $thisfile_riff[$RIFFsubtype]['COMM'][0]['data'];
                 $thisfile_riff_audio['channels'] = Helper::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 0, 2), true);
                 $thisfile_riff_audio['total_samples'] = Helper::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 2, 4), false);
                 $thisfile_riff_audio['bits_per_sample'] = Helper::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 6, 2), true);
                 $thisfile_riff_audio['sample_rate'] = (int) Helper::BigEndian2Float(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 8, 10));
                 if ($thisfile_riff[$RIFFsubtype]['COMM'][0]['size'] > 18) {
                     $thisfile_riff_audio['codec_fourcc'] = substr($thisfile_riff_RIFFsubtype_COMM_0_data, 18, 4);
                     $CodecNameSize = Helper::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 22, 1), false);
                     $thisfile_riff_audio['codec_name'] = substr($thisfile_riff_RIFFsubtype_COMM_0_data, 23, $CodecNameSize);
                     switch ($thisfile_riff_audio['codec_name']) {
                         case 'NONE':
                             $thisfile_audio['codec'] = 'Pulse Code Modulation (PCM)';
                             $thisfile_audio['lossless'] = true;
                             break;
                         case '':
                             switch ($thisfile_riff_audio['codec_fourcc']) {
                                 // http://developer.apple.com/qa/snd/snd07.html
                                 case 'sowt':
                                     $thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Little-Endian PCM';
                                     $thisfile_audio['lossless'] = true;
                                     break;
                                 case 'twos':
                                     $thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Big-Endian PCM';
                                     $thisfile_audio['lossless'] = true;
                                     break;
                                 default:
                                     break;
                             }
                             break;
                         default:
                             $thisfile_audio['codec'] = $thisfile_riff_audio['codec_name'];
                             $thisfile_audio['lossless'] = false;
                             break;
                     }
                 }
                 $thisfile_audio['channels'] = $thisfile_riff_audio['channels'];
                 if ($thisfile_riff_audio['bits_per_sample'] > 0) {
                     $thisfile_audio['bits_per_sample'] = $thisfile_riff_audio['bits_per_sample'];
                 }
                 $thisfile_audio['sample_rate'] = $thisfile_riff_audio['sample_rate'];
                 if ($thisfile_audio['sample_rate'] == 0) {
                     $info['error'][] = 'Corrupted AIFF file: sample_rate == zero';
                     return false;
                 }
                 $info['playtime_seconds'] = $thisfile_riff_audio['total_samples'] / $thisfile_audio['sample_rate'];
             }
             if (isset($thisfile_riff[$RIFFsubtype]['COMT'])) {
                 $offset = 0;
                 $CommentCount = Helper::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false);
                 $offset += 2;
                 for ($i = 0; $i < $CommentCount; ++$i) {
                     $info['comments_raw'][$i]['timestamp'] = Helper::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 4), false);
                     $offset += 4;
                     $info['comments_raw'][$i]['marker_id'] = Helper::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), true);
                     $offset += 2;
                     $CommentLength = Helper::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false);
                     $offset += 2;
                     $info['comments_raw'][$i]['comment'] = substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, $CommentLength);
                     $offset += $CommentLength;
                     $info['comments_raw'][$i]['timestamp_unix'] = Helper::DateMac2Unix($info['comments_raw'][$i]['timestamp']);
                     $thisfile_riff['comments']['comment'][] = $info['comments_raw'][$i]['comment'];
                 }
             }
             $CommentsChunkNames = array('NAME' => 'title', 'author' => 'artist', '(c) ' => 'copyright', 'ANNO' => 'comment');
             foreach ($CommentsChunkNames as $key => $value) {
                 if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) {
                     $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data'];
                 }
             }
             break;
         case '8SVX':
             $thisfile_audio['bitrate_mode'] = 'cbr';
             $thisfile_audio_dataformat = '8svx';
             $thisfile_audio['bits_per_sample'] = 8;
             $thisfile_audio['channels'] = 1;
             // overridden below, if need be
             $info['mime_type'] = 'audio/x-aiff';
             if (isset($thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'])) {
                 $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'] + 8;
                 $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['BODY'][0]['size'];
                 if ($info['avdataend'] > $info['filesize']) {
                     $info['warning'][] = 'Probable truncated AIFF file: expecting ' . $thisfile_riff[$RIFFsubtype]['BODY'][0]['size'] . ' bytes of audio data, only ' . ($info['filesize'] - $info['avdataoffset']) . ' bytes found';
                 }
             }
             if (isset($thisfile_riff[$RIFFsubtype]['VHDR'][0]['offset'])) {
                 // shortcut
                 $thisfile_riff_RIFFsubtype_VHDR_0 =& $thisfile_riff[$RIFFsubtype]['VHDR'][0];
                 $thisfile_riff_RIFFsubtype_VHDR_0['oneShotHiSamples'] = Helper::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 0, 4));
                 $thisfile_riff_RIFFsubtype_VHDR_0['repeatHiSamples'] = Helper::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 4, 4));
                 $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerHiCycle'] = Helper::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 8, 4));
                 $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec'] = Helper::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 12, 2));
                 $thisfile_riff_RIFFsubtype_VHDR_0['ctOctave'] = Helper::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 14, 1));
                 $thisfile_riff_RIFFsubtype_VHDR_0['sCompression'] = Helper::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 15, 1));
                 $thisfile_riff_RIFFsubtype_VHDR_0['Volume'] = Helper::FixedPoint16_16(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 16, 4));
                 $thisfile_audio['sample_rate'] = $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec'];
                 switch ($thisfile_riff_RIFFsubtype_VHDR_0['sCompression']) {
                     case 0:
                         $thisfile_audio['codec'] = 'Pulse Code Modulation (PCM)';
                         $thisfile_audio['lossless'] = true;
                         $ActualBitsPerSample = 8;
                         break;
                     case 1:
                         $thisfile_audio['codec'] = 'Fibonacci-delta encoding';
                         $thisfile_audio['lossless'] = false;
                         $ActualBitsPerSample = 4;
                         break;
                     default:
                         $info['warning'][] = 'Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "' . sCompression . '"';
                         break;
                 }
             }
             if (isset($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'])) {
                 $ChannelsIndex = Helper::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'], 0, 4));
                 switch ($ChannelsIndex) {
                     case 6:
                         // Stereo
                         $thisfile_audio['channels'] = 2;
                         break;
                     case 2:
                         // Left channel only
                     // Left channel only
                     case 4:
                         // Right channel only
                         $thisfile_audio['channels'] = 1;
                         break;
                     default:
                         $info['warning'][] = 'Unexpected value in 8SVX.CHAN chunk - expecting 2 or 4 or 6, found "' . $ChannelsIndex . '"';
                         break;
                 }
             }
             $CommentsChunkNames = array('NAME' => 'title', 'author' => 'artist', '(c) ' => 'copyright', 'ANNO' => 'comment');
             foreach ($CommentsChunkNames as $key => $value) {
                 if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) {
                     $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data'];
                 }
             }
             $thisfile_audio['bitrate'] = $thisfile_audio['sample_rate'] * $ActualBitsPerSample * $thisfile_audio['channels'];
             if (!empty($thisfile_audio['bitrate'])) {
                 $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) / ($thisfile_audio['bitrate'] / 8);
             }
             break;
         case 'CDXA':
             $info['mime_type'] = 'video/mpeg';
             if (!empty($thisfile_riff['CDXA']['data'][0]['size'])) {
                 if (class_exists('GetId3\\Module\\AudioVideo\\Mpeg')) {
                     $getid3_temp = new GetId3Core();
                     $getid3_temp->openfile($this->getid3->filename);
                     $getid3_mpeg = new Mpeg($getid3_temp);
                     $getid3_mpeg->analyze();
                     if (empty($getid3_temp->info['error'])) {
                         $info['audio'] = $getid3_temp->info['audio'];
                         $info['video'] = $getid3_temp->info['video'];
                         $info['mpeg'] = $getid3_temp->info['mpeg'];
                         $info['warning'] = $getid3_temp->info['warning'];
                     }
                     unset($getid3_temp, $getid3_mpeg);
                 }
             }
             break;
         default:
             $info['error'][] = 'Unknown RIFF type: expecting one of (WAVE|RMP3|AVI |CDDA|AIFF|AIFC|8SVX|CDXA), found "' . $RIFFsubtype . '" instead';
             unset($info['fileformat']);
             break;
     }
     switch ($RIFFsubtype) {
         case 'WAVE':
         case 'AIFF':
         case 'AIFC':
             if (isset($thisfile_riff[$RIFFsubtype]['ID3 ']) && !array_key_exists('id3 ', $thisfile_riff[$RIFFsubtype])) {
                 $info['warning'][] = 'mapping "ID3 " chunk to "id3 "';
             }
             if (isset($thisfile_riff[$RIFFsubtype]['tag ']) && !array_key_exists('id3 ', $thisfile_riff[$RIFFsubtype])) {
                 $info['warning'][] = 'mapping "tag " chunk to "id3 "';
             }
             if (isset($thisfile_riff[$RIFFsubtype]['id3 '])) {
                 $getid3_temp = new GetId3Core();
                 $getid3_temp->openfile($this->getid3->filename);
                 $getid3_id3v2 = new Id3v2($getid3_temp);
                 $getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['id3 '][0]['offset'] + 8;
                 if ($thisfile_riff[$RIFFsubtype]['id3 '][0]['valid'] = $getid3_id3v2->analyze()) {
                     $info['id3v2'] = $getid3_temp->info['id3v2'];
                 }
                 unset($getid3_temp, $getid3_id3v2);
             }
             break;
     }
     if (isset($thisfile_riff_raw['fmt ']['wFormatTag']) && $thisfile_riff_raw['fmt ']['wFormatTag'] == 1) {
         // http://www.mega-nerd.com/erikd/Blog/Windiots/dts.html
         fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
         $FirstFourBytes = fread($this->getid3->fp, 4);
         if (preg_match('/^\\xFF\\x1F\\x00\\xE8/s', $FirstFourBytes)) {
             // DTSWAV
             $thisfile_audio_dataformat = 'dts';
         } elseif (preg_match('/^\\x7F\\xFF\\x80\\x01/s', $FirstFourBytes)) {
             // DTS, but this probably shouldn't happen
             $thisfile_audio_dataformat = 'dts';
         }
     }
     if (isset($thisfile_riff_WAVE['DISP']) && is_array($thisfile_riff_WAVE['DISP'])) {
         $thisfile_riff['comments']['title'][] = trim(substr($thisfile_riff_WAVE['DISP'][count($thisfile_riff_WAVE['DISP']) - 1]['data'], 4));
     }
     if (isset($thisfile_riff_WAVE['INFO']) && is_array($thisfile_riff_WAVE['INFO'])) {
         $this->RIFFcommentsParse($thisfile_riff_WAVE['INFO'], $thisfile_riff['comments']);
     }
     if (isset($thisfile_riff['AVI ']['INFO']) && is_array($thisfile_riff['AVI ']['INFO'])) {
         $this->RIFFcommentsParse($thisfile_riff['AVI ']['INFO'], $thisfile_riff['comments']);
     }
     if (empty($thisfile_audio['encoder']) && !empty($info['mpeg']['audio']['LAME']['short_version'])) {
         $thisfile_audio['encoder'] = $info['mpeg']['audio']['LAME']['short_version'];
     }
     if (!isset($info['playtime_seconds'])) {
         $info['playtime_seconds'] = 0;
     }
     if (isset($thisfile_riff_raw['strh'][0]['dwLength']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) {
         // needed for >2GB AVIs where 'avih' chunk only lists number of frames in that chunk, not entire movie
         $info['playtime_seconds'] = $thisfile_riff_raw['strh'][0]['dwLength'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000);
     } elseif (isset($thisfile_riff_raw['avih']['dwTotalFrames']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) {
         $info['playtime_seconds'] = $thisfile_riff_raw['avih']['dwTotalFrames'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000);
     }
     if ($info['playtime_seconds'] > 0) {
         if (isset($thisfile_riff_audio) && isset($thisfile_riff_video)) {
             if (!isset($info['bitrate'])) {
                 $info['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds'] * 8;
             }
         } elseif (isset($thisfile_riff_audio) && !isset($thisfile_riff_video)) {
             if (!isset($thisfile_audio['bitrate'])) {
                 $thisfile_audio['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds'] * 8;
             }
         } elseif (!isset($thisfile_riff_audio) && isset($thisfile_riff_video)) {
             if (!isset($thisfile_video['bitrate'])) {
                 $thisfile_video['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds'] * 8;
             }
         }
     }
     if (isset($thisfile_riff_video) && isset($thisfile_audio['bitrate']) && $thisfile_audio['bitrate'] > 0 && $info['playtime_seconds'] > 0) {
         $info['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds'] * 8;
         $thisfile_audio['bitrate'] = 0;
         $thisfile_video['bitrate'] = $info['bitrate'];
         foreach ($thisfile_riff_audio as $channelnumber => $audioinfoarray) {
             $thisfile_video['bitrate'] -= $audioinfoarray['bitrate'];
             $thisfile_audio['bitrate'] += $audioinfoarray['bitrate'];
         }
         if ($thisfile_video['bitrate'] <= 0) {
             unset($thisfile_video['bitrate']);
         }
         if ($thisfile_audio['bitrate'] <= 0) {
             unset($thisfile_audio['bitrate']);
         }
     }
     if (isset($info['mpeg']['audio'])) {
         $thisfile_audio_dataformat = 'mp' . $info['mpeg']['audio']['layer'];
         $thisfile_audio['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
         $thisfile_audio['channels'] = $info['mpeg']['audio']['channels'];
         $thisfile_audio['bitrate'] = $info['mpeg']['audio']['bitrate'];
         $thisfile_audio['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
         if (!empty($info['mpeg']['audio']['codec'])) {
             $thisfile_audio['codec'] = $info['mpeg']['audio']['codec'] . ' ' . $thisfile_audio['codec'];
         }
         if (!empty($thisfile_audio['streams'])) {
             foreach ($thisfile_audio['streams'] as $streamnumber => $streamdata) {
                 if ($streamdata['dataformat'] == $thisfile_audio_dataformat) {
                     $thisfile_audio['streams'][$streamnumber]['sample_rate'] = $thisfile_audio['sample_rate'];
                     $thisfile_audio['streams'][$streamnumber]['channels'] = $thisfile_audio['channels'];
                     $thisfile_audio['streams'][$streamnumber]['bitrate'] = $thisfile_audio['bitrate'];
                     $thisfile_audio['streams'][$streamnumber]['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
                     $thisfile_audio['streams'][$streamnumber]['codec'] = $thisfile_audio['codec'];
                 }
             }
         }
         $getid3_mp3 = new Mp3($this->getid3);
         $thisfile_audio['encoder_options'] = $getid3_mp3->GuessEncoderOptions();
         unset($getid3_mp3);
     }
     if (!empty($thisfile_riff_raw['fmt ']['wBitsPerSample']) && $thisfile_riff_raw['fmt ']['wBitsPerSample'] > 0) {
         switch ($thisfile_audio_dataformat) {
             case 'ac3':
                 // ignore bits_per_sample
                 break;
             default:
                 $thisfile_audio['bits_per_sample'] = $thisfile_riff_raw['fmt ']['wBitsPerSample'];
                 break;
         }
     }
     if (empty($thisfile_riff_raw)) {
         unset($thisfile_riff['raw']);
     }
     if (empty($thisfile_riff_audio)) {
         unset($thisfile_riff['audio']);
     }
     if (empty($thisfile_riff_video)) {
         unset($thisfile_riff['video']);
     }
     return true;
 }