function getid3_riff(&$fd, &$ThisFileInfo) { // initialize these values to an empty array, otherwise they default to NULL // and you can't append array values to a NULL value $ThisFileInfo['riff'] = array('raw' => array()); // Shortcuts $thisfile_riff =& $ThisFileInfo['riff']; $thisfile_riff_raw =& $thisfile_riff['raw']; $thisfile_audio =& $ThisFileInfo['audio']; $thisfile_video =& $ThisFileInfo['video']; $thisfile_avdataoffset =& $ThisFileInfo['avdataoffset']; $thisfile_avdataend =& $ThisFileInfo['avdataend']; $thisfile_audio_dataformat =& $thisfile_audio['dataformat']; $thisfile_riff_audio =& $thisfile_riff['audio']; $thisfile_riff_video =& $thisfile_riff['video']; $Original['avdataoffset'] = $thisfile_avdataoffset; $Original['avdataend'] = $thisfile_avdataend; fseek($fd, $thisfile_avdataoffset, SEEK_SET); $RIFFheader = fread($fd, 12); $RIFFsubtype = substr($RIFFheader, 8, 4); switch (substr($RIFFheader, 0, 4)) { case 'FORM': $ThisFileInfo['fileformat'] = 'aiff'; $RIFFheaderSize = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($RIFFheader, 4, 4)); $thisfile_riff[$RIFFsubtype] = getid3_riff::ParseRIFF($fd, $thisfile_avdataoffset + 12, $thisfile_avdataoffset + $RIFFheaderSize, $ThisFileInfo); $thisfile_riff['header_size'] = $RIFFheaderSize; break; case 'RIFF': 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 if ($RIFFsubtype == 'RMP3') { // RMP3 is identical to WAVE, just renamed. Used by [unknown program] when creating RIFF-MP3s $RIFFsubtype = 'WAVE'; } $ThisFileInfo['fileformat'] = 'riff'; $RIFFheaderSize = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($RIFFheader, 4, 4)); $thisfile_riff[$RIFFsubtype] = getid3_riff::ParseRIFF($fd, $thisfile_avdataoffset + 12, $thisfile_avdataoffset + $RIFFheaderSize, $ThisFileInfo); $thisfile_riff['header_size'] = $RIFFheaderSize; if ($RIFFsubtype == 'WAVE') { $thisfile_riff_WAVE =& $thisfile_riff['WAVE']; } break; default: $ThisFileInfo['error'][] = 'Cannot parse RIFF (this is maybe not a RIFF / WAV / AVI file?) - expecting "FORM|RIFF|SDSS|RMP3" found "' . $RIFFsubtype . '" instead'; unset($ThisFileInfo['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'])) { $thisfile_avdataoffset = $thisfile_riff_WAVE['data'][0]['offset'] + 8; $thisfile_avdataend = $thisfile_avdataoffset + $thisfile_riff_WAVE['data'][0]['size']; } if (isset($thisfile_riff_WAVE['fmt '][0]['data'])) { $thisfile_riff_audio[$streamindex] = getid3_riff::RIFFparseWAVEFORMATex($thisfile_riff_WAVE['fmt '][0]['data']); $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag']; if (@$thisfile_riff_audio[$streamindex]['bitrate'] == 0) { $ThisFileInfo['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 = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]); if (substr($thisfile_audio['codec'], 0, strlen('unknown: 0x')) == 'unknown: 0x') { $ThisFileInfo['warning'][] = 'Audio codec = ' . $thisfile_audio['codec']; } $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate']; $ThisFileInfo['playtime_seconds'] = (double) (($thisfile_avdataend - $thisfile_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'] = getid3_lib::LittleEndian2Float(substr($rgadData, 0, 4)); $thisfile_riff_raw_rgad['nRadioRgAdjust'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($rgadData, 4, 2)); $thisfile_riff_raw_rgad['nAudiophileRgAdjust'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($rgadData, 6, 2)); $nRadioRgAdjustBitstring = str_pad(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nRadioRgAdjust']), 16, '0', STR_PAD_LEFT); $nAudiophileRgAdjustBitstring = str_pad(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nAudiophileRgAdjust']), 16, '0', STR_PAD_LEFT); $thisfile_riff_raw_rgad_track['name'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 0, 3)); $thisfile_riff_raw_rgad_track['originator'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 3, 3)); $thisfile_riff_raw_rgad_track['signbit'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 6, 1)); $thisfile_riff_raw_rgad_track['adjustment'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 7, 9)); $thisfile_riff_raw_rgad_album['name'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 0, 3)); $thisfile_riff_raw_rgad_album['originator'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 3, 3)); $thisfile_riff_raw_rgad_album['signbit'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 6, 1)); $thisfile_riff_raw_rgad_album['adjustment'] = getid3_lib::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'] = getid3_lib::RGADnameLookup($thisfile_riff_raw_rgad_track['name']); $thisfile_riff['rgad']['track']['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_riff_raw_rgad_track['originator']); $thisfile_riff['rgad']['track']['adjustment'] = getid3_lib::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'] = getid3_lib::RGADnameLookup($thisfile_riff_raw_rgad_album['name']); $thisfile_riff['rgad']['album']['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_riff_raw_rgad_album['originator']); $thisfile_riff['rgad']['album']['adjustment'] = getid3_lib::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'] = getid3_riff::EitherEndian2Int($ThisFileInfo, 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'])) { // $ThisFileInfo['playtime_seconds'] = (float) $thisfile_riff_raw['fact']['NumberOfSamples'] / $thisfile_riff_raw['fmt ']['nSamplesPerSec']; // } } if (!empty($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'])) { $thisfile_audio['bitrate'] = getid3_lib::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'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 338, 8)); $thisfile_riff_WAVE_bext_0['bwf_version'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 346, 1)); $thisfile_riff_WAVE_bext_0['reserved'] = getid3_lib::LittleEndian2Int(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))); $thisfile_riff_WAVE_bext_0['origin_date_unix'] = gmmktime(substr($thisfile_riff_WAVE_bext_0['origin_time'], 0, 2), substr($thisfile_riff_WAVE_bext_0['origin_time'], 3, 2), substr($thisfile_riff_WAVE_bext_0['origin_time'], 6, 2), substr($thisfile_riff_WAVE_bext_0['origin_date'], 5, 2), substr($thisfile_riff_WAVE_bext_0['origin_date'], 8, 2), substr($thisfile_riff_WAVE_bext_0['origin_date'], 0, 4)); $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'] = getid3_lib::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'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 2, 2)); } $thisfile_riff_WAVE_MEXT_0['anciliary_data_length'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 6, 2)); $thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] = getid3_lib::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'] = getid3_lib::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'] = getid3_lib::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_audio['bitrate']) && isset($thisfile_riff_audio[$streamindex]['bitrate'])) { $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate']; $ThisFileInfo['playtime_seconds'] = (double) (($thisfile_avdataend - $thisfile_avdataoffset) * 8 / $thisfile_audio['bitrate']); } if (!empty($ThisFileInfo['wavpack'])) { $thisfile_audio_dataformat = 'wavpack'; $thisfile_audio['bitrate_mode'] = 'vbr'; $thisfile_audio['encoder'] = 'WavPack v' . $ThisFileInfo['wavpack']['version']; // Reset to the way it was - RIFF parsing will have messed this up $thisfile_avdataend = $Original['avdataend']; $thisfile_audio['bitrate'] = ($thisfile_avdataend - $thisfile_avdataoffset) * 8 / $ThisFileInfo['playtime_seconds']; fseek($fd, $thisfile_avdataoffset - 44, SEEK_SET); $RIFFdata = fread($fd, 44); $OrignalRIFFheaderSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 4, 4)) + 8; $OrignalRIFFdataSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44; if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) { $thisfile_avdataend -= $OrignalRIFFheaderSize - $OrignalRIFFdataSize; fseek($fd, $thisfile_avdataend, SEEK_SET); $RIFFdata .= fread($fd, $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::ParseRIFFdata($RIFFdata, $ThisFileInfo); } if (isset($thisfile_riff_raw['fmt ']['wFormatTag'])) { switch ($thisfile_riff_raw['fmt ']['wFormatTag']) { 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'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 18, 1)); $thisfile_riff_litewave_raw['compression_flags'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 19, 1)); $thisfile_riff_litewave_raw['m_dwScale'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 20, 4)); $thisfile_riff_litewave_raw['m_dwBlockSize'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 24, 4)); $thisfile_riff_litewave_raw['m_wQuality'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 28, 2)); $thisfile_riff_litewave_raw['m_wMarkDistance'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 30, 2)); $thisfile_riff_litewave_raw['m_wReserved'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 32, 2)); $thisfile_riff_litewave_raw['m_dwOrgSize'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 34, 4)); $thisfile_riff_litewave_raw['m_bFactExists'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 38, 2)); $thisfile_riff_litewave_raw['m_dwRiffChunkSize'] = getid3_lib::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 ($thisfile_avdataend > $ThisFileInfo['filesize']) { switch (@$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 ($thisfile_avdataend - $ThisFileInfo['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 $ThisFileInfo['warning'][] = 'Probably truncated file - expecting ' . $thisfile_riff[$RIFFsubtype]['data'][0]['size'] . ' bytes of data, only found ' . ($ThisFileInfo['filesize'] - $thisfile_avdataoffset) . ' (short by ' . ($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($ThisFileInfo['filesize'] - $thisfile_avdataoffset)) . ' bytes)'; $thisfile_avdataend = $ThisFileInfo['filesize']; } break; default: if ($thisfile_avdataend - $ThisFileInfo['filesize'] == 1 && $thisfile_riff[$RIFFsubtype]['data'][0]['size'] % 2 == 0 && ($ThisFileInfo['filesize'] - $thisfile_avdataoffset) % 2 == 1) { // output file appears to be incorrectly *not* padded to nearest WORD boundary // Output less severe warning $ThisFileInfo['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 ' . ($ThisFileInfo['filesize'] - $thisfile_avdataoffset) . ' therefore short by ' . ($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($ThisFileInfo['filesize'] - $thisfile_avdataoffset)) . ' bytes)'; $thisfile_avdataend = $ThisFileInfo['filesize']; break; } // Short by more than one byte, throw warning $ThisFileInfo['warning'][] = 'Probably truncated file - expecting ' . $thisfile_riff[$RIFFsubtype]['data'][0]['size'] . ' bytes of data, only found ' . ($ThisFileInfo['filesize'] - $thisfile_avdataoffset) . ' (short by ' . ($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($ThisFileInfo['filesize'] - $thisfile_avdataoffset)) . ' bytes)'; $thisfile_avdataend = $ThisFileInfo['filesize']; break; } } if (!empty($ThisFileInfo['mpeg']['audio']['LAME']['audio_bytes'])) { if ($thisfile_avdataend - $thisfile_avdataoffset - $ThisFileInfo['mpeg']['audio']['LAME']['audio_bytes'] == 1) { $thisfile_avdataend--; $ThisFileInfo['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored'; } } if (@$thisfile_audio_dataformat == 'ac3') { unset($thisfile_audio['bits_per_sample']); if (!empty($ThisFileInfo['ac3']['bitrate']) && $ThisFileInfo['ac3']['bitrate'] != $thisfile_audio['bitrate']) { $thisfile_audio['bitrate'] = $ThisFileInfo['ac3']['bitrate']; } } break; case 'AVI ': $thisfile_video['bitrate_mode'] = 'vbr'; // maybe not, but probably $thisfile_video['dataformat'] = 'avi'; $ThisFileInfo['mime_type'] = 'video/avi'; if (isset($thisfile_riff[$RIFFsubtype]['movi']['offset'])) { $thisfile_avdataoffset = $thisfile_riff[$RIFFsubtype]['movi']['offset'] + 8; $thisfile_avdataend = $thisfile_avdataoffset + $thisfile_riff[$RIFFsubtype]['movi']['size']; if ($thisfile_avdataend > $ThisFileInfo['filesize']) { $ThisFileInfo['warning'][] = 'Probably truncated file - expecting ' . $thisfile_riff[$RIFFsubtype]['movi']['size'] . ' bytes of data, only found ' . ($ThisFileInfo['filesize'] - $thisfile_avdataoffset) . ' (short by ' . ($thisfile_riff[$RIFFsubtype]['movi']['size'] - ($ThisFileInfo['filesize'] - $thisfile_avdataoffset)) . ' bytes)'; $thisfile_avdataend = $ThisFileInfo['filesize']; } } 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'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 0, 4)); // frame display rate (or 0L) if ($thisfile_riff_raw_avih['dwMicroSecPerFrame'] == 0) { $ThisFileInfo['error'][] = 'Corrupt RIFF file: avih.dwMicroSecPerFrame == zero'; return false; } $thisfile_riff_raw_avih['dwMaxBytesPerSec'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 4, 4)); // max. transfer rate $thisfile_riff_raw_avih['dwPaddingGranularity'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 8, 4)); // pad to multiples of this size; normally 2K. $thisfile_riff_raw_avih['dwFlags'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 12, 4)); // the ever-present flags $thisfile_riff_raw_avih['dwTotalFrames'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 16, 4)); // # frames in file $thisfile_riff_raw_avih['dwInitialFrames'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 20, 4)); $thisfile_riff_raw_avih['dwStreams'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 24, 4)); $thisfile_riff_raw_avih['dwSuggestedBufferSize'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 28, 4)); $thisfile_riff_raw_avih['dwWidth'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 32, 4)); $thisfile_riff_raw_avih['dwHeight'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 36, 4)); $thisfile_riff_raw_avih['dwScale'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 40, 4)); $thisfile_riff_raw_avih['dwRate'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 44, 4)); $thisfile_riff_raw_avih['dwStart'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 48, 4)); $thisfile_riff_raw_avih['dwLength'] = getid3_riff::EitherEndian2Int($ThisFileInfo, 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] = getid3_riff::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 = getid3_lib::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'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 8, 4)); // Contains AVITF_* flags $thisfile_riff_raw_strh_current['wPriority'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 12, 2)); $thisfile_riff_raw_strh_current['wLanguage'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 14, 2)); $thisfile_riff_raw_strh_current['dwInitialFrames'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 16, 4)); $thisfile_riff_raw_strh_current['dwScale'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 20, 4)); $thisfile_riff_raw_strh_current['dwRate'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 24, 4)); $thisfile_riff_raw_strh_current['dwStart'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 28, 4)); $thisfile_riff_raw_strh_current['dwLength'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 32, 4)); $thisfile_riff_raw_strh_current['dwSuggestedBufferSize'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 36, 4)); $thisfile_riff_raw_strh_current['dwQuality'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 40, 4)); $thisfile_riff_raw_strh_current['dwSampleSize'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 44, 4)); $thisfile_riff_raw_strh_current['rcFrame'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 48, 4)); $thisfile_riff_video_current['codec'] = getid3_riff::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']) && getid3_riff::RIFFfourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) { $thisfile_riff_video_current['codec'] = getid3_riff::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['biSize'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 0, 4)); // number of bytes required by the BITMAPINFOHEADER structure $thisfile_riff_raw_strf_strhfccType_streamindex['biWidth'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 4, 4)); // width of the bitmap in pixels $thisfile_riff_raw_strf_strhfccType_streamindex['biHeight'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 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 $thisfile_riff_raw_strf_strhfccType_streamindex['biPlanes'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 12, 2)); // number of color planes on the target device. In most cases this value must be set to 1 $thisfile_riff_raw_strf_strhfccType_streamindex['biBitCount'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 14, 2)); // Specifies the number of bits per pixels $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'] = substr($strfData, 16, 4); // $thisfile_riff_raw_strf_strhfccType_streamindex['biSizeImage'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 20, 4)); // size of the bitmap data section of the image (the actual pixel data, excluding BITMAPINFOHEADER and RGBQUAD structures) $thisfile_riff_raw_strf_strhfccType_streamindex['biXPelsPerMeter'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 24, 4)); // horizontal resolution, in pixels per metre, of the target device $thisfile_riff_raw_strf_strhfccType_streamindex['biYPelsPerMeter'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 28, 4)); // vertical resolution, in pixels per metre, of the target device $thisfile_riff_raw_strf_strhfccType_streamindex['biClrUsed'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 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 $thisfile_riff_raw_strf_strhfccType_streamindex['biClrImportant'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 36, 4)); // number of color indices that are considered important for displaying the bitmap. If this value is zero, all colors are important $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: $ThisFileInfo['warning'][] = 'Unhandled fccType for stream (' . $i . '): "' . $strhfccType . '"'; break; } } } if (isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) && getid3_riff::RIFFfourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) { $thisfile_riff_video_current['codec'] = getid3_riff::RIFFfourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']); $thisfile_video['codec'] = $thisfile_riff_video_current['codec']; $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']; 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($ThisFileInfo['mime_type']); $thisfile_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'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 0, 2)); $thisfile_riff_CDDA_fmt_0['track_num'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 2, 2)); $thisfile_riff_CDDA_fmt_0['disc_id'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 4, 4)); $thisfile_riff_CDDA_fmt_0['start_offset_frame'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 8, 4)); $thisfile_riff_CDDA_fmt_0['playtime_frames'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 12, 4)); $thisfile_riff_CDDA_fmt_0['unknown6'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 16, 4)); $thisfile_riff_CDDA_fmt_0['unknown7'] = getid3_riff::EitherEndian2Int($ThisFileInfo, 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; $ThisFileInfo['comments']['track'] = $thisfile_riff_CDDA_fmt_0['track_num']; $ThisFileInfo['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; $ThisFileInfo['mime_type'] = 'audio/x-aiff'; if (isset($thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'])) { $thisfile_avdataoffset = $thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'] + 8; $thisfile_avdataend = $thisfile_avdataoffset + $thisfile_riff[$RIFFsubtype]['SSND'][0]['size']; if ($thisfile_avdataend > $ThisFileInfo['filesize']) { if ($thisfile_avdataend == $ThisFileInfo['filesize'] + 1 && $ThisFileInfo['filesize'] % 2 == 1) { // structures rounded to 2-byte boundary, but dumb encoders // forget to pad end of file to make this actually work } else { $ThisFileInfo['warning'][] = 'Probable truncated AIFF file: expecting ' . $thisfile_riff[$RIFFsubtype]['SSND'][0]['size'] . ' bytes of audio data, only ' . ($ThisFileInfo['filesize'] - $thisfile_avdataoffset) . ' bytes found'; } $thisfile_avdataend = $ThisFileInfo['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'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 0, 2), true); $thisfile_riff_audio['total_samples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 2, 4), false); $thisfile_riff_audio['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 6, 2), true); $thisfile_riff_audio['sample_rate'] = (int) getid3_lib::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 = getid3_lib::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) { $ThisFileInfo['error'][] = 'Corrupted AIFF file: sample_rate == zero'; return false; } $ThisFileInfo['playtime_seconds'] = $thisfile_riff_audio['total_samples'] / $thisfile_audio['sample_rate']; } if (isset($thisfile_riff[$RIFFsubtype]['COMT'])) { $offset = 0; $CommentCount = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false); $offset += 2; for ($i = 0; $i < $CommentCount; $i++) { $ThisFileInfo['comments_raw'][$i]['timestamp'] = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 4), false); $offset += 4; $ThisFileInfo['comments_raw'][$i]['marker_id'] = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), true); $offset += 2; $CommentLength = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false); $offset += 2; $ThisFileInfo['comments_raw'][$i]['comment'] = substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, $CommentLength); $offset += $CommentLength; $ThisFileInfo['comments_raw'][$i]['timestamp_unix'] = getid3_lib::DateMac2Unix($ThisFileInfo['comments_raw'][$i]['timestamp']); $thisfile_riff['comments']['comment'][] = $ThisFileInfo['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 $ThisFileInfo['mime_type'] = 'audio/x-aiff'; if (isset($thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'])) { $thisfile_avdataoffset = $thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'] + 8; $thisfile_avdataend = $thisfile_avdataoffset + $thisfile_riff[$RIFFsubtype]['BODY'][0]['size']; if ($thisfile_avdataend > $ThisFileInfo['filesize']) { $ThisFileInfo['warning'][] = 'Probable truncated AIFF file: expecting ' . $thisfile_riff[$RIFFsubtype]['BODY'][0]['size'] . ' bytes of audio data, only ' . ($ThisFileInfo['filesize'] - $thisfile_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'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 0, 4)); $thisfile_riff_RIFFsubtype_VHDR_0['repeatHiSamples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 4, 4)); $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerHiCycle'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 8, 4)); $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 12, 2)); $thisfile_riff_RIFFsubtype_VHDR_0['ctOctave'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 14, 1)); $thisfile_riff_RIFFsubtype_VHDR_0['sCompression'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 15, 1)); $thisfile_riff_RIFFsubtype_VHDR_0['Volume'] = getid3_lib::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: $ThisFileInfo['warning'][] = 'Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "' . sCompression . '"'; break; } } if (isset($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'])) { $ChannelsIndex = getid3_lib::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: $ThisFileInfo['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'])) { $ThisFileInfo['playtime_seconds'] = ($thisfile_avdataend - $thisfile_avdataoffset) / ($thisfile_audio['bitrate'] / 8); } break; case 'CDXA': $ThisFileInfo['mime_type'] = 'video/mpeg'; if (!empty($thisfile_riff['CDXA']['data'][0]['size'])) { $GETID3_ERRORARRAY =& $ThisFileInfo['warning']; if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio-video.mpeg.php', __FILE__, false)) { $dummy = $ThisFileInfo; $dummy['error'] = array(); $mpeg_scanner = new getid3_mpeg($fd, $dummy); if (empty($dummy['error'])) { $ThisFileInfo['audio'] = $dummy['audio']; $ThisFileInfo['video'] = $dummy['video']; $ThisFileInfo['mpeg'] = $dummy['mpeg']; $ThisFileInfo['warning'] = $dummy['warning']; } } } break; default: $ThisFileInfo['error'][] = 'Unknown RIFF type: expecting one of (WAVE|RMP3|AVI |CDDA|AIFF|AIFC|8SVX|CDXA), found "' . $RIFFsubtype . '" instead'; unset($ThisFileInfo['fileformat']); break; } 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 (empty($thisfile_audio['encoder']) && !empty($ThisFileInfo['mpeg']['audio']['LAME']['short_version'])) { $thisfile_audio['encoder'] = $ThisFileInfo['mpeg']['audio']['LAME']['short_version']; } if (!isset($ThisFileInfo['playtime_seconds'])) { $ThisFileInfo['playtime_seconds'] = 0; } if (isset($thisfile_riff_raw['avih']['dwTotalFrames']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) { $ThisFileInfo['playtime_seconds'] = $thisfile_riff_raw['avih']['dwTotalFrames'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000); } if ($ThisFileInfo['playtime_seconds'] > 0) { if (isset($thisfile_riff_audio) && isset($thisfile_riff_video)) { if (!isset($ThisFileInfo['bitrate'])) { $ThisFileInfo['bitrate'] = ($thisfile_avdataend - $thisfile_avdataoffset) / $ThisFileInfo['playtime_seconds'] * 8; } } elseif (isset($thisfile_riff_audio) && !isset($thisfile_riff_video)) { if (!isset($thisfile_audio['bitrate'])) { $thisfile_audio['bitrate'] = ($thisfile_avdataend - $thisfile_avdataoffset) / $ThisFileInfo['playtime_seconds'] * 8; } } elseif (!isset($thisfile_riff_audio) && isset($thisfile_riff_video)) { if (!isset($thisfile_video['bitrate'])) { $thisfile_video['bitrate'] = ($thisfile_avdataend - $thisfile_avdataoffset) / $ThisFileInfo['playtime_seconds'] * 8; } } } if (isset($thisfile_riff_video) && isset($thisfile_audio['bitrate']) && $thisfile_audio['bitrate'] > 0 && $ThisFileInfo['playtime_seconds'] > 0) { $ThisFileInfo['bitrate'] = ($thisfile_avdataend - $thisfile_avdataoffset) / $ThisFileInfo['playtime_seconds'] * 8; $thisfile_audio['bitrate'] = 0; $thisfile_video['bitrate'] = $ThisFileInfo['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($ThisFileInfo['mpeg']['audio'])) { $thisfile_audio_dataformat = 'mp' . $ThisFileInfo['mpeg']['audio']['layer']; $thisfile_audio['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate']; $thisfile_audio['channels'] = $ThisFileInfo['mpeg']['audio']['channels']; $thisfile_audio['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate']; $thisfile_audio['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']); if (!empty($ThisFileInfo['mpeg']['audio']['codec'])) { $thisfile_audio['codec'] = $ThisFileInfo['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']; } } } $thisfile_audio['encoder_options'] = getid3_mp3::GuessEncoderOptions($ThisFileInfo); } 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; }
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']; $this->fseek($info['avdataoffset']); $RIFFheader = $this->fread(12); $offset = $this->ftell(); $RIFFtype = substr($RIFFheader, 0, 4); $RIFFsize = substr($RIFFheader, 4, 4); $RIFFsubtype = substr($RIFFheader, 8, 4); switch ($RIFFtype) { case 'FORM': // AIFF, AIFC //$info['fileformat'] = 'aiff'; $this->container = 'aiff'; $thisfile_riff['header_size'] = $this->EitherEndian2Int($RIFFsize); $thisfile_riff[$RIFFsubtype] = $this->ParseRIFF($offset, $offset + $thisfile_riff['header_size'] - 4); 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'; $this->container = 'riff'; $thisfile_riff['header_size'] = $this->EitherEndian2Int($RIFFsize); if ($RIFFsubtype == 'RMP3') { // RMP3 is identical to WAVE, just renamed. Used by [unknown program] when creating RIFF-MP3s $RIFFsubtype = 'WAVE'; } if ($RIFFsubtype != 'AMV ') { // AMV files are RIFF-AVI files with parts of the spec deliberately broken, such as chunk size fields hardcoded to zero (because players known in hardware that these fields are always a certain size // Handled separately in ParseRIFFAMV() $thisfile_riff[$RIFFsubtype] = $this->ParseRIFF($offset, $offset + $thisfile_riff['header_size'] - 4); } 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'])) { try { $this->fseek($nextRIFFoffset); } catch (getid3_exception $e) { if ($e->getCode() == 10) { //$this->warning('RIFF parser: '.$e->getMessage()); $this->error('AVI extends beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB and PHP filesystem functions cannot read that far, playtime may be wrong'); $this->warning('[avdataend] value may be incorrect, multiple AVIX chunks may be present'); break; } else { throw $e; } } $nextRIFFheader = $this->fread(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': $chunkdata['chunks'] = $this->ParseRIFF($chunkdata['offset'] + 4, $nextRIFFoffset); if (!isset($thisfile_riff[$nextRIFFtype])) { $thisfile_riff[$nextRIFFtype] = array(); } $thisfile_riff[$nextRIFFtype][] = $chunkdata; break; case 'AMV ': unset($info['riff']); $info['amv'] = $this->ParseRIFFAMV($chunkdata['offset'] + 4, $nextRIFFoffset); break; case 'JUNK': // ignore $thisfile_riff[$nextRIFFheaderID][] = $chunkdata; break; case 'IDVX': $info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunkdata['size'])); break; default: if ($info['filesize'] == $chunkdata['offset'] - 8 + 128) { $DIVXTAG = $nextRIFFheader . $this->fread(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 $this->warning('Found wrongly-structured DIVXTAG at offset ' . ($this->ftell() - 128) . ', parsing anyway'); $info['divxtag']['comments'] = self::ParseDIVXTAG($DIVXTAG); break 2; } } $this->warning('Expecting "RIFF|JUNK|IDVX" at ' . $nextRIFFoffset . ', found "' . $nextRIFFheaderID . '" (' . getid3_lib::PrintHexBytes($nextRIFFheaderID) . ') - skipping rest of file'); break 2; } } if ($RIFFsubtype == 'WAVE') { $thisfile_riff_WAVE =& $thisfile_riff['WAVE']; } break; default: $this->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; } $streamindex = 0; switch ($RIFFsubtype) { // http://en.wikipedia.org/wiki/Wav case 'WAVE': $info['fileformat'] = 'wav'; 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::parseWAVEFORMATex($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 = getid3_lib::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']; if (empty($info['playtime_seconds'])) { // may already be set (e.g. DTS-WAV) $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'] = getid3_lib::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(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nRadioRgAdjust']), 16, '0', STR_PAD_LEFT); $nAudiophileRgAdjustBitstring = str_pad(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nAudiophileRgAdjust']), 16, '0', STR_PAD_LEFT); $thisfile_riff_raw_rgad_track['name'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 0, 3)); $thisfile_riff_raw_rgad_track['originator'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 3, 3)); $thisfile_riff_raw_rgad_track['signbit'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 6, 1)); $thisfile_riff_raw_rgad_track['adjustment'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 7, 9)); $thisfile_riff_raw_rgad_album['name'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 0, 3)); $thisfile_riff_raw_rgad_album['originator'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 3, 3)); $thisfile_riff_raw_rgad_album['signbit'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 6, 1)); $thisfile_riff_raw_rgad_album['adjustment'] = getid3_lib::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'] = getid3_lib::RGADnameLookup($thisfile_riff_raw_rgad_track['name']); $thisfile_riff['rgad']['track']['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_riff_raw_rgad_track['originator']); $thisfile_riff['rgad']['track']['adjustment'] = getid3_lib::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'] = getid3_lib::RGADnameLookup($thisfile_riff_raw_rgad_album['name']); $thisfile_riff['rgad']['album']['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_riff_raw_rgad_album['originator']); $thisfile_riff['rgad']['album']['adjustment'] = getid3_lib::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'] = getid3_lib::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'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 338, 8)); $thisfile_riff_WAVE_bext_0['bwf_version'] = getid3_lib::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'] = getid3_lib::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'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 2, 2)); } $thisfile_riff_WAVE_MEXT_0['anciliary_data_length'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 6, 2)); $thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] = getid3_lib::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'] = getid3_lib::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'] = getid3_lib::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 = getid3_lib::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 = getid3_lib::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 2)); $SNDM_thisTagOffset += 2; $SNDM_thisTagDataFlags = getid3_lib::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 = self::waveSNDMtagLookup($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 = getid3_lib::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']; $this->fseek($info['avdataoffset'] - 44); $RIFFdata = $this->fread(44); $OrignalRIFFheaderSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 4, 4)) + 8; $OrignalRIFFdataSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44; if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) { $info['avdataend'] -= $OrignalRIFFheaderSize - $OrignalRIFFdataSize; $this->fseek($info['avdataend']); $RIFFdata .= $this->fread($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 getid3_riff($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'] = self::wFormatTagLookup($thisfile_audio['wformattag']); $thisfile_audio['lossless'] = false; $thisfile_audio['bitrate'] = $info['ac3']['bitrate']; $thisfile_audio['sample_rate'] = $info['ac3']['sample_rate']; } if (!empty($info['dts'])) { // Dolby DTS files masquerade as PCM-WAV, but they're not $thisfile_audio['wformattag'] = 0x2001; $thisfile_audio['codec'] = self::wFormatTagLookup($thisfile_audio['wformattag']); $thisfile_audio['lossless'] = false; $thisfile_audio['bitrate'] = $info['dts']['bitrate']; $thisfile_audio['sample_rate'] = $info['dts']['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(); $riff_litewave =& $thisfile_riff['litewave']; $riff_litewave_raw =& $riff_litewave['raw']; $flags = array('compression_method' => 1, 'compression_flags' => 1, 'm_dwScale' => 4, 'm_dwBlockSize' => 4, 'm_wQuality' => 2, 'm_wMarkDistance' => 2, 'm_wReserved' => 2, 'm_dwOrgSize' => 4, 'm_bFactExists' => 2, 'm_dwRiffChunkSize' => 4); $litewave_offset = 18; foreach ($flags as $flag => $length) { $riff_litewave_raw[$flag] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], $litewave_offset, $length)); $litewave_offset += $length; } //$riff_litewave['quality_factor'] = intval(round((2000 - $riff_litewave_raw['m_dwScale']) / 20)); $riff_litewave['quality_factor'] = $riff_litewave_raw['m_wQuality']; $riff_litewave['flags']['raw_source'] = $riff_litewave_raw['compression_flags'] & 0x1 ? false : true; $riff_litewave['flags']['vbr_blocksize'] = $riff_litewave_raw['compression_flags'] & 0x2 ? false : true; $riff_litewave['flags']['seekpoints'] = (bool) ($riff_litewave_raw['compression_flags'] & 0x4); $thisfile_audio['lossless'] = $riff_litewave_raw['m_wQuality'] == 100 ? true : false; $thisfile_audio['encoder_options'] = '-q' . $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; // http://en.wikipedia.org/wiki/Audio_Video_Interleave // http://en.wikipedia.org/wiki/Audio_Video_Interleave case 'AVI ': $info['fileformat'] = 'avi'; $info['mime_type'] = 'video/avi'; $thisfile_video['bitrate_mode'] = 'vbr'; // maybe not, but probably $thisfile_video['dataformat'] = '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) { $ahsisd =& $thisfile_riff['AVI ']['hdrl']['strl']['indx'][$streamnumber]['data']; $thisfile_riff_raw['indx'][$streamnumber]['wLongsPerEntry'] = $this->EitherEndian2Int(substr($ahsisd, 0, 2)); $thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType'] = $this->EitherEndian2Int(substr($ahsisd, 2, 1)); $thisfile_riff_raw['indx'][$streamnumber]['bIndexType'] = $this->EitherEndian2Int(substr($ahsisd, 3, 1)); $thisfile_riff_raw['indx'][$streamnumber]['nEntriesInUse'] = $this->EitherEndian2Int(substr($ahsisd, 4, 4)); $thisfile_riff_raw['indx'][$streamnumber]['dwChunkId'] = substr($ahsisd, 8, 4); $thisfile_riff_raw['indx'][$streamnumber]['dwReserved'] = $this->EitherEndian2Int(substr($ahsisd, 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($ahsisd); } } 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; } $flags = array('dwMaxBytesPerSec', 'dwPaddingGranularity', 'dwFlags', 'dwTotalFrames', 'dwInitialFrames', 'dwStreams', 'dwSuggestedBufferSize', 'dwWidth', 'dwHeight', 'dwScale', 'dwRate', 'dwStart', 'dwLength'); $avih_offset = 4; foreach ($flags as $flag) { $thisfile_riff_raw_avih[$flag] = $this->EitherEndian2Int(substr($avihData, $avih_offset, 4)); $avih_offset += 4; } $flags = array('hasindex' => 0x10, 'mustuseindex' => 0x20, 'interleaved' => 0x100, 'trustcktype' => 0x800, 'capturedfile' => 0x10000, 'copyrighted' => 0x20010); foreach ($flags as $flag => $value) { $thisfile_riff_raw_avih['flags'][$flag] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & $value); } // 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::parseWAVEFORMATex($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 = getid3_lib::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::fourccLookup($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::fourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) { $thisfile_riff_video_current['codec'] = self::fourccLookup($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), $this->container == 'riff'); $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::fourccLookup($thisfile_video['fourcc'])) { $thisfile_riff_video_current['codec'] = self::fourccLookup($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 'AMV ': $info['fileformat'] = 'amv'; $info['mime_type'] = 'video/amv'; $thisfile_video['bitrate_mode'] = 'vbr'; // it's MJPEG, presumably contant-quality encoding, thereby VBR $thisfile_video['dataformat'] = 'mjpeg'; $thisfile_video['codec'] = 'mjpeg'; $thisfile_video['lossless'] = false; $thisfile_video['bits_per_sample'] = 24; $thisfile_audio['dataformat'] = 'adpcm'; $thisfile_audio['lossless'] = false; break; // http://en.wikipedia.org/wiki/CD-DA // http://en.wikipedia.org/wiki/CD-DA case 'CDDA': $info['fileformat'] = 'cda'; unset($info['mime_type']); $thisfile_audio_dataformat = 'cda'; $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['lossless'] = true; $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; // http://en.wikipedia.org/wiki/AIFF // http://en.wikipedia.org/wiki/AIFF case 'AIFF': case 'AIFC': $info['fileformat'] = 'aiff'; $info['mime_type'] = 'audio/x-aiff'; $thisfile_audio['bitrate_mode'] = 'cbr'; $thisfile_audio_dataformat = 'aiff'; $thisfile_audio['lossless'] = true; 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'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 0, 2), true); $thisfile_riff_audio['total_samples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 2, 4), false); $thisfile_riff_audio['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 6, 2), true); $thisfile_riff_audio['sample_rate'] = (int) getid3_lib::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 = getid3_lib::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 = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false); $offset += 2; for ($i = 0; $i < $CommentCount; $i++) { $info['comments_raw'][$i]['timestamp'] = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 4), false); $offset += 4; $info['comments_raw'][$i]['marker_id'] = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), true); $offset += 2; $CommentLength = getid3_lib::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'] = getid3_lib::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']; } } /* if (isset($thisfile_riff[$RIFFsubtype]['ID3 '])) { getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true); $getid3_temp = new getID3(); $getid3_temp->openfile($this->getid3->filename); $getid3_id3v2 = new getid3_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; // http://en.wikipedia.org/wiki/8SVX // http://en.wikipedia.org/wiki/8SVX case '8SVX': $info['fileformat'] = '8svx'; $info['mime_type'] = 'audio/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 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'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 0, 4)); $thisfile_riff_RIFFsubtype_VHDR_0['repeatHiSamples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 4, 4)); $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerHiCycle'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 8, 4)); $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 12, 2)); $thisfile_riff_RIFFsubtype_VHDR_0['ctOctave'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 14, 1)); $thisfile_riff_RIFFsubtype_VHDR_0['sCompression'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 15, 1)); $thisfile_riff_RIFFsubtype_VHDR_0['Volume'] = getid3_lib::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 = getid3_lib::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['fileformat'] = 'vcd'; // Asume Video CD $info['mime_type'] = 'video/mpeg'; if (!empty($thisfile_riff['CDXA']['data'][0]['size'])) { getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio-video.mpeg.php', __FILE__, true); $getid3_temp = new getID3(); $getid3_temp->openfile($this->getid3->filename); $getid3_mpeg = new getid3_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']); } switch ($RIFFsubtype) { case 'WAVE': case 'AIFF': case 'AIFC': $ID3v2_key_good = 'id3 '; $ID3v2_keys_bad = array('ID3 ', 'tag '); foreach ($ID3v2_keys_bad as $ID3v2_key_bad) { if (isset($thisfile_riff[$RIFFsubtype][$ID3v2_key_bad]) && !array_key_exists($ID3v2_key_good, $thisfile_riff[$RIFFsubtype])) { $thisfile_riff[$RIFFsubtype][$ID3v2_key_good] = $thisfile_riff[$RIFFsubtype][$ID3v2_key_bad]; $info['warning'][] = 'mapping "' . $ID3v2_key_bad . '" chunk to "' . $ID3v2_key_good . '"'; } } if (isset($thisfile_riff[$RIFFsubtype]['id3 '])) { getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.tag.id3v2.php', __FILE__, true); $getid3_temp = new getID3(); $getid3_temp->openfile($this->getid3->filename); $getid3_id3v2 = new getid3_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_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'])) { self::parseComments($thisfile_riff_WAVE['INFO'], $thisfile_riff['comments']); } if (isset($thisfile_riff['AVI ']['INFO']) && is_array($thisfile_riff['AVI ']['INFO'])) { self::parseComments($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 getid3_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; }
function Analyze() { $info =& $this->getid3->info; fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); while (true) { $wavpackheader = fread($this->getid3->fp, 32); if (ftell($this->getid3->fp) >= $info['avdataend']) { break; } elseif (feof($this->getid3->fp)) { break; } elseif (isset($info['wavpack']['blockheader']['total_samples']) && isset($info['wavpack']['blockheader']['block_samples']) && $info['wavpack']['blockheader']['total_samples'] > 0 && $info['wavpack']['blockheader']['block_samples'] > 0 && (!isset($info['wavpack']['riff_trailer_size']) || $info['wavpack']['riff_trailer_size'] <= 0) && (isset($info['wavpack']['config_flags']['md5_checksum']) && $info['wavpack']['config_flags']['md5_checksum'] === false || !empty($info['md5_data_source']))) { break; } $blockheader_offset = ftell($this->getid3->fp) - 32; $blockheader_magic = substr($wavpackheader, 0, 4); $blockheader_size = getid3_lib::LittleEndian2Int(substr($wavpackheader, 4, 4)); $magic = 'wvpk'; if ($blockheader_magic != $magic) { $info['error'][] = 'Expecting "' . getid3_lib::PrintHexBytes($magic) . '" at offset ' . $blockheader_offset . ', found "' . getid3_lib::PrintHexBytes($blockheader_magic) . '"'; switch (isset($info['audio']['dataformat']) ? $info['audio']['dataformat'] : '') { case 'wavpack': case 'wvc': break; default: unset($info['fileformat']); unset($info['audio']); unset($info['wavpack']); break; } return false; } if (empty($info['wavpack']['blockheader']['block_samples']) || empty($info['wavpack']['blockheader']['total_samples']) || $info['wavpack']['blockheader']['block_samples'] <= 0 || $info['wavpack']['blockheader']['total_samples'] <= 0) { // Also, it is possible that the first block might not have // any samples (block_samples == 0) and in this case you should skip blocks // until you find one with samples because the other information (like // total_samples) are not guaranteed to be correct until (block_samples > 0) // Finally, I have defined a format for files in which the length is not known // (for example when raw files are created using pipes). In these cases // total_samples will be -1 and you must seek to the final block to determine // the total number of samples. $info['audio']['dataformat'] = 'wavpack'; $info['fileformat'] = 'wavpack'; $info['audio']['lossless'] = true; $info['audio']['bitrate_mode'] = 'vbr'; $info['wavpack']['blockheader']['offset'] = $blockheader_offset; $info['wavpack']['blockheader']['magic'] = $blockheader_magic; $info['wavpack']['blockheader']['size'] = $blockheader_size; if ($info['wavpack']['blockheader']['size'] >= 0x100000) { $info['error'][] = 'Expecting WavPack block size less than "0x100000", found "' . $info['wavpack']['blockheader']['size'] . '" at offset ' . $info['wavpack']['blockheader']['offset']; switch (isset($info['audio']['dataformat']) ? $info['audio']['dataformat'] : '') { case 'wavpack': case 'wvc': break; default: unset($info['fileformat']); unset($info['audio']); unset($info['wavpack']); break; } return false; } $info['wavpack']['blockheader']['minor_version'] = ord($wavpackheader[8]); $info['wavpack']['blockheader']['major_version'] = ord($wavpackheader[9]); if ($info['wavpack']['blockheader']['major_version'] != 4 || $info['wavpack']['blockheader']['minor_version'] < 4 && $info['wavpack']['blockheader']['minor_version'] > 16) { $info['error'][] = 'Expecting WavPack version between "4.2" and "4.16", found version "' . $info['wavpack']['blockheader']['major_version'] . '.' . $info['wavpack']['blockheader']['minor_version'] . '" at offset ' . $info['wavpack']['blockheader']['offset']; switch (isset($info['audio']['dataformat']) ? $info['audio']['dataformat'] : '') { case 'wavpack': case 'wvc': break; default: unset($info['fileformat']); unset($info['audio']); unset($info['wavpack']); break; } return false; } $info['wavpack']['blockheader']['track_number'] = ord($wavpackheader[10]); // unused $info['wavpack']['blockheader']['index_number'] = ord($wavpackheader[11]); // unused $info['wavpack']['blockheader']['total_samples'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 12, 4)); $info['wavpack']['blockheader']['block_index'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 16, 4)); $info['wavpack']['blockheader']['block_samples'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 20, 4)); $info['wavpack']['blockheader']['flags_raw'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 24, 4)); $info['wavpack']['blockheader']['crc'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 28, 4)); $info['wavpack']['blockheader']['flags']['bytes_per_sample'] = 1 + ($info['wavpack']['blockheader']['flags_raw'] & 0x3); $info['wavpack']['blockheader']['flags']['mono'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x4); $info['wavpack']['blockheader']['flags']['hybrid'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x8); $info['wavpack']['blockheader']['flags']['joint_stereo'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x10); $info['wavpack']['blockheader']['flags']['cross_decorrelation'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x20); $info['wavpack']['blockheader']['flags']['hybrid_noiseshape'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x40); $info['wavpack']['blockheader']['flags']['ieee_32bit_float'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x80); $info['wavpack']['blockheader']['flags']['int_32bit'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x100); $info['wavpack']['blockheader']['flags']['hybrid_bitrate_noise'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x200); $info['wavpack']['blockheader']['flags']['hybrid_balance_noise'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x400); $info['wavpack']['blockheader']['flags']['multichannel_initial'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x800); $info['wavpack']['blockheader']['flags']['multichannel_final'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x1000); $info['audio']['lossless'] = !$info['wavpack']['blockheader']['flags']['hybrid']; } while (!feof($this->getid3->fp) && ftell($this->getid3->fp) < $blockheader_offset + $blockheader_size + 8) { $metablock = array('offset' => ftell($this->getid3->fp)); $metablockheader = fread($this->getid3->fp, 2); if (feof($this->getid3->fp)) { break; } $metablock['id'] = ord($metablockheader[0]); $metablock['function_id'] = $metablock['id'] & 0x3f; $metablock['function_name'] = $this->WavPackMetablockNameLookup($metablock['function_id']); // The 0x20 bit in the id of the meta subblocks (which is defined as // ID_OPTIONAL_DATA) is a permanent part of the id. The idea is that // if a decoder encounters an id that it does not know about, it uses // that "ID_OPTIONAL_DATA" flag to determine what to do. If it is set // then the decoder simply ignores the metadata, but if it is zero // then the decoder should quit because it means that an understanding // of the metadata is required to correctly decode the audio. $metablock['non_decoder'] = (bool) ($metablock['id'] & 0x20); $metablock['padded_data'] = (bool) ($metablock['id'] & 0x40); $metablock['large_block'] = (bool) ($metablock['id'] & 0x80); if ($metablock['large_block']) { $metablockheader .= fread($this->getid3->fp, 2); } $metablock['size'] = getid3_lib::LittleEndian2Int(substr($metablockheader, 1)) * 2; // size is stored in words $metablock['data'] = null; if ($metablock['size'] > 0) { switch ($metablock['function_id']) { case 0x21: // ID_RIFF_HEADER // ID_RIFF_HEADER case 0x22: // ID_RIFF_TRAILER // ID_RIFF_TRAILER case 0x23: // ID_REPLAY_GAIN // ID_REPLAY_GAIN case 0x24: // ID_CUESHEET // ID_CUESHEET case 0x25: // ID_CONFIG_BLOCK // ID_CONFIG_BLOCK case 0x26: // ID_MD5_CHECKSUM $metablock['data'] = fread($this->getid3->fp, $metablock['size']); if ($metablock['padded_data']) { // padded to the nearest even byte $metablock['size']--; $metablock['data'] = substr($metablock['data'], 0, -1); } break; case 0x0: // ID_DUMMY // ID_DUMMY case 0x1: // ID_ENCODER_INFO // ID_ENCODER_INFO case 0x2: // ID_DECORR_TERMS // ID_DECORR_TERMS case 0x3: // ID_DECORR_WEIGHTS // ID_DECORR_WEIGHTS case 0x4: // ID_DECORR_SAMPLES // ID_DECORR_SAMPLES case 0x5: // ID_ENTROPY_VARS // ID_ENTROPY_VARS case 0x6: // ID_HYBRID_PROFILE // ID_HYBRID_PROFILE case 0x7: // ID_SHAPING_WEIGHTS // ID_SHAPING_WEIGHTS case 0x8: // ID_FLOAT_INFO // ID_FLOAT_INFO case 0x9: // ID_INT32_INFO // ID_INT32_INFO case 0xa: // ID_WV_BITSTREAM // ID_WV_BITSTREAM case 0xb: // ID_WVC_BITSTREAM // ID_WVC_BITSTREAM case 0xc: // ID_WVX_BITSTREAM // ID_WVX_BITSTREAM case 0xd: // ID_CHANNEL_INFO fseek($this->getid3->fp, $metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size'], SEEK_SET); break; default: $info['warning'][] = 'Unexpected metablock type "0x' . str_pad(dechex($metablock['function_id']), 2, '0', STR_PAD_LEFT) . '" at offset ' . $metablock['offset']; fseek($this->getid3->fp, $metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size'], SEEK_SET); break; } switch ($metablock['function_id']) { case 0x21: // ID_RIFF_HEADER getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio-video.riff.php', __FILE__, true); $original_wav_filesize = getid3_lib::LittleEndian2Int(substr($metablock['data'], 4, 4)); $getid3_temp = new getID3(); $getid3_temp->openfile($this->getid3->filename); $getid3_riff = new getid3_riff($getid3_temp); $getid3_riff->ParseRIFFdata($metablock['data']); $metablock['riff'] = $getid3_temp->info['riff']; $info['audio']['sample_rate'] = $getid3_temp->info['riff']['raw']['fmt ']['nSamplesPerSec']; unset($getid3_riff, $getid3_temp); $metablock['riff']['original_filesize'] = $original_wav_filesize; $info['wavpack']['riff_trailer_size'] = $original_wav_filesize - $metablock['riff']['WAVE']['data'][0]['size'] - $metablock['riff']['header_size']; $info['playtime_seconds'] = $info['wavpack']['blockheader']['total_samples'] / $info['audio']['sample_rate']; // Safe RIFF header in case there's a RIFF footer later $metablockRIFFheader = $metablock['data']; break; case 0x22: // ID_RIFF_TRAILER $metablockRIFFfooter = $metablockRIFFheader . $metablock['data']; getid3_lib::IncludeDependency(GETID3_INCLUDEPATH . 'module.audio-video.riff.php', __FILE__, true); $startoffset = $metablock['offset'] + ($metablock['large_block'] ? 4 : 2); $getid3_temp = new getID3(); $getid3_temp->openfile($this->getid3->filename); $getid3_temp->info['avdataend'] = $info['avdataend']; $getid3_temp->info['fileformat'] = 'riff'; $getid3_riff = new getid3_riff($getid3_temp); $metablock['riff'] = $getid3_riff->ParseRIFF($startoffset, $startoffset + $metablock['size']); if (!empty($metablock['riff']['INFO'])) { $getid3_riff->RIFFcommentsParse($metablock['riff']['INFO'], $metablock['comments']); $info['tags']['riff'] = $metablock['comments']; } unset($getid3_temp, $getid3_riff); break; case 0x23: // ID_REPLAY_GAIN $info['warning'][] = 'WavPack "Replay Gain" contents not yet handled by getID3() in metablock at offset ' . $metablock['offset']; break; case 0x24: // ID_CUESHEET $info['warning'][] = 'WavPack "Cuesheet" contents not yet handled by getID3() in metablock at offset ' . $metablock['offset']; break; case 0x25: // ID_CONFIG_BLOCK $metablock['flags_raw'] = getid3_lib::LittleEndian2Int(substr($metablock['data'], 0, 3)); $metablock['flags']['adobe_mode'] = (bool) ($metablock['flags_raw'] & 0x1); // "adobe" mode for 32-bit floats $metablock['flags']['fast_flag'] = (bool) ($metablock['flags_raw'] & 0x2); // fast mode $metablock['flags']['very_fast_flag'] = (bool) ($metablock['flags_raw'] & 0x4); // double fast $metablock['flags']['high_flag'] = (bool) ($metablock['flags_raw'] & 0x8); // high quality mode $metablock['flags']['very_high_flag'] = (bool) ($metablock['flags_raw'] & 0x10); // double high (not used yet) $metablock['flags']['bitrate_kbps'] = (bool) ($metablock['flags_raw'] & 0x20); // bitrate is kbps, not bits / sample $metablock['flags']['auto_shaping'] = (bool) ($metablock['flags_raw'] & 0x40); // automatic noise shaping $metablock['flags']['shape_override'] = (bool) ($metablock['flags_raw'] & 0x80); // shaping mode specified $metablock['flags']['joint_override'] = (bool) ($metablock['flags_raw'] & 0x100); // joint-stereo mode specified $metablock['flags']['copy_time'] = (bool) ($metablock['flags_raw'] & 0x200); // copy file-time from source $metablock['flags']['create_exe'] = (bool) ($metablock['flags_raw'] & 0x400); // create executable $metablock['flags']['create_wvc'] = (bool) ($metablock['flags_raw'] & 0x800); // create correction file $metablock['flags']['optimize_wvc'] = (bool) ($metablock['flags_raw'] & 0x1000); // maximize bybrid compression $metablock['flags']['quality_mode'] = (bool) ($metablock['flags_raw'] & 0x2000); // psychoacoustic quality mode $metablock['flags']['raw_flag'] = (bool) ($metablock['flags_raw'] & 0x4000); // raw mode (not implemented yet) $metablock['flags']['calc_noise'] = (bool) ($metablock['flags_raw'] & 0x8000); // calc noise in hybrid mode $metablock['flags']['lossy_mode'] = (bool) ($metablock['flags_raw'] & 0x10000); // obsolete (for information) $metablock['flags']['extra_mode'] = (bool) ($metablock['flags_raw'] & 0x20000); // extra processing mode $metablock['flags']['skip_wvx'] = (bool) ($metablock['flags_raw'] & 0x40000); // no wvx stream w/ floats & big ints $metablock['flags']['md5_checksum'] = (bool) ($metablock['flags_raw'] & 0x80000); // compute & store MD5 signature $metablock['flags']['quiet_mode'] = (bool) ($metablock['flags_raw'] & 0x100000); // don't report progress % $info['wavpack']['config_flags'] = $metablock['flags']; $info['audio']['encoder_options'] = ''; if ($info['wavpack']['blockheader']['flags']['hybrid']) { $info['audio']['encoder_options'] .= ' -b???'; } $info['audio']['encoder_options'] .= $metablock['flags']['adobe_mode'] ? ' -a' : ''; $info['audio']['encoder_options'] .= $metablock['flags']['optimize_wvc'] ? ' -cc' : ''; $info['audio']['encoder_options'] .= $metablock['flags']['create_exe'] ? ' -e' : ''; $info['audio']['encoder_options'] .= $metablock['flags']['fast_flag'] ? ' -f' : ''; $info['audio']['encoder_options'] .= $metablock['flags']['joint_override'] ? ' -j?' : ''; $info['audio']['encoder_options'] .= $metablock['flags']['high_flag'] ? ' -h' : ''; $info['audio']['encoder_options'] .= $metablock['flags']['md5_checksum'] ? ' -m' : ''; $info['audio']['encoder_options'] .= $metablock['flags']['calc_noise'] ? ' -n' : ''; $info['audio']['encoder_options'] .= $metablock['flags']['shape_override'] ? ' -s?' : ''; $info['audio']['encoder_options'] .= $metablock['flags']['extra_mode'] ? ' -x?' : ''; if (!empty($info['audio']['encoder_options'])) { $info['audio']['encoder_options'] = trim($info['audio']['encoder_options']); } elseif (isset($info['audio']['encoder_options'])) { unset($info['audio']['encoder_options']); } break; case 0x26: // ID_MD5_CHECKSUM if (strlen($metablock['data']) == 16) { $info['md5_data_source'] = strtolower(getid3_lib::PrintHexBytes($metablock['data'], true, false, false)); } else { $info['warning'][] = 'Expecting 16 bytes of WavPack "MD5 Checksum" in metablock at offset ' . $metablock['offset'] . ', but found ' . strlen($metablock['data']) . ' bytes'; } break; case 0x0: // ID_DUMMY // ID_DUMMY case 0x1: // ID_ENCODER_INFO // ID_ENCODER_INFO case 0x2: // ID_DECORR_TERMS // ID_DECORR_TERMS case 0x3: // ID_DECORR_WEIGHTS // ID_DECORR_WEIGHTS case 0x4: // ID_DECORR_SAMPLES // ID_DECORR_SAMPLES case 0x5: // ID_ENTROPY_VARS // ID_ENTROPY_VARS case 0x6: // ID_HYBRID_PROFILE // ID_HYBRID_PROFILE case 0x7: // ID_SHAPING_WEIGHTS // ID_SHAPING_WEIGHTS case 0x8: // ID_FLOAT_INFO // ID_FLOAT_INFO case 0x9: // ID_INT32_INFO // ID_INT32_INFO case 0xa: // ID_WV_BITSTREAM // ID_WV_BITSTREAM case 0xb: // ID_WVC_BITSTREAM // ID_WVC_BITSTREAM case 0xc: // ID_WVX_BITSTREAM // ID_WVX_BITSTREAM case 0xd: // ID_CHANNEL_INFO unset($metablock); break; } } if (!empty($metablock)) { $info['wavpack']['metablocks'][] = $metablock; } } } $info['audio']['encoder'] = 'WavPack v' . $info['wavpack']['blockheader']['major_version'] . '.' . str_pad($info['wavpack']['blockheader']['minor_version'], 2, '0', STR_PAD_LEFT); $info['audio']['bits_per_sample'] = $info['wavpack']['blockheader']['flags']['bytes_per_sample'] * 8; $info['audio']['channels'] = $info['wavpack']['blockheader']['flags']['mono'] ? 1 : 2; if (!empty($info['playtime_seconds'])) { $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds']; } else { $info['audio']['dataformat'] = 'wvc'; } return true; }
function ParseOptimFROGheader45(&$fd, &$ThisFileInfo) { // for fileformat of v4.50a and higher $RIFFdata = ''; fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET); while (!feof($fd) && ftell($fd) < $ThisFileInfo['avdataend']) { $BlockOffset = ftell($fd); $BlockData = fread($fd, 8); $offset = 8; $BlockName = substr($BlockData, 0, 4); $BlockSize = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 4)); if ($BlockName == 'OFRX') { $BlockName = 'OFR '; } if (!isset($ThisFileInfo['ofr'][$BlockName])) { $ThisFileInfo['ofr'][$BlockName] = array(); } $thisfile_ofr_thisblock =& $ThisFileInfo['ofr'][$BlockName]; switch ($BlockName) { case 'OFR ': // shortcut $thisfile_ofr_thisblock['offset'] = $BlockOffset; $thisfile_ofr_thisblock['size'] = $BlockSize; $ThisFileInfo['audio']['encoder'] = 'OptimFROG 4.50 alpha'; switch ($BlockSize) { case 12: case 15: // good break; default: $ThisFileInfo['warning'][] = '"' . $BlockName . '" contains more data than expected (expected 12 or 15 bytes, found ' . $BlockSize . ' bytes)'; break; } $BlockData .= fread($fd, $BlockSize); $thisfile_ofr_thisblock['total_samples'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 6)); $offset += 6; $thisfile_ofr_thisblock['raw']['sample_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1)); $thisfile_ofr_thisblock['sample_type'] = $this->OptimFROGsampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']); $offset += 1; $thisfile_ofr_thisblock['channel_config'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1)); $thisfile_ofr_thisblock['channels'] = $thisfile_ofr_thisblock['channel_config']; $offset += 1; $thisfile_ofr_thisblock['sample_rate'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4)); $offset += 4; if ($BlockSize > 12) { // OFR 4.504b or higher $thisfile_ofr_thisblock['channels'] = $this->OptimFROGchannelConfigNumChannelsLookup($thisfile_ofr_thisblock['channel_config']); $thisfile_ofr_thisblock['raw']['encoder_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2)); $thisfile_ofr_thisblock['encoder'] = $this->OptimFROGencoderNameLookup($thisfile_ofr_thisblock['raw']['encoder_id']); $offset += 2; $thisfile_ofr_thisblock['raw']['compression'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1)); $thisfile_ofr_thisblock['compression'] = $this->OptimFROGcompressionLookup($thisfile_ofr_thisblock['raw']['compression']); $thisfile_ofr_thisblock['speedup'] = $this->OptimFROGspeedupLookup($thisfile_ofr_thisblock['raw']['compression']); $offset += 1; $ThisFileInfo['audio']['encoder'] = 'OptimFROG ' . $thisfile_ofr_thisblock['encoder']; $ThisFileInfo['audio']['encoder_options'] = '--mode ' . $thisfile_ofr_thisblock['compression']; if (($thisfile_ofr_thisblock['raw']['encoder_id'] & 0xf0) >> 4 == 7) { // v4.507 if (strtolower(getid3_lib::fileextension($ThisFileInfo['filename'])) == 'ofs') { // OptimFROG DualStream format is lossy, but as of v4.507 there is no way to tell the difference // between lossless and lossy other than the file extension. $ThisFileInfo['audio']['dataformat'] = 'ofs'; $ThisFileInfo['audio']['lossless'] = true; } } } $ThisFileInfo['audio']['channels'] = $thisfile_ofr_thisblock['channels']; $ThisFileInfo['audio']['sample_rate'] = $thisfile_ofr_thisblock['sample_rate']; $ThisFileInfo['audio']['bits_per_sample'] = $this->OptimFROGbitsPerSampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']); break; case 'COMP': // unlike other block types, there CAN be multiple COMP blocks $COMPdata['offset'] = $BlockOffset; $COMPdata['size'] = $BlockSize; if ($ThisFileInfo['avdataoffset'] == 0) { $ThisFileInfo['avdataoffset'] = $BlockOffset; } // Only interested in first 14 bytes (only first 12 needed for v4.50 alpha), not actual audio data $BlockData .= fread($fd, 14); fseek($fd, $BlockSize - 14, SEEK_CUR); $COMPdata['crc_32'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4)); $offset += 4; $COMPdata['sample_count'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4)); $offset += 4; $COMPdata['raw']['sample_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1)); $COMPdata['sample_type'] = $this->OptimFROGsampleTypeLookup($COMPdata['raw']['sample_type']); $offset += 1; $COMPdata['raw']['channel_configuration'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1)); $COMPdata['channel_configuration'] = $this->OptimFROGchannelConfigurationLookup($COMPdata['raw']['channel_configuration']); $offset += 1; $COMPdata['raw']['algorithm_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2)); //$COMPdata['algorithm'] = OptimFROGalgorithmNameLookup($COMPdata['raw']['algorithm_id']); $offset += 2; if ($ThisFileInfo['ofr']['OFR ']['size'] > 12) { // OFR 4.504b or higher $COMPdata['raw']['encoder_id'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2)); $COMPdata['encoder'] = $this->OptimFROGencoderNameLookup($COMPdata['raw']['encoder_id']); $offset += 2; } if ($COMPdata['crc_32'] == 0x454e4f4e) { // ASCII value of 'NONE' - placeholder value in v4.50a $COMPdata['crc_32'] = false; } $thisfile_ofr_thisblock[] = $COMPdata; break; case 'HEAD': $thisfile_ofr_thisblock['offset'] = $BlockOffset; $thisfile_ofr_thisblock['size'] = $BlockSize; $RIFFdata .= fread($fd, $BlockSize); break; case 'TAIL': $thisfile_ofr_thisblock['offset'] = $BlockOffset; $thisfile_ofr_thisblock['size'] = $BlockSize; if ($BlockSize > 0) { $RIFFdata .= fread($fd, $BlockSize); } break; case 'RECV': // block contains no useful meta data - simply note and skip $thisfile_ofr_thisblock['offset'] = $BlockOffset; $thisfile_ofr_thisblock['size'] = $BlockSize; fseek($fd, $BlockSize, SEEK_CUR); break; case 'APET': // APEtag v2 $thisfile_ofr_thisblock['offset'] = $BlockOffset; $thisfile_ofr_thisblock['size'] = $BlockSize; $ThisFileInfo['warning'][] = 'APEtag processing inside OptimFROG not supported in this version (' . GETID3_VERSION . ') of getID3()'; fseek($fd, $BlockSize, SEEK_CUR); break; case 'MD5 ': // APEtag v2 $thisfile_ofr_thisblock['offset'] = $BlockOffset; $thisfile_ofr_thisblock['size'] = $BlockSize; if ($BlockSize == 16) { $thisfile_ofr_thisblock['md5_binary'] = fread($fd, $BlockSize); $thisfile_ofr_thisblock['md5_string'] = getid3_lib::PrintHexBytes($thisfile_ofr_thisblock['md5_binary'], true, false, false); $ThisFileInfo['md5_data_source'] = $thisfile_ofr_thisblock['md5_string']; } else { $ThisFileInfo['warning'][] = 'Expecting block size of 16 in "MD5 " chunk, found ' . $BlockSize . ' instead'; fseek($fd, $BlockSize, SEEK_CUR); } break; default: $thisfile_ofr_thisblock['offset'] = $BlockOffset; $thisfile_ofr_thisblock['size'] = $BlockSize; $ThisFileInfo['warning'][] = 'Unhandled OptimFROG block type "' . $BlockName . '" at offset ' . $thisfile_ofr_thisblock['offset']; fseek($fd, $BlockSize, SEEK_CUR); break; } } if (isset($ThisFileInfo['ofr']['TAIL']['offset'])) { $ThisFileInfo['avdataend'] = $ThisFileInfo['ofr']['TAIL']['offset']; } $ThisFileInfo['playtime_seconds'] = (double) $ThisFileInfo['ofr']['OFR ']['total_samples'] / ($ThisFileInfo['audio']['channels'] * $ThisFileInfo['audio']['sample_rate']); $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['playtime_seconds']; // 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::ParseRIFFdata($RIFFdata, $ThisFileInfo); return true; }