public function Analyze() { $info =& $this->getid3->info; $this->fseek($info['avdataoffset']); $AUheader = $this->fread(8); $magic = '.snd'; if (substr($AUheader, 0, 4) != $magic) { $info['error'][] = 'Expecting "' . Utils::PrintHexBytes($magic) . '" (".snd") at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes(substr($AUheader, 0, 4)) . '"'; return false; } // shortcut $info['au'] = array(); $thisfile_au =& $info['au']; $info['fileformat'] = 'au'; $info['audio']['dataformat'] = 'au'; $info['audio']['bitrate_mode'] = 'cbr'; $thisfile_au['encoding'] = 'ISO-8859-1'; $thisfile_au['header_length'] = Utils::BigEndian2Int(substr($AUheader, 4, 4)); $AUheader .= $this->fread($thisfile_au['header_length'] - 8); $info['avdataoffset'] += $thisfile_au['header_length']; $thisfile_au['data_size'] = Utils::BigEndian2Int(substr($AUheader, 8, 4)); $thisfile_au['data_format_id'] = Utils::BigEndian2Int(substr($AUheader, 12, 4)); $thisfile_au['sample_rate'] = Utils::BigEndian2Int(substr($AUheader, 16, 4)); $thisfile_au['channels'] = Utils::BigEndian2Int(substr($AUheader, 20, 4)); $thisfile_au['comments']['comment'][] = trim(substr($AUheader, 24)); $thisfile_au['data_format'] = $this->AUdataFormatNameLookup($thisfile_au['data_format_id']); $thisfile_au['used_bits_per_sample'] = $this->AUdataFormatUsedBitsPerSampleLookup($thisfile_au['data_format_id']); if ($thisfile_au['bits_per_sample'] = $this->AUdataFormatBitsPerSampleLookup($thisfile_au['data_format_id'])) { $info['audio']['bits_per_sample'] = $thisfile_au['bits_per_sample']; } else { unset($thisfile_au['bits_per_sample']); } $info['audio']['sample_rate'] = $thisfile_au['sample_rate']; $info['audio']['channels'] = $thisfile_au['channels']; if ($info['avdataoffset'] + $thisfile_au['data_size'] > $info['avdataend']) { $info['warning'][] = 'Possible truncated file - expecting "' . $thisfile_au['data_size'] . '" bytes of audio data, only found ' . ($info['avdataend'] - $info['avdataoffset']) . ' bytes"'; } $info['playtime_seconds'] = $thisfile_au['data_size'] / ($thisfile_au['sample_rate'] * $thisfile_au['channels'] * ($thisfile_au['used_bits_per_sample'] / 8)); $info['audio']['bitrate'] = $thisfile_au['data_size'] * 8 / $info['playtime_seconds']; return true; }
public function Analyze() { $info =& $this->getid3->info; // based loosely on code from TTwinVQ by Jurgen Faul <jfaulØgmx*de> // http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html $info['fileformat'] = 'vqf'; $info['audio']['dataformat'] = 'vqf'; $info['audio']['bitrate_mode'] = 'cbr'; $info['audio']['lossless'] = false; // shortcut $info['vqf']['raw'] = array(); $thisfile_vqf =& $info['vqf']; $thisfile_vqf_raw =& $thisfile_vqf['raw']; $this->fseek($info['avdataoffset']); $VQFheaderData = $this->fread(16); $offset = 0; $thisfile_vqf_raw['header_tag'] = substr($VQFheaderData, $offset, 4); $magic = 'TWIN'; if ($thisfile_vqf_raw['header_tag'] != $magic) { $info['error'][] = 'Expecting "' . Utils::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes($thisfile_vqf_raw['header_tag']) . '"'; unset($info['vqf']); unset($info['fileformat']); return false; } $offset += 4; $thisfile_vqf_raw['version'] = substr($VQFheaderData, $offset, 8); $offset += 8; $thisfile_vqf_raw['size'] = Utils::BigEndian2Int(substr($VQFheaderData, $offset, 4)); $offset += 4; while ($this->ftell() < $info['avdataend']) { $ChunkBaseOffset = $this->ftell(); $chunkoffset = 0; $ChunkData = $this->fread(8); $ChunkName = substr($ChunkData, $chunkoffset, 4); if ($ChunkName == 'DATA') { $info['avdataoffset'] = $ChunkBaseOffset; break; } $chunkoffset += 4; $ChunkSize = Utils::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); $chunkoffset += 4; if ($ChunkSize > $info['avdataend'] - $this->ftell()) { $info['error'][] = 'Invalid chunk size (' . $ChunkSize . ') for chunk "' . $ChunkName . '" at offset ' . $ChunkBaseOffset; break; } if ($ChunkSize > 0) { $ChunkData .= $this->fread($ChunkSize); } switch ($ChunkName) { case 'COMM': // shortcut $thisfile_vqf['COMM'] = array(); $thisfile_vqf_COMM =& $thisfile_vqf['COMM']; $thisfile_vqf_COMM['channel_mode'] = Utils::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); $chunkoffset += 4; $thisfile_vqf_COMM['bitrate'] = Utils::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); $chunkoffset += 4; $thisfile_vqf_COMM['sample_rate'] = Utils::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); $chunkoffset += 4; $thisfile_vqf_COMM['security_level'] = Utils::BigEndian2Int(substr($ChunkData, $chunkoffset, 4)); $chunkoffset += 4; $info['audio']['channels'] = $thisfile_vqf_COMM['channel_mode'] + 1; $info['audio']['sample_rate'] = $this->VQFchannelFrequencyLookup($thisfile_vqf_COMM['sample_rate']); $info['audio']['bitrate'] = $thisfile_vqf_COMM['bitrate'] * 1000; $info['audio']['encoder_options'] = 'CBR' . ceil($info['audio']['bitrate'] / 1000); if ($info['audio']['bitrate'] == 0) { $info['error'][] = 'Corrupt VQF file: bitrate_audio == zero'; return false; } break; case 'NAME': case 'AUTH': case '(c) ': case 'FILE': case 'COMT': case 'ALBM': $thisfile_vqf['comments'][$this->VQFcommentNiceNameLookup($ChunkName)][] = trim(substr($ChunkData, 8)); break; case 'DSIZ': $thisfile_vqf['DSIZ'] = Utils::BigEndian2Int(substr($ChunkData, 8, 4)); break; default: $info['warning'][] = 'Unhandled chunk type "' . $ChunkName . '" at offset ' . $ChunkBaseOffset; break; } } $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['audio']['bitrate']; if (isset($thisfile_vqf['DSIZ']) && $thisfile_vqf['DSIZ'] != $info['avdataend'] - $info['avdataoffset'] - strlen('DATA')) { switch ($thisfile_vqf['DSIZ']) { case 0: case 1: $info['warning'][] = 'Invalid DSIZ value "' . $thisfile_vqf['DSIZ'] . '". This is known to happen with VQF files encoded by Ahead Nero, and seems to be its way of saying this is TwinVQF v' . ($thisfile_vqf['DSIZ'] + 1) . '.0'; $info['audio']['encoder'] = 'Ahead Nero'; break; default: $info['warning'][] = 'Probable corrupted file - should be ' . $thisfile_vqf['DSIZ'] . ' bytes, actually ' . ($info['avdataend'] - $info['avdataoffset'] - strlen('DATA')); break; } } return true; }
private function EitherEndian2Int($byteword, $signed = false) { if ($this->container == 'riff') { return Utils::LittleEndian2Int($byteword, $signed); } return Utils::BigEndian2Int($byteword, false, $signed); }
public function getBit() { $result = Utils::BigEndian2Int(substr($this->sps, $this->currentBytes, 1)) >> 7 - $this->currentBits & 0x1; $this->skipBits(1); return $result; }
public function TIFFendian2Int($bytestring, $byteorder) { if ($byteorder == 'Intel') { return Utils::LittleEndian2Int($bytestring); } elseif ($byteorder == 'Motorola') { return Utils::BigEndian2Int($bytestring); } return false; }
public function Analyze() { $info =& $this->getid3->info; $this->fseek($info['avdataoffset']); $LPACheader = $this->fread(14); if (substr($LPACheader, 0, 4) != 'LPAC') { $info['error'][] = 'Expected "LPAC" at offset ' . $info['avdataoffset'] . ', found "' . $StreamMarker . '"'; return false; } $info['avdataoffset'] += 14; $info['fileformat'] = 'lpac'; $info['audio']['dataformat'] = 'lpac'; $info['audio']['lossless'] = true; $info['audio']['bitrate_mode'] = 'vbr'; $info['lpac']['file_version'] = Utils::BigEndian2Int(substr($LPACheader, 4, 1)); $flags['audio_type'] = Utils::BigEndian2Int(substr($LPACheader, 5, 1)); $info['lpac']['total_samples'] = Utils::BigEndian2Int(substr($LPACheader, 6, 4)); $flags['parameters'] = Utils::BigEndian2Int(substr($LPACheader, 10, 4)); $info['lpac']['flags']['is_wave'] = (bool) ($flags['audio_type'] & 0x40); $info['lpac']['flags']['stereo'] = (bool) ($flags['audio_type'] & 0x4); $info['lpac']['flags']['24_bit'] = (bool) ($flags['audio_type'] & 0x2); $info['lpac']['flags']['16_bit'] = (bool) ($flags['audio_type'] & 0x1); if ($info['lpac']['flags']['24_bit'] && $info['lpac']['flags']['16_bit']) { $info['warning'][] = '24-bit and 16-bit flags cannot both be set'; } $info['lpac']['flags']['fast_compress'] = (bool) ($flags['parameters'] & 0x40000000); $info['lpac']['flags']['random_access'] = (bool) ($flags['parameters'] & 0x8000000); $info['lpac']['block_length'] = pow(2, ($flags['parameters'] & 0x7000000) >> 24) * 256; $info['lpac']['flags']['adaptive_prediction_order'] = (bool) ($flags['parameters'] & 0x800000); $info['lpac']['flags']['adaptive_quantization'] = (bool) ($flags['parameters'] & 0x400000); $info['lpac']['flags']['joint_stereo'] = (bool) ($flags['parameters'] & 0x40000); $info['lpac']['quantization'] = ($flags['parameters'] & 0x1f00) >> 8; $info['lpac']['max_prediction_order'] = $flags['parameters'] & 0x3f; if ($info['lpac']['flags']['fast_compress'] && $info['lpac']['max_prediction_order'] != 3) { $info['warning'][] = 'max_prediction_order expected to be "3" if fast_compress is true, actual value is "' . $info['lpac']['max_prediction_order'] . '"'; } switch ($info['lpac']['file_version']) { case 6: if ($info['lpac']['flags']['adaptive_quantization']) { $info['warning'][] = 'adaptive_quantization expected to be false in LPAC file stucture v6, actually true'; } if ($info['lpac']['quantization'] != 20) { $info['warning'][] = 'Quantization expected to be 20 in LPAC file stucture v6, actually ' . $info['lpac']['flags']['Q']; } break; default: //$info['warning'][] = 'This version of getID3() ['.$this->getid3->version().'] only supports LPAC file format version 6, this file is version '.$info['lpac']['file_version'].' - please report to info@getid3.org'; break; } $getid3_temp = new GetID3(); $getid3_temp->openfile($this->getid3->filename); $getid3_temp->info = $info; $getid3_riff = new Riff($getid3_temp); $getid3_riff->Analyze(); $info['avdataoffset'] = $getid3_temp->info['avdataoffset']; $info['riff'] = $getid3_temp->info['riff']; $info['error'] = $getid3_temp->info['error']; $info['warning'] = $getid3_temp->info['warning']; $info['lpac']['comments']['comment'] = $getid3_temp->info['comments']; $info['audio']['sample_rate'] = $getid3_temp->info['audio']['sample_rate']; unset($getid3_temp, $getid3_riff); $info['audio']['channels'] = $info['lpac']['flags']['stereo'] ? 2 : 1; if ($info['lpac']['flags']['24_bit']) { $info['audio']['bits_per_sample'] = $info['riff']['audio'][0]['bits_per_sample']; } elseif ($info['lpac']['flags']['16_bit']) { $info['audio']['bits_per_sample'] = 16; } else { $info['audio']['bits_per_sample'] = 8; } if ($info['lpac']['flags']['fast_compress']) { // fast $info['audio']['encoder_options'] = '-1'; } else { switch ($info['lpac']['max_prediction_order']) { case 20: // simple $info['audio']['encoder_options'] = '-2'; break; case 30: // medium $info['audio']['encoder_options'] = '-3'; break; case 40: // high $info['audio']['encoder_options'] = '-4'; break; case 60: // extrahigh $info['audio']['encoder_options'] = '-5'; break; } } $info['playtime_seconds'] = $info['lpac']['total_samples'] / $info['audio']['sample_rate']; $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds']; return true; }
public function Analyze() { $info =& $this->getid3->info; // http://cui.unige.ch/OSG/info/AudioFormats/ap11.html // http://www.btinternet.com/~AnthonyJ/Atari/programming/avr_format.html // offset type length name comments // --------------------------------------------------------------------- // 0 char 4 ID format ID == "2BIT" // 4 char 8 name sample name (unused space filled with 0) // 12 short 1 mono/stereo 0=mono, -1 (0xFFFF)=stereo // With stereo, samples are alternated, // the first voice is the left : // (LRLRLRLRLRLRLRLRLR...) // 14 short 1 resolution 8, 12 or 16 (bits) // 16 short 1 signed or not 0=unsigned, -1 (0xFFFF)=signed // 18 short 1 loop or not 0=no loop, -1 (0xFFFF)=loop on // 20 short 1 MIDI note 0xFFnn, where 0 <= nn <= 127 // 0xFFFF means "no MIDI note defined" // 22 byte 1 Replay speed Frequence in the Replay software // 0=5.485 Khz, 1=8.084 Khz, 2=10.971 Khz, // 3=16.168 Khz, 4=21.942 Khz, 5=32.336 Khz // 6=43.885 Khz, 7=47.261 Khz // -1 (0xFF)=no defined Frequence // 23 byte 3 sample rate in Hertz // 26 long 1 size in bytes (2 * bytes in stereo) // 30 long 1 loop begin 0 for no loop // 34 long 1 loop size equal to 'size' for no loop // 38 short 2 Reserved, MIDI keyboard split */ // 40 short 2 Reserved, sample compression */ // 42 short 2 Reserved */ // 44 char 20; Additional filename space, used if (name[7] != 0) // 64 byte 64 user data // 128 bytes ? sample data (12 bits samples are coded on 16 bits: // 0000 xxxx xxxx xxxx) // --------------------------------------------------------------------- // Note that all values are in motorola (big-endian) format, and that long is // assumed to be 4 bytes, and short 2 bytes. // When reading the samples, you should handle both signed and unsigned data, // and be prepared to convert 16->8 bit, or mono->stereo if needed. To convert // 8-bit data between signed/unsigned just add 127 to the sample values. // Simularly for 16-bit data you should add 32769 $info['fileformat'] = 'avr'; $this->fseek($info['avdataoffset']); $AVRheader = $this->fread(128); $info['avr']['raw']['magic'] = substr($AVRheader, 0, 4); $magic = '2BIT'; if ($info['avr']['raw']['magic'] != $magic) { $info['error'][] = 'Expecting "' . Utils::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes($info['avr']['raw']['magic']) . '"'; unset($info['fileformat']); unset($info['avr']); return false; } $info['avdataoffset'] += 128; $info['avr']['sample_name'] = rtrim(substr($AVRheader, 4, 8)); $info['avr']['raw']['mono'] = Utils::BigEndian2Int(substr($AVRheader, 12, 2)); $info['avr']['bits_per_sample'] = Utils::BigEndian2Int(substr($AVRheader, 14, 2)); $info['avr']['raw']['signed'] = Utils::BigEndian2Int(substr($AVRheader, 16, 2)); $info['avr']['raw']['loop'] = Utils::BigEndian2Int(substr($AVRheader, 18, 2)); $info['avr']['raw']['midi'] = Utils::BigEndian2Int(substr($AVRheader, 20, 2)); $info['avr']['raw']['replay_freq'] = Utils::BigEndian2Int(substr($AVRheader, 22, 1)); $info['avr']['sample_rate'] = Utils::BigEndian2Int(substr($AVRheader, 23, 3)); $info['avr']['sample_length'] = Utils::BigEndian2Int(substr($AVRheader, 26, 4)); $info['avr']['loop_start'] = Utils::BigEndian2Int(substr($AVRheader, 30, 4)); $info['avr']['loop_end'] = Utils::BigEndian2Int(substr($AVRheader, 34, 4)); $info['avr']['midi_split'] = Utils::BigEndian2Int(substr($AVRheader, 38, 2)); $info['avr']['sample_compression'] = Utils::BigEndian2Int(substr($AVRheader, 40, 2)); $info['avr']['reserved'] = Utils::BigEndian2Int(substr($AVRheader, 42, 2)); $info['avr']['sample_name_extra'] = rtrim(substr($AVRheader, 44, 20)); $info['avr']['comment'] = rtrim(substr($AVRheader, 64, 64)); $info['avr']['flags']['stereo'] = $info['avr']['raw']['mono'] == 0 ? false : true; $info['avr']['flags']['signed'] = $info['avr']['raw']['signed'] == 0 ? false : true; $info['avr']['flags']['loop'] = $info['avr']['raw']['loop'] == 0 ? false : true; $info['avr']['midi_notes'] = array(); if (($info['avr']['raw']['midi'] & 0xff00) != 0xff00) { $info['avr']['midi_notes'][] = ($info['avr']['raw']['midi'] & 0xff00) >> 8; } if (($info['avr']['raw']['midi'] & 0xff) != 0xff) { $info['avr']['midi_notes'][] = $info['avr']['raw']['midi'] & 0xff; } if ($info['avdataend'] - $info['avdataoffset'] != $info['avr']['sample_length'] * ($info['avr']['bits_per_sample'] == 8 ? 1 : 2)) { $info['warning'][] = 'Probable truncated file: expecting ' . $info['avr']['sample_length'] * ($info['avr']['bits_per_sample'] == 8 ? 1 : 2) . ' bytes of audio data, found ' . ($info['avdataend'] - $info['avdataoffset']); } $info['audio']['dataformat'] = 'avr'; $info['audio']['lossless'] = true; $info['audio']['bitrate_mode'] = 'cbr'; $info['audio']['bits_per_sample'] = $info['avr']['bits_per_sample']; $info['audio']['sample_rate'] = $info['avr']['sample_rate']; $info['audio']['channels'] = $info['avr']['flags']['stereo'] ? 2 : 1; $info['playtime_seconds'] = $info['avr']['sample_length'] / $info['audio']['channels'] / $info['avr']['sample_rate']; $info['audio']['bitrate'] = $info['avr']['sample_length'] * ($info['avr']['bits_per_sample'] == 8 ? 8 : 16) / $info['playtime_seconds']; return true; }
public function Analyze() { $info =& $this->getid3->info; $this->fseek($info['avdataoffset']); $SZIPHeader = $this->fread(6); if (substr($SZIPHeader, 0, 4) != "SZ\n") { $info['error'][] = 'Expecting "53 5A 0A 04" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes(substr($SZIPHeader, 0, 4)) . '"'; return false; } $info['fileformat'] = 'szip'; $info['szip']['major_version'] = Utils::BigEndian2Int(substr($SZIPHeader, 4, 1)); $info['szip']['minor_version'] = Utils::BigEndian2Int(substr($SZIPHeader, 5, 1)); $info['error'][] = 'SZIP parsing not enabled in this version of getID3() [' . $this->getid3->version() . ']'; return false; while (!$this->feof()) { $NextBlockID = $this->fread(2); switch ($NextBlockID) { case 'SZ': // Note that szip files can be concatenated, this has the same effect as // concatenating the files. this also means that global header blocks // might be present between directory/data blocks. $this->fseek(4, SEEK_CUR); break; case 'BH': $BHheaderbytes = Utils::BigEndian2Int($this->fread(3)); $BHheaderdata = $this->fread($BHheaderbytes); $BHheaderoffset = 0; while (strpos($BHheaderdata, "", $BHheaderoffset) > 0) { //filename as \0 terminated string (empty string indicates end) //owner as \0 terminated string (empty is same as last file) //group as \0 terminated string (empty is same as last file) //3 byte filelength in this block //2 byte access flags //4 byte creation time (like in unix) //4 byte modification time (like in unix) //4 byte access time (like in unix) $BHdataArray['filename'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "")); $BHheaderoffset += strlen($BHdataArray['filename']) + 1; $BHdataArray['owner'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "")); $BHheaderoffset += strlen($BHdataArray['owner']) + 1; $BHdataArray['group'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "")); $BHheaderoffset += strlen($BHdataArray['group']) + 1; $BHdataArray['filelength'] = Utils::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 3)); $BHheaderoffset += 3; $BHdataArray['access_flags'] = Utils::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 2)); $BHheaderoffset += 2; $BHdataArray['creation_time'] = Utils::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4)); $BHheaderoffset += 4; $BHdataArray['modification_time'] = Utils::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4)); $BHheaderoffset += 4; $BHdataArray['access_time'] = Utils::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4)); $BHheaderoffset += 4; $info['szip']['BH'][] = $BHdataArray; } break; default: break 2; } } return true; }
public function Analyze() { $info =& $this->getid3->info; // shortcut $info['midi']['raw'] = array(); $thisfile_midi =& $info['midi']; $thisfile_midi_raw =& $thisfile_midi['raw']; $info['fileformat'] = 'midi'; $info['audio']['dataformat'] = 'midi'; $this->fseek($info['avdataoffset']); $MIDIdata = $this->fread($this->getid3->fread_buffer_size()); $offset = 0; $MIDIheaderID = substr($MIDIdata, $offset, 4); // 'MThd' if ($MIDIheaderID !== self::MAGIC_MTHD) { $info['error'][] = 'Expecting "' . Utils::PrintHexBytes(self::MAGIC_MTHD) . '" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes($MIDIheaderID) . '"'; unset($info['fileformat']); return false; } $offset += 4; $thisfile_midi_raw['headersize'] = Utils::BigEndian2Int(substr($MIDIdata, $offset, 4)); $offset += 4; $thisfile_midi_raw['fileformat'] = Utils::BigEndian2Int(substr($MIDIdata, $offset, 2)); $offset += 2; $thisfile_midi_raw['tracks'] = Utils::BigEndian2Int(substr($MIDIdata, $offset, 2)); $offset += 2; $thisfile_midi_raw['ticksperqnote'] = Utils::BigEndian2Int(substr($MIDIdata, $offset, 2)); $offset += 2; for ($i = 0; $i < $thisfile_midi_raw['tracks']; $i++) { while (strlen($MIDIdata) - $offset < 8) { if ($buffer = $this->fread($this->getid3->fread_buffer_size())) { $MIDIdata .= $buffer; } else { $info['warning'][] = 'only processed ' . ($i - 1) . ' of ' . $thisfile_midi_raw['tracks'] . ' tracks'; $info['error'][] = 'Unabled to read more file data at ' . $this->ftell() . ' (trying to seek to : ' . $offset . '), was expecting at least 8 more bytes'; return false; } } $trackID = substr($MIDIdata, $offset, 4); $offset += 4; if ($trackID === self::MAGIC_MTRK) { $tracksize = Utils::BigEndian2Int(substr($MIDIdata, $offset, 4)); $offset += 4; //$thisfile_midi['tracks'][$i]['size'] = $tracksize; $trackdataarray[$i] = substr($MIDIdata, $offset, $tracksize); $offset += $tracksize; } else { $info['error'][] = 'Expecting "' . Utils::PrintHexBytes(self::MAGIC_MTRK) . '" at ' . ($offset - 4) . ', found "' . Utils::PrintHexBytes($trackID) . '" instead'; return false; } } if (!isset($trackdataarray) || !is_array($trackdataarray)) { $info['error'][] = 'Cannot find MIDI track information'; unset($thisfile_midi); unset($info['fileformat']); return false; } if ($this->scanwholefile) { // this can take quite a long time, so have the option to bypass it if speed is very important $thisfile_midi['totalticks'] = 0; $info['playtime_seconds'] = 0; $CurrentMicroSecondsPerBeat = 500000; // 120 beats per minute; 60,000,000 microseconds per minute -> 500,000 microseconds per beat $CurrentBeatsPerMinute = 120; // 120 beats per minute; 60,000,000 microseconds per minute -> 500,000 microseconds per beat $MicroSecondsPerQuarterNoteAfter = array(); foreach ($trackdataarray as $tracknumber => $trackdata) { $eventsoffset = 0; $LastIssuedMIDIcommand = 0; $LastIssuedMIDIchannel = 0; $CumulativeDeltaTime = 0; $TicksAtCurrentBPM = 0; while ($eventsoffset < strlen($trackdata)) { $eventid = 0; if (isset($MIDIevents[$tracknumber]) && is_array($MIDIevents[$tracknumber])) { $eventid = count($MIDIevents[$tracknumber]); } $deltatime = 0; for ($i = 0; $i < 4; $i++) { $deltatimebyte = ord(substr($trackdata, $eventsoffset++, 1)); $deltatime = ($deltatime << 7) + ($deltatimebyte & 0x7f); if ($deltatimebyte & 0x80) { // another byte follows } else { break; } } $CumulativeDeltaTime += $deltatime; $TicksAtCurrentBPM += $deltatime; $MIDIevents[$tracknumber][$eventid]['deltatime'] = $deltatime; $MIDI_event_channel = ord(substr($trackdata, $eventsoffset++, 1)); if ($MIDI_event_channel & 0x80) { // OK, normal event - MIDI command has MSB set $LastIssuedMIDIcommand = $MIDI_event_channel >> 4; $LastIssuedMIDIchannel = $MIDI_event_channel & 0xf; } else { // running event - assume last command $eventsoffset--; } $MIDIevents[$tracknumber][$eventid]['eventid'] = $LastIssuedMIDIcommand; $MIDIevents[$tracknumber][$eventid]['channel'] = $LastIssuedMIDIchannel; if ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x8) { // Note off (key is released) $notenumber = ord(substr($trackdata, $eventsoffset++, 1)); $velocity = ord(substr($trackdata, $eventsoffset++, 1)); } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x9) { // Note on (key is pressed) $notenumber = ord(substr($trackdata, $eventsoffset++, 1)); $velocity = ord(substr($trackdata, $eventsoffset++, 1)); } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0xa) { // Key after-touch $notenumber = ord(substr($trackdata, $eventsoffset++, 1)); $velocity = ord(substr($trackdata, $eventsoffset++, 1)); } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0xb) { // Control Change $controllernum = ord(substr($trackdata, $eventsoffset++, 1)); $newvalue = ord(substr($trackdata, $eventsoffset++, 1)); } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0xc) { // Program (patch) change $newprogramnum = ord(substr($trackdata, $eventsoffset++, 1)); $thisfile_midi_raw['track'][$tracknumber]['instrumentid'] = $newprogramnum; if ($tracknumber == 10) { $thisfile_midi_raw['track'][$tracknumber]['instrument'] = $this->GeneralMIDIpercussionLookup($newprogramnum); } else { $thisfile_midi_raw['track'][$tracknumber]['instrument'] = $this->GeneralMIDIinstrumentLookup($newprogramnum); } } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0xd) { // Channel after-touch $channelnumber = ord(substr($trackdata, $eventsoffset++, 1)); } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0xe) { // Pitch wheel change (2000H is normal or no change) $changeLSB = ord(substr($trackdata, $eventsoffset++, 1)); $changeMSB = ord(substr($trackdata, $eventsoffset++, 1)); $pitchwheelchange = ($changeMSB & 0x7f) << 7 & ($changeLSB & 0x7f); } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0xf && $MIDIevents[$tracknumber][$eventid]['channel'] == 0xf) { $METAeventCommand = ord(substr($trackdata, $eventsoffset++, 1)); $METAeventLength = ord(substr($trackdata, $eventsoffset++, 1)); $METAeventData = substr($trackdata, $eventsoffset, $METAeventLength); $eventsoffset += $METAeventLength; switch ($METAeventCommand) { case 0x0: // Set track sequence number $track_sequence_number = Utils::BigEndian2Int(substr($METAeventData, 0, $METAeventLength)); //$thisfile_midi_raw['events'][$tracknumber][$eventid]['seqno'] = $track_sequence_number; break; case 0x1: // Text: generic $text_generic = substr($METAeventData, 0, $METAeventLength); //$thisfile_midi_raw['events'][$tracknumber][$eventid]['text'] = $text_generic; $thisfile_midi['comments']['comment'][] = $text_generic; break; case 0x2: // Text: copyright $text_copyright = substr($METAeventData, 0, $METAeventLength); //$thisfile_midi_raw['events'][$tracknumber][$eventid]['copyright'] = $text_copyright; $thisfile_midi['comments']['copyright'][] = $text_copyright; break; case 0x3: // Text: track name $text_trackname = substr($METAeventData, 0, $METAeventLength); $thisfile_midi_raw['track'][$tracknumber]['name'] = $text_trackname; break; case 0x4: // Text: track instrument name $text_instrument = substr($METAeventData, 0, $METAeventLength); //$thisfile_midi_raw['events'][$tracknumber][$eventid]['instrument'] = $text_instrument; break; case 0x5: // Text: lyrics $text_lyrics = substr($METAeventData, 0, $METAeventLength); //$thisfile_midi_raw['events'][$tracknumber][$eventid]['lyrics'] = $text_lyrics; if (!isset($thisfile_midi['lyrics'])) { $thisfile_midi['lyrics'] = ''; } $thisfile_midi['lyrics'] .= $text_lyrics . "\n"; break; case 0x6: // Text: marker $text_marker = substr($METAeventData, 0, $METAeventLength); //$thisfile_midi_raw['events'][$tracknumber][$eventid]['marker'] = $text_marker; break; case 0x7: // Text: cue point $text_cuepoint = substr($METAeventData, 0, $METAeventLength); //$thisfile_midi_raw['events'][$tracknumber][$eventid]['cuepoint'] = $text_cuepoint; break; case 0x2f: // End Of Track //$thisfile_midi_raw['events'][$tracknumber][$eventid]['EOT'] = $CumulativeDeltaTime; break; case 0x51: // Tempo: microseconds / quarter note $CurrentMicroSecondsPerBeat = Utils::BigEndian2Int(substr($METAeventData, 0, $METAeventLength)); if ($CurrentMicroSecondsPerBeat == 0) { $info['error'][] = 'Corrupt MIDI file: CurrentMicroSecondsPerBeat == zero'; return false; } $thisfile_midi_raw['events'][$tracknumber][$CumulativeDeltaTime]['us_qnote'] = $CurrentMicroSecondsPerBeat; $CurrentBeatsPerMinute = 1000000 / $CurrentMicroSecondsPerBeat * 60; $MicroSecondsPerQuarterNoteAfter[$CumulativeDeltaTime] = $CurrentMicroSecondsPerBeat; $TicksAtCurrentBPM = 0; break; case 0x58: // Time signature $timesig_numerator = Utils::BigEndian2Int($METAeventData[0]); $timesig_denominator = pow(2, Utils::BigEndian2Int($METAeventData[1])); // $02 -> x/4, $03 -> x/8, etc $timesig_32inqnote = Utils::BigEndian2Int($METAeventData[2]); // number of 32nd notes to the quarter note //$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_32inqnote'] = $timesig_32inqnote; //$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_numerator'] = $timesig_numerator; //$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_denominator'] = $timesig_denominator; //$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_text'] = $timesig_numerator.'/'.$timesig_denominator; $thisfile_midi['timesignature'][] = $timesig_numerator . '/' . $timesig_denominator; break; case 0x59: // Keysignature $keysig_sharpsflats = Utils::BigEndian2Int($METAeventData[0]); if ($keysig_sharpsflats & 0x80) { // (-7 -> 7 flats, 0 ->key of C, 7 -> 7 sharps) $keysig_sharpsflats -= 256; } $keysig_majorminor = Utils::BigEndian2Int($METAeventData[1]); // 0 -> major, 1 -> minor $keysigs = array(-7 => 'Cb', -6 => 'Gb', -5 => 'Db', -4 => 'Ab', -3 => 'Eb', -2 => 'Bb', -1 => 'F', 0 => 'C', 1 => 'G', 2 => 'D', 3 => 'A', 4 => 'E', 5 => 'B', 6 => 'F#', 7 => 'C#'); //$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_sharps'] = (($keysig_sharpsflats > 0) ? abs($keysig_sharpsflats) : 0); //$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_flats'] = (($keysig_sharpsflats < 0) ? abs($keysig_sharpsflats) : 0); //$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_minor'] = (bool) $keysig_majorminor; //$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_text'] = $keysigs[$keysig_sharpsflats].' '.($thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_minor'] ? 'minor' : 'major'); // $keysigs[$keysig_sharpsflats] gets an int key (correct) - $keysigs["$keysig_sharpsflats"] gets a string key (incorrect) $thisfile_midi['keysignature'][] = $keysigs[$keysig_sharpsflats] . ' ' . ((bool) $keysig_majorminor ? 'minor' : 'major'); break; case 0x7f: // Sequencer specific information $custom_data = substr($METAeventData, 0, $METAeventLength); break; default: $info['warning'][] = 'Unhandled META Event Command: ' . $METAeventCommand; break; } } else { $info['warning'][] = 'Unhandled MIDI Event ID: ' . $MIDIevents[$tracknumber][$eventid]['eventid'] . ' + Channel ID: ' . $MIDIevents[$tracknumber][$eventid]['channel']; } } if ($tracknumber > 0 || count($trackdataarray) == 1) { $thisfile_midi['totalticks'] = max($thisfile_midi['totalticks'], $CumulativeDeltaTime); } } $previoustickoffset = null; ksort($MicroSecondsPerQuarterNoteAfter); foreach ($MicroSecondsPerQuarterNoteAfter as $tickoffset => $microsecondsperbeat) { if (is_null($previoustickoffset)) { $prevmicrosecondsperbeat = $microsecondsperbeat; $previoustickoffset = $tickoffset; continue; } if ($thisfile_midi['totalticks'] > $tickoffset) { if ($thisfile_midi_raw['ticksperqnote'] == 0) { $info['error'][] = 'Corrupt MIDI file: ticksperqnote == zero'; return false; } $info['playtime_seconds'] += ($tickoffset - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote'] * ($prevmicrosecondsperbeat / 1000000); $prevmicrosecondsperbeat = $microsecondsperbeat; $previoustickoffset = $tickoffset; } } if ($thisfile_midi['totalticks'] > $previoustickoffset) { if ($thisfile_midi_raw['ticksperqnote'] == 0) { $info['error'][] = 'Corrupt MIDI file: ticksperqnote == zero'; return false; } $info['playtime_seconds'] += ($thisfile_midi['totalticks'] - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote'] * ($microsecondsperbeat / 1000000); } } if (!empty($info['playtime_seconds'])) { $info['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds']; } if (!empty($thisfile_midi['lyrics'])) { $thisfile_midi['comments']['lyrics'][] = $thisfile_midi['lyrics']; } return true; }
public function ParseMPCsv8() { // this is SV8 // http://trac.musepack.net/trac/wiki/SV8Specification $info =& $this->getid3->info; $thisfile_mpc_header =& $info['mpc']['header']; $keyNameSize = 2; $maxHandledPacketLength = 9; // specs say: "n*8; 0 < n < 10" $offset = $this->ftell(); while ($offset < $info['avdataend']) { $thisPacket = array(); $thisPacket['offset'] = $offset; $packet_offset = 0; // Size is a variable-size field, could be 1-4 bytes (possibly more?) // read enough data in and figure out the exact size later $MPCheaderData = $this->fread($keyNameSize + $maxHandledPacketLength); $packet_offset += $keyNameSize; $thisPacket['key'] = substr($MPCheaderData, 0, $keyNameSize); $thisPacket['key_name'] = $this->MPCsv8PacketName($thisPacket['key']); if ($thisPacket['key'] == $thisPacket['key_name']) { $info['error'][] = 'Found unexpected key value "' . $thisPacket['key'] . '" at offset ' . $thisPacket['offset']; return false; } $packetLength = 0; $thisPacket['packet_size'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $keyNameSize), $packetLength); // includes keyname and packet_size field if ($thisPacket['packet_size'] === false) { $info['error'][] = 'Did not find expected packet length within ' . $maxHandledPacketLength . ' bytes at offset ' . ($thisPacket['offset'] + $keyNameSize); return false; } $packet_offset += $packetLength; $offset += $thisPacket['packet_size']; switch ($thisPacket['key']) { case 'SH': // Stream Header $moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength; if ($moreBytesToRead > 0) { $MPCheaderData .= $this->fread($moreBytesToRead); } $thisPacket['crc'] = Utils::BigEndian2Int(substr($MPCheaderData, $packet_offset, 4)); $packet_offset += 4; $thisPacket['stream_version'] = Utils::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1)); $packet_offset += 1; $packetLength = 0; $thisPacket['sample_count'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength); $packet_offset += $packetLength; $packetLength = 0; $thisPacket['beginning_silence'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength); $packet_offset += $packetLength; $otherUsefulData = Utils::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2)); $packet_offset += 2; $thisPacket['sample_frequency_raw'] = ($otherUsefulData & 0xe000) >> 13; $thisPacket['max_bands_used'] = ($otherUsefulData & 0x1f00) >> 8; $thisPacket['channels'] = (($otherUsefulData & 0xf0) >> 4) + 1; $thisPacket['ms_used'] = (bool) (($otherUsefulData & 0x8) >> 3); $thisPacket['audio_block_frames'] = ($otherUsefulData & 0x7) >> 0; $thisPacket['sample_frequency'] = $this->MPCfrequencyLookup($thisPacket['sample_frequency_raw']); $thisfile_mpc_header['mid_side_stereo'] = $thisPacket['ms_used']; $thisfile_mpc_header['sample_rate'] = $thisPacket['sample_frequency']; $thisfile_mpc_header['samples'] = $thisPacket['sample_count']; $thisfile_mpc_header['stream_version_major'] = $thisPacket['stream_version']; $info['audio']['channels'] = $thisPacket['channels']; $info['audio']['sample_rate'] = $thisPacket['sample_frequency']; $info['playtime_seconds'] = $thisPacket['sample_count'] / $thisPacket['sample_frequency']; $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds']; break; case 'RG': // Replay Gain $moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength; if ($moreBytesToRead > 0) { $MPCheaderData .= $this->fread($moreBytesToRead); } $thisPacket['replaygain_version'] = Utils::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1)); $packet_offset += 1; $thisPacket['replaygain_title_gain'] = Utils::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2)); $packet_offset += 2; $thisPacket['replaygain_title_peak'] = Utils::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2)); $packet_offset += 2; $thisPacket['replaygain_album_gain'] = Utils::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2)); $packet_offset += 2; $thisPacket['replaygain_album_peak'] = Utils::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2)); $packet_offset += 2; if ($thisPacket['replaygain_title_gain']) { $info['replay_gain']['title']['gain'] = $thisPacket['replaygain_title_gain']; } if ($thisPacket['replaygain_title_peak']) { $info['replay_gain']['title']['peak'] = $thisPacket['replaygain_title_peak']; } if ($thisPacket['replaygain_album_gain']) { $info['replay_gain']['album']['gain'] = $thisPacket['replaygain_album_gain']; } if ($thisPacket['replaygain_album_peak']) { $info['replay_gain']['album']['peak'] = $thisPacket['replaygain_album_peak']; } break; case 'EI': // Encoder Info $moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength; if ($moreBytesToRead > 0) { $MPCheaderData .= $this->fread($moreBytesToRead); } $profile_pns = Utils::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1)); $packet_offset += 1; $quality_int = ($profile_pns & 0xf0) >> 4; $quality_dec = ($profile_pns & 0xe) >> 3; $thisPacket['quality'] = (double) $quality_int + $quality_dec / 8; $thisPacket['pns_tool'] = (bool) (($profile_pns & 0x1) >> 0); $thisPacket['version_major'] = Utils::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1)); $packet_offset += 1; $thisPacket['version_minor'] = Utils::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1)); $packet_offset += 1; $thisPacket['version_build'] = Utils::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1)); $packet_offset += 1; $thisPacket['version'] = $thisPacket['version_major'] . '.' . $thisPacket['version_minor'] . '.' . $thisPacket['version_build']; $info['audio']['encoder'] = 'MPC v' . $thisPacket['version'] . ' (' . ($thisPacket['version_minor'] % 2 ? 'unstable' : 'stable') . ')'; $thisfile_mpc_header['encoder_version'] = $info['audio']['encoder']; //$thisfile_mpc_header['quality'] = (float) ($thisPacket['quality'] / 1.5875); // values can range from 0.000 to 15.875, mapped to qualities of 0.0 to 10.0 $thisfile_mpc_header['quality'] = (double) ($thisPacket['quality'] - 5); // values can range from 0.000 to 15.875, of which 0..4 are "reserved/experimental", and 5..15 are mapped to qualities of 0.0 to 10.0 break; case 'SO': // Seek Table Offset $packetLength = 0; $thisPacket['seek_table_offset'] = $thisPacket['offset'] + $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength); $packet_offset += $packetLength; break; case 'ST': // Seek Table // Seek Table case 'SE': // Stream End // Stream End case 'AP': // Audio Data // nothing useful here, just skip this packet $thisPacket = array(); break; default: $info['error'][] = 'Found unhandled key type "' . $thisPacket['key'] . '" at offset ' . $thisPacket['offset']; return false; break; } if (!empty($thisPacket)) { $info['mpc']['packets'][] = $thisPacket; } $this->fseek($offset); } $thisfile_mpc_header['size'] = $offset; return true; }
public function getAACADTSheaderFilepointer($MaxFramesToScan = 1000000, $ReturnExtendedInfo = false) { $info =& $this->getid3->info; // based loosely on code from AACfile by Jurgen Faul <jfaulØgmx.de> // http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html // http://faac.sourceforge.net/wiki/index.php?page=ADTS // dead link // http://wiki.multimedia.cx/index.php?title=ADTS // * ADTS Fixed Header: these don't change from frame to frame // syncword 12 always: '111111111111' // ID 1 0: MPEG-4, 1: MPEG-2 // MPEG layer 2 If you send AAC in MPEG-TS, set to 0 // protection_absent 1 0: CRC present; 1: no CRC // profile 2 0: AAC Main; 1: AAC LC (Low Complexity); 2: AAC SSR (Scalable Sample Rate); 3: AAC LTP (Long Term Prediction) // sampling_frequency_index 4 15 not allowed // private_bit 1 usually 0 // channel_configuration 3 // original/copy 1 0: original; 1: copy // home 1 usually 0 // emphasis 2 only if ID == 0 (ie MPEG-4) // not present in some documentation? // * ADTS Variable Header: these can change from frame to frame // copyright_identification_bit 1 // copyright_identification_start 1 // aac_frame_length 13 length of the frame including header (in bytes) // adts_buffer_fullness 11 0x7FF indicates VBR // no_raw_data_blocks_in_frame 2 // * ADTS Error check // crc_check 16 only if protection_absent == 0 $byteoffset = $info['avdataoffset']; $framenumber = 0; // Init bit pattern array static $decbin = array(); // Populate $bindec for ($i = 0; $i < 256; $i++) { $decbin[chr($i)] = str_pad(decbin($i), 8, '0', STR_PAD_LEFT); } // used to calculate bitrate below $BitrateCache = array(); while (true) { // breaks out when end-of-file encountered, or invalid data found, // or MaxFramesToScan frames have been scanned if (!Utils::intValueSupported($byteoffset)) { $info['warning'][] = 'Unable to parse AAC file beyond ' . $this->ftell() . ' (PHP does not support file operations beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB)'; return false; } $this->fseek($byteoffset); // First get substring $substring = $this->fread(9); // header is 7 bytes (or 9 if CRC is present) $substringlength = strlen($substring); if ($substringlength != 9) { $info['error'][] = 'Failed to read 7 bytes at offset ' . ($this->ftell() - $substringlength) . ' (only read ' . $substringlength . ' bytes)'; return false; } // this would be easier with 64-bit math, but split it up to allow for 32-bit: $header1 = Utils::BigEndian2Int(substr($substring, 0, 2)); $header2 = Utils::BigEndian2Int(substr($substring, 2, 4)); $header3 = Utils::BigEndian2Int(substr($substring, 6, 1)); $info['aac']['header']['raw']['syncword'] = ($header1 & 0xfff0) >> 4; if ($info['aac']['header']['raw']['syncword'] != 0xfff) { $info['error'][] = 'Synch pattern (0x0FFF) not found at offset ' . ($this->ftell() - $substringlength) . ' (found 0x0' . strtoupper(dechex($info['aac']['header']['raw']['syncword'])) . ' instead)'; //if ($info['fileformat'] == 'aac') { // return true; //} unset($info['aac']); return false; } // Gather info for first frame only - this takes time to do 1000 times! if ($framenumber == 0) { $info['aac']['header_type'] = 'ADTS'; $info['fileformat'] = 'aac'; $info['audio']['dataformat'] = 'aac'; $info['aac']['header']['raw']['mpeg_version'] = ($header1 & 0x8) >> 3; $info['aac']['header']['raw']['mpeg_layer'] = ($header1 & 0x6) >> 1; $info['aac']['header']['raw']['protection_absent'] = ($header1 & 0x1) >> 0; $info['aac']['header']['raw']['profile_code'] = ($header2 & 0xc0000000) >> 30; $info['aac']['header']['raw']['sample_rate_code'] = ($header2 & 0x3c000000) >> 26; $info['aac']['header']['raw']['private_stream'] = ($header2 & 0x2000000) >> 25; $info['aac']['header']['raw']['channels_code'] = ($header2 & 0x1c00000) >> 22; $info['aac']['header']['raw']['original'] = ($header2 & 0x200000) >> 21; $info['aac']['header']['raw']['home'] = ($header2 & 0x100000) >> 20; $info['aac']['header']['raw']['copyright_stream'] = ($header2 & 0x80000) >> 19; $info['aac']['header']['raw']['copyright_start'] = ($header2 & 0x40000) >> 18; $info['aac']['header']['raw']['frame_length'] = ($header2 & 0x3ffe0) >> 5; $info['aac']['header']['mpeg_version'] = $info['aac']['header']['raw']['mpeg_version'] ? 2 : 4; $info['aac']['header']['crc_present'] = $info['aac']['header']['raw']['protection_absent'] ? false : true; $info['aac']['header']['profile'] = self::AACprofileLookup($info['aac']['header']['raw']['profile_code'], $info['aac']['header']['mpeg_version']); $info['aac']['header']['sample_frequency'] = self::AACsampleRateLookup($info['aac']['header']['raw']['sample_rate_code']); $info['aac']['header']['private'] = (bool) $info['aac']['header']['raw']['private_stream']; $info['aac']['header']['original'] = (bool) $info['aac']['header']['raw']['original']; $info['aac']['header']['home'] = (bool) $info['aac']['header']['raw']['home']; $info['aac']['header']['channels'] = $info['aac']['header']['raw']['channels_code'] == 7 ? 8 : $info['aac']['header']['raw']['channels_code']; if ($ReturnExtendedInfo) { $info['aac'][$framenumber]['copyright_id_bit'] = (bool) $info['aac']['header']['raw']['copyright_stream']; $info['aac'][$framenumber]['copyright_id_start'] = (bool) $info['aac']['header']['raw']['copyright_start']; } if ($info['aac']['header']['raw']['mpeg_layer'] != 0) { $info['warning'][] = 'Layer error - expected "0", found "' . $info['aac']['header']['raw']['mpeg_layer'] . '" instead'; } if ($info['aac']['header']['sample_frequency'] == 0) { $info['error'][] = 'Corrupt AAC file: sample_frequency == zero'; return false; } $info['audio']['sample_rate'] = $info['aac']['header']['sample_frequency']; $info['audio']['channels'] = $info['aac']['header']['channels']; } $FrameLength = ($header2 & 0x3ffe0) >> 5; if (!isset($BitrateCache[$FrameLength])) { $BitrateCache[$FrameLength] = $info['aac']['header']['sample_frequency'] / 1024 * $FrameLength * 8; } Utils::safe_inc($info['aac']['bitrate_distribution'][$BitrateCache[$FrameLength]], 1); $info['aac'][$framenumber]['aac_frame_length'] = $FrameLength; $info['aac'][$framenumber]['adts_buffer_fullness'] = ($header2 & 0x1f) << 6 & ($header3 & 0xfc) >> 2; if ($info['aac'][$framenumber]['adts_buffer_fullness'] == 0x7ff) { $info['audio']['bitrate_mode'] = 'vbr'; } else { $info['audio']['bitrate_mode'] = 'cbr'; } $info['aac'][$framenumber]['num_raw_data_blocks'] = ($header3 & 0x3) >> 0; if ($info['aac']['header']['crc_present']) { //$info['aac'][$framenumber]['crc'] = Utils::BigEndian2Int(substr($substring, 7, 2); } if (!$ReturnExtendedInfo) { unset($info['aac'][$framenumber]); } /* $rounded_precision = 5000; $info['aac']['bitrate_distribution_rounded'] = array(); foreach ($info['aac']['bitrate_distribution'] as $bitrate => $count) { $rounded_bitrate = round($bitrate / $rounded_precision) * $rounded_precision; Utils::safe_inc($info['aac']['bitrate_distribution_rounded'][$rounded_bitrate], $count); } ksort($info['aac']['bitrate_distribution_rounded']); */ $byteoffset += $FrameLength; if (++$framenumber < $MaxFramesToScan && $byteoffset + 10 < $info['avdataend']) { // keep scanning } else { $info['aac']['frames'] = $framenumber; $info['playtime_seconds'] = $info['avdataend'] / $byteoffset * ($framenumber * 1024 / $info['aac']['header']['sample_frequency']); // (1 / % of file scanned) * (samples / (samples/sec)) = seconds if ($info['playtime_seconds'] == 0) { $info['error'][] = 'Corrupt AAC file: playtime_seconds == zero'; return false; } $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds']; ksort($info['aac']['bitrate_distribution']); $info['audio']['encoder_options'] = $info['aac']['header_type'] . ' ' . $info['aac']['header']['profile']; return true; } } // should never get here. }
public function Analyze() { $info =& $this->getid3->info; $this->fseek($info['avdataoffset']); $TSheader = $this->fread(19); $magic = "G"; if (substr($TSheader, 0, 1) != $magic) { $info['error'][] = 'Expecting "' . Utils::PrintHexBytes($magic) . '" at ' . $info['avdataoffset'] . ', found ' . Utils::PrintHexBytes(substr($TSheader, 0, 1)) . ' instead.'; return false; } $info['fileformat'] = 'ts'; // http://en.wikipedia.org/wiki/.ts $offset = 0; $info['ts']['packet']['sync'] = Utils::BigEndian2Int(substr($TSheader, $offset, 1)); $offset += 1; $pid_flags_raw = Utils::BigEndian2Int(substr($TSheader, $offset, 2)); $offset += 2; $SAC_raw = Utils::BigEndian2Int(substr($TSheader, $offset, 1)); $offset += 1; $info['ts']['packet']['flags']['transport_error_indicator'] = (bool) ($pid_flags_raw & 0x8000); // Set by demodulator if can't correct errors in the stream, to tell the demultiplexer that the packet has an uncorrectable error $info['ts']['packet']['flags']['payload_unit_start_indicator'] = (bool) ($pid_flags_raw & 0x4000); // 1 means start of PES data or PSI otherwise zero only. $info['ts']['packet']['flags']['transport_high_priority'] = (bool) ($pid_flags_raw & 0x2000); // 1 means higher priority than other packets with the same PID. $info['ts']['packet']['packet_id'] = ($pid_flags_raw & 0x1fff) >> 0; $info['ts']['packet']['raw']['scrambling_control'] = ($SAC_raw & 0xc0) >> 6; $info['ts']['packet']['flags']['adaption_field_exists'] = (bool) ($SAC_raw & 0x20); $info['ts']['packet']['flags']['payload_exists'] = (bool) ($SAC_raw & 0x10); $info['ts']['packet']['continuity_counter'] = ($SAC_raw & 0xf) >> 0; // Incremented only when a payload is present $info['ts']['packet']['scrambling_control'] = $this->TSscramblingControlLookup($info['ts']['packet']['raw']['scrambling_control']); if ($info['ts']['packet']['flags']['adaption_field_exists']) { $AdaptionField_raw = Utils::BigEndian2Int(substr($TSheader, $offset, 2)); $offset += 2; $info['ts']['packet']['adaption']['field_length'] = ($AdaptionField_raw & 0xff00) >> 8; // Number of bytes in the adaptation field immediately following this byte $info['ts']['packet']['adaption']['flags']['discontinuity'] = (bool) ($AdaptionField_raw & 0x80); // Set to 1 if current TS packet is in a discontinuity state with respect to either the continuity counter or the program clock reference $info['ts']['packet']['adaption']['flags']['random_access'] = (bool) ($AdaptionField_raw & 0x40); // Set to 1 if the PES packet in this TS packet starts a video/audio sequence $info['ts']['packet']['adaption']['flags']['high_priority'] = (bool) ($AdaptionField_raw & 0x20); // 1 = higher priority $info['ts']['packet']['adaption']['flags']['pcr'] = (bool) ($AdaptionField_raw & 0x10); // 1 means adaptation field does contain a PCR field $info['ts']['packet']['adaption']['flags']['opcr'] = (bool) ($AdaptionField_raw & 0x8); // 1 means adaptation field does contain an OPCR field $info['ts']['packet']['adaption']['flags']['splice_point'] = (bool) ($AdaptionField_raw & 0x4); // 1 means presence of splice countdown field in adaptation field $info['ts']['packet']['adaption']['flags']['private_data'] = (bool) ($AdaptionField_raw & 0x2); // 1 means presence of private data bytes in adaptation field $info['ts']['packet']['adaption']['flags']['extension'] = (bool) ($AdaptionField_raw & 0x1); // 1 means presence of adaptation field extension if ($info['ts']['packet']['adaption']['flags']['pcr']) { $info['ts']['packet']['adaption']['raw']['pcr'] = Utils::BigEndian2Int(substr($TSheader, $offset, 6)); $offset += 6; } if ($info['ts']['packet']['adaption']['flags']['opcr']) { $info['ts']['packet']['adaption']['raw']['opcr'] = Utils::BigEndian2Int(substr($TSheader, $offset, 6)); $offset += 6; } } $info['error'][] = 'MPEG Transport Stream (.ts) parsing not enabled in this version of getID3() [' . $this->getid3->version() . ']'; return false; }
public function Analyze() { $info =& $this->getid3->info; $info['fileformat'] = 'swf'; $info['video']['dataformat'] = 'swf'; // http://www.openswf.org/spec/SWFfileformat.html $this->fseek($info['avdataoffset']); $SWFfileData = $this->fread($info['avdataend'] - $info['avdataoffset']); // 8 + 2 + 2 + max(9) bytes NOT including Frame_Size RECT data $info['swf']['header']['signature'] = substr($SWFfileData, 0, 3); switch ($info['swf']['header']['signature']) { case 'FWS': $info['swf']['header']['compressed'] = false; break; case 'CWS': $info['swf']['header']['compressed'] = true; break; default: $info['error'][] = 'Expecting "FWS" or "CWS" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes($info['swf']['header']['signature']) . '"'; unset($info['swf']); unset($info['fileformat']); return false; break; } $info['swf']['header']['version'] = Utils::LittleEndian2Int(substr($SWFfileData, 3, 1)); $info['swf']['header']['length'] = Utils::LittleEndian2Int(substr($SWFfileData, 4, 4)); if ($info['swf']['header']['compressed']) { $SWFHead = substr($SWFfileData, 0, 8); $SWFfileData = substr($SWFfileData, 8); if ($decompressed = @gzuncompress($SWFfileData)) { $SWFfileData = $SWFHead . $decompressed; } else { $info['error'][] = 'Error decompressing compressed SWF data (' . strlen($SWFfileData) . ' bytes compressed, should be ' . ($info['swf']['header']['length'] - 8) . ' bytes uncompressed)'; return false; } } $FrameSizeBitsPerValue = (ord(substr($SWFfileData, 8, 1)) & 0xf8) >> 3; $FrameSizeDataLength = ceil((5 + 4 * $FrameSizeBitsPerValue) / 8); $FrameSizeDataString = str_pad(decbin(ord(substr($SWFfileData, 8, 1)) & 0x7), 3, '0', STR_PAD_LEFT); for ($i = 1; $i < $FrameSizeDataLength; $i++) { $FrameSizeDataString .= str_pad(decbin(ord(substr($SWFfileData, 8 + $i, 1))), 8, '0', STR_PAD_LEFT); } list($X1, $X2, $Y1, $Y2) = explode("\n", wordwrap($FrameSizeDataString, $FrameSizeBitsPerValue, "\n", 1)); $info['swf']['header']['frame_width'] = Utils::Bin2Dec($X2); $info['swf']['header']['frame_height'] = Utils::Bin2Dec($Y2); // http://www-lehre.informatik.uni-osnabrueck.de/~fbstark/diplom/docs/swf/Flash_Uncovered.htm // Next in the header is the frame rate, which is kind of weird. // It is supposed to be stored as a 16bit integer, but the first byte // (or last depending on how you look at it) is completely ignored. // Example: 0x000C -> 0x0C -> 12 So the frame rate is 12 fps. // Byte at (8 + $FrameSizeDataLength) is always zero and ignored $info['swf']['header']['frame_rate'] = Utils::LittleEndian2Int(substr($SWFfileData, 9 + $FrameSizeDataLength, 1)); $info['swf']['header']['frame_count'] = Utils::LittleEndian2Int(substr($SWFfileData, 10 + $FrameSizeDataLength, 2)); $info['video']['frame_rate'] = $info['swf']['header']['frame_rate']; $info['video']['resolution_x'] = intval(round($info['swf']['header']['frame_width'] / 20)); $info['video']['resolution_y'] = intval(round($info['swf']['header']['frame_height'] / 20)); $info['video']['pixel_aspect_ratio'] = (double) 1; if ($info['swf']['header']['frame_count'] > 0 && $info['swf']['header']['frame_rate'] > 0) { $info['playtime_seconds'] = $info['swf']['header']['frame_count'] / $info['swf']['header']['frame_rate']; } //echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>'; // SWF tags $CurrentOffset = 12 + $FrameSizeDataLength; $SWFdataLength = strlen($SWFfileData); while ($CurrentOffset < $SWFdataLength) { //echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>'; $TagIDTagLength = Utils::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 2)); $TagID = ($TagIDTagLength & 0xfffc) >> 6; $TagLength = $TagIDTagLength & 0x3f; $CurrentOffset += 2; if ($TagLength == 0x3f) { $TagLength = Utils::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 4)); $CurrentOffset += 4; } unset($TagData); $TagData['offset'] = $CurrentOffset; $TagData['size'] = $TagLength; $TagData['id'] = $TagID; $TagData['data'] = substr($SWFfileData, $CurrentOffset, $TagLength); switch ($TagID) { case 0: // end of movie break 2; case 9: // Set background color //$info['swf']['tags'][] = $TagData; $info['swf']['bgcolor'] = strtoupper(str_pad(dechex(Utils::BigEndian2Int($TagData['data'])), 6, '0', STR_PAD_LEFT)); break; default: if ($this->ReturnAllTagData) { $info['swf']['tags'][] = $TagData; } break; } $CurrentOffset += $TagLength; } return true; }
private static function EBML2Int($EBMLstring) { // http://matroska.org/specs/ // Element ID coded with an UTF-8 like system: // 1xxx xxxx - Class A IDs (2^7 -2 possible values) (base 0x8X) // 01xx xxxx xxxx xxxx - Class B IDs (2^14-2 possible values) (base 0x4X 0xXX) // 001x xxxx xxxx xxxx xxxx xxxx - Class C IDs (2^21-2 possible values) (base 0x2X 0xXX 0xXX) // 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx - Class D IDs (2^28-2 possible values) (base 0x1X 0xXX 0xXX 0xXX) // Values with all x at 0 and 1 are reserved (hence the -2). // Data size, in octets, is also coded with an UTF-8 like system : // 1xxx xxxx - value 0 to 2^7-2 // 01xx xxxx xxxx xxxx - value 0 to 2^14-2 // 001x xxxx xxxx xxxx xxxx xxxx - value 0 to 2^21-2 // 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^28-2 // 0000 1xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^35-2 // 0000 01xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^42-2 // 0000 001x xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^49-2 // 0000 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^56-2 $first_byte_int = ord($EBMLstring[0]); if (0x80 & $first_byte_int) { $EBMLstring[0] = chr($first_byte_int & 0x7f); } elseif (0x40 & $first_byte_int) { $EBMLstring[0] = chr($first_byte_int & 0x3f); } elseif (0x20 & $first_byte_int) { $EBMLstring[0] = chr($first_byte_int & 0x1f); } elseif (0x10 & $first_byte_int) { $EBMLstring[0] = chr($first_byte_int & 0xf); } elseif (0x8 & $first_byte_int) { $EBMLstring[0] = chr($first_byte_int & 0x7); } elseif (0x4 & $first_byte_int) { $EBMLstring[0] = chr($first_byte_int & 0x3); } elseif (0x2 & $first_byte_int) { $EBMLstring[0] = chr($first_byte_int & 0x1); } elseif (0x1 & $first_byte_int) { $EBMLstring[0] = chr($first_byte_int & 0x0); } return Utils::BigEndian2Int($EBMLstring); }