Пример #1
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $info['fileformat'] = 'rar';
     if ($this->option_use_rar_extension === true) {
         if (function_exists('rar_open')) {
             if ($rp = rar_open($info['filenamepath'])) {
                 $info['rar']['files'] = array();
                 $entries = rar_list($rp);
                 foreach ($entries as $entry) {
                     $info['rar']['files'] = Utils::array_merge_clobber($info['rar']['files'], Utils::CreateDeepArray($entry->getName(), '/', $entry->getUnpackedSize()));
                 }
                 rar_close($rp);
                 return true;
             } else {
                 $info['error'][] = 'failed to rar_open(' . $info['filename'] . ')';
             }
         } else {
             $info['error'][] = 'RAR support does not appear to be available in this PHP installation';
         }
     } else {
         $info['error'][] = 'PHP-RAR processing has been disabled (set ' . get_class($this) . '->option_use_rar_extension=true to enable)';
     }
     return false;
 }
Пример #2
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $this->fseek($info['avdataoffset']);
     $EXEheader = $this->fread(28);
     $magic = 'MZ';
     if (substr($EXEheader, 0, 2) != $magic) {
         $info['error'][] = 'Expecting "' . Utils::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes(substr($EXEheader, 0, 2)) . '"';
         return false;
     }
     $info['fileformat'] = 'exe';
     $info['exe']['mz']['magic'] = 'MZ';
     $info['exe']['mz']['raw']['last_page_size'] = Utils::LittleEndian2Int(substr($EXEheader, 2, 2));
     $info['exe']['mz']['raw']['page_count'] = Utils::LittleEndian2Int(substr($EXEheader, 4, 2));
     $info['exe']['mz']['raw']['relocation_count'] = Utils::LittleEndian2Int(substr($EXEheader, 6, 2));
     $info['exe']['mz']['raw']['header_paragraphs'] = Utils::LittleEndian2Int(substr($EXEheader, 8, 2));
     $info['exe']['mz']['raw']['min_memory_paragraphs'] = Utils::LittleEndian2Int(substr($EXEheader, 10, 2));
     $info['exe']['mz']['raw']['max_memory_paragraphs'] = Utils::LittleEndian2Int(substr($EXEheader, 12, 2));
     $info['exe']['mz']['raw']['initial_ss'] = Utils::LittleEndian2Int(substr($EXEheader, 14, 2));
     $info['exe']['mz']['raw']['initial_sp'] = Utils::LittleEndian2Int(substr($EXEheader, 16, 2));
     $info['exe']['mz']['raw']['checksum'] = Utils::LittleEndian2Int(substr($EXEheader, 18, 2));
     $info['exe']['mz']['raw']['cs_ip'] = Utils::LittleEndian2Int(substr($EXEheader, 20, 4));
     $info['exe']['mz']['raw']['relocation_table_offset'] = Utils::LittleEndian2Int(substr($EXEheader, 24, 2));
     $info['exe']['mz']['raw']['overlay_number'] = Utils::LittleEndian2Int(substr($EXEheader, 26, 2));
     $info['exe']['mz']['byte_size'] = ($info['exe']['mz']['raw']['page_count'] - 1) * 512 + $info['exe']['mz']['raw']['last_page_size'];
     $info['exe']['mz']['header_size'] = $info['exe']['mz']['raw']['header_paragraphs'] * 16;
     $info['exe']['mz']['memory_minimum'] = $info['exe']['mz']['raw']['min_memory_paragraphs'] * 16;
     $info['exe']['mz']['memory_recommended'] = $info['exe']['mz']['raw']['max_memory_paragraphs'] * 16;
     $info['error'][] = 'EXE parsing not enabled in this version of getID3() [' . $this->getid3->version() . ']';
     return false;
 }
Пример #3
0
 public function WriteVorbisComment()
 {
     // Create file with new comments
     $tempcommentsfilename = tempnam(Utils::getTempDirectory(), 'getID3');
     if (is_writable($tempcommentsfilename) && is_file($tempcommentsfilename) && ($fpcomments = fopen($tempcommentsfilename, 'wb'))) {
         foreach ($this->tag_data as $key => $value) {
             foreach ($value as $commentdata) {
                 fwrite($fpcomments, $this->CleanVorbisCommentName($key) . '=' . $commentdata . "\n");
             }
         }
         fclose($fpcomments);
     } else {
         $this->errors[] = 'failed to open temporary tags file "' . $tempcommentsfilename . '", tags not written';
         return false;
     }
     $oldignoreuserabort = ignore_user_abort(true);
     if (Utils::isWindows()) {
         if (file_exists(Utils::getHelperAppDirectory() . 'vorbiscomment.exe')) {
             //$commandline = '"'.Utils::getHelperAppDirectory().'vorbiscomment.exe" -w --raw -c "'.$tempcommentsfilename.'" "'.str_replace('/', '\\', $this->filename).'"';
             //  vorbiscomment works fine if you copy-paste the above commandline into a command prompt,
             //  but refuses to work with `backtick` if there are "doublequotes" present around BOTH
             //  the metaflac pathname and the target filename. For whatever reason...??
             //  The solution is simply ensure that the metaflac pathname has no spaces,
             //  and therefore does not need to be quoted
             // On top of that, if error messages are not always captured properly under Windows
             // To at least see if there was a problem, compare file modification timestamps before and after writing
             clearstatcache();
             $timestampbeforewriting = filemtime($this->filename);
             $commandline = Utils::getHelperAppDirectory() . 'vorbiscomment.exe -w --raw -c "' . $tempcommentsfilename . '" "' . $this->filename . '" 2>&1';
             $VorbiscommentError = `{$commandline}`;
             if (empty($VorbiscommentError)) {
                 clearstatcache();
                 if ($timestampbeforewriting == filemtime($this->filename)) {
                     $VorbiscommentError = 'File modification timestamp has not changed - it looks like the tags were not written';
                 }
             }
         } else {
             $VorbiscommentError = 'vorbiscomment.exe not found in ' . Utils::getHelperAppDirectory();
         }
     } else {
         $commandline = 'vorbiscomment -w --raw -c "' . $tempcommentsfilename . '" "' . $this->filename . '" 2>&1';
         $VorbiscommentError = `{$commandline}`;
     }
     // Remove temporary comments file
     unlink($tempcommentsfilename);
     ignore_user_abort($oldignoreuserabort);
     if (!empty($VorbiscommentError)) {
         $this->errors[] = 'system call to vorbiscomment failed with message: ' . "\n\n" . $VorbiscommentError;
         return false;
     }
     return true;
 }
Пример #4
0
 public function ParseBink()
 {
     $info =& $this->getid3->info;
     $info['fileformat'] = 'bink';
     $info['video']['dataformat'] = 'bink';
     $fileData = 'BIK' . $this->fread(13);
     $info['bink']['data_size'] = Utils::LittleEndian2Int(substr($fileData, 4, 4));
     $info['bink']['frame_count'] = Utils::LittleEndian2Int(substr($fileData, 8, 2));
     if ($info['avdataend'] - $info['avdataoffset'] != $info['bink']['data_size'] + 8) {
         $info['error'][] = 'Probably truncated file: expecting ' . $info['bink']['data_size'] . ' bytes, found ' . ($info['avdataend'] - $info['avdataoffset']);
     }
     return true;
 }
Пример #5
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $this->fseek($info['avdataoffset']);
     $DSSheader = $this->fread(1540);
     if (!preg_match('#^(\\x02|\\x03)ds[s2]#', $DSSheader)) {
         $info['error'][] = 'Expecting "[02-03] 64 73 [73|32]" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes(substr($DSSheader, 0, 4)) . '"';
         return false;
     }
     // some structure information taken from http://cpansearch.perl.org/src/RGIBSON/Audio-DSS-0.02/lib/Audio/DSS.pm
     $info['encoding'] = 'ISO-8859-1';
     // not certain, but assumed
     $info['dss'] = array();
     $info['fileformat'] = 'dss';
     $info['mime_type'] = 'audio/x-' . substr($DSSheader, 1, 3);
     // "audio/x-dss" or "audio/x-ds2"
     $info['audio']['dataformat'] = substr($DSSheader, 1, 3);
     //         "dss" or         "ds2"
     $info['audio']['bitrate_mode'] = 'cbr';
     $info['dss']['version'] = ord(substr($DSSheader, 0, 1));
     $info['dss']['hardware'] = trim(substr($DSSheader, 12, 16));
     // identification string for hardware used to create the file, e.g. "DPM 9600", "DS2400"
     $info['dss']['unknown1'] = Utils::LittleEndian2Int(substr($DSSheader, 28, 4));
     // 32-37 = "FE FF FE FF F7 FF" in all the sample files I've seen
     $info['dss']['date_create'] = $this->DSSdateStringToUnixDate(substr($DSSheader, 38, 12));
     $info['dss']['date_complete'] = $this->DSSdateStringToUnixDate(substr($DSSheader, 50, 12));
     $info['dss']['playtime_sec'] = intval(substr($DSSheader, 62, 2) * 3600 + substr($DSSheader, 64, 2) * 60 + substr($DSSheader, 66, 2));
     // approximate file playtime in HHMMSS
     $info['dss']['playtime_ms'] = Utils::LittleEndian2Int(substr($DSSheader, 512, 4));
     // exact file playtime in milliseconds. Has also been observed at offset 530 in one sample file, with something else (unknown) at offset 512
     $info['dss']['priority'] = ord(substr($DSSheader, 793, 1));
     $info['dss']['comments'] = trim(substr($DSSheader, 798, 100));
     $info['dss']['sample_rate_index'] = ord(substr($DSSheader, 1538, 1));
     // this isn't certain, this may or may not be where the sample rate info is stored, but it seems consistent on my small selection of sample files
     $info['audio']['bits_per_sample'] = 16;
     // maybe, maybe not -- most compressed audio formats don't have a fixed bits-per-sample value, but this is a reasonable approximation
     $info['audio']['sample_rate'] = $this->DSSsampleRateLookup($info['dss']['sample_rate_index']);
     $info['audio']['channels'] = 1;
     $info['playtime_seconds'] = $info['dss']['playtime_ms'] / 1000;
     if (floor($info['dss']['playtime_ms'] / 1000) != $info['dss']['playtime_sec']) {
         // *should* just be playtime_ms / 1000 but at least one sample file has playtime_ms at offset 530 instead of offset 512, so safety check
         $info['playtime_seconds'] = $info['dss']['playtime_sec'];
         $this->getid3->warning('playtime_ms (' . number_format($info['dss']['playtime_ms'] / 1000, 3) . ') does not match playtime_sec (' . number_format($info['dss']['playtime_sec']) . ') - using playtime_sec value');
     }
     $info['audio']['bitrate'] = $info['filesize'] * 8 / $info['playtime_seconds'];
     return true;
 }
Пример #6
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $this->fseek($info['avdataoffset']);
     $RKAUHeader = $this->fread(20);
     $magic = 'RKA';
     if (substr($RKAUHeader, 0, 3) != $magic) {
         $info['error'][] = 'Expecting "' . Utils::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes(substr($RKAUHeader, 0, 3)) . '"';
         return false;
     }
     $info['fileformat'] = 'rkau';
     $info['audio']['dataformat'] = 'rkau';
     $info['audio']['bitrate_mode'] = 'vbr';
     $info['rkau']['raw']['version'] = Utils::LittleEndian2Int(substr($RKAUHeader, 3, 1));
     $info['rkau']['version'] = '1.' . str_pad($info['rkau']['raw']['version'] & 0xf, 2, '0', STR_PAD_LEFT);
     if ($info['rkau']['version'] > 1.07 || $info['rkau']['version'] < 1.06) {
         $info['error'][] = 'This version of getID3() [' . $this->getid3->version() . '] can only parse RKAU files v1.06 and 1.07 (this file is v' . $info['rkau']['version'] . ')';
         unset($info['rkau']);
         return false;
     }
     $info['rkau']['source_bytes'] = Utils::LittleEndian2Int(substr($RKAUHeader, 4, 4));
     $info['rkau']['sample_rate'] = Utils::LittleEndian2Int(substr($RKAUHeader, 8, 4));
     $info['rkau']['channels'] = Utils::LittleEndian2Int(substr($RKAUHeader, 12, 1));
     $info['rkau']['bits_per_sample'] = Utils::LittleEndian2Int(substr($RKAUHeader, 13, 1));
     $info['rkau']['raw']['quality'] = Utils::LittleEndian2Int(substr($RKAUHeader, 14, 1));
     $this->RKAUqualityLookup($info['rkau']);
     $info['rkau']['raw']['flags'] = Utils::LittleEndian2Int(substr($RKAUHeader, 15, 1));
     $info['rkau']['flags']['joint_stereo'] = (bool) (!($info['rkau']['raw']['flags'] & 0x1));
     $info['rkau']['flags']['streaming'] = (bool) ($info['rkau']['raw']['flags'] & 0x2);
     $info['rkau']['flags']['vrq_lossy_mode'] = (bool) ($info['rkau']['raw']['flags'] & 0x4);
     if ($info['rkau']['flags']['streaming']) {
         $info['avdataoffset'] += 20;
         $info['rkau']['compressed_bytes'] = Utils::LittleEndian2Int(substr($RKAUHeader, 16, 4));
     } else {
         $info['avdataoffset'] += 16;
         $info['rkau']['compressed_bytes'] = $info['avdataend'] - $info['avdataoffset'] - 1;
     }
     // Note: compressed_bytes does not always equal what appears to be the actual number of compressed bytes,
     // sometimes it's more, sometimes less. No idea why(?)
     $info['audio']['lossless'] = $info['rkau']['lossless'];
     $info['audio']['channels'] = $info['rkau']['channels'];
     $info['audio']['bits_per_sample'] = $info['rkau']['bits_per_sample'];
     $info['audio']['sample_rate'] = $info['rkau']['sample_rate'];
     $info['playtime_seconds'] = $info['rkau']['source_bytes'] / ($info['rkau']['sample_rate'] * $info['rkau']['channels'] * ($info['rkau']['bits_per_sample'] / 8));
     $info['audio']['bitrate'] = $info['rkau']['compressed_bytes'] * 8 / $info['playtime_seconds'];
     return true;
 }
Пример #7
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $this->fseek($info['avdataoffset']);
     $AMRheader = $this->fread(6);
     $magic = '#!AMR' . "\n";
     if (substr($AMRheader, 0, 6) != $magic) {
         $info['error'][] = 'Expecting "' . Utils::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes(substr($AMRheader, 0, 6)) . '"';
         return false;
     }
     // shortcut
     $info['amr'] = array();
     $thisfile_amr =& $info['amr'];
     $info['fileformat'] = 'amr';
     $info['audio']['dataformat'] = 'amr';
     $info['audio']['bitrate_mode'] = 'vbr';
     // within a small predefined range: 4.75kbps to 12.2kbps
     $info['audio']['bits_per_sample'] = 13;
     // http://en.wikipedia.org/wiki/Adaptive_Multi-Rate_audio_codec: "Sampling frequency 8 kHz/13-bit (160 samples for 20 ms frames), filtered to 200–3400 Hz"
     $info['audio']['sample_rate'] = 8000;
     // http://en.wikipedia.org/wiki/Adaptive_Multi-Rate_audio_codec: "Sampling frequency 8 kHz/13-bit (160 samples for 20 ms frames), filtered to 200–3400 Hz"
     $info['audio']['channels'] = 1;
     $thisfile_amr['frame_mode_count'] = array(0 => 0, 1 => 0, 2 => 0, 3 => 0, 4 => 0, 5 => 0, 6 => 0, 7 => 0);
     $buffer = '';
     do {
         if (strlen($buffer) < $this->getid3->fread_buffer_size() && !feof($this->getid3->fp)) {
             $buffer .= $this->fread($this->getid3->fread_buffer_size() * 2);
         }
         $AMR_frame_header = ord(substr($buffer, 0, 1));
         $codec_mode_request = ($AMR_frame_header & 0x78) >> 3;
         // The 2nd bit through 5th bit (counting the most significant bit as the first bit) comprise the CMR (Codec Mode Request), values 0-7 being valid for AMR. The top bit of the CMR can actually be ignored, though it is used when AMR forms RTP payloads. The lower 3-bits of the header are reserved and are not used. Viewing the header from most significant bit to least significant bit, the encoding is XCCCCXXX, where Xs are reserved (typically 0) and the Cs are the CMR.
         if ($codec_mode_request > 7) {
             $info['error'][] = '';
             break;
         }
         $thisfile_amr['frame_mode_count'][$codec_mode_request]++;
         $buffer = substr($buffer, $this->amr_mode_bytes_per_frame($codec_mode_request));
     } while (strlen($buffer) > 0);
     $info['playtime_seconds'] = array_sum($thisfile_amr['frame_mode_count']) * 0.02;
     // each frame contain 160 samples and is 20 milliseconds long
     $info['audio']['bitrate'] = 8 * ($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds'];
     // bitrate could be calculated from average bitrate by distributation of frame types. That would give effective audio bitrate, this gives overall file bitrate which will be a little bit higher since every frame will waste 8 bits for header, plus a few bits for octet padding
     $info['bitrate'] = $info['audio']['bitrate'];
     return true;
 }
Пример #8
0
 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;
 }
Пример #9
0
 public function parseAPEheaderFooter($APEheaderFooterData)
 {
     // http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html
     // shortcut
     $headerfooterinfo['raw'] = array();
     $headerfooterinfo_raw =& $headerfooterinfo['raw'];
     $headerfooterinfo_raw['footer_tag'] = substr($APEheaderFooterData, 0, 8);
     if ($headerfooterinfo_raw['footer_tag'] != 'APETAGEX') {
         return false;
     }
     $headerfooterinfo_raw['version'] = Utils::LittleEndian2Int(substr($APEheaderFooterData, 8, 4));
     $headerfooterinfo_raw['tagsize'] = Utils::LittleEndian2Int(substr($APEheaderFooterData, 12, 4));
     $headerfooterinfo_raw['tag_items'] = Utils::LittleEndian2Int(substr($APEheaderFooterData, 16, 4));
     $headerfooterinfo_raw['global_flags'] = Utils::LittleEndian2Int(substr($APEheaderFooterData, 20, 4));
     $headerfooterinfo_raw['reserved'] = substr($APEheaderFooterData, 24, 8);
     $headerfooterinfo['tag_version'] = $headerfooterinfo_raw['version'] / 1000;
     if ($headerfooterinfo['tag_version'] >= 2) {
         $headerfooterinfo['flags'] = $this->parseAPEtagFlags($headerfooterinfo_raw['global_flags']);
     }
     return $headerfooterinfo;
 }
Пример #10
0
 public function getBit()
 {
     $result = Utils::BigEndian2Int(substr($this->sps, $this->currentBytes, 1)) >> 7 - $this->currentBits & 0x1;
     $this->skipBits(1);
     return $result;
 }
Пример #11
0
 public function TIFFendian2Int($bytestring, $byteorder)
 {
     if ($byteorder == 'Intel') {
         return Utils::LittleEndian2Int($bytestring);
     } elseif ($byteorder == 'Motorola') {
         return Utils::BigEndian2Int($bytestring);
     }
     return false;
 }
Пример #12
0
 public function saveAttachment($name, $offset, $length, $image_mime = null)
 {
     try {
         // do not extract at all
         if ($this->getid3->option_save_attachments === GetID3::ATTACHMENTS_NONE) {
             $attachment = null;
             // do not set any
             // extract to return array
         } elseif ($this->getid3->option_save_attachments === GetID3::ATTACHMENTS_INLINE) {
             $this->fseek($offset);
             $attachment = $this->fread($length);
             // get whole data in one pass, till it is anyway stored in memory
             if ($attachment === false || strlen($attachment) != $length) {
                 throw new \Exception('failed to read attachment data');
             }
             // assume directory path is given
         } else {
             // set up destination path
             $dir = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->getid3->option_save_attachments), DIRECTORY_SEPARATOR);
             if (!is_dir($dir) || !is_writable($dir)) {
                 // check supplied directory
                 throw new \Exception('supplied path (' . $dir . ') does not exist, or is not writable');
             }
             $dest = $dir . DIRECTORY_SEPARATOR . $name . ($image_mime ? '.' . Utils::ImageExtFromMime($image_mime) : '');
             // create dest file
             if (($fp_dest = fopen($dest, 'wb')) == false) {
                 throw new \Exception('failed to create file ' . $dest);
             }
             // copy data
             $this->fseek($offset);
             $buffersize = $this->data_string_flag ? $length : $this->getid3->fread_buffer_size();
             $bytesleft = $length;
             while ($bytesleft > 0) {
                 if (($buffer = $this->fread(min($buffersize, $bytesleft))) === false || ($byteswritten = fwrite($fp_dest, $buffer)) === false || $byteswritten === 0) {
                     throw new \Exception($buffer === false ? 'not enough data to read' : 'failed to write to destination file, may be not enough disk space');
                 }
                 $bytesleft -= $byteswritten;
             }
             fclose($fp_dest);
             $attachment = $dest;
         }
     } catch (\Exception $e) {
         // close and remove dest file if created
         if (isset($fp_dest) && is_resource($fp_dest)) {
             fclose($fp_dest);
             unlink($dest);
         }
         // do not set any is case of error
         $attachment = null;
         $this->warning('Failed to extract attachment ' . $name . ': ' . $e->getMessage());
     }
     // seek to the end of attachment
     $this->fseek($offset + $length);
     return $attachment;
 }
Пример #13
0
 public function GeneralMIDIpercussionLookup($instrumentid)
 {
     $begin = __LINE__;
     /** This is not a comment!
     
     			35	Acoustic Bass Drum
     			36	Bass Drum 1
     			37	Side Stick
     			38	Acoustic Snare
     			39	Hand Clap
     			40	Electric Snare
     			41	Low Floor Tom
     			42	Closed Hi-Hat
     			43	High Floor Tom
     			44	Pedal Hi-Hat
     			45	Low Tom
     			46	Open Hi-Hat
     			47	Low-Mid Tom
     			48	Hi-Mid Tom
     			49	Crash Cymbal 1
     			50	High Tom
     			51	Ride Cymbal 1
     			52	Chinese Cymbal
     			53	Ride Bell
     			54	Tambourine
     			55	Splash Cymbal
     			56	Cowbell
     			57	Crash Cymbal 2
     			59	Ride Cymbal 2
     			60	Hi Bongo
     			61	Low Bongo
     			62	Mute Hi Conga
     			63	Open Hi Conga
     			64	Low Conga
     			65	High Timbale
     			66	Low Timbale
     			67	High Agogo
     			68	Low Agogo
     			69	Cabasa
     			70	Maracas
     			71	Short Whistle
     			72	Long Whistle
     			73	Short Guiro
     			74	Long Guiro
     			75	Claves
     			76	Hi Wood Block
     			77	Low Wood Block
     			78	Mute Cuica
     			79	Open Cuica
     			80	Mute Triangle
     			81	Open Triangle
     
     		*/
     return Utils::EmbeddedLookup($instrumentid, $begin, __LINE__, __FILE__, 'GeneralMIDIpercussion');
 }
Пример #14
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $info['fileformat'] = 'tta';
     $info['audio']['dataformat'] = 'tta';
     $info['audio']['lossless'] = true;
     $info['audio']['bitrate_mode'] = 'vbr';
     $this->fseek($info['avdataoffset']);
     $ttaheader = $this->fread(26);
     $info['tta']['magic'] = substr($ttaheader, 0, 3);
     $magic = 'TTA';
     if ($info['tta']['magic'] != $magic) {
         $info['error'][] = 'Expecting "' . Utils::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes($info['tta']['magic']) . '"';
         unset($info['fileformat']);
         unset($info['audio']);
         unset($info['tta']);
         return false;
     }
     switch ($ttaheader[3]) {
         case "":
             // TTA v1.x
         // TTA v1.x
         case "":
             // TTA v1.x
         // TTA v1.x
         case "":
             // TTA v1.x
             // "It was the demo-version of the TTA encoder. There is no released format with such header. TTA encoder v1 is not supported about a year."
             $info['tta']['major_version'] = 1;
             $info['avdataoffset'] += 16;
             $info['tta']['compression_level'] = ord($ttaheader[3]);
             $info['tta']['channels'] = Utils::LittleEndian2Int(substr($ttaheader, 4, 2));
             $info['tta']['bits_per_sample'] = Utils::LittleEndian2Int(substr($ttaheader, 6, 2));
             $info['tta']['sample_rate'] = Utils::LittleEndian2Int(substr($ttaheader, 8, 4));
             $info['tta']['samples_per_channel'] = Utils::LittleEndian2Int(substr($ttaheader, 12, 4));
             $info['audio']['encoder_options'] = '-e' . $info['tta']['compression_level'];
             $info['playtime_seconds'] = $info['tta']['samples_per_channel'] / $info['tta']['sample_rate'];
             break;
         case '2':
             // TTA v2.x
             // "I have hurried to release the TTA 2.0 encoder. Format documentation is removed from our site. This format still in development. Please wait the TTA2 format, encoder v4."
             $info['tta']['major_version'] = 2;
             $info['avdataoffset'] += 20;
             $info['tta']['compression_level'] = Utils::LittleEndian2Int(substr($ttaheader, 4, 2));
             $info['tta']['audio_format'] = Utils::LittleEndian2Int(substr($ttaheader, 6, 2));
             $info['tta']['channels'] = Utils::LittleEndian2Int(substr($ttaheader, 8, 2));
             $info['tta']['bits_per_sample'] = Utils::LittleEndian2Int(substr($ttaheader, 10, 2));
             $info['tta']['sample_rate'] = Utils::LittleEndian2Int(substr($ttaheader, 12, 4));
             $info['tta']['data_length'] = Utils::LittleEndian2Int(substr($ttaheader, 16, 4));
             $info['audio']['encoder_options'] = '-e' . $info['tta']['compression_level'];
             $info['playtime_seconds'] = $info['tta']['data_length'] / $info['tta']['sample_rate'];
             break;
         case '1':
             // TTA v3.x
             // "This is a first stable release of the TTA format. It will be supported by the encoders v3 or higher."
             $info['tta']['major_version'] = 3;
             $info['avdataoffset'] += 26;
             $info['tta']['audio_format'] = Utils::LittleEndian2Int(substr($ttaheader, 4, 2));
             $info['tta']['channels'] = Utils::LittleEndian2Int(substr($ttaheader, 6, 2));
             $info['tta']['bits_per_sample'] = Utils::LittleEndian2Int(substr($ttaheader, 8, 2));
             $info['tta']['sample_rate'] = Utils::LittleEndian2Int(substr($ttaheader, 10, 4));
             $info['tta']['data_length'] = Utils::LittleEndian2Int(substr($ttaheader, 14, 4));
             $info['tta']['crc32_footer'] = substr($ttaheader, 18, 4);
             $info['tta']['seek_point'] = Utils::LittleEndian2Int(substr($ttaheader, 22, 4));
             $info['playtime_seconds'] = $info['tta']['data_length'] / $info['tta']['sample_rate'];
             break;
         default:
             $info['error'][] = 'This version of getID3() [' . $this->getid3->version() . '] only knows how to handle TTA v1 and v2 - it may not work correctly with this file which appears to be TTA v' . $ttaheader[3];
             return false;
             break;
     }
     $info['audio']['encoder'] = 'TTA v' . $info['tta']['major_version'];
     $info['audio']['bits_per_sample'] = $info['tta']['bits_per_sample'];
     $info['audio']['sample_rate'] = $info['tta']['sample_rate'];
     $info['audio']['channels'] = $info['tta']['channels'];
     $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
     return true;
 }
Пример #15
0
 private function EitherEndian2Int($byteword, $signed = false)
 {
     if ($this->container == 'riff') {
         return Utils::LittleEndian2Int($byteword, $signed);
     }
     return Utils::BigEndian2Int($byteword, false, $signed);
 }
Пример #16
0
 public function ZIPparseEndOfCentralDirectory()
 {
     $EndOfCentralDirectory['offset'] = $this->ftell();
     $ZIPendOfCentralDirectory = $this->fread(22);
     $EndOfCentralDirectory['signature'] = Utils::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 0, 4));
     if ($EndOfCentralDirectory['signature'] != 0x6054b50) {
         // invalid End Of Central Directory Signature
         $this->fseek($EndOfCentralDirectory['offset']);
         // seek back to where filepointer originally was so it can be handled properly
         return false;
     }
     $EndOfCentralDirectory['disk_number_current'] = Utils::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 4, 2));
     $EndOfCentralDirectory['disk_number_start_directory'] = Utils::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 6, 2));
     $EndOfCentralDirectory['directory_entries_this_disk'] = Utils::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 8, 2));
     $EndOfCentralDirectory['directory_entries_total'] = Utils::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 10, 2));
     $EndOfCentralDirectory['directory_size'] = Utils::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 12, 4));
     $EndOfCentralDirectory['directory_offset'] = Utils::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 16, 4));
     $EndOfCentralDirectory['comment_length'] = Utils::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 20, 2));
     if ($EndOfCentralDirectory['comment_length'] > 0) {
         $EndOfCentralDirectory['comment'] = $this->fread($EndOfCentralDirectory['comment_length']);
     }
     return $EndOfCentralDirectory;
 }
Пример #17
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $this->fseek($info['avdataoffset']);
     while (true) {
         $wavpackheader = $this->fread(32);
         if ($this->ftell() >= $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 = $this->ftell() - 32;
         $blockheader_magic = substr($wavpackheader, 0, 4);
         $blockheader_size = Utils::LittleEndian2Int(substr($wavpackheader, 4, 4));
         $magic = 'wvpk';
         if ($blockheader_magic != $magic) {
             $info['error'][] = 'Expecting "' . Utils::PrintHexBytes($magic) . '" at offset ' . $blockheader_offset . ', found "' . Utils::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'] = Utils::LittleEndian2Int(substr($wavpackheader, 12, 4));
             $info['wavpack']['blockheader']['block_index'] = Utils::LittleEndian2Int(substr($wavpackheader, 16, 4));
             $info['wavpack']['blockheader']['block_samples'] = Utils::LittleEndian2Int(substr($wavpackheader, 20, 4));
             $info['wavpack']['blockheader']['flags_raw'] = Utils::LittleEndian2Int(substr($wavpackheader, 24, 4));
             $info['wavpack']['blockheader']['crc'] = Utils::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) && $this->ftell() < $blockheader_offset + $blockheader_size + 8) {
             $metablock = array('offset' => $this->ftell());
             $metablockheader = $this->fread(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 .= $this->fread(2);
             }
             $metablock['size'] = Utils::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'] = $this->fread($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
                         $this->fseek($metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size']);
                         break;
                     default:
                         $info['warning'][] = 'Unexpected metablock type "0x' . str_pad(dechex($metablock['function_id']), 2, '0', STR_PAD_LEFT) . '" at offset ' . $metablock['offset'];
                         $this->fseek($metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size']);
                         break;
                 }
                 switch ($metablock['function_id']) {
                     case 0x21:
                         // ID_RIFF_HEADER
                         $original_wav_filesize = Utils::LittleEndian2Int(substr($metablock['data'], 4, 4));
                         $getid3_temp = new GetID3();
                         $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 GetID3();
                         $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'])) {
                             Riff::parseComments($metablock['riff']['INFO'], $metablock['comments']);
                             $info['tags']['riff'] = $metablock['comments'];
                         }
                         unset($getid3_temp, $getid3_riff);
                         break;
                     case 0x23:
                         // ID_REPLAY_GAIN
                         $info['warning'][] = 'WavPack "Replay Gain" contents not yet handled by getID3() in metablock at offset ' . $metablock['offset'];
                         break;
                     case 0x24:
                         // ID_CUESHEET
                         $info['warning'][] = 'WavPack "Cuesheet" contents not yet handled by getID3() in metablock at offset ' . $metablock['offset'];
                         break;
                     case 0x25:
                         // ID_CONFIG_BLOCK
                         $metablock['flags_raw'] = Utils::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(Utils::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;
 }
Пример #18
0
 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;
 }
Пример #19
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     // shortcuts
     $info['bmp']['header']['raw'] = array();
     $thisfile_bmp =& $info['bmp'];
     $thisfile_bmp_header =& $thisfile_bmp['header'];
     $thisfile_bmp_header_raw =& $thisfile_bmp_header['raw'];
     // BITMAPFILEHEADER [14 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_62uq.asp
     // all versions
     // WORD    bfType;
     // DWORD   bfSize;
     // WORD    bfReserved1;
     // WORD    bfReserved2;
     // DWORD   bfOffBits;
     $this->fseek($info['avdataoffset']);
     $offset = 0;
     $BMPheader = $this->fread(14 + 40);
     $thisfile_bmp_header_raw['identifier'] = substr($BMPheader, $offset, 2);
     $offset += 2;
     $magic = 'BM';
     if ($thisfile_bmp_header_raw['identifier'] != $magic) {
         $info['error'][] = 'Expecting "' . Utils::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes($thisfile_bmp_header_raw['identifier']) . '"';
         unset($info['fileformat']);
         unset($info['bmp']);
         return false;
     }
     $thisfile_bmp_header_raw['filesize'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
     $offset += 4;
     $thisfile_bmp_header_raw['reserved1'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 2));
     $offset += 2;
     $thisfile_bmp_header_raw['reserved2'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 2));
     $offset += 2;
     $thisfile_bmp_header_raw['data_offset'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
     $offset += 4;
     $thisfile_bmp_header_raw['header_size'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
     $offset += 4;
     // check if the hardcoded-to-1 "planes" is at offset 22 or 26
     $planes22 = Utils::LittleEndian2Int(substr($BMPheader, 22, 2));
     $planes26 = Utils::LittleEndian2Int(substr($BMPheader, 26, 2));
     if ($planes22 == 1 && $planes26 != 1) {
         $thisfile_bmp['type_os'] = 'OS/2';
         $thisfile_bmp['type_version'] = 1;
     } elseif ($planes26 == 1 && $planes22 != 1) {
         $thisfile_bmp['type_os'] = 'Windows';
         $thisfile_bmp['type_version'] = 1;
     } elseif ($thisfile_bmp_header_raw['header_size'] == 12) {
         $thisfile_bmp['type_os'] = 'OS/2';
         $thisfile_bmp['type_version'] = 1;
     } elseif ($thisfile_bmp_header_raw['header_size'] == 40) {
         $thisfile_bmp['type_os'] = 'Windows';
         $thisfile_bmp['type_version'] = 1;
     } elseif ($thisfile_bmp_header_raw['header_size'] == 84) {
         $thisfile_bmp['type_os'] = 'Windows';
         $thisfile_bmp['type_version'] = 4;
     } elseif ($thisfile_bmp_header_raw['header_size'] == 100) {
         $thisfile_bmp['type_os'] = 'Windows';
         $thisfile_bmp['type_version'] = 5;
     } else {
         $info['error'][] = 'Unknown BMP subtype (or not a BMP file)';
         unset($info['fileformat']);
         unset($info['bmp']);
         return false;
     }
     $info['fileformat'] = 'bmp';
     $info['video']['dataformat'] = 'bmp';
     $info['video']['lossless'] = true;
     $info['video']['pixel_aspect_ratio'] = (double) 1;
     if ($thisfile_bmp['type_os'] == 'OS/2') {
         // OS/2-format BMP
         // http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm
         // DWORD  Size;             /* Size of this structure in bytes */
         // DWORD  Width;            /* Bitmap width in pixels */
         // DWORD  Height;           /* Bitmap height in pixel */
         // WORD   NumPlanes;        /* Number of bit planes (color depth) */
         // WORD   BitsPerPixel;     /* Number of bits per pixel per plane */
         $thisfile_bmp_header_raw['width'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 2));
         $offset += 2;
         $thisfile_bmp_header_raw['height'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 2));
         $offset += 2;
         $thisfile_bmp_header_raw['planes'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 2));
         $offset += 2;
         $thisfile_bmp_header_raw['bits_per_pixel'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 2));
         $offset += 2;
         $info['video']['resolution_x'] = $thisfile_bmp_header_raw['width'];
         $info['video']['resolution_y'] = $thisfile_bmp_header_raw['height'];
         $info['video']['codec'] = 'BI_RGB ' . $thisfile_bmp_header_raw['bits_per_pixel'] . '-bit';
         $info['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel'];
         if ($thisfile_bmp['type_version'] >= 2) {
             // DWORD  Compression;      /* Bitmap compression scheme */
             // DWORD  ImageDataSize;    /* Size of bitmap data in bytes */
             // DWORD  XResolution;      /* X resolution of display device */
             // DWORD  YResolution;      /* Y resolution of display device */
             // DWORD  ColorsUsed;       /* Number of color table indices used */
             // DWORD  ColorsImportant;  /* Number of important color indices */
             // WORD   Units;            /* Type of units used to measure resolution */
             // WORD   Reserved;         /* Pad structure to 4-byte boundary */
             // WORD   Recording;        /* Recording algorithm */
             // WORD   Rendering;        /* Halftoning algorithm used */
             // DWORD  Size1;            /* Reserved for halftoning algorithm use */
             // DWORD  Size2;            /* Reserved for halftoning algorithm use */
             // DWORD  ColorEncoding;    /* Color model used in bitmap */
             // DWORD  Identifier;       /* Reserved for application use */
             $thisfile_bmp_header_raw['compression'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['bmp_data_size'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['resolution_h'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['resolution_v'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['colors_used'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['colors_important'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['resolution_units'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 2));
             $offset += 2;
             $thisfile_bmp_header_raw['reserved1'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 2));
             $offset += 2;
             $thisfile_bmp_header_raw['recording'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 2));
             $offset += 2;
             $thisfile_bmp_header_raw['rendering'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 2));
             $offset += 2;
             $thisfile_bmp_header_raw['size1'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['size2'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['color_encoding'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['identifier'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header['compression'] = $this->BMPcompressionOS2Lookup($thisfile_bmp_header_raw['compression']);
             $info['video']['codec'] = $thisfile_bmp_header['compression'] . ' ' . $thisfile_bmp_header_raw['bits_per_pixel'] . '-bit';
         }
     } elseif ($thisfile_bmp['type_os'] == 'Windows') {
         // Windows-format BMP
         // BITMAPINFOHEADER - [40 bytes] http://msdn.microsoft.com/library/en-us/gdi/bitmaps_1rw2.asp
         // all versions
         // DWORD  biSize;
         // LONG   biWidth;
         // LONG   biHeight;
         // WORD   biPlanes;
         // WORD   biBitCount;
         // DWORD  biCompression;
         // DWORD  biSizeImage;
         // LONG   biXPelsPerMeter;
         // LONG   biYPelsPerMeter;
         // DWORD  biClrUsed;
         // DWORD  biClrImportant;
         // possibly integrate this section and module.audio-video.riff.php::ParseBITMAPINFOHEADER() ?
         $thisfile_bmp_header_raw['width'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
         $offset += 4;
         $thisfile_bmp_header_raw['height'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
         $offset += 4;
         $thisfile_bmp_header_raw['planes'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 2));
         $offset += 2;
         $thisfile_bmp_header_raw['bits_per_pixel'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 2));
         $offset += 2;
         $thisfile_bmp_header_raw['compression'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
         $offset += 4;
         $thisfile_bmp_header_raw['bmp_data_size'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
         $offset += 4;
         $thisfile_bmp_header_raw['resolution_h'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
         $offset += 4;
         $thisfile_bmp_header_raw['resolution_v'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
         $offset += 4;
         $thisfile_bmp_header_raw['colors_used'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
         $offset += 4;
         $thisfile_bmp_header_raw['colors_important'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
         $offset += 4;
         $thisfile_bmp_header['compression'] = $this->BMPcompressionWindowsLookup($thisfile_bmp_header_raw['compression']);
         $info['video']['resolution_x'] = $thisfile_bmp_header_raw['width'];
         $info['video']['resolution_y'] = $thisfile_bmp_header_raw['height'];
         $info['video']['codec'] = $thisfile_bmp_header['compression'] . ' ' . $thisfile_bmp_header_raw['bits_per_pixel'] . '-bit';
         $info['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel'];
         if ($thisfile_bmp['type_version'] >= 4 || $thisfile_bmp_header_raw['compression'] == 3) {
             // should only be v4+, but BMPs with type_version==1 and BI_BITFIELDS compression have been seen
             $BMPheader .= $this->fread(44);
             // BITMAPV4HEADER - [44 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_2k1e.asp
             // Win95+, WinNT4.0+
             // DWORD        bV4RedMask;
             // DWORD        bV4GreenMask;
             // DWORD        bV4BlueMask;
             // DWORD        bV4AlphaMask;
             // DWORD        bV4CSType;
             // CIEXYZTRIPLE bV4Endpoints;
             // DWORD        bV4GammaRed;
             // DWORD        bV4GammaGreen;
             // DWORD        bV4GammaBlue;
             $thisfile_bmp_header_raw['red_mask'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['green_mask'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['blue_mask'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['alpha_mask'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['cs_type'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['ciexyz_red'] = substr($BMPheader, $offset, 4);
             $offset += 4;
             $thisfile_bmp_header_raw['ciexyz_green'] = substr($BMPheader, $offset, 4);
             $offset += 4;
             $thisfile_bmp_header_raw['ciexyz_blue'] = substr($BMPheader, $offset, 4);
             $offset += 4;
             $thisfile_bmp_header_raw['gamma_red'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['gamma_green'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['gamma_blue'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header['ciexyz_red'] = Utils::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_red']));
             $thisfile_bmp_header['ciexyz_green'] = Utils::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_green']));
             $thisfile_bmp_header['ciexyz_blue'] = Utils::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_blue']));
         }
         if ($thisfile_bmp['type_version'] >= 5) {
             $BMPheader .= $this->fread(16);
             // BITMAPV5HEADER - [16 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_7c36.asp
             // Win98+, Win2000+
             // DWORD        bV5Intent;
             // DWORD        bV5ProfileData;
             // DWORD        bV5ProfileSize;
             // DWORD        bV5Reserved;
             $thisfile_bmp_header_raw['intent'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['profile_data_offset'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['profile_data_size'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['reserved3'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
         }
     } else {
         $info['error'][] = 'Unknown BMP format in header.';
         return false;
     }
     if ($this->ExtractPalette || $this->ExtractData) {
         $PaletteEntries = 0;
         if ($thisfile_bmp_header_raw['bits_per_pixel'] < 16) {
             $PaletteEntries = pow(2, $thisfile_bmp_header_raw['bits_per_pixel']);
         } elseif (isset($thisfile_bmp_header_raw['colors_used']) && $thisfile_bmp_header_raw['colors_used'] > 0 && $thisfile_bmp_header_raw['colors_used'] <= 256) {
             $PaletteEntries = $thisfile_bmp_header_raw['colors_used'];
         }
         if ($PaletteEntries > 0) {
             $BMPpalette = $this->fread(4 * $PaletteEntries);
             $paletteoffset = 0;
             for ($i = 0; $i < $PaletteEntries; $i++) {
                 // RGBQUAD          - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_5f8y.asp
                 // BYTE    rgbBlue;
                 // BYTE    rgbGreen;
                 // BYTE    rgbRed;
                 // BYTE    rgbReserved;
                 $blue = Utils::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
                 $green = Utils::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
                 $red = Utils::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
                 if ($thisfile_bmp['type_os'] == 'OS/2' && $thisfile_bmp['type_version'] == 1) {
                     // no padding byte
                 } else {
                     $paletteoffset++;
                     // padding byte
                 }
                 $thisfile_bmp['palette'][$i] = $red << 16 | $green << 8 | $blue;
             }
         }
     }
     if ($this->ExtractData) {
         $this->fseek($thisfile_bmp_header_raw['data_offset']);
         $RowByteLength = ceil($thisfile_bmp_header_raw['width'] * ($thisfile_bmp_header_raw['bits_per_pixel'] / 8) / 4) * 4;
         // round up to nearest DWORD boundry
         $BMPpixelData = $this->fread($thisfile_bmp_header_raw['height'] * $RowByteLength);
         $pixeldataoffset = 0;
         $thisfile_bmp_header_raw['compression'] = isset($thisfile_bmp_header_raw['compression']) ? $thisfile_bmp_header_raw['compression'] : '';
         switch ($thisfile_bmp_header_raw['compression']) {
             case 0:
                 // BI_RGB
                 switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
                     case 1:
                         for ($row = $thisfile_bmp_header_raw['height'] - 1; $row >= 0; $row--) {
                             for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) {
                                 $paletteindexbyte = ord($BMPpixelData[$pixeldataoffset++]);
                                 for ($i = 7; $i >= 0; $i--) {
                                     $paletteindex = ($paletteindexbyte & 0x1 << $i) >> $i;
                                     $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
                                     $col++;
                                 }
                             }
                             while ($pixeldataoffset % 4 != 0) {
                                 // lines are padded to nearest DWORD
                                 $pixeldataoffset++;
                             }
                         }
                         break;
                     case 4:
                         for ($row = $thisfile_bmp_header_raw['height'] - 1; $row >= 0; $row--) {
                             for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) {
                                 $paletteindexbyte = ord($BMPpixelData[$pixeldataoffset++]);
                                 for ($i = 1; $i >= 0; $i--) {
                                     $paletteindex = ($paletteindexbyte & 0xf << 4 * $i) >> 4 * $i;
                                     $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
                                     $col++;
                                 }
                             }
                             while ($pixeldataoffset % 4 != 0) {
                                 // lines are padded to nearest DWORD
                                 $pixeldataoffset++;
                             }
                         }
                         break;
                     case 8:
                         for ($row = $thisfile_bmp_header_raw['height'] - 1; $row >= 0; $row--) {
                             for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
                                 $paletteindex = ord($BMPpixelData[$pixeldataoffset++]);
                                 $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
                             }
                             while ($pixeldataoffset % 4 != 0) {
                                 // lines are padded to nearest DWORD
                                 $pixeldataoffset++;
                             }
                         }
                         break;
                     case 24:
                         for ($row = $thisfile_bmp_header_raw['height'] - 1; $row >= 0; $row--) {
                             for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
                                 $thisfile_bmp['data'][$row][$col] = ord($BMPpixelData[$pixeldataoffset + 2]) << 16 | ord($BMPpixelData[$pixeldataoffset + 1]) << 8 | ord($BMPpixelData[$pixeldataoffset]);
                                 $pixeldataoffset += 3;
                             }
                             while ($pixeldataoffset % 4 != 0) {
                                 // lines are padded to nearest DWORD
                                 $pixeldataoffset++;
                             }
                         }
                         break;
                     case 32:
                         for ($row = $thisfile_bmp_header_raw['height'] - 1; $row >= 0; $row--) {
                             for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
                                 $thisfile_bmp['data'][$row][$col] = ord($BMPpixelData[$pixeldataoffset + 3]) << 24 | ord($BMPpixelData[$pixeldataoffset + 2]) << 16 | ord($BMPpixelData[$pixeldataoffset + 1]) << 8 | ord($BMPpixelData[$pixeldataoffset]);
                                 $pixeldataoffset += 4;
                             }
                             while ($pixeldataoffset % 4 != 0) {
                                 // lines are padded to nearest DWORD
                                 $pixeldataoffset++;
                             }
                         }
                         break;
                     case 16:
                         // ?
                         break;
                     default:
                         $info['error'][] = 'Unknown bits-per-pixel value (' . $thisfile_bmp_header_raw['bits_per_pixel'] . ') - cannot read pixel data';
                         break;
                 }
                 break;
             case 1:
                 // BI_RLE8 - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_6x0u.asp
                 switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
                     case 8:
                         $pixelcounter = 0;
                         while ($pixeldataoffset < strlen($BMPpixelData)) {
                             $firstbyte = Utils::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
                             $secondbyte = Utils::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
                             if ($firstbyte == 0) {
                                 // escaped/absolute mode - the first byte of the pair can be set to zero to
                                 // indicate an escape character that denotes the end of a line, the end of
                                 // a bitmap, or a delta, depending on the value of the second byte.
                                 switch ($secondbyte) {
                                     case 0:
                                         // end of line
                                         // no need for special processing, just ignore
                                         break;
                                     case 1:
                                         // end of bitmap
                                         $pixeldataoffset = strlen($BMPpixelData);
                                         // force to exit loop just in case
                                         break;
                                     case 2:
                                         // delta - The 2 bytes following the escape contain unsigned values
                                         // indicating the horizontal and vertical offsets of the next pixel
                                         // from the current position.
                                         $colincrement = Utils::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
                                         $rowincrement = Utils::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
                                         $col = $pixelcounter % $thisfile_bmp_header_raw['width'] + $colincrement;
                                         $row = $thisfile_bmp_header_raw['height'] - 1 - ($pixelcounter - $col) / $thisfile_bmp_header_raw['width'] - $rowincrement;
                                         $pixelcounter = $row * $thisfile_bmp_header_raw['width'] + $col;
                                         break;
                                     default:
                                         // In absolute mode, the first byte is zero and the second byte is a
                                         // value in the range 03H through FFH. The second byte represents the
                                         // number of bytes that follow, each of which contains the color index
                                         // of a single pixel. Each run must be aligned on a word boundary.
                                         for ($i = 0; $i < $secondbyte; $i++) {
                                             $paletteindex = Utils::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
                                             $col = $pixelcounter % $thisfile_bmp_header_raw['width'];
                                             $row = $thisfile_bmp_header_raw['height'] - 1 - ($pixelcounter - $col) / $thisfile_bmp_header_raw['width'];
                                             $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
                                             $pixelcounter++;
                                         }
                                         while ($pixeldataoffset % 2 != 0) {
                                             // Each run must be aligned on a word boundary.
                                             $pixeldataoffset++;
                                         }
                                         break;
                                 }
                             } else {
                                 // encoded mode - the first byte specifies the number of consecutive pixels
                                 // to be drawn using the color index contained in the second byte.
                                 for ($i = 0; $i < $firstbyte; $i++) {
                                     $col = $pixelcounter % $thisfile_bmp_header_raw['width'];
                                     $row = $thisfile_bmp_header_raw['height'] - 1 - ($pixelcounter - $col) / $thisfile_bmp_header_raw['width'];
                                     $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$secondbyte];
                                     $pixelcounter++;
                                 }
                             }
                         }
                         break;
                     default:
                         $info['error'][] = 'Unknown bits-per-pixel value (' . $thisfile_bmp_header_raw['bits_per_pixel'] . ') - cannot read pixel data';
                         break;
                 }
                 break;
             case 2:
                 // BI_RLE4 - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_6x0u.asp
                 switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
                     case 4:
                         $pixelcounter = 0;
                         while ($pixeldataoffset < strlen($BMPpixelData)) {
                             $firstbyte = Utils::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
                             $secondbyte = Utils::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
                             if ($firstbyte == 0) {
                                 // escaped/absolute mode - the first byte of the pair can be set to zero to
                                 // indicate an escape character that denotes the end of a line, the end of
                                 // a bitmap, or a delta, depending on the value of the second byte.
                                 switch ($secondbyte) {
                                     case 0:
                                         // end of line
                                         // no need for special processing, just ignore
                                         break;
                                     case 1:
                                         // end of bitmap
                                         $pixeldataoffset = strlen($BMPpixelData);
                                         // force to exit loop just in case
                                         break;
                                     case 2:
                                         // delta - The 2 bytes following the escape contain unsigned values
                                         // indicating the horizontal and vertical offsets of the next pixel
                                         // from the current position.
                                         $colincrement = Utils::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
                                         $rowincrement = Utils::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
                                         $col = $pixelcounter % $thisfile_bmp_header_raw['width'] + $colincrement;
                                         $row = $thisfile_bmp_header_raw['height'] - 1 - ($pixelcounter - $col) / $thisfile_bmp_header_raw['width'] - $rowincrement;
                                         $pixelcounter = $row * $thisfile_bmp_header_raw['width'] + $col;
                                         break;
                                     default:
                                         // In absolute mode, the first byte is zero. The second byte contains the number
                                         // of color indexes that follow. Subsequent bytes contain color indexes in their
                                         // high- and low-order 4 bits, one color index for each pixel. In absolute mode,
                                         // each run must be aligned on a word boundary.
                                         unset($paletteindexes);
                                         for ($i = 0; $i < ceil($secondbyte / 2); $i++) {
                                             $paletteindexbyte = Utils::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
                                             $paletteindexes[] = ($paletteindexbyte & 0xf0) >> 4;
                                             $paletteindexes[] = $paletteindexbyte & 0xf;
                                         }
                                         while ($pixeldataoffset % 2 != 0) {
                                             // Each run must be aligned on a word boundary.
                                             $pixeldataoffset++;
                                         }
                                         foreach ($paletteindexes as $paletteindex) {
                                             $col = $pixelcounter % $thisfile_bmp_header_raw['width'];
                                             $row = $thisfile_bmp_header_raw['height'] - 1 - ($pixelcounter - $col) / $thisfile_bmp_header_raw['width'];
                                             $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
                                             $pixelcounter++;
                                         }
                                         break;
                                 }
                             } else {
                                 // encoded mode - the first byte of the pair contains the number of pixels to be
                                 // drawn using the color indexes in the second byte. The second byte contains two
                                 // color indexes, one in its high-order 4 bits and one in its low-order 4 bits.
                                 // The first of the pixels is drawn using the color specified by the high-order
                                 // 4 bits, the second is drawn using the color in the low-order 4 bits, the third
                                 // is drawn using the color in the high-order 4 bits, and so on, until all the
                                 // pixels specified by the first byte have been drawn.
                                 $paletteindexes[0] = ($secondbyte & 0xf0) >> 4;
                                 $paletteindexes[1] = $secondbyte & 0xf;
                                 for ($i = 0; $i < $firstbyte; $i++) {
                                     $col = $pixelcounter % $thisfile_bmp_header_raw['width'];
                                     $row = $thisfile_bmp_header_raw['height'] - 1 - ($pixelcounter - $col) / $thisfile_bmp_header_raw['width'];
                                     $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindexes[$i % 2]];
                                     $pixelcounter++;
                                 }
                             }
                         }
                         break;
                     default:
                         $info['error'][] = 'Unknown bits-per-pixel value (' . $thisfile_bmp_header_raw['bits_per_pixel'] . ') - cannot read pixel data';
                         break;
                 }
                 break;
             case 3:
                 // BI_BITFIELDS
                 switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
                     case 16:
                     case 32:
                         $redshift = 0;
                         $greenshift = 0;
                         $blueshift = 0;
                         while (($thisfile_bmp_header_raw['red_mask'] >> $redshift & 0x1) == 0) {
                             $redshift++;
                         }
                         while (($thisfile_bmp_header_raw['green_mask'] >> $greenshift & 0x1) == 0) {
                             $greenshift++;
                         }
                         while (($thisfile_bmp_header_raw['blue_mask'] >> $blueshift & 0x1) == 0) {
                             $blueshift++;
                         }
                         for ($row = $thisfile_bmp_header_raw['height'] - 1; $row >= 0; $row--) {
                             for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
                                 $pixelvalue = Utils::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset, $thisfile_bmp_header_raw['bits_per_pixel'] / 8));
                                 $pixeldataoffset += $thisfile_bmp_header_raw['bits_per_pixel'] / 8;
                                 $red = intval(round((($pixelvalue & $thisfile_bmp_header_raw['red_mask']) >> $redshift) / ($thisfile_bmp_header_raw['red_mask'] >> $redshift) * 255));
                                 $green = intval(round((($pixelvalue & $thisfile_bmp_header_raw['green_mask']) >> $greenshift) / ($thisfile_bmp_header_raw['green_mask'] >> $greenshift) * 255));
                                 $blue = intval(round((($pixelvalue & $thisfile_bmp_header_raw['blue_mask']) >> $blueshift) / ($thisfile_bmp_header_raw['blue_mask'] >> $blueshift) * 255));
                                 $thisfile_bmp['data'][$row][$col] = $red << 16 | $green << 8 | $blue;
                             }
                             while ($pixeldataoffset % 4 != 0) {
                                 // lines are padded to nearest DWORD
                                 $pixeldataoffset++;
                             }
                         }
                         break;
                     default:
                         $info['error'][] = 'Unknown bits-per-pixel value (' . $thisfile_bmp_header_raw['bits_per_pixel'] . ') - cannot read pixel data';
                         break;
                 }
                 break;
             default:
                 // unhandled compression type
                 $info['error'][] = 'Unknown/unhandled compression type value (' . $thisfile_bmp_header_raw['compression'] . ') - cannot decompress pixel data';
                 break;
         }
     }
     return true;
 }
Пример #20
0
 public function getLyrics3Data($endoffset, $version, $length)
 {
     // http://www.volweb.cz/str/tags.htm
     $info =& $this->getid3->info;
     if (!Utils::intValueSupported($endoffset)) {
         $info['warning'][] = 'Unable to check for Lyrics3 because file is larger than ' . round(PHP_INT_MAX / 1073741824) . 'GB';
         return false;
     }
     $this->fseek($endoffset);
     if ($length <= 0) {
         return false;
     }
     $rawdata = $this->fread($length);
     $ParsedLyrics3['raw']['lyrics3version'] = $version;
     $ParsedLyrics3['raw']['lyrics3tagsize'] = $length;
     $ParsedLyrics3['tag_offset_start'] = $endoffset;
     $ParsedLyrics3['tag_offset_end'] = $endoffset + $length - 1;
     if (substr($rawdata, 0, 11) != 'LYRICSBEGIN') {
         if (strpos($rawdata, 'LYRICSBEGIN') !== false) {
             $info['warning'][] = '"LYRICSBEGIN" expected at ' . $endoffset . ' but actually found at ' . ($endoffset + strpos($rawdata, 'LYRICSBEGIN')) . ' - this is invalid for Lyrics3 v' . $version;
             $info['avdataend'] = $endoffset + strpos($rawdata, 'LYRICSBEGIN');
             $rawdata = substr($rawdata, strpos($rawdata, 'LYRICSBEGIN'));
             $length = strlen($rawdata);
             $ParsedLyrics3['tag_offset_start'] = $info['avdataend'];
             $ParsedLyrics3['raw']['lyrics3tagsize'] = $length;
         } else {
             $info['error'][] = '"LYRICSBEGIN" expected at ' . $endoffset . ' but found "' . substr($rawdata, 0, 11) . '" instead';
             return false;
         }
     }
     switch ($version) {
         case 1:
             if (substr($rawdata, strlen($rawdata) - 9, 9) == 'LYRICSEND') {
                 $ParsedLyrics3['raw']['LYR'] = trim(substr($rawdata, 11, strlen($rawdata) - 11 - 9));
                 $this->Lyrics3LyricsTimestampParse($ParsedLyrics3);
             } else {
                 $info['error'][] = '"LYRICSEND" expected at ' . ($this->ftell() - 11 + $length - 9) . ' but found "' . substr($rawdata, strlen($rawdata) - 9, 9) . '" instead';
                 return false;
             }
             break;
         case 2:
             if (substr($rawdata, strlen($rawdata) - 9, 9) == 'LYRICS200') {
                 $ParsedLyrics3['raw']['unparsed'] = substr($rawdata, 11, strlen($rawdata) - 11 - 9 - 6);
                 // LYRICSBEGIN + LYRICS200 + LSZ
                 $rawdata = $ParsedLyrics3['raw']['unparsed'];
                 while (strlen($rawdata) > 0) {
                     $fieldname = substr($rawdata, 0, 3);
                     $fieldsize = (int) substr($rawdata, 3, 5);
                     $ParsedLyrics3['raw'][$fieldname] = substr($rawdata, 8, $fieldsize);
                     $rawdata = substr($rawdata, 3 + 5 + $fieldsize);
                 }
                 if (isset($ParsedLyrics3['raw']['IND'])) {
                     $i = 0;
                     $flagnames = array('lyrics', 'timestamps', 'inhibitrandom');
                     foreach ($flagnames as $flagname) {
                         if (strlen($ParsedLyrics3['raw']['IND']) > $i++) {
                             $ParsedLyrics3['flags'][$flagname] = $this->IntString2Bool(substr($ParsedLyrics3['raw']['IND'], $i, 1 - 1));
                         }
                     }
                 }
                 $fieldnametranslation = array('ETT' => 'title', 'EAR' => 'artist', 'EAL' => 'album', 'INF' => 'comment', 'AUT' => 'author');
                 foreach ($fieldnametranslation as $key => $value) {
                     if (isset($ParsedLyrics3['raw'][$key])) {
                         $ParsedLyrics3['comments'][$value][] = trim($ParsedLyrics3['raw'][$key]);
                     }
                 }
                 if (isset($ParsedLyrics3['raw']['IMG'])) {
                     $imagestrings = explode("\r\n", $ParsedLyrics3['raw']['IMG']);
                     foreach ($imagestrings as $key => $imagestring) {
                         if (strpos($imagestring, '||') !== false) {
                             $imagearray = explode('||', $imagestring);
                             $ParsedLyrics3['images'][$key]['filename'] = isset($imagearray[0]) ? $imagearray[0] : '';
                             $ParsedLyrics3['images'][$key]['description'] = isset($imagearray[1]) ? $imagearray[1] : '';
                             $ParsedLyrics3['images'][$key]['timestamp'] = $this->Lyrics3Timestamp2Seconds(isset($imagearray[2]) ? $imagearray[2] : '');
                         }
                     }
                 }
                 if (isset($ParsedLyrics3['raw']['LYR'])) {
                     $this->Lyrics3LyricsTimestampParse($ParsedLyrics3);
                 }
             } else {
                 $info['error'][] = '"LYRICS200" expected at ' . ($this->ftell() - 11 + $length - 9) . ' but found "' . substr($rawdata, strlen($rawdata) - 9, 9) . '" instead';
                 return false;
             }
             break;
         default:
             $info['error'][] = 'Cannot process Lyrics3 version ' . $version . ' (only v1 and v2)';
             return false;
             break;
     }
     if (isset($info['id3v1']['tag_offset_start']) && $info['id3v1']['tag_offset_start'] <= $ParsedLyrics3['tag_offset_end']) {
         $info['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in Lyrics3 tag data';
         unset($info['id3v1']);
         foreach ($info['warning'] as $key => $value) {
             if ($value == 'Some ID3v1 fields do not use NULL characters for padding') {
                 unset($info['warning'][$key]);
                 sort($info['warning']);
                 break;
             }
         }
     }
     $info['lyrics3'] = $ParsedLyrics3;
     return true;
 }
Пример #21
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     // based loosely on code from TMonkey by Jurgen Faul <jfaulØgmx*de>
     // http://jfaul.de/atl  or  http://j-faul.virtualave.net/atl/atl.html
     $info['fileformat'] = 'mac';
     $info['audio']['dataformat'] = 'mac';
     $info['audio']['bitrate_mode'] = 'vbr';
     $info['audio']['lossless'] = true;
     $info['monkeys_audio']['raw'] = array();
     $thisfile_monkeysaudio =& $info['monkeys_audio'];
     $thisfile_monkeysaudio_raw =& $thisfile_monkeysaudio['raw'];
     $this->fseek($info['avdataoffset']);
     $MACheaderData = $this->fread(74);
     $thisfile_monkeysaudio_raw['magic'] = substr($MACheaderData, 0, 4);
     $magic = 'MAC ';
     if ($thisfile_monkeysaudio_raw['magic'] != $magic) {
         $info['error'][] = 'Expecting "' . Utils::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes($thisfile_monkeysaudio_raw['magic']) . '"';
         unset($info['fileformat']);
         return false;
     }
     $thisfile_monkeysaudio_raw['nVersion'] = Utils::LittleEndian2Int(substr($MACheaderData, 4, 2));
     // appears to be uint32 in 3.98+
     if ($thisfile_monkeysaudio_raw['nVersion'] < 3980) {
         $thisfile_monkeysaudio_raw['nCompressionLevel'] = Utils::LittleEndian2Int(substr($MACheaderData, 6, 2));
         $thisfile_monkeysaudio_raw['nFormatFlags'] = Utils::LittleEndian2Int(substr($MACheaderData, 8, 2));
         $thisfile_monkeysaudio_raw['nChannels'] = Utils::LittleEndian2Int(substr($MACheaderData, 10, 2));
         $thisfile_monkeysaudio_raw['nSampleRate'] = Utils::LittleEndian2Int(substr($MACheaderData, 12, 4));
         $thisfile_monkeysaudio_raw['nHeaderDataBytes'] = Utils::LittleEndian2Int(substr($MACheaderData, 16, 4));
         $thisfile_monkeysaudio_raw['nWAVTerminatingBytes'] = Utils::LittleEndian2Int(substr($MACheaderData, 20, 4));
         $thisfile_monkeysaudio_raw['nTotalFrames'] = Utils::LittleEndian2Int(substr($MACheaderData, 24, 4));
         $thisfile_monkeysaudio_raw['nFinalFrameSamples'] = Utils::LittleEndian2Int(substr($MACheaderData, 28, 4));
         $thisfile_monkeysaudio_raw['nPeakLevel'] = Utils::LittleEndian2Int(substr($MACheaderData, 32, 4));
         $thisfile_monkeysaudio_raw['nSeekElements'] = Utils::LittleEndian2Int(substr($MACheaderData, 38, 2));
         $offset = 8;
     } else {
         $offset = 8;
         // APE_DESCRIPTOR
         $thisfile_monkeysaudio_raw['nDescriptorBytes'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['nHeaderBytes'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['nSeekTableBytes'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['nHeaderDataBytes'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['nAPEFrameDataBytes'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['nAPEFrameDataBytesHigh'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['nTerminatingDataBytes'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['cFileMD5'] = substr($MACheaderData, $offset, 16);
         $offset += 16;
         // APE_HEADER
         $thisfile_monkeysaudio_raw['nCompressionLevel'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 2));
         $offset += 2;
         $thisfile_monkeysaudio_raw['nFormatFlags'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 2));
         $offset += 2;
         $thisfile_monkeysaudio_raw['nBlocksPerFrame'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['nFinalFrameBlocks'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['nTotalFrames'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['nBitsPerSample'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 2));
         $offset += 2;
         $thisfile_monkeysaudio_raw['nChannels'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 2));
         $offset += 2;
         $thisfile_monkeysaudio_raw['nSampleRate'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
     }
     $thisfile_monkeysaudio['flags']['8-bit'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x1);
     $thisfile_monkeysaudio['flags']['crc-32'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x2);
     $thisfile_monkeysaudio['flags']['peak_level'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x4);
     $thisfile_monkeysaudio['flags']['24-bit'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x8);
     $thisfile_monkeysaudio['flags']['seek_elements'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x10);
     $thisfile_monkeysaudio['flags']['no_wav_header'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x20);
     $thisfile_monkeysaudio['version'] = $thisfile_monkeysaudio_raw['nVersion'] / 1000;
     $thisfile_monkeysaudio['compression'] = $this->MonkeyCompressionLevelNameLookup($thisfile_monkeysaudio_raw['nCompressionLevel']);
     if ($thisfile_monkeysaudio_raw['nVersion'] < 3980) {
         $thisfile_monkeysaudio['samples_per_frame'] = $this->MonkeySamplesPerFrame($thisfile_monkeysaudio_raw['nVersion'], $thisfile_monkeysaudio_raw['nCompressionLevel']);
     }
     $thisfile_monkeysaudio['bits_per_sample'] = $thisfile_monkeysaudio['flags']['24-bit'] ? 24 : ($thisfile_monkeysaudio['flags']['8-bit'] ? 8 : 16);
     $thisfile_monkeysaudio['channels'] = $thisfile_monkeysaudio_raw['nChannels'];
     $info['audio']['channels'] = $thisfile_monkeysaudio['channels'];
     $thisfile_monkeysaudio['sample_rate'] = $thisfile_monkeysaudio_raw['nSampleRate'];
     if ($thisfile_monkeysaudio['sample_rate'] == 0) {
         $info['error'][] = 'Corrupt MAC file: frequency == zero';
         return false;
     }
     $info['audio']['sample_rate'] = $thisfile_monkeysaudio['sample_rate'];
     if ($thisfile_monkeysaudio['flags']['peak_level']) {
         $thisfile_monkeysaudio['peak_level'] = $thisfile_monkeysaudio_raw['nPeakLevel'];
         $thisfile_monkeysaudio['peak_ratio'] = $thisfile_monkeysaudio['peak_level'] / pow(2, $thisfile_monkeysaudio['bits_per_sample'] - 1);
     }
     if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) {
         $thisfile_monkeysaudio['samples'] = ($thisfile_monkeysaudio_raw['nTotalFrames'] - 1) * $thisfile_monkeysaudio_raw['nBlocksPerFrame'] + $thisfile_monkeysaudio_raw['nFinalFrameBlocks'];
     } else {
         $thisfile_monkeysaudio['samples'] = ($thisfile_monkeysaudio_raw['nTotalFrames'] - 1) * $thisfile_monkeysaudio['samples_per_frame'] + $thisfile_monkeysaudio_raw['nFinalFrameSamples'];
     }
     $thisfile_monkeysaudio['playtime'] = $thisfile_monkeysaudio['samples'] / $thisfile_monkeysaudio['sample_rate'];
     if ($thisfile_monkeysaudio['playtime'] == 0) {
         $info['error'][] = 'Corrupt MAC file: playtime == zero';
         return false;
     }
     $info['playtime_seconds'] = $thisfile_monkeysaudio['playtime'];
     $thisfile_monkeysaudio['compressed_size'] = $info['avdataend'] - $info['avdataoffset'];
     $thisfile_monkeysaudio['uncompressed_size'] = $thisfile_monkeysaudio['samples'] * $thisfile_monkeysaudio['channels'] * ($thisfile_monkeysaudio['bits_per_sample'] / 8);
     if ($thisfile_monkeysaudio['uncompressed_size'] == 0) {
         $info['error'][] = 'Corrupt MAC file: uncompressed_size == zero';
         return false;
     }
     $thisfile_monkeysaudio['compression_ratio'] = $thisfile_monkeysaudio['compressed_size'] / ($thisfile_monkeysaudio['uncompressed_size'] + $thisfile_monkeysaudio_raw['nHeaderDataBytes']);
     $thisfile_monkeysaudio['bitrate'] = $thisfile_monkeysaudio['samples'] * $thisfile_monkeysaudio['channels'] * $thisfile_monkeysaudio['bits_per_sample'] / $thisfile_monkeysaudio['playtime'] * $thisfile_monkeysaudio['compression_ratio'];
     $info['audio']['bitrate'] = $thisfile_monkeysaudio['bitrate'];
     // add size of MAC header to avdataoffset
     if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) {
         $info['avdataoffset'] += $thisfile_monkeysaudio_raw['nDescriptorBytes'];
         $info['avdataoffset'] += $thisfile_monkeysaudio_raw['nHeaderBytes'];
         $info['avdataoffset'] += $thisfile_monkeysaudio_raw['nSeekTableBytes'];
         $info['avdataoffset'] += $thisfile_monkeysaudio_raw['nHeaderDataBytes'];
         $info['avdataend'] -= $thisfile_monkeysaudio_raw['nTerminatingDataBytes'];
     } else {
         $info['avdataoffset'] += $offset;
     }
     if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) {
         if ($thisfile_monkeysaudio_raw['cFileMD5'] === str_repeat("", 16)) {
             //$info['warning'][] = 'cFileMD5 is null';
         } else {
             $info['md5_data_source'] = '';
             $md5 = $thisfile_monkeysaudio_raw['cFileMD5'];
             for ($i = 0; $i < strlen($md5); $i++) {
                 $info['md5_data_source'] .= str_pad(dechex(ord($md5[$i])), 2, '00', STR_PAD_LEFT);
             }
             if (!preg_match('/^[0-9a-f]{32}$/', $info['md5_data_source'])) {
                 unset($info['md5_data_source']);
             }
         }
     }
     $info['audio']['bits_per_sample'] = $thisfile_monkeysaudio['bits_per_sample'];
     $info['audio']['encoder'] = 'MAC v' . number_format($thisfile_monkeysaudio['version'], 2);
     $info['audio']['encoder_options'] = ucfirst($thisfile_monkeysaudio['compression']) . ' compression';
     return true;
 }
Пример #22
0
 public function ParseOptimFROGheader45()
 {
     // for fileformat of v4.50a and higher
     $info =& $this->getid3->info;
     $RIFFdata = '';
     $this->fseek($info['avdataoffset']);
     while (!feof($this->getid3->fp) && $this->ftell() < $info['avdataend']) {
         $BlockOffset = $this->ftell();
         $BlockData = $this->fread(8);
         $offset = 8;
         $BlockName = substr($BlockData, 0, 4);
         $BlockSize = Utils::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 .= $this->fread($BlockSize);
                 $thisfile_ofr_thisblock['total_samples'] = Utils::LittleEndian2Int(substr($BlockData, $offset, 6));
                 $offset += 6;
                 $thisfile_ofr_thisblock['raw']['sample_type'] = Utils::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'] = Utils::LittleEndian2Int(substr($BlockData, $offset, 1));
                 $thisfile_ofr_thisblock['channels'] = $thisfile_ofr_thisblock['channel_config'];
                 $offset += 1;
                 $thisfile_ofr_thisblock['sample_rate'] = Utils::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'] = Utils::LittleEndian2Int(substr($BlockData, $offset, 2));
                     $thisfile_ofr_thisblock['encoder'] = $this->OptimFROGencoderNameLookup($thisfile_ofr_thisblock['raw']['encoder_id']);
                     $offset += 2;
                     $thisfile_ofr_thisblock['raw']['compression'] = Utils::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(Utils::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 .= $this->fread(14);
                 $this->fseek($BlockSize - 14, SEEK_CUR);
                 $COMPdata['crc_32'] = Utils::LittleEndian2Int(substr($BlockData, $offset, 4));
                 $offset += 4;
                 $COMPdata['sample_count'] = Utils::LittleEndian2Int(substr($BlockData, $offset, 4));
                 $offset += 4;
                 $COMPdata['raw']['sample_type'] = Utils::LittleEndian2Int(substr($BlockData, $offset, 1));
                 $COMPdata['sample_type'] = $this->OptimFROGsampleTypeLookup($COMPdata['raw']['sample_type']);
                 $offset += 1;
                 $COMPdata['raw']['channel_configuration'] = Utils::LittleEndian2Int(substr($BlockData, $offset, 1));
                 $COMPdata['channel_configuration'] = $this->OptimFROGchannelConfigurationLookup($COMPdata['raw']['channel_configuration']);
                 $offset += 1;
                 $COMPdata['raw']['algorithm_id'] = Utils::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'] = Utils::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 .= $this->fread($BlockSize);
                 break;
             case 'TAIL':
                 $thisfile_ofr_thisblock['offset'] = $BlockOffset;
                 $thisfile_ofr_thisblock['size'] = $BlockSize;
                 if ($BlockSize > 0) {
                     $RIFFdata .= $this->fread($BlockSize);
                 }
                 break;
             case 'RECV':
                 // block contains no useful meta data - simply note and skip
                 $thisfile_ofr_thisblock['offset'] = $BlockOffset;
                 $thisfile_ofr_thisblock['size'] = $BlockSize;
                 $this->fseek($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 getID3()';
                 $this->fseek($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'] = $this->fread($BlockSize);
                     $thisfile_ofr_thisblock['md5_string'] = Utils::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';
                     $this->fseek($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'];
                 $this->fseek($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 GetID3();
     $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;
 }
Пример #23
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     if (!Utils::intValueSupported($info['filesize'])) {
         $info['warning'][] = 'Unable to check for ID3v1 because file is larger than ' . round(PHP_INT_MAX / 1073741824) . 'GB';
         return false;
     }
     $this->fseek(-256, SEEK_END);
     $preid3v1 = $this->fread(128);
     $id3v1tag = $this->fread(128);
     if (substr($id3v1tag, 0, 3) == 'TAG') {
         $info['avdataend'] = $info['filesize'] - 128;
         $ParsedID3v1['title'] = $this->cutfield(substr($id3v1tag, 3, 30));
         $ParsedID3v1['artist'] = $this->cutfield(substr($id3v1tag, 33, 30));
         $ParsedID3v1['album'] = $this->cutfield(substr($id3v1tag, 63, 30));
         $ParsedID3v1['year'] = $this->cutfield(substr($id3v1tag, 93, 4));
         $ParsedID3v1['comment'] = substr($id3v1tag, 97, 30);
         // can't remove nulls yet, track detection depends on them
         $ParsedID3v1['genreid'] = ord(substr($id3v1tag, 127, 1));
         // If second-last byte of comment field is null and last byte of comment field is non-null
         // then this is ID3v1.1 and the comment field is 28 bytes long and the 30th byte is the track number
         if ($id3v1tag[125] === "" && $id3v1tag[126] !== "") {
             $ParsedID3v1['track'] = ord(substr($ParsedID3v1['comment'], 29, 1));
             $ParsedID3v1['comment'] = substr($ParsedID3v1['comment'], 0, 28);
         }
         $ParsedID3v1['comment'] = $this->cutfield($ParsedID3v1['comment']);
         $ParsedID3v1['genre'] = $this->LookupGenreName($ParsedID3v1['genreid']);
         if (!empty($ParsedID3v1['genre'])) {
             unset($ParsedID3v1['genreid']);
         }
         if (isset($ParsedID3v1['genre']) && (empty($ParsedID3v1['genre']) || $ParsedID3v1['genre'] == 'Unknown')) {
             unset($ParsedID3v1['genre']);
         }
         foreach ($ParsedID3v1 as $key => $value) {
             $ParsedID3v1['comments'][$key][0] = $value;
         }
         // ID3v1 data is supposed to be padded with NULL characters, but some taggers pad with spaces
         $GoodFormatID3v1tag = $this->GenerateID3v1Tag($ParsedID3v1['title'], $ParsedID3v1['artist'], $ParsedID3v1['album'], $ParsedID3v1['year'], isset($ParsedID3v1['genre']) ? $this->LookupGenreID($ParsedID3v1['genre']) : false, $ParsedID3v1['comment'], !empty($ParsedID3v1['track']) ? $ParsedID3v1['track'] : '');
         $ParsedID3v1['padding_valid'] = true;
         if ($id3v1tag !== $GoodFormatID3v1tag) {
             $ParsedID3v1['padding_valid'] = false;
             $info['warning'][] = 'Some ID3v1 fields do not use NULL characters for padding';
         }
         $ParsedID3v1['tag_offset_end'] = $info['filesize'];
         $ParsedID3v1['tag_offset_start'] = $ParsedID3v1['tag_offset_end'] - 128;
         $info['id3v1'] = $ParsedID3v1;
     }
     if (substr($preid3v1, 0, 3) == 'TAG') {
         // The way iTunes handles tags is, well, brain-damaged.
         // It completely ignores v1 if ID3v2 is present.
         // This goes as far as adding a new v1 tag *even if there already is one*
         // A suspected double-ID3v1 tag has been detected, but it could be that
         // the "TAG" identifier is a legitimate part of an APE or Lyrics3 tag
         if (substr($preid3v1, 96, 8) == 'APETAGEX') {
             // an APE tag footer was found before the last ID3v1, assume false "TAG" synch
         } elseif (substr($preid3v1, 119, 6) == 'LYRICS') {
             // a Lyrics3 tag footer was found before the last ID3v1, assume false "TAG" synch
         } else {
             // APE and Lyrics3 footers not found - assume double ID3v1
             $info['warning'][] = 'Duplicate ID3v1 tag detected - this has been known to happen with iTunes';
             $info['avdataend'] -= 128;
         }
     }
     return true;
 }
Пример #24
0
 public function getNSVfHeaderFilepointer($fileoffset, $getTOCoffsets = false)
 {
     $info =& $this->getid3->info;
     $this->fseek($fileoffset);
     $NSVfheader = $this->fread(28);
     $offset = 0;
     $info['nsv']['NSVf']['identifier'] = substr($NSVfheader, $offset, 4);
     $offset += 4;
     if ($info['nsv']['NSVf']['identifier'] != 'NSVf') {
         $info['error'][] = 'expected "NSVf" at offset (' . $fileoffset . '), found "' . $info['nsv']['NSVf']['identifier'] . '" instead';
         unset($info['nsv']['NSVf']);
         return false;
     }
     $info['nsv']['NSVs']['offset'] = $fileoffset;
     $info['nsv']['NSVf']['header_length'] = Utils::LittleEndian2Int(substr($NSVfheader, $offset, 4));
     $offset += 4;
     $info['nsv']['NSVf']['file_size'] = Utils::LittleEndian2Int(substr($NSVfheader, $offset, 4));
     $offset += 4;
     if ($info['nsv']['NSVf']['file_size'] > $info['avdataend']) {
         $info['warning'][] = 'truncated file - NSVf header indicates ' . $info['nsv']['NSVf']['file_size'] . ' bytes, file actually ' . $info['avdataend'] . ' bytes';
     }
     $info['nsv']['NSVf']['playtime_ms'] = Utils::LittleEndian2Int(substr($NSVfheader, $offset, 4));
     $offset += 4;
     $info['nsv']['NSVf']['meta_size'] = Utils::LittleEndian2Int(substr($NSVfheader, $offset, 4));
     $offset += 4;
     $info['nsv']['NSVf']['TOC_entries_1'] = Utils::LittleEndian2Int(substr($NSVfheader, $offset, 4));
     $offset += 4;
     $info['nsv']['NSVf']['TOC_entries_2'] = Utils::LittleEndian2Int(substr($NSVfheader, $offset, 4));
     $offset += 4;
     if ($info['nsv']['NSVf']['playtime_ms'] == 0) {
         $info['error'][] = 'Corrupt NSV file: NSVf.playtime_ms == zero';
         return false;
     }
     $NSVfheader .= $this->fread($info['nsv']['NSVf']['meta_size'] + 4 * $info['nsv']['NSVf']['TOC_entries_1'] + 4 * $info['nsv']['NSVf']['TOC_entries_2']);
     $NSVfheaderlength = strlen($NSVfheader);
     $info['nsv']['NSVf']['metadata'] = substr($NSVfheader, $offset, $info['nsv']['NSVf']['meta_size']);
     $offset += $info['nsv']['NSVf']['meta_size'];
     if ($getTOCoffsets) {
         $TOCcounter = 0;
         while ($TOCcounter < $info['nsv']['NSVf']['TOC_entries_1']) {
             if ($TOCcounter < $info['nsv']['NSVf']['TOC_entries_1']) {
                 $info['nsv']['NSVf']['TOC_1'][$TOCcounter] = Utils::LittleEndian2Int(substr($NSVfheader, $offset, 4));
                 $offset += 4;
                 $TOCcounter++;
             }
         }
     }
     if (trim($info['nsv']['NSVf']['metadata']) != '') {
         $info['nsv']['NSVf']['metadata'] = str_replace('`', "", $info['nsv']['NSVf']['metadata']);
         $CommentPairArray = explode("" . ' ', $info['nsv']['NSVf']['metadata']);
         foreach ($CommentPairArray as $CommentPair) {
             if (strstr($CommentPair, '=' . "")) {
                 list($key, $value) = explode('=' . "", $CommentPair, 2);
                 $info['nsv']['comments'][strtolower($key)][] = trim(str_replace("", '', $value));
             }
         }
     }
     $info['playtime_seconds'] = $info['nsv']['NSVf']['playtime_ms'] / 1000;
     $info['bitrate'] = $info['nsv']['NSVf']['file_size'] * 8 / $info['playtime_seconds'];
     return true;
 }
Пример #25
0
 public function RemoveReal()
 {
     // File MUST be writeable - CHMOD(646) at least
     if (is_writeable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'r+b'))) {
         // Initialize getID3 engine
         $getID3 = new GetID3();
         $OldThisFileInfo = $getID3->analyze($this->filename);
         if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) {
             $this->errors[] = 'Cannot remove Real tags from old-style file format';
             fclose($fp_source);
             return false;
         }
         if (empty($OldThisFileInfo['real']['chunks'])) {
             $this->errors[] = 'Cannot remove Real tags because cannot find DATA chunk in file';
             fclose($fp_source);
             return false;
         }
         foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) {
             $oldChunkInfo[$chunkarray['name']] = $chunkarray;
         }
         if (empty($oldChunkInfo['CONT'])) {
             // no existing CONT chunk
             fclose($fp_source);
             return true;
         }
         $BeforeOffset = $oldChunkInfo['CONT']['offset'];
         $AfterOffset = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length'];
         if ($tempfilename = tempnam(Utils::getTempDirectory(), 'getID3')) {
             if (is_writable($tempfilename) && is_file($tempfilename) && ($fp_temp = fopen($tempfilename, 'wb'))) {
                 rewind($fp_source);
                 fwrite($fp_temp, fread($fp_source, $BeforeOffset));
                 fseek($fp_source, $AfterOffset);
                 while ($buffer = fread($fp_source, $this->fread_buffer_size)) {
                     fwrite($fp_temp, $buffer, strlen($buffer));
                 }
                 fclose($fp_temp);
                 if (copy($tempfilename, $this->filename)) {
                     unlink($tempfilename);
                     fclose($fp_source);
                     return true;
                 }
                 unlink($tempfilename);
                 $this->errors[] = 'FAILED: copy(' . $tempfilename . ', ' . $this->filename . ')';
             } else {
                 $this->errors[] = 'Could not fopen("' . $tempfilename . '", "wb")';
             }
         }
         fclose($fp_source);
         return false;
     }
     $this->errors[] = 'Could not fopen("' . $this->filename . '", "r+b")';
     return false;
 }
Пример #26
0
 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;
 }
Пример #27
0
 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;
 }
Пример #28
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $this->fseek($info['avdataoffset']);
     $ShortenHeader = $this->fread(8);
     $magic = 'ajkg';
     if (substr($ShortenHeader, 0, 4) != $magic) {
         $info['error'][] = 'Expecting "' . Utils::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes(substr($ShortenHeader, 0, 4)) . '"';
         return false;
     }
     $info['fileformat'] = 'shn';
     $info['audio']['dataformat'] = 'shn';
     $info['audio']['lossless'] = true;
     $info['audio']['bitrate_mode'] = 'vbr';
     $info['shn']['version'] = Utils::LittleEndian2Int(substr($ShortenHeader, 4, 1));
     $this->fseek($info['avdataend'] - 12);
     $SeekTableSignatureTest = $this->fread(12);
     $info['shn']['seektable']['present'] = (bool) (substr($SeekTableSignatureTest, 4, 8) == 'SHNAMPSK');
     if ($info['shn']['seektable']['present']) {
         $info['shn']['seektable']['length'] = Utils::LittleEndian2Int(substr($SeekTableSignatureTest, 0, 4));
         $info['shn']['seektable']['offset'] = $info['avdataend'] - $info['shn']['seektable']['length'];
         $this->fseek($info['shn']['seektable']['offset']);
         $SeekTableMagic = $this->fread(4);
         $magic = 'SEEK';
         if ($SeekTableMagic != $magic) {
             $info['error'][] = 'Expecting "' . Utils::PrintHexBytes($magic) . '" at offset ' . $info['shn']['seektable']['offset'] . ', found "' . Utils::PrintHexBytes($SeekTableMagic) . '"';
             return false;
         } else {
             // typedef struct tag_TSeekEntry
             // {
             //   unsigned long SampleNumber;
             //   unsigned long SHNFileByteOffset;
             //   unsigned long SHNLastBufferReadPosition;
             //   unsigned short SHNByteGet;
             //   unsigned short SHNBufferOffset;
             //   unsigned short SHNFileBitOffset;
             //   unsigned long SHNGBuffer;
             //   unsigned short SHNBitShift;
             //   long CBuf0[3];
             //   long CBuf1[3];
             //   long Offset0[4];
             //   long Offset1[4];
             // }TSeekEntry;
             $SeekTableData = $this->fread($info['shn']['seektable']['length'] - 16);
             $info['shn']['seektable']['entry_count'] = floor(strlen($SeekTableData) / 80);
             //$info['shn']['seektable']['entries'] = array();
             //$SeekTableOffset = 0;
             //for ($i = 0; $i < $info['shn']['seektable']['entry_count']; $i++) {
             //	$SeekTableEntry['sample_number'] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
             //	$SeekTableOffset += 4;
             //	$SeekTableEntry['shn_file_byte_offset'] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
             //	$SeekTableOffset += 4;
             //	$SeekTableEntry['shn_last_buffer_read_position'] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
             //	$SeekTableOffset += 4;
             //	$SeekTableEntry['shn_byte_get'] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2));
             //	$SeekTableOffset += 2;
             //	$SeekTableEntry['shn_buffer_offset'] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2));
             //	$SeekTableOffset += 2;
             //	$SeekTableEntry['shn_file_bit_offset'] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2));
             //	$SeekTableOffset += 2;
             //	$SeekTableEntry['shn_gbuffer'] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
             //	$SeekTableOffset += 4;
             //	$SeekTableEntry['shn_bit_shift'] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2));
             //	$SeekTableOffset += 2;
             //	for ($j = 0; $j < 3; $j++) {
             //		$SeekTableEntry['cbuf0'][$j] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
             //		$SeekTableOffset += 4;
             //	}
             //	for ($j = 0; $j < 3; $j++) {
             //		$SeekTableEntry['cbuf1'][$j] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
             //		$SeekTableOffset += 4;
             //	}
             //	for ($j = 0; $j < 4; $j++) {
             //		$SeekTableEntry['offset0'][$j] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
             //		$SeekTableOffset += 4;
             //	}
             //	for ($j = 0; $j < 4; $j++) {
             //		$SeekTableEntry['offset1'][$j] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
             //		$SeekTableOffset += 4;
             //	}
             //
             //	$info['shn']['seektable']['entries'][] = $SeekTableEntry;
             //}
         }
     }
     if (Utils::isWindows()) {
         $RequiredFiles = array('shorten.exe', 'cygwin1.dll', 'head.exe');
         foreach ($RequiredFiles as $required_file) {
             if (!is_readable(Utils::getHelperAppDirectory() . $required_file)) {
                 $info['error'][] = Utils::getHelperAppDirectory() . $required_file . ' does not exist';
                 return false;
             }
         }
         $commandline = Utils::getHelperAppDirectory() . 'shorten.exe -x "' . $info['filenamepath'] . '" - | ' . Utils::getHelperAppDirectory() . 'head.exe -c 64';
         $commandline = str_replace('/', '\\', $commandline);
     } else {
         static $shorten_present;
         if (!isset($shorten_present)) {
             $shorten_present = file_exists('/usr/local/bin/shorten') || `which shorten`;
         }
         if (!$shorten_present) {
             $info['error'][] = 'shorten binary was not found in path or /usr/local/bin';
             return false;
         }
         $commandline = (file_exists('/usr/local/bin/shorten') ? '/usr/local/bin/' : '') . 'shorten -x ' . escapeshellarg($info['filenamepath']) . ' - | head -c 64';
     }
     $output = `{$commandline}`;
     if (!empty($output) && substr($output, 12, 4) == 'fmt ') {
         $fmt_size = Utils::LittleEndian2Int(substr($output, 16, 4));
         $DecodedWAVFORMATEX = Riff::parseWAVEFORMATex(substr($output, 20, $fmt_size));
         $info['audio']['channels'] = $DecodedWAVFORMATEX['channels'];
         $info['audio']['bits_per_sample'] = $DecodedWAVFORMATEX['bits_per_sample'];
         $info['audio']['sample_rate'] = $DecodedWAVFORMATEX['sample_rate'];
         if (substr($output, 20 + $fmt_size, 4) == 'data') {
             $info['playtime_seconds'] = Utils::LittleEndian2Int(substr($output, 20 + 4 + $fmt_size, 4)) / $DecodedWAVFORMATEX['raw']['nAvgBytesPerSec'];
         } else {
             $info['error'][] = 'shorten failed to decode DATA chunk to expected location, cannot determine playtime';
             return false;
         }
         $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds'] * 8;
     } else {
         $info['error'][] = 'shorten failed to decode file to WAV for parsing';
         return false;
     }
     return true;
 }
Пример #29
0
 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;
 }
Пример #30
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $OriginalAVdataOffset = $info['avdataoffset'];
     $this->fseek($info['avdataoffset']);
     $VOCheader = $this->fread(26);
     $magic = 'Creative Voice File';
     if (substr($VOCheader, 0, 19) != $magic) {
         $info['error'][] = 'Expecting "' . Utils::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes(substr($VOCheader, 0, 19)) . '"';
         return false;
     }
     // shortcuts
     $thisfile_audio =& $info['audio'];
     $info['voc'] = array();
     $thisfile_voc =& $info['voc'];
     $info['fileformat'] = 'voc';
     $thisfile_audio['dataformat'] = 'voc';
     $thisfile_audio['bitrate_mode'] = 'cbr';
     $thisfile_audio['lossless'] = true;
     $thisfile_audio['channels'] = 1;
     // might be overriden below
     $thisfile_audio['bits_per_sample'] = 8;
     // might be overriden below
     // byte #     Description
     // ------     ------------------------------------------
     // 00-12      'Creative Voice File'
     // 13         1A (eof to abort printing of file)
     // 14-15      Offset of first datablock in .voc file (std 1A 00 in Intel Notation)
     // 16-17      Version number (minor,major) (VOC-HDR puts 0A 01)
     // 18-19      2's Comp of Ver. # + 1234h (VOC-HDR puts 29 11)
     $thisfile_voc['header']['datablock_offset'] = Utils::LittleEndian2Int(substr($VOCheader, 20, 2));
     $thisfile_voc['header']['minor_version'] = Utils::LittleEndian2Int(substr($VOCheader, 22, 1));
     $thisfile_voc['header']['major_version'] = Utils::LittleEndian2Int(substr($VOCheader, 23, 1));
     do {
         $BlockOffset = $this->ftell();
         $BlockData = $this->fread(4);
         $BlockType = ord($BlockData[0]);
         $BlockSize = Utils::LittleEndian2Int(substr($BlockData, 1, 3));
         $ThisBlock = array();
         Utils::safe_inc($thisfile_voc['blocktypes'][$BlockType], 1);
         switch ($BlockType) {
             case 0:
                 // Terminator
                 // do nothing, we'll break out of the loop down below
                 break;
             case 1:
                 // Sound data
                 $BlockData .= $this->fread(2);
                 if ($info['avdataoffset'] <= $OriginalAVdataOffset) {
                     $info['avdataoffset'] = $this->ftell();
                 }
                 $this->fseek($BlockSize - 2, SEEK_CUR);
                 $ThisBlock['sample_rate_id'] = Utils::LittleEndian2Int(substr($BlockData, 4, 1));
                 $ThisBlock['compression_type'] = Utils::LittleEndian2Int(substr($BlockData, 5, 1));
                 $ThisBlock['compression_name'] = $this->VOCcompressionTypeLookup($ThisBlock['compression_type']);
                 if ($ThisBlock['compression_type'] <= 3) {
                     $thisfile_voc['compressed_bits_per_sample'] = Utils::CastAsInt(str_replace('-bit', '', $ThisBlock['compression_name']));
                 }
                 // Less accurate sample_rate calculation than the Extended block (#8) data (but better than nothing if Extended Block is not available)
                 if (empty($thisfile_audio['sample_rate'])) {
                     // SR byte = 256 - (1000000 / sample_rate)
                     $thisfile_audio['sample_rate'] = Utils::trunc(1000000 / (256 - $ThisBlock['sample_rate_id']) / $thisfile_audio['channels']);
                 }
                 break;
             case 2:
                 // Sound continue
             // Sound continue
             case 3:
                 // Silence
             // Silence
             case 4:
                 // Marker
             // Marker
             case 6:
                 // Repeat
             // Repeat
             case 7:
                 // End repeat
                 // nothing useful, just skip
                 $this->fseek($BlockSize, SEEK_CUR);
                 break;
             case 8:
                 // Extended
                 $BlockData .= $this->fread(4);
                 //00-01  Time Constant:
                 //   Mono: 65536 - (256000000 / sample_rate)
                 // Stereo: 65536 - (256000000 / (sample_rate * 2))
                 $ThisBlock['time_constant'] = Utils::LittleEndian2Int(substr($BlockData, 4, 2));
                 $ThisBlock['pack_method'] = Utils::LittleEndian2Int(substr($BlockData, 6, 1));
                 $ThisBlock['stereo'] = (bool) Utils::LittleEndian2Int(substr($BlockData, 7, 1));
                 $thisfile_audio['channels'] = $ThisBlock['stereo'] ? 2 : 1;
                 $thisfile_audio['sample_rate'] = Utils::trunc(256000000 / (65536 - $ThisBlock['time_constant']) / $thisfile_audio['channels']);
                 break;
             case 9:
                 // data block that supersedes blocks 1 and 8. Used for stereo, 16 bit
                 $BlockData .= $this->fread(12);
                 if ($info['avdataoffset'] <= $OriginalAVdataOffset) {
                     $info['avdataoffset'] = $this->ftell();
                 }
                 $this->fseek($BlockSize - 12, SEEK_CUR);
                 $ThisBlock['sample_rate'] = Utils::LittleEndian2Int(substr($BlockData, 4, 4));
                 $ThisBlock['bits_per_sample'] = Utils::LittleEndian2Int(substr($BlockData, 8, 1));
                 $ThisBlock['channels'] = Utils::LittleEndian2Int(substr($BlockData, 9, 1));
                 $ThisBlock['wFormat'] = Utils::LittleEndian2Int(substr($BlockData, 10, 2));
                 $ThisBlock['compression_name'] = $this->VOCwFormatLookup($ThisBlock['wFormat']);
                 if ($this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat'])) {
                     $thisfile_voc['compressed_bits_per_sample'] = $this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat']);
                 }
                 $thisfile_audio['sample_rate'] = $ThisBlock['sample_rate'];
                 $thisfile_audio['bits_per_sample'] = $ThisBlock['bits_per_sample'];
                 $thisfile_audio['channels'] = $ThisBlock['channels'];
                 break;
             default:
                 $info['warning'][] = 'Unhandled block type "' . $BlockType . '" at offset ' . $BlockOffset;
                 $this->fseek($BlockSize, SEEK_CUR);
                 break;
         }
         if (!empty($ThisBlock)) {
             $ThisBlock['block_offset'] = $BlockOffset;
             $ThisBlock['block_size'] = $BlockSize;
             $ThisBlock['block_type_id'] = $BlockType;
             $thisfile_voc['blocks'][] = $ThisBlock;
         }
     } while (!feof($this->getid3->fp) && $BlockType != 0);
     // Terminator block doesn't have size field, so seek back 3 spaces
     $this->fseek(-3, SEEK_CUR);
     ksort($thisfile_voc['blocktypes']);
     if (!empty($thisfile_voc['compressed_bits_per_sample'])) {
         $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / ($thisfile_voc['compressed_bits_per_sample'] * $thisfile_audio['channels'] * $thisfile_audio['sample_rate']);
         $thisfile_audio['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
     }
     return true;
 }