rlm@3: | rlm@3: // | Allan Hansen | rlm@3: // +----------------------------------------------------------------------+ rlm@3: // | module.audio.monkey.php | rlm@3: // | Module for analyzing Monkey's Audio files | rlm@3: // | dependencies: NONE | rlm@3: // +----------------------------------------------------------------------+ rlm@3: // rlm@3: // $Id: module.audio.monkey.php,v 1.2 2006/11/02 10:48:01 ah Exp $ rlm@3: rlm@3: rlm@3: rlm@3: class getid3_monkey extends getid3_handler rlm@3: { rlm@3: rlm@3: public function Analyze() { rlm@3: rlm@3: $getid3 = $this->getid3; rlm@3: rlm@3: // based loosely on code from TMonkey by Jurgen Faul rlm@3: // http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html rlm@3: rlm@3: $getid3->info['fileformat'] = 'mac'; rlm@3: $getid3->info['audio']['dataformat'] = 'mac'; rlm@3: $getid3->info['audio']['bitrate_mode'] = 'vbr'; rlm@3: $getid3->info['audio']['lossless'] = true; rlm@3: rlm@3: $getid3->info['monkeys_audio']['raw'] = array (); rlm@3: $info_monkeys_audio = &$getid3->info['monkeys_audio']; rlm@3: $info_monkeys_audio_raw = &$info_monkeys_audio['raw']; rlm@3: rlm@3: // Read file header rlm@3: fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET); rlm@3: $mac_header_data = fread($getid3->fp, 74); rlm@3: rlm@3: $info_monkeys_audio_raw['magic'] = 'MAC '; // Magic bytes rlm@3: rlm@3: // Read MAC version rlm@3: $info_monkeys_audio_raw['nVersion'] = getid3_lib::LittleEndian2Int(substr($mac_header_data, 4, 2)); // appears to be uint32 in 3.98+ rlm@3: rlm@3: // Parse MAC Header < v3980 rlm@3: if ($info_monkeys_audio_raw['nVersion'] < 3980) { rlm@3: rlm@3: getid3_lib::ReadSequence("LittleEndian2Int", $info_monkeys_audio_raw, $mac_header_data, 6, rlm@3: array ( rlm@3: 'nCompressionLevel' => 2, rlm@3: 'nFormatFlags' => 2, rlm@3: 'nChannels' => 2, rlm@3: 'nSampleRate' => 4, rlm@3: 'nHeaderDataBytes' => 4, rlm@3: 'nWAVTerminatingBytes' => 4, rlm@3: 'nTotalFrames' => 4, rlm@3: 'nFinalFrameSamples' => 4, rlm@3: 'nPeakLevel' => 4, rlm@3: 'IGNORE-1' => 2, rlm@3: 'nSeekElements' => 2 rlm@3: ) rlm@3: ); rlm@3: } rlm@3: rlm@3: // Parse MAC Header >= v3980 rlm@3: else { rlm@3: rlm@3: getid3_lib::ReadSequence("LittleEndian2Int", $info_monkeys_audio_raw, $mac_header_data, 8, rlm@3: array ( rlm@3: // APE_DESCRIPTOR rlm@3: 'nDescriptorBytes' => 4, rlm@3: 'nHeaderBytes' => 4, rlm@3: 'nSeekTableBytes' => 4, rlm@3: 'nHeaderDataBytes' => 4, rlm@3: 'nAPEFrameDataBytes' => 4, rlm@3: 'nAPEFrameDataBytesHigh'=> 4, rlm@3: 'nTerminatingDataBytes' => 4, rlm@3: rlm@3: // MD5 - string rlm@3: 'cFileMD5' => -16, rlm@3: rlm@3: // APE_HEADER rlm@3: 'nCompressionLevel' => 2, rlm@3: 'nFormatFlags' => 2, rlm@3: 'nBlocksPerFrame' => 4, rlm@3: 'nFinalFrameBlocks' => 4, rlm@3: 'nTotalFrames' => 4, rlm@3: 'nBitsPerSample' => 2, rlm@3: 'nChannels' => 2, rlm@3: 'nSampleRate' => 4 rlm@3: ) rlm@3: ); rlm@3: } rlm@3: rlm@3: // Process data rlm@3: $info_monkeys_audio['flags']['8-bit'] = (bool)($info_monkeys_audio_raw['nFormatFlags'] & 0x0001); rlm@3: $info_monkeys_audio['flags']['crc-32'] = (bool)($info_monkeys_audio_raw['nFormatFlags'] & 0x0002); rlm@3: $info_monkeys_audio['flags']['peak_level'] = (bool)($info_monkeys_audio_raw['nFormatFlags'] & 0x0004); rlm@3: $info_monkeys_audio['flags']['24-bit'] = (bool)($info_monkeys_audio_raw['nFormatFlags'] & 0x0008); rlm@3: $info_monkeys_audio['flags']['seek_elements'] = (bool)($info_monkeys_audio_raw['nFormatFlags'] & 0x0010); rlm@3: $info_monkeys_audio['flags']['no_wav_header'] = (bool)($info_monkeys_audio_raw['nFormatFlags'] & 0x0020); rlm@3: rlm@3: $info_monkeys_audio['version'] = $info_monkeys_audio_raw['nVersion'] / 1000; rlm@3: rlm@3: $info_monkeys_audio['compression'] = getid3_monkey::MonkeyCompressionLevelNameLookup($info_monkeys_audio_raw['nCompressionLevel']); rlm@3: rlm@3: $info_monkeys_audio['bits_per_sample'] = ($info_monkeys_audio['flags']['24-bit'] ? 24 : ($info_monkeys_audio['flags']['8-bit'] ? 8 : 16)); rlm@3: rlm@3: $info_monkeys_audio['channels'] = $info_monkeys_audio_raw['nChannels']; rlm@3: rlm@3: $getid3->info['audio']['channels'] = $info_monkeys_audio['channels']; rlm@3: rlm@3: $info_monkeys_audio['sample_rate'] = $info_monkeys_audio_raw['nSampleRate']; rlm@3: rlm@3: $getid3->info['audio']['sample_rate'] = $info_monkeys_audio['sample_rate']; rlm@3: rlm@3: if ($info_monkeys_audio['flags']['peak_level']) { rlm@3: $info_monkeys_audio['peak_level'] = $info_monkeys_audio_raw['nPeakLevel']; rlm@3: $info_monkeys_audio['peak_ratio'] = $info_monkeys_audio['peak_level'] / pow(2, $info_monkeys_audio['bits_per_sample'] - 1); rlm@3: } rlm@3: rlm@3: // MAC >= v3980 rlm@3: if ($info_monkeys_audio_raw['nVersion'] >= 3980) { rlm@3: $info_monkeys_audio['samples'] = (($info_monkeys_audio_raw['nTotalFrames'] - 1) * $info_monkeys_audio_raw['nBlocksPerFrame']) + $info_monkeys_audio_raw['nFinalFrameBlocks']; rlm@3: } rlm@3: rlm@3: // MAC < v3980 rlm@3: else { rlm@3: $info_monkeys_audio['samples_per_frame'] = getid3_monkey::MonkeySamplesPerFrame($info_monkeys_audio_raw['nVersion'], $info_monkeys_audio_raw['nCompressionLevel']); rlm@3: $info_monkeys_audio['samples'] = (($info_monkeys_audio_raw['nTotalFrames'] - 1) * $info_monkeys_audio['samples_per_frame']) + $info_monkeys_audio_raw['nFinalFrameSamples']; rlm@3: } rlm@3: rlm@3: $info_monkeys_audio['playtime'] = $info_monkeys_audio['samples'] / $info_monkeys_audio['sample_rate']; rlm@3: rlm@3: $getid3->info['playtime_seconds'] = $info_monkeys_audio['playtime']; rlm@3: rlm@3: $info_monkeys_audio['compressed_size'] = $getid3->info['avdataend'] - $getid3->info['avdataoffset']; rlm@3: $info_monkeys_audio['uncompressed_size'] = $info_monkeys_audio['samples'] * $info_monkeys_audio['channels'] * ($info_monkeys_audio['bits_per_sample'] / 8); rlm@3: $info_monkeys_audio['compression_ratio'] = $info_monkeys_audio['compressed_size'] / ($info_monkeys_audio['uncompressed_size'] + $info_monkeys_audio_raw['nHeaderDataBytes']); rlm@3: $info_monkeys_audio['bitrate'] = (($info_monkeys_audio['samples'] * $info_monkeys_audio['channels'] * $info_monkeys_audio['bits_per_sample']) / $info_monkeys_audio['playtime']) * $info_monkeys_audio['compression_ratio']; rlm@3: rlm@3: $getid3->info['audio']['bitrate'] = $info_monkeys_audio['bitrate']; rlm@3: rlm@3: $getid3->info['audio']['bits_per_sample'] = $info_monkeys_audio['bits_per_sample']; rlm@3: $getid3->info['audio']['encoder'] = 'MAC v'.number_format($info_monkeys_audio['version'], 2); rlm@3: $getid3->info['audio']['encoder_options'] = ucfirst($info_monkeys_audio['compression']).' compression'; rlm@3: rlm@3: // MAC >= v3980 - get avdataoffsets from MAC header rlm@3: if ($info_monkeys_audio_raw['nVersion'] >= 3980) { rlm@3: $getid3->info['avdataoffset'] += $info_monkeys_audio_raw['nDescriptorBytes'] + $info_monkeys_audio_raw['nHeaderBytes'] + $info_monkeys_audio_raw['nSeekTableBytes'] + $info_monkeys_audio_raw['nHeaderDataBytes']; rlm@3: $getid3->info['avdataend'] -= $info_monkeys_audio_raw['nTerminatingDataBytes']; rlm@3: } rlm@3: rlm@3: // MAC < v3980 Add size of MAC header to avdataoffset rlm@3: else { rlm@3: $getid3->info['avdataoffset'] += 8; rlm@3: } rlm@3: rlm@3: // Convert md5sum to 32 byte string rlm@3: if (@$info_monkeys_audio_raw['cFileMD5']) { rlm@3: if ($info_monkeys_audio_raw['cFileMD5'] !== str_repeat("\x00", 16)) { rlm@3: $getid3->info['md5_data_source'] = ''; rlm@3: $md5 = $info_monkeys_audio_raw['cFileMD5']; rlm@3: for ($i = 0; $i < strlen($md5); $i++) { rlm@3: $getid3->info['md5_data_source'] .= str_pad(dechex(ord($md5{$i})), 2, '00', STR_PAD_LEFT); rlm@3: } rlm@3: if (!preg_match('/^[0-9a-f]{32}$/', $getid3->info['md5_data_source'])) { rlm@3: unset($getid3->info['md5_data_source']); rlm@3: } rlm@3: } rlm@3: } rlm@3: rlm@3: rlm@3: return true; rlm@3: } rlm@3: rlm@3: rlm@3: rlm@3: public static function MonkeyCompressionLevelNameLookup($compression_level) { rlm@3: rlm@3: static $lookup = array ( rlm@3: 0 => 'unknown', rlm@3: 1000 => 'fast', rlm@3: 2000 => 'normal', rlm@3: 3000 => 'high', rlm@3: 4000 => 'extra-high', rlm@3: 5000 => 'insane' rlm@3: ); rlm@3: return (isset($lookup[$compression_level]) ? $lookup[$compression_level] : 'invalid'); rlm@3: } rlm@3: rlm@3: rlm@3: rlm@3: public static function MonkeySamplesPerFrame($version_id, $compression_level) { rlm@3: rlm@3: if ($version_id >= 3950) { rlm@3: return 73728 * 4; rlm@3: } rlm@3: if (($version_id >= 3900) || (($version_id >= 3800) && ($compression_level == 4000))) { rlm@3: return 73728; rlm@3: } rlm@3: return 9216; rlm@3: } rlm@3: rlm@3: } rlm@3: rlm@3: ?>