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; }