/** * @return bool */ public 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 = Helper::LittleEndian2Int(substr($wavpackheader, 4, 4)); $magic = 'wvpk'; if ($blockheader_magic != $magic) { $info['error'][] = 'Expecting "' . Helper::PrintHexBytes($magic) . '" at offset ' . $blockheader_offset . ', found "' . Helper::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'] = Helper::LittleEndian2Int(substr($wavpackheader, 12, 4)); $info['wavpack']['blockheader']['block_index'] = Helper::LittleEndian2Int(substr($wavpackheader, 16, 4)); $info['wavpack']['blockheader']['block_samples'] = Helper::LittleEndian2Int(substr($wavpackheader, 20, 4)); $info['wavpack']['blockheader']['flags_raw'] = Helper::LittleEndian2Int(substr($wavpackheader, 24, 4)); $info['wavpack']['blockheader']['crc'] = Helper::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'] = Helper::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 $original_wav_filesize = Helper::LittleEndian2Int(substr($metablock['data'], 4, 4)); $getid3_temp = new GetId3Core(); $getid3_temp->openfile($this->getid3->filename); $getid3_riff = new 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']; $startoffset = $metablock['offset'] + ($metablock['large_block'] ? 4 : 2); $getid3_temp = new GetId3Core(); $getid3_temp->openfile($this->getid3->filename); $getid3_temp->info['avdataend'] = $info['avdataend']; $getid3_temp->info['fileformat'] = 'riff'; $getid3_riff = new 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 GetId3Core() in metablock at offset ' . $metablock['offset']; break; case 0x24: // ID_CUESHEET $info['warning'][] = 'WavPack "Cuesheet" contents not yet handled by GetId3Core() in metablock at offset ' . $metablock['offset']; break; case 0x25: // ID_CONFIG_BLOCK $metablock['flags_raw'] = Helper::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(Helper::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; }
/** * @return bool */ public function ParseOptimFROGheader45() { // for fileformat of v4.50a and higher $info =& $this->getid3->info; $RIFFdata = ''; fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET); while (!feof($this->getid3->fp) && ftell($this->getid3->fp) < $info['avdataend']) { $BlockOffset = ftell($this->getid3->fp); $BlockData = fread($this->getid3->fp, 8); $offset = 8; $BlockName = substr($BlockData, 0, 4); $BlockSize = Helper::LittleEndian2Int(substr($BlockData, 4, 4)); if ($BlockName == 'OFRX') { $BlockName = 'OFR '; } if (!isset($info['ofr'][$BlockName])) { $info['ofr'][$BlockName] = array(); } $thisfile_ofr_thisblock =& $info['ofr'][$BlockName]; switch ($BlockName) { case 'OFR ': // shortcut $thisfile_ofr_thisblock['offset'] = $BlockOffset; $thisfile_ofr_thisblock['size'] = $BlockSize; $info['audio']['encoder'] = 'OptimFROG 4.50 alpha'; switch ($BlockSize) { case 12: case 15: // good break; default: $info['warning'][] = '"' . $BlockName . '" contains more data than expected (expected 12 or 15 bytes, found ' . $BlockSize . ' bytes)'; break; } $BlockData .= fread($this->getid3->fp, $BlockSize); $thisfile_ofr_thisblock['total_samples'] = Helper::LittleEndian2Int(substr($BlockData, $offset, 6)); $offset += 6; $thisfile_ofr_thisblock['raw']['sample_type'] = Helper::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'] = Helper::LittleEndian2Int(substr($BlockData, $offset, 1)); $thisfile_ofr_thisblock['channels'] = $thisfile_ofr_thisblock['channel_config']; $offset += 1; $thisfile_ofr_thisblock['sample_rate'] = Helper::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'] = Helper::LittleEndian2Int(substr($BlockData, $offset, 2)); $thisfile_ofr_thisblock['encoder'] = $this->OptimFROGencoderNameLookup($thisfile_ofr_thisblock['raw']['encoder_id']); $offset += 2; $thisfile_ofr_thisblock['raw']['compression'] = Helper::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; $info['audio']['encoder'] = 'OptimFROG ' . $thisfile_ofr_thisblock['encoder']; $info['audio']['encoder_options'] = '--mode ' . $thisfile_ofr_thisblock['compression']; if (($thisfile_ofr_thisblock['raw']['encoder_id'] & 0xf0) >> 4 == 7) { // v4.507 if (strtolower(Helper::fileextension($info['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. $info['audio']['dataformat'] = 'ofs'; $info['audio']['lossless'] = true; } } } $info['audio']['channels'] = $thisfile_ofr_thisblock['channels']; $info['audio']['sample_rate'] = $thisfile_ofr_thisblock['sample_rate']; $info['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 ($info['avdataoffset'] == 0) { $info['avdataoffset'] = $BlockOffset; } // Only interested in first 14 bytes (only first 12 needed for v4.50 alpha), not actual audio data $BlockData .= fread($this->getid3->fp, 14); fseek($this->getid3->fp, $BlockSize - 14, SEEK_CUR); $COMPdata['crc_32'] = Helper::LittleEndian2Int(substr($BlockData, $offset, 4)); $offset += 4; $COMPdata['sample_count'] = Helper::LittleEndian2Int(substr($BlockData, $offset, 4)); $offset += 4; $COMPdata['raw']['sample_type'] = Helper::LittleEndian2Int(substr($BlockData, $offset, 1)); $COMPdata['sample_type'] = $this->OptimFROGsampleTypeLookup($COMPdata['raw']['sample_type']); $offset += 1; $COMPdata['raw']['channel_configuration'] = Helper::LittleEndian2Int(substr($BlockData, $offset, 1)); $COMPdata['channel_configuration'] = $this->OptimFROGchannelConfigurationLookup($COMPdata['raw']['channel_configuration']); $offset += 1; $COMPdata['raw']['algorithm_id'] = Helper::LittleEndian2Int(substr($BlockData, $offset, 2)); //$COMPdata['algorithm'] = OptimFROGalgorithmNameLookup($COMPdata['raw']['algorithm_id']); $offset += 2; if ($info['ofr']['OFR ']['size'] > 12) { // OFR 4.504b or higher $COMPdata['raw']['encoder_id'] = Helper::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($this->getid3->fp, $BlockSize); break; case 'TAIL': $thisfile_ofr_thisblock['offset'] = $BlockOffset; $thisfile_ofr_thisblock['size'] = $BlockSize; if ($BlockSize > 0) { $RIFFdata .= fread($this->getid3->fp, $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($this->getid3->fp, $BlockSize, SEEK_CUR); break; case 'APET': // APEtag v2 $thisfile_ofr_thisblock['offset'] = $BlockOffset; $thisfile_ofr_thisblock['size'] = $BlockSize; $info['warning'][] = 'APEtag processing inside OptimFROG not supported in this version (' . $this->getid3->version() . ') of GetId3Core()'; fseek($this->getid3->fp, $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($this->getid3->fp, $BlockSize); $thisfile_ofr_thisblock['md5_string'] = Helper::PrintHexBytes($thisfile_ofr_thisblock['md5_binary'], true, false, false); $info['md5_data_source'] = $thisfile_ofr_thisblock['md5_string']; } else { $info['warning'][] = 'Expecting block size of 16 in "MD5 " chunk, found ' . $BlockSize . ' instead'; fseek($this->getid3->fp, $BlockSize, SEEK_CUR); } break; default: $thisfile_ofr_thisblock['offset'] = $BlockOffset; $thisfile_ofr_thisblock['size'] = $BlockSize; $info['warning'][] = 'Unhandled OptimFROG block type "' . $BlockName . '" at offset ' . $thisfile_ofr_thisblock['offset']; fseek($this->getid3->fp, $BlockSize, SEEK_CUR); break; } } if (isset($info['ofr']['TAIL']['offset'])) { $info['avdataend'] = $info['ofr']['TAIL']['offset']; } $info['playtime_seconds'] = (double) $info['ofr']['OFR ']['total_samples'] / ($info['audio']['channels'] * $info['audio']['sample_rate']); $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['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_temp = new GetId3Core(); $getid3_temp->openfile($this->getid3->filename); $getid3_temp->info['avdataoffset'] = $info['avdataoffset']; $getid3_temp->info['avdataend'] = $info['avdataend']; $getid3_riff = new Riff($getid3_temp); $getid3_riff->ParseRIFFdata($RIFFdata); $info['riff'] = $getid3_temp->info['riff']; unset($getid3_riff, $getid3_temp, $RIFFdata); return true; }