Пример #1
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;
 }
Пример #2
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;
 }
Пример #3
0
 private function EitherEndian2Int($byteword, $signed = false)
 {
     if ($this->container == 'riff') {
         return Utils::LittleEndian2Int($byteword, $signed);
     }
     return Utils::BigEndian2Int($byteword, false, $signed);
 }
Пример #4
0
 public function getBit()
 {
     $result = Utils::BigEndian2Int(substr($this->sps, $this->currentBytes, 1)) >> 7 - $this->currentBits & 0x1;
     $this->skipBits(1);
     return $result;
 }
Пример #5
0
 public function TIFFendian2Int($bytestring, $byteorder)
 {
     if ($byteorder == 'Intel') {
         return Utils::LittleEndian2Int($bytestring);
     } elseif ($byteorder == 'Motorola') {
         return Utils::BigEndian2Int($bytestring);
     }
     return false;
 }
Пример #6
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;
 }
Пример #7
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;
 }
Пример #8
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;
 }
Пример #9
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     // shortcut
     $info['midi']['raw'] = array();
     $thisfile_midi =& $info['midi'];
     $thisfile_midi_raw =& $thisfile_midi['raw'];
     $info['fileformat'] = 'midi';
     $info['audio']['dataformat'] = 'midi';
     $this->fseek($info['avdataoffset']);
     $MIDIdata = $this->fread($this->getid3->fread_buffer_size());
     $offset = 0;
     $MIDIheaderID = substr($MIDIdata, $offset, 4);
     // 'MThd'
     if ($MIDIheaderID !== self::MAGIC_MTHD) {
         $info['error'][] = 'Expecting "' . Utils::PrintHexBytes(self::MAGIC_MTHD) . '" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes($MIDIheaderID) . '"';
         unset($info['fileformat']);
         return false;
     }
     $offset += 4;
     $thisfile_midi_raw['headersize'] = Utils::BigEndian2Int(substr($MIDIdata, $offset, 4));
     $offset += 4;
     $thisfile_midi_raw['fileformat'] = Utils::BigEndian2Int(substr($MIDIdata, $offset, 2));
     $offset += 2;
     $thisfile_midi_raw['tracks'] = Utils::BigEndian2Int(substr($MIDIdata, $offset, 2));
     $offset += 2;
     $thisfile_midi_raw['ticksperqnote'] = Utils::BigEndian2Int(substr($MIDIdata, $offset, 2));
     $offset += 2;
     for ($i = 0; $i < $thisfile_midi_raw['tracks']; $i++) {
         while (strlen($MIDIdata) - $offset < 8) {
             if ($buffer = $this->fread($this->getid3->fread_buffer_size())) {
                 $MIDIdata .= $buffer;
             } else {
                 $info['warning'][] = 'only processed ' . ($i - 1) . ' of ' . $thisfile_midi_raw['tracks'] . ' tracks';
                 $info['error'][] = 'Unabled to read more file data at ' . $this->ftell() . ' (trying to seek to : ' . $offset . '), was expecting at least 8 more bytes';
                 return false;
             }
         }
         $trackID = substr($MIDIdata, $offset, 4);
         $offset += 4;
         if ($trackID === self::MAGIC_MTRK) {
             $tracksize = Utils::BigEndian2Int(substr($MIDIdata, $offset, 4));
             $offset += 4;
             //$thisfile_midi['tracks'][$i]['size'] = $tracksize;
             $trackdataarray[$i] = substr($MIDIdata, $offset, $tracksize);
             $offset += $tracksize;
         } else {
             $info['error'][] = 'Expecting "' . Utils::PrintHexBytes(self::MAGIC_MTRK) . '" at ' . ($offset - 4) . ', found "' . Utils::PrintHexBytes($trackID) . '" instead';
             return false;
         }
     }
     if (!isset($trackdataarray) || !is_array($trackdataarray)) {
         $info['error'][] = 'Cannot find MIDI track information';
         unset($thisfile_midi);
         unset($info['fileformat']);
         return false;
     }
     if ($this->scanwholefile) {
         // this can take quite a long time, so have the option to bypass it if speed is very important
         $thisfile_midi['totalticks'] = 0;
         $info['playtime_seconds'] = 0;
         $CurrentMicroSecondsPerBeat = 500000;
         // 120 beats per minute;  60,000,000 microseconds per minute -> 500,000 microseconds per beat
         $CurrentBeatsPerMinute = 120;
         // 120 beats per minute;  60,000,000 microseconds per minute -> 500,000 microseconds per beat
         $MicroSecondsPerQuarterNoteAfter = array();
         foreach ($trackdataarray as $tracknumber => $trackdata) {
             $eventsoffset = 0;
             $LastIssuedMIDIcommand = 0;
             $LastIssuedMIDIchannel = 0;
             $CumulativeDeltaTime = 0;
             $TicksAtCurrentBPM = 0;
             while ($eventsoffset < strlen($trackdata)) {
                 $eventid = 0;
                 if (isset($MIDIevents[$tracknumber]) && is_array($MIDIevents[$tracknumber])) {
                     $eventid = count($MIDIevents[$tracknumber]);
                 }
                 $deltatime = 0;
                 for ($i = 0; $i < 4; $i++) {
                     $deltatimebyte = ord(substr($trackdata, $eventsoffset++, 1));
                     $deltatime = ($deltatime << 7) + ($deltatimebyte & 0x7f);
                     if ($deltatimebyte & 0x80) {
                         // another byte follows
                     } else {
                         break;
                     }
                 }
                 $CumulativeDeltaTime += $deltatime;
                 $TicksAtCurrentBPM += $deltatime;
                 $MIDIevents[$tracknumber][$eventid]['deltatime'] = $deltatime;
                 $MIDI_event_channel = ord(substr($trackdata, $eventsoffset++, 1));
                 if ($MIDI_event_channel & 0x80) {
                     // OK, normal event - MIDI command has MSB set
                     $LastIssuedMIDIcommand = $MIDI_event_channel >> 4;
                     $LastIssuedMIDIchannel = $MIDI_event_channel & 0xf;
                 } else {
                     // running event - assume last command
                     $eventsoffset--;
                 }
                 $MIDIevents[$tracknumber][$eventid]['eventid'] = $LastIssuedMIDIcommand;
                 $MIDIevents[$tracknumber][$eventid]['channel'] = $LastIssuedMIDIchannel;
                 if ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x8) {
                     // Note off (key is released)
                     $notenumber = ord(substr($trackdata, $eventsoffset++, 1));
                     $velocity = ord(substr($trackdata, $eventsoffset++, 1));
                 } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x9) {
                     // Note on (key is pressed)
                     $notenumber = ord(substr($trackdata, $eventsoffset++, 1));
                     $velocity = ord(substr($trackdata, $eventsoffset++, 1));
                 } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0xa) {
                     // Key after-touch
                     $notenumber = ord(substr($trackdata, $eventsoffset++, 1));
                     $velocity = ord(substr($trackdata, $eventsoffset++, 1));
                 } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0xb) {
                     // Control Change
                     $controllernum = ord(substr($trackdata, $eventsoffset++, 1));
                     $newvalue = ord(substr($trackdata, $eventsoffset++, 1));
                 } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0xc) {
                     // Program (patch) change
                     $newprogramnum = ord(substr($trackdata, $eventsoffset++, 1));
                     $thisfile_midi_raw['track'][$tracknumber]['instrumentid'] = $newprogramnum;
                     if ($tracknumber == 10) {
                         $thisfile_midi_raw['track'][$tracknumber]['instrument'] = $this->GeneralMIDIpercussionLookup($newprogramnum);
                     } else {
                         $thisfile_midi_raw['track'][$tracknumber]['instrument'] = $this->GeneralMIDIinstrumentLookup($newprogramnum);
                     }
                 } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0xd) {
                     // Channel after-touch
                     $channelnumber = ord(substr($trackdata, $eventsoffset++, 1));
                 } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0xe) {
                     // Pitch wheel change (2000H is normal or no change)
                     $changeLSB = ord(substr($trackdata, $eventsoffset++, 1));
                     $changeMSB = ord(substr($trackdata, $eventsoffset++, 1));
                     $pitchwheelchange = ($changeMSB & 0x7f) << 7 & ($changeLSB & 0x7f);
                 } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0xf && $MIDIevents[$tracknumber][$eventid]['channel'] == 0xf) {
                     $METAeventCommand = ord(substr($trackdata, $eventsoffset++, 1));
                     $METAeventLength = ord(substr($trackdata, $eventsoffset++, 1));
                     $METAeventData = substr($trackdata, $eventsoffset, $METAeventLength);
                     $eventsoffset += $METAeventLength;
                     switch ($METAeventCommand) {
                         case 0x0:
                             // Set track sequence number
                             $track_sequence_number = Utils::BigEndian2Int(substr($METAeventData, 0, $METAeventLength));
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['seqno'] = $track_sequence_number;
                             break;
                         case 0x1:
                             // Text: generic
                             $text_generic = substr($METAeventData, 0, $METAeventLength);
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['text'] = $text_generic;
                             $thisfile_midi['comments']['comment'][] = $text_generic;
                             break;
                         case 0x2:
                             // Text: copyright
                             $text_copyright = substr($METAeventData, 0, $METAeventLength);
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['copyright'] = $text_copyright;
                             $thisfile_midi['comments']['copyright'][] = $text_copyright;
                             break;
                         case 0x3:
                             // Text: track name
                             $text_trackname = substr($METAeventData, 0, $METAeventLength);
                             $thisfile_midi_raw['track'][$tracknumber]['name'] = $text_trackname;
                             break;
                         case 0x4:
                             // Text: track instrument name
                             $text_instrument = substr($METAeventData, 0, $METAeventLength);
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['instrument'] = $text_instrument;
                             break;
                         case 0x5:
                             // Text: lyrics
                             $text_lyrics = substr($METAeventData, 0, $METAeventLength);
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['lyrics'] = $text_lyrics;
                             if (!isset($thisfile_midi['lyrics'])) {
                                 $thisfile_midi['lyrics'] = '';
                             }
                             $thisfile_midi['lyrics'] .= $text_lyrics . "\n";
                             break;
                         case 0x6:
                             // Text: marker
                             $text_marker = substr($METAeventData, 0, $METAeventLength);
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['marker'] = $text_marker;
                             break;
                         case 0x7:
                             // Text: cue point
                             $text_cuepoint = substr($METAeventData, 0, $METAeventLength);
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['cuepoint'] = $text_cuepoint;
                             break;
                         case 0x2f:
                             // End Of Track
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['EOT'] = $CumulativeDeltaTime;
                             break;
                         case 0x51:
                             // Tempo: microseconds / quarter note
                             $CurrentMicroSecondsPerBeat = Utils::BigEndian2Int(substr($METAeventData, 0, $METAeventLength));
                             if ($CurrentMicroSecondsPerBeat == 0) {
                                 $info['error'][] = 'Corrupt MIDI file: CurrentMicroSecondsPerBeat == zero';
                                 return false;
                             }
                             $thisfile_midi_raw['events'][$tracknumber][$CumulativeDeltaTime]['us_qnote'] = $CurrentMicroSecondsPerBeat;
                             $CurrentBeatsPerMinute = 1000000 / $CurrentMicroSecondsPerBeat * 60;
                             $MicroSecondsPerQuarterNoteAfter[$CumulativeDeltaTime] = $CurrentMicroSecondsPerBeat;
                             $TicksAtCurrentBPM = 0;
                             break;
                         case 0x58:
                             // Time signature
                             $timesig_numerator = Utils::BigEndian2Int($METAeventData[0]);
                             $timesig_denominator = pow(2, Utils::BigEndian2Int($METAeventData[1]));
                             // $02 -> x/4, $03 -> x/8, etc
                             $timesig_32inqnote = Utils::BigEndian2Int($METAeventData[2]);
                             // number of 32nd notes to the quarter note
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_32inqnote']   = $timesig_32inqnote;
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_numerator']   = $timesig_numerator;
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_denominator'] = $timesig_denominator;
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_text']        = $timesig_numerator.'/'.$timesig_denominator;
                             $thisfile_midi['timesignature'][] = $timesig_numerator . '/' . $timesig_denominator;
                             break;
                         case 0x59:
                             // Keysignature
                             $keysig_sharpsflats = Utils::BigEndian2Int($METAeventData[0]);
                             if ($keysig_sharpsflats & 0x80) {
                                 // (-7 -> 7 flats, 0 ->key of C, 7 -> 7 sharps)
                                 $keysig_sharpsflats -= 256;
                             }
                             $keysig_majorminor = Utils::BigEndian2Int($METAeventData[1]);
                             // 0 -> major, 1 -> minor
                             $keysigs = array(-7 => 'Cb', -6 => 'Gb', -5 => 'Db', -4 => 'Ab', -3 => 'Eb', -2 => 'Bb', -1 => 'F', 0 => 'C', 1 => 'G', 2 => 'D', 3 => 'A', 4 => 'E', 5 => 'B', 6 => 'F#', 7 => 'C#');
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_sharps'] = (($keysig_sharpsflats > 0) ? abs($keysig_sharpsflats) : 0);
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_flats']  = (($keysig_sharpsflats < 0) ? abs($keysig_sharpsflats) : 0);
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_minor']  = (bool) $keysig_majorminor;
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_text']   = $keysigs[$keysig_sharpsflats].' '.($thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_minor'] ? 'minor' : 'major');
                             // $keysigs[$keysig_sharpsflats] gets an int key (correct) - $keysigs["$keysig_sharpsflats"] gets a string key (incorrect)
                             $thisfile_midi['keysignature'][] = $keysigs[$keysig_sharpsflats] . ' ' . ((bool) $keysig_majorminor ? 'minor' : 'major');
                             break;
                         case 0x7f:
                             // Sequencer specific information
                             $custom_data = substr($METAeventData, 0, $METAeventLength);
                             break;
                         default:
                             $info['warning'][] = 'Unhandled META Event Command: ' . $METAeventCommand;
                             break;
                     }
                 } else {
                     $info['warning'][] = 'Unhandled MIDI Event ID: ' . $MIDIevents[$tracknumber][$eventid]['eventid'] . ' + Channel ID: ' . $MIDIevents[$tracknumber][$eventid]['channel'];
                 }
             }
             if ($tracknumber > 0 || count($trackdataarray) == 1) {
                 $thisfile_midi['totalticks'] = max($thisfile_midi['totalticks'], $CumulativeDeltaTime);
             }
         }
         $previoustickoffset = null;
         ksort($MicroSecondsPerQuarterNoteAfter);
         foreach ($MicroSecondsPerQuarterNoteAfter as $tickoffset => $microsecondsperbeat) {
             if (is_null($previoustickoffset)) {
                 $prevmicrosecondsperbeat = $microsecondsperbeat;
                 $previoustickoffset = $tickoffset;
                 continue;
             }
             if ($thisfile_midi['totalticks'] > $tickoffset) {
                 if ($thisfile_midi_raw['ticksperqnote'] == 0) {
                     $info['error'][] = 'Corrupt MIDI file: ticksperqnote == zero';
                     return false;
                 }
                 $info['playtime_seconds'] += ($tickoffset - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote'] * ($prevmicrosecondsperbeat / 1000000);
                 $prevmicrosecondsperbeat = $microsecondsperbeat;
                 $previoustickoffset = $tickoffset;
             }
         }
         if ($thisfile_midi['totalticks'] > $previoustickoffset) {
             if ($thisfile_midi_raw['ticksperqnote'] == 0) {
                 $info['error'][] = 'Corrupt MIDI file: ticksperqnote == zero';
                 return false;
             }
             $info['playtime_seconds'] += ($thisfile_midi['totalticks'] - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote'] * ($microsecondsperbeat / 1000000);
         }
     }
     if (!empty($info['playtime_seconds'])) {
         $info['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
     }
     if (!empty($thisfile_midi['lyrics'])) {
         $thisfile_midi['comments']['lyrics'][] = $thisfile_midi['lyrics'];
     }
     return true;
 }
Пример #10
0
 public function ParseMPCsv8()
 {
     // this is SV8
     // http://trac.musepack.net/trac/wiki/SV8Specification
     $info =& $this->getid3->info;
     $thisfile_mpc_header =& $info['mpc']['header'];
     $keyNameSize = 2;
     $maxHandledPacketLength = 9;
     // specs say: "n*8; 0 < n < 10"
     $offset = $this->ftell();
     while ($offset < $info['avdataend']) {
         $thisPacket = array();
         $thisPacket['offset'] = $offset;
         $packet_offset = 0;
         // Size is a variable-size field, could be 1-4 bytes (possibly more?)
         // read enough data in and figure out the exact size later
         $MPCheaderData = $this->fread($keyNameSize + $maxHandledPacketLength);
         $packet_offset += $keyNameSize;
         $thisPacket['key'] = substr($MPCheaderData, 0, $keyNameSize);
         $thisPacket['key_name'] = $this->MPCsv8PacketName($thisPacket['key']);
         if ($thisPacket['key'] == $thisPacket['key_name']) {
             $info['error'][] = 'Found unexpected key value "' . $thisPacket['key'] . '" at offset ' . $thisPacket['offset'];
             return false;
         }
         $packetLength = 0;
         $thisPacket['packet_size'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $keyNameSize), $packetLength);
         // includes keyname and packet_size field
         if ($thisPacket['packet_size'] === false) {
             $info['error'][] = 'Did not find expected packet length within ' . $maxHandledPacketLength . ' bytes at offset ' . ($thisPacket['offset'] + $keyNameSize);
             return false;
         }
         $packet_offset += $packetLength;
         $offset += $thisPacket['packet_size'];
         switch ($thisPacket['key']) {
             case 'SH':
                 // Stream Header
                 $moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
                 if ($moreBytesToRead > 0) {
                     $MPCheaderData .= $this->fread($moreBytesToRead);
                 }
                 $thisPacket['crc'] = Utils::BigEndian2Int(substr($MPCheaderData, $packet_offset, 4));
                 $packet_offset += 4;
                 $thisPacket['stream_version'] = Utils::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
                 $packet_offset += 1;
                 $packetLength = 0;
                 $thisPacket['sample_count'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
                 $packet_offset += $packetLength;
                 $packetLength = 0;
                 $thisPacket['beginning_silence'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
                 $packet_offset += $packetLength;
                 $otherUsefulData = Utils::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
                 $packet_offset += 2;
                 $thisPacket['sample_frequency_raw'] = ($otherUsefulData & 0xe000) >> 13;
                 $thisPacket['max_bands_used'] = ($otherUsefulData & 0x1f00) >> 8;
                 $thisPacket['channels'] = (($otherUsefulData & 0xf0) >> 4) + 1;
                 $thisPacket['ms_used'] = (bool) (($otherUsefulData & 0x8) >> 3);
                 $thisPacket['audio_block_frames'] = ($otherUsefulData & 0x7) >> 0;
                 $thisPacket['sample_frequency'] = $this->MPCfrequencyLookup($thisPacket['sample_frequency_raw']);
                 $thisfile_mpc_header['mid_side_stereo'] = $thisPacket['ms_used'];
                 $thisfile_mpc_header['sample_rate'] = $thisPacket['sample_frequency'];
                 $thisfile_mpc_header['samples'] = $thisPacket['sample_count'];
                 $thisfile_mpc_header['stream_version_major'] = $thisPacket['stream_version'];
                 $info['audio']['channels'] = $thisPacket['channels'];
                 $info['audio']['sample_rate'] = $thisPacket['sample_frequency'];
                 $info['playtime_seconds'] = $thisPacket['sample_count'] / $thisPacket['sample_frequency'];
                 $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
                 break;
             case 'RG':
                 // Replay Gain
                 $moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
                 if ($moreBytesToRead > 0) {
                     $MPCheaderData .= $this->fread($moreBytesToRead);
                 }
                 $thisPacket['replaygain_version'] = Utils::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
                 $packet_offset += 1;
                 $thisPacket['replaygain_title_gain'] = Utils::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
                 $packet_offset += 2;
                 $thisPacket['replaygain_title_peak'] = Utils::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
                 $packet_offset += 2;
                 $thisPacket['replaygain_album_gain'] = Utils::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
                 $packet_offset += 2;
                 $thisPacket['replaygain_album_peak'] = Utils::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
                 $packet_offset += 2;
                 if ($thisPacket['replaygain_title_gain']) {
                     $info['replay_gain']['title']['gain'] = $thisPacket['replaygain_title_gain'];
                 }
                 if ($thisPacket['replaygain_title_peak']) {
                     $info['replay_gain']['title']['peak'] = $thisPacket['replaygain_title_peak'];
                 }
                 if ($thisPacket['replaygain_album_gain']) {
                     $info['replay_gain']['album']['gain'] = $thisPacket['replaygain_album_gain'];
                 }
                 if ($thisPacket['replaygain_album_peak']) {
                     $info['replay_gain']['album']['peak'] = $thisPacket['replaygain_album_peak'];
                 }
                 break;
             case 'EI':
                 // Encoder Info
                 $moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
                 if ($moreBytesToRead > 0) {
                     $MPCheaderData .= $this->fread($moreBytesToRead);
                 }
                 $profile_pns = Utils::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
                 $packet_offset += 1;
                 $quality_int = ($profile_pns & 0xf0) >> 4;
                 $quality_dec = ($profile_pns & 0xe) >> 3;
                 $thisPacket['quality'] = (double) $quality_int + $quality_dec / 8;
                 $thisPacket['pns_tool'] = (bool) (($profile_pns & 0x1) >> 0);
                 $thisPacket['version_major'] = Utils::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
                 $packet_offset += 1;
                 $thisPacket['version_minor'] = Utils::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
                 $packet_offset += 1;
                 $thisPacket['version_build'] = Utils::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
                 $packet_offset += 1;
                 $thisPacket['version'] = $thisPacket['version_major'] . '.' . $thisPacket['version_minor'] . '.' . $thisPacket['version_build'];
                 $info['audio']['encoder'] = 'MPC v' . $thisPacket['version'] . ' (' . ($thisPacket['version_minor'] % 2 ? 'unstable' : 'stable') . ')';
                 $thisfile_mpc_header['encoder_version'] = $info['audio']['encoder'];
                 //$thisfile_mpc_header['quality']         = (float) ($thisPacket['quality'] / 1.5875); // values can range from 0.000 to 15.875, mapped to qualities of 0.0 to 10.0
                 $thisfile_mpc_header['quality'] = (double) ($thisPacket['quality'] - 5);
                 // values can range from 0.000 to 15.875, of which 0..4 are "reserved/experimental", and 5..15 are mapped to qualities of 0.0 to 10.0
                 break;
             case 'SO':
                 // Seek Table Offset
                 $packetLength = 0;
                 $thisPacket['seek_table_offset'] = $thisPacket['offset'] + $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
                 $packet_offset += $packetLength;
                 break;
             case 'ST':
                 // Seek Table
             // Seek Table
             case 'SE':
                 // Stream End
             // Stream End
             case 'AP':
                 // Audio Data
                 // nothing useful here, just skip this packet
                 $thisPacket = array();
                 break;
             default:
                 $info['error'][] = 'Found unhandled key type "' . $thisPacket['key'] . '" at offset ' . $thisPacket['offset'];
                 return false;
                 break;
         }
         if (!empty($thisPacket)) {
             $info['mpc']['packets'][] = $thisPacket;
         }
         $this->fseek($offset);
     }
     $thisfile_mpc_header['size'] = $offset;
     return true;
 }
Пример #11
0
 public function getAACADTSheaderFilepointer($MaxFramesToScan = 1000000, $ReturnExtendedInfo = false)
 {
     $info =& $this->getid3->info;
     // based loosely on code from AACfile by Jurgen Faul  <jfaulØgmx.de>
     // http://jfaul.de/atl  or  http://j-faul.virtualave.net/atl/atl.html
     // http://faac.sourceforge.net/wiki/index.php?page=ADTS // dead link
     // http://wiki.multimedia.cx/index.php?title=ADTS
     // * ADTS Fixed Header: these don't change from frame to frame
     // syncword                                       12    always: '111111111111'
     // ID                                              1    0: MPEG-4, 1: MPEG-2
     // MPEG layer                                      2    If you send AAC in MPEG-TS, set to 0
     // protection_absent                               1    0: CRC present; 1: no CRC
     // profile                                         2    0: AAC Main; 1: AAC LC (Low Complexity); 2: AAC SSR (Scalable Sample Rate); 3: AAC LTP (Long Term Prediction)
     // sampling_frequency_index                        4    15 not allowed
     // private_bit                                     1    usually 0
     // channel_configuration                           3
     // original/copy                                   1    0: original; 1: copy
     // home                                            1    usually 0
     // emphasis                                        2    only if ID == 0 (ie MPEG-4)  // not present in some documentation?
     // * ADTS Variable Header: these can change from frame to frame
     // copyright_identification_bit                    1
     // copyright_identification_start                  1
     // aac_frame_length                               13    length of the frame including header (in bytes)
     // adts_buffer_fullness                           11    0x7FF indicates VBR
     // no_raw_data_blocks_in_frame                     2
     // * ADTS Error check
     // crc_check                                      16    only if protection_absent == 0
     $byteoffset = $info['avdataoffset'];
     $framenumber = 0;
     // Init bit pattern array
     static $decbin = array();
     // Populate $bindec
     for ($i = 0; $i < 256; $i++) {
         $decbin[chr($i)] = str_pad(decbin($i), 8, '0', STR_PAD_LEFT);
     }
     // used to calculate bitrate below
     $BitrateCache = array();
     while (true) {
         // breaks out when end-of-file encountered, or invalid data found,
         // or MaxFramesToScan frames have been scanned
         if (!Utils::intValueSupported($byteoffset)) {
             $info['warning'][] = 'Unable to parse AAC file beyond ' . $this->ftell() . ' (PHP does not support file operations beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB)';
             return false;
         }
         $this->fseek($byteoffset);
         // First get substring
         $substring = $this->fread(9);
         // header is 7 bytes (or 9 if CRC is present)
         $substringlength = strlen($substring);
         if ($substringlength != 9) {
             $info['error'][] = 'Failed to read 7 bytes at offset ' . ($this->ftell() - $substringlength) . ' (only read ' . $substringlength . ' bytes)';
             return false;
         }
         // this would be easier with 64-bit math, but split it up to allow for 32-bit:
         $header1 = Utils::BigEndian2Int(substr($substring, 0, 2));
         $header2 = Utils::BigEndian2Int(substr($substring, 2, 4));
         $header3 = Utils::BigEndian2Int(substr($substring, 6, 1));
         $info['aac']['header']['raw']['syncword'] = ($header1 & 0xfff0) >> 4;
         if ($info['aac']['header']['raw']['syncword'] != 0xfff) {
             $info['error'][] = 'Synch pattern (0x0FFF) not found at offset ' . ($this->ftell() - $substringlength) . ' (found 0x0' . strtoupper(dechex($info['aac']['header']['raw']['syncword'])) . ' instead)';
             //if ($info['fileformat'] == 'aac') {
             //	return true;
             //}
             unset($info['aac']);
             return false;
         }
         // Gather info for first frame only - this takes time to do 1000 times!
         if ($framenumber == 0) {
             $info['aac']['header_type'] = 'ADTS';
             $info['fileformat'] = 'aac';
             $info['audio']['dataformat'] = 'aac';
             $info['aac']['header']['raw']['mpeg_version'] = ($header1 & 0x8) >> 3;
             $info['aac']['header']['raw']['mpeg_layer'] = ($header1 & 0x6) >> 1;
             $info['aac']['header']['raw']['protection_absent'] = ($header1 & 0x1) >> 0;
             $info['aac']['header']['raw']['profile_code'] = ($header2 & 0xc0000000) >> 30;
             $info['aac']['header']['raw']['sample_rate_code'] = ($header2 & 0x3c000000) >> 26;
             $info['aac']['header']['raw']['private_stream'] = ($header2 & 0x2000000) >> 25;
             $info['aac']['header']['raw']['channels_code'] = ($header2 & 0x1c00000) >> 22;
             $info['aac']['header']['raw']['original'] = ($header2 & 0x200000) >> 21;
             $info['aac']['header']['raw']['home'] = ($header2 & 0x100000) >> 20;
             $info['aac']['header']['raw']['copyright_stream'] = ($header2 & 0x80000) >> 19;
             $info['aac']['header']['raw']['copyright_start'] = ($header2 & 0x40000) >> 18;
             $info['aac']['header']['raw']['frame_length'] = ($header2 & 0x3ffe0) >> 5;
             $info['aac']['header']['mpeg_version'] = $info['aac']['header']['raw']['mpeg_version'] ? 2 : 4;
             $info['aac']['header']['crc_present'] = $info['aac']['header']['raw']['protection_absent'] ? false : true;
             $info['aac']['header']['profile'] = self::AACprofileLookup($info['aac']['header']['raw']['profile_code'], $info['aac']['header']['mpeg_version']);
             $info['aac']['header']['sample_frequency'] = self::AACsampleRateLookup($info['aac']['header']['raw']['sample_rate_code']);
             $info['aac']['header']['private'] = (bool) $info['aac']['header']['raw']['private_stream'];
             $info['aac']['header']['original'] = (bool) $info['aac']['header']['raw']['original'];
             $info['aac']['header']['home'] = (bool) $info['aac']['header']['raw']['home'];
             $info['aac']['header']['channels'] = $info['aac']['header']['raw']['channels_code'] == 7 ? 8 : $info['aac']['header']['raw']['channels_code'];
             if ($ReturnExtendedInfo) {
                 $info['aac'][$framenumber]['copyright_id_bit'] = (bool) $info['aac']['header']['raw']['copyright_stream'];
                 $info['aac'][$framenumber]['copyright_id_start'] = (bool) $info['aac']['header']['raw']['copyright_start'];
             }
             if ($info['aac']['header']['raw']['mpeg_layer'] != 0) {
                 $info['warning'][] = 'Layer error - expected "0", found "' . $info['aac']['header']['raw']['mpeg_layer'] . '" instead';
             }
             if ($info['aac']['header']['sample_frequency'] == 0) {
                 $info['error'][] = 'Corrupt AAC file: sample_frequency == zero';
                 return false;
             }
             $info['audio']['sample_rate'] = $info['aac']['header']['sample_frequency'];
             $info['audio']['channels'] = $info['aac']['header']['channels'];
         }
         $FrameLength = ($header2 & 0x3ffe0) >> 5;
         if (!isset($BitrateCache[$FrameLength])) {
             $BitrateCache[$FrameLength] = $info['aac']['header']['sample_frequency'] / 1024 * $FrameLength * 8;
         }
         Utils::safe_inc($info['aac']['bitrate_distribution'][$BitrateCache[$FrameLength]], 1);
         $info['aac'][$framenumber]['aac_frame_length'] = $FrameLength;
         $info['aac'][$framenumber]['adts_buffer_fullness'] = ($header2 & 0x1f) << 6 & ($header3 & 0xfc) >> 2;
         if ($info['aac'][$framenumber]['adts_buffer_fullness'] == 0x7ff) {
             $info['audio']['bitrate_mode'] = 'vbr';
         } else {
             $info['audio']['bitrate_mode'] = 'cbr';
         }
         $info['aac'][$framenumber]['num_raw_data_blocks'] = ($header3 & 0x3) >> 0;
         if ($info['aac']['header']['crc_present']) {
             //$info['aac'][$framenumber]['crc'] = Utils::BigEndian2Int(substr($substring, 7, 2);
         }
         if (!$ReturnExtendedInfo) {
             unset($info['aac'][$framenumber]);
         }
         /*
         $rounded_precision = 5000;
         $info['aac']['bitrate_distribution_rounded'] = array();
         foreach ($info['aac']['bitrate_distribution'] as $bitrate => $count) {
         	$rounded_bitrate = round($bitrate / $rounded_precision) * $rounded_precision;
         	Utils::safe_inc($info['aac']['bitrate_distribution_rounded'][$rounded_bitrate], $count);
         }
         ksort($info['aac']['bitrate_distribution_rounded']);
         */
         $byteoffset += $FrameLength;
         if (++$framenumber < $MaxFramesToScan && $byteoffset + 10 < $info['avdataend']) {
             // keep scanning
         } else {
             $info['aac']['frames'] = $framenumber;
             $info['playtime_seconds'] = $info['avdataend'] / $byteoffset * ($framenumber * 1024 / $info['aac']['header']['sample_frequency']);
             // (1 / % of file scanned) * (samples / (samples/sec)) = seconds
             if ($info['playtime_seconds'] == 0) {
                 $info['error'][] = 'Corrupt AAC file: playtime_seconds == zero';
                 return false;
             }
             $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
             ksort($info['aac']['bitrate_distribution']);
             $info['audio']['encoder_options'] = $info['aac']['header_type'] . ' ' . $info['aac']['header']['profile'];
             return true;
         }
     }
     // should never get here.
 }
Пример #12
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $this->fseek($info['avdataoffset']);
     $TSheader = $this->fread(19);
     $magic = "G";
     if (substr($TSheader, 0, 1) != $magic) {
         $info['error'][] = 'Expecting "' . Utils::PrintHexBytes($magic) . '" at ' . $info['avdataoffset'] . ', found ' . Utils::PrintHexBytes(substr($TSheader, 0, 1)) . ' instead.';
         return false;
     }
     $info['fileformat'] = 'ts';
     // http://en.wikipedia.org/wiki/.ts
     $offset = 0;
     $info['ts']['packet']['sync'] = Utils::BigEndian2Int(substr($TSheader, $offset, 1));
     $offset += 1;
     $pid_flags_raw = Utils::BigEndian2Int(substr($TSheader, $offset, 2));
     $offset += 2;
     $SAC_raw = Utils::BigEndian2Int(substr($TSheader, $offset, 1));
     $offset += 1;
     $info['ts']['packet']['flags']['transport_error_indicator'] = (bool) ($pid_flags_raw & 0x8000);
     // Set by demodulator if can't correct errors in the stream, to tell the demultiplexer that the packet has an uncorrectable error
     $info['ts']['packet']['flags']['payload_unit_start_indicator'] = (bool) ($pid_flags_raw & 0x4000);
     // 1 means start of PES data or PSI otherwise zero only.
     $info['ts']['packet']['flags']['transport_high_priority'] = (bool) ($pid_flags_raw & 0x2000);
     // 1 means higher priority than other packets with the same PID.
     $info['ts']['packet']['packet_id'] = ($pid_flags_raw & 0x1fff) >> 0;
     $info['ts']['packet']['raw']['scrambling_control'] = ($SAC_raw & 0xc0) >> 6;
     $info['ts']['packet']['flags']['adaption_field_exists'] = (bool) ($SAC_raw & 0x20);
     $info['ts']['packet']['flags']['payload_exists'] = (bool) ($SAC_raw & 0x10);
     $info['ts']['packet']['continuity_counter'] = ($SAC_raw & 0xf) >> 0;
     // Incremented only when a payload is present
     $info['ts']['packet']['scrambling_control'] = $this->TSscramblingControlLookup($info['ts']['packet']['raw']['scrambling_control']);
     if ($info['ts']['packet']['flags']['adaption_field_exists']) {
         $AdaptionField_raw = Utils::BigEndian2Int(substr($TSheader, $offset, 2));
         $offset += 2;
         $info['ts']['packet']['adaption']['field_length'] = ($AdaptionField_raw & 0xff00) >> 8;
         // Number of bytes in the adaptation field immediately following this byte
         $info['ts']['packet']['adaption']['flags']['discontinuity'] = (bool) ($AdaptionField_raw & 0x80);
         // Set to 1 if current TS packet is in a discontinuity state with respect to either the continuity counter or the program clock reference
         $info['ts']['packet']['adaption']['flags']['random_access'] = (bool) ($AdaptionField_raw & 0x40);
         // Set to 1 if the PES packet in this TS packet starts a video/audio sequence
         $info['ts']['packet']['adaption']['flags']['high_priority'] = (bool) ($AdaptionField_raw & 0x20);
         // 1 = higher priority
         $info['ts']['packet']['adaption']['flags']['pcr'] = (bool) ($AdaptionField_raw & 0x10);
         // 1 means adaptation field does contain a PCR field
         $info['ts']['packet']['adaption']['flags']['opcr'] = (bool) ($AdaptionField_raw & 0x8);
         // 1 means adaptation field does contain an OPCR field
         $info['ts']['packet']['adaption']['flags']['splice_point'] = (bool) ($AdaptionField_raw & 0x4);
         // 1 means presence of splice countdown field in adaptation field
         $info['ts']['packet']['adaption']['flags']['private_data'] = (bool) ($AdaptionField_raw & 0x2);
         // 1 means presence of private data bytes in adaptation field
         $info['ts']['packet']['adaption']['flags']['extension'] = (bool) ($AdaptionField_raw & 0x1);
         // 1 means presence of adaptation field extension
         if ($info['ts']['packet']['adaption']['flags']['pcr']) {
             $info['ts']['packet']['adaption']['raw']['pcr'] = Utils::BigEndian2Int(substr($TSheader, $offset, 6));
             $offset += 6;
         }
         if ($info['ts']['packet']['adaption']['flags']['opcr']) {
             $info['ts']['packet']['adaption']['raw']['opcr'] = Utils::BigEndian2Int(substr($TSheader, $offset, 6));
             $offset += 6;
         }
     }
     $info['error'][] = 'MPEG Transport Stream (.ts) parsing not enabled in this version of getID3() [' . $this->getid3->version() . ']';
     return false;
 }
Пример #13
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $info['fileformat'] = 'swf';
     $info['video']['dataformat'] = 'swf';
     // http://www.openswf.org/spec/SWFfileformat.html
     $this->fseek($info['avdataoffset']);
     $SWFfileData = $this->fread($info['avdataend'] - $info['avdataoffset']);
     // 8 + 2 + 2 + max(9) bytes NOT including Frame_Size RECT data
     $info['swf']['header']['signature'] = substr($SWFfileData, 0, 3);
     switch ($info['swf']['header']['signature']) {
         case 'FWS':
             $info['swf']['header']['compressed'] = false;
             break;
         case 'CWS':
             $info['swf']['header']['compressed'] = true;
             break;
         default:
             $info['error'][] = 'Expecting "FWS" or "CWS" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes($info['swf']['header']['signature']) . '"';
             unset($info['swf']);
             unset($info['fileformat']);
             return false;
             break;
     }
     $info['swf']['header']['version'] = Utils::LittleEndian2Int(substr($SWFfileData, 3, 1));
     $info['swf']['header']['length'] = Utils::LittleEndian2Int(substr($SWFfileData, 4, 4));
     if ($info['swf']['header']['compressed']) {
         $SWFHead = substr($SWFfileData, 0, 8);
         $SWFfileData = substr($SWFfileData, 8);
         if ($decompressed = @gzuncompress($SWFfileData)) {
             $SWFfileData = $SWFHead . $decompressed;
         } else {
             $info['error'][] = 'Error decompressing compressed SWF data (' . strlen($SWFfileData) . ' bytes compressed, should be ' . ($info['swf']['header']['length'] - 8) . ' bytes uncompressed)';
             return false;
         }
     }
     $FrameSizeBitsPerValue = (ord(substr($SWFfileData, 8, 1)) & 0xf8) >> 3;
     $FrameSizeDataLength = ceil((5 + 4 * $FrameSizeBitsPerValue) / 8);
     $FrameSizeDataString = str_pad(decbin(ord(substr($SWFfileData, 8, 1)) & 0x7), 3, '0', STR_PAD_LEFT);
     for ($i = 1; $i < $FrameSizeDataLength; $i++) {
         $FrameSizeDataString .= str_pad(decbin(ord(substr($SWFfileData, 8 + $i, 1))), 8, '0', STR_PAD_LEFT);
     }
     list($X1, $X2, $Y1, $Y2) = explode("\n", wordwrap($FrameSizeDataString, $FrameSizeBitsPerValue, "\n", 1));
     $info['swf']['header']['frame_width'] = Utils::Bin2Dec($X2);
     $info['swf']['header']['frame_height'] = Utils::Bin2Dec($Y2);
     // http://www-lehre.informatik.uni-osnabrueck.de/~fbstark/diplom/docs/swf/Flash_Uncovered.htm
     // Next in the header is the frame rate, which is kind of weird.
     // It is supposed to be stored as a 16bit integer, but the first byte
     // (or last depending on how you look at it) is completely ignored.
     // Example: 0x000C  ->  0x0C  ->  12     So the frame rate is 12 fps.
     // Byte at (8 + $FrameSizeDataLength) is always zero and ignored
     $info['swf']['header']['frame_rate'] = Utils::LittleEndian2Int(substr($SWFfileData, 9 + $FrameSizeDataLength, 1));
     $info['swf']['header']['frame_count'] = Utils::LittleEndian2Int(substr($SWFfileData, 10 + $FrameSizeDataLength, 2));
     $info['video']['frame_rate'] = $info['swf']['header']['frame_rate'];
     $info['video']['resolution_x'] = intval(round($info['swf']['header']['frame_width'] / 20));
     $info['video']['resolution_y'] = intval(round($info['swf']['header']['frame_height'] / 20));
     $info['video']['pixel_aspect_ratio'] = (double) 1;
     if ($info['swf']['header']['frame_count'] > 0 && $info['swf']['header']['frame_rate'] > 0) {
         $info['playtime_seconds'] = $info['swf']['header']['frame_count'] / $info['swf']['header']['frame_rate'];
     }
     //echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
     // SWF tags
     $CurrentOffset = 12 + $FrameSizeDataLength;
     $SWFdataLength = strlen($SWFfileData);
     while ($CurrentOffset < $SWFdataLength) {
         //echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
         $TagIDTagLength = Utils::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 2));
         $TagID = ($TagIDTagLength & 0xfffc) >> 6;
         $TagLength = $TagIDTagLength & 0x3f;
         $CurrentOffset += 2;
         if ($TagLength == 0x3f) {
             $TagLength = Utils::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 4));
             $CurrentOffset += 4;
         }
         unset($TagData);
         $TagData['offset'] = $CurrentOffset;
         $TagData['size'] = $TagLength;
         $TagData['id'] = $TagID;
         $TagData['data'] = substr($SWFfileData, $CurrentOffset, $TagLength);
         switch ($TagID) {
             case 0:
                 // end of movie
                 break 2;
             case 9:
                 // Set background color
                 //$info['swf']['tags'][] = $TagData;
                 $info['swf']['bgcolor'] = strtoupper(str_pad(dechex(Utils::BigEndian2Int($TagData['data'])), 6, '0', STR_PAD_LEFT));
                 break;
             default:
                 if ($this->ReturnAllTagData) {
                     $info['swf']['tags'][] = $TagData;
                 }
                 break;
         }
         $CurrentOffset += $TagLength;
     }
     return true;
 }
Пример #14
0
 private static function EBML2Int($EBMLstring)
 {
     // http://matroska.org/specs/
     // Element ID coded with an UTF-8 like system:
     // 1xxx xxxx                                  - Class A IDs (2^7 -2 possible values) (base 0x8X)
     // 01xx xxxx  xxxx xxxx                       - Class B IDs (2^14-2 possible values) (base 0x4X 0xXX)
     // 001x xxxx  xxxx xxxx  xxxx xxxx            - Class C IDs (2^21-2 possible values) (base 0x2X 0xXX 0xXX)
     // 0001 xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx - Class D IDs (2^28-2 possible values) (base 0x1X 0xXX 0xXX 0xXX)
     // Values with all x at 0 and 1 are reserved (hence the -2).
     // Data size, in octets, is also coded with an UTF-8 like system :
     // 1xxx xxxx                                                                              - value 0 to  2^7-2
     // 01xx xxxx  xxxx xxxx                                                                   - value 0 to 2^14-2
     // 001x xxxx  xxxx xxxx  xxxx xxxx                                                        - value 0 to 2^21-2
     // 0001 xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx                                             - value 0 to 2^28-2
     // 0000 1xxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx                                  - value 0 to 2^35-2
     // 0000 01xx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx                       - value 0 to 2^42-2
     // 0000 001x  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx            - value 0 to 2^49-2
     // 0000 0001  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx - value 0 to 2^56-2
     $first_byte_int = ord($EBMLstring[0]);
     if (0x80 & $first_byte_int) {
         $EBMLstring[0] = chr($first_byte_int & 0x7f);
     } elseif (0x40 & $first_byte_int) {
         $EBMLstring[0] = chr($first_byte_int & 0x3f);
     } elseif (0x20 & $first_byte_int) {
         $EBMLstring[0] = chr($first_byte_int & 0x1f);
     } elseif (0x10 & $first_byte_int) {
         $EBMLstring[0] = chr($first_byte_int & 0xf);
     } elseif (0x8 & $first_byte_int) {
         $EBMLstring[0] = chr($first_byte_int & 0x7);
     } elseif (0x4 & $first_byte_int) {
         $EBMLstring[0] = chr($first_byte_int & 0x3);
     } elseif (0x2 & $first_byte_int) {
         $EBMLstring[0] = chr($first_byte_int & 0x1);
     } elseif (0x1 & $first_byte_int) {
         $EBMLstring[0] = chr($first_byte_int & 0x0);
     }
     return Utils::BigEndian2Int($EBMLstring);
 }