[ Index ]

PHP Cross Reference of Drupal 6 (yi-drupal)

title

Body

[close]

/sites/all/modules/getid3/getid3/ -> module.audio.ogg.php (source)

   1  <?php
   2  /////////////////////////////////////////////////////////////////
   3  /// getID3() by James Heinrich <info@getid3.org>               //
   4  //  available at http://getid3.sourceforge.net                 //
   5  //            or http://www.getid3.org                         //
   6  /////////////////////////////////////////////////////////////////
   7  // See readme.txt for more details                             //
   8  /////////////////////////////////////////////////////////////////
   9  //                                                             //
  10  // module.audio.ogg.php                                        //
  11  // module for analyzing Ogg Vorbis, OggFLAC and Speex files    //
  12  // dependencies: module.audio.flac.php                         //
  13  //                                                            ///
  14  /////////////////////////////////////////////////////////////////
  15  
  16  getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.flac.php', __FILE__, true);
  17  
  18  class getid3_ogg
  19  {
  20  
  21  	function getid3_ogg(&$fd, &$ThisFileInfo) {
  22  
  23          $ThisFileInfo['fileformat'] = 'ogg';
  24  
  25          // Warn about illegal tags - only vorbiscomments are allowed
  26          if (isset($ThisFileInfo['id3v2'])) {
  27              $ThisFileInfo['warning'][] = 'Illegal ID3v2 tag present.';
  28          }
  29          if (isset($ThisFileInfo['id3v1'])) {
  30              $ThisFileInfo['warning'][] = 'Illegal ID3v1 tag present.';
  31          }
  32          if (isset($ThisFileInfo['ape'])) {
  33              $ThisFileInfo['warning'][] = 'Illegal APE tag present.';
  34          }
  35  
  36  
  37          // Page 1 - Stream Header
  38  
  39          fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
  40  
  41          $oggpageinfo = getid3_ogg::ParseOggPageHeader($fd);
  42          $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
  43  
  44          if (ftell($fd) >= GETID3_FREAD_BUFFER_SIZE) {
  45              $ThisFileInfo['error'][] = 'Could not find start of Ogg page in the first '.GETID3_FREAD_BUFFER_SIZE.' bytes (this might not be an Ogg-Vorbis file?)';
  46              unset($ThisFileInfo['fileformat']);
  47              unset($ThisFileInfo['ogg']);
  48              return false;
  49          }
  50  
  51          $filedata = fread($fd, $oggpageinfo['page_length']);
  52          $filedataoffset = 0;
  53  
  54          if (substr($filedata, 0, 4) == 'fLaC') {
  55  
  56              $ThisFileInfo['audio']['dataformat']   = 'flac';
  57              $ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
  58              $ThisFileInfo['audio']['lossless']     = true;
  59  
  60          } elseif (substr($filedata, 1, 6) == 'vorbis') {
  61  
  62              $this->ParseVorbisPageHeader($filedata, $filedataoffset, $ThisFileInfo, $oggpageinfo);
  63  
  64          } elseif (substr($filedata, 0, 8) == 'Speex   ') {
  65  
  66              // http://www.speex.org/manual/node10.html
  67  
  68              $ThisFileInfo['audio']['dataformat']   = 'speex';
  69              $ThisFileInfo['mime_type']             = 'audio/speex';
  70              $ThisFileInfo['audio']['bitrate_mode'] = 'abr';
  71              $ThisFileInfo['audio']['lossless']     = false;
  72  
  73              $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_string']           =                              substr($filedata, $filedataoffset, 8); // hard-coded to 'Speex   '
  74              $filedataoffset += 8;
  75              $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version']          =                              substr($filedata, $filedataoffset, 20);
  76              $filedataoffset += 20;
  77              $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version_id']       = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
  78              $filedataoffset += 4;
  79              $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['header_size']            = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
  80              $filedataoffset += 4;
  81              $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate']                   = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
  82              $filedataoffset += 4;
  83              $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode']                   = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
  84              $filedataoffset += 4;
  85              $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode_bitstream_version'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
  86              $filedataoffset += 4;
  87              $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels']            = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
  88              $filedataoffset += 4;
  89              $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['bitrate']                = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
  90              $filedataoffset += 4;
  91              $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['framesize']              = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
  92              $filedataoffset += 4;
  93              $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr']                    = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
  94              $filedataoffset += 4;
  95              $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['frames_per_packet']      = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
  96              $filedataoffset += 4;
  97              $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['extra_headers']          = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
  98              $filedataoffset += 4;
  99              $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved1']              = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 100              $filedataoffset += 4;
 101              $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved2']              = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 102              $filedataoffset += 4;
 103  
 104              $ThisFileInfo['speex']['speex_version'] = trim($ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version']);
 105              $ThisFileInfo['speex']['sample_rate']   = $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'];
 106              $ThisFileInfo['speex']['channels']      = $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'];
 107              $ThisFileInfo['speex']['vbr']           = (bool) $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'];
 108              $ThisFileInfo['speex']['band_type']     = getid3_ogg::SpeexBandModeLookup($ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode']);
 109  
 110              $ThisFileInfo['audio']['sample_rate']   = $ThisFileInfo['speex']['sample_rate'];
 111              $ThisFileInfo['audio']['channels']      = $ThisFileInfo['speex']['channels'];
 112              if ($ThisFileInfo['speex']['vbr']) {
 113                  $ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
 114              }
 115  
 116          } else {
 117  
 118              $ThisFileInfo['error'][] = 'Expecting either "Speex   " or "vorbis" identifier strings, found neither';
 119              unset($ThisFileInfo['ogg']);
 120              unset($ThisFileInfo['mime_type']);
 121              return false;
 122  
 123          }
 124  
 125  
 126          // Page 2 - Comment Header
 127  
 128          $oggpageinfo = getid3_ogg::ParseOggPageHeader($fd);
 129          $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
 130  
 131          switch ($ThisFileInfo['audio']['dataformat']) {
 132  
 133              case 'vorbis':
 134                  $filedata = fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
 135                  $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, 0, 1));
 136                  $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] =                              substr($filedata, 1, 6); // hard-coded to 'vorbis'
 137  
 138                  getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo);
 139                  break;
 140  
 141              case 'flac':
 142                  if (!getid3_flac::FLACparseMETAdata($fd, $ThisFileInfo)) {
 143                      $ThisFileInfo['error'][] = 'Failed to parse FLAC headers';
 144                      return false;
 145                  }
 146                  break;
 147  
 148              case 'speex':
 149                  fseek($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length'], SEEK_CUR);
 150                  getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo);
 151                  break;
 152  
 153          }
 154  
 155  
 156  
 157          // Last Page - Number of Samples
 158  
 159          if ($ThisFileInfo['avdataend'] >= pow(2, 31)) {
 160  
 161              $ThisFileInfo['warning'][] = 'Unable to parse Ogg end chunk file (PHP does not support file operations beyond 2GB)';
 162  
 163          } else {
 164  
 165              fseek($fd, max($ThisFileInfo['avdataend'] - GETID3_FREAD_BUFFER_SIZE, 0), SEEK_SET);
 166              $LastChunkOfOgg = strrev(fread($fd, GETID3_FREAD_BUFFER_SIZE));
 167              if ($LastOggSpostion = strpos($LastChunkOfOgg, 'SggO')) {
 168                  fseek($fd, $ThisFileInfo['avdataend'] - ($LastOggSpostion + strlen('SggO')), SEEK_SET);
 169                  $ThisFileInfo['avdataend'] = ftell($fd);
 170                  $ThisFileInfo['ogg']['pageheader']['eos'] = getid3_ogg::ParseOggPageHeader($fd);
 171                  $ThisFileInfo['ogg']['samples']   = $ThisFileInfo['ogg']['pageheader']['eos']['pcm_abs_position'];
 172                  if ($ThisFileInfo['ogg']['samples'] == 0) {
 173                      $ThisFileInfo['error'][] = 'Corrupt Ogg file: eos.number of samples == zero';
 174                      return false;
 175                  }
 176                  $ThisFileInfo['ogg']['bitrate_average'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / ($ThisFileInfo['ogg']['samples'] / $ThisFileInfo['audio']['sample_rate']);
 177              }
 178  
 179          }
 180  
 181          if (!empty($ThisFileInfo['ogg']['bitrate_average'])) {
 182              $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['ogg']['bitrate_average'];
 183          } elseif (!empty($ThisFileInfo['ogg']['bitrate_nominal'])) {
 184              $ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['ogg']['bitrate_nominal'];
 185          } elseif (!empty($ThisFileInfo['ogg']['bitrate_min']) && !empty($ThisFileInfo['ogg']['bitrate_max'])) {
 186              $ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['ogg']['bitrate_min'] + $ThisFileInfo['ogg']['bitrate_max']) / 2;
 187          }
 188          if (isset($ThisFileInfo['audio']['bitrate']) && !isset($ThisFileInfo['playtime_seconds'])) {
 189              if ($ThisFileInfo['audio']['bitrate'] == 0) {
 190                  $ThisFileInfo['error'][] = 'Corrupt Ogg file: bitrate_audio == zero';
 191                  return false;
 192              }
 193              $ThisFileInfo['playtime_seconds'] = (float) ((($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['audio']['bitrate']);
 194          }
 195  
 196          if (isset($ThisFileInfo['ogg']['vendor'])) {
 197              $ThisFileInfo['audio']['encoder'] = preg_replace('/^Encoded with /', '', $ThisFileInfo['ogg']['vendor']);
 198  
 199              // Vorbis only
 200              if ($ThisFileInfo['audio']['dataformat'] == 'vorbis') {
 201  
 202                  // Vorbis 1.0 starts with Xiph.Org
 203                  if  (preg_match('/^Xiph.Org/', $ThisFileInfo['audio']['encoder'])) {
 204  
 205                      if ($ThisFileInfo['audio']['bitrate_mode'] == 'abr') {
 206  
 207                          // Set -b 128 on abr files
 208                          $ThisFileInfo['audio']['encoder_options'] = '-b '.round($ThisFileInfo['ogg']['bitrate_nominal'] / 1000);
 209  
 210                      } elseif (($ThisFileInfo['audio']['bitrate_mode'] == 'vbr') && ($ThisFileInfo['audio']['channels'] == 2) && ($ThisFileInfo['audio']['sample_rate'] >= 44100) && ($ThisFileInfo['audio']['sample_rate'] <= 48000)) {
 211                          // Set -q N on vbr files
 212                          $ThisFileInfo['audio']['encoder_options'] = '-q '.$this->get_quality_from_nominal_bitrate($ThisFileInfo['ogg']['bitrate_nominal']);
 213  
 214                      }
 215                  }
 216  
 217                  if (empty($ThisFileInfo['audio']['encoder_options']) && !empty($ThisFileInfo['ogg']['bitrate_nominal'])) {
 218                      $ThisFileInfo['audio']['encoder_options'] = 'Nominal bitrate: '.intval(round($ThisFileInfo['ogg']['bitrate_nominal'] / 1000)).'kbps';
 219                  }
 220              }
 221          }
 222  
 223          return true;
 224      }
 225  
 226  	function ParseVorbisPageHeader(&$filedata, &$filedataoffset, &$ThisFileInfo, &$oggpageinfo) {
 227          $ThisFileInfo['audio']['dataformat'] = 'vorbis';
 228          $ThisFileInfo['audio']['lossless']   = false;
 229  
 230          $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 231          $filedataoffset += 1;
 232          $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, $filedataoffset, 6); // hard-coded to 'vorbis'
 233          $filedataoffset += 6;
 234          $ThisFileInfo['ogg']['bitstreamversion'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 235          $filedataoffset += 4;
 236          $ThisFileInfo['ogg']['numberofchannels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 237          $filedataoffset += 1;
 238          $ThisFileInfo['audio']['channels']       = $ThisFileInfo['ogg']['numberofchannels'];
 239          $ThisFileInfo['ogg']['samplerate']       = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 240          $filedataoffset += 4;
 241          if ($ThisFileInfo['ogg']['samplerate'] == 0) {
 242              $ThisFileInfo['error'][] = 'Corrupt Ogg file: sample rate == zero';
 243              return false;
 244          }
 245          $ThisFileInfo['audio']['sample_rate']    = $ThisFileInfo['ogg']['samplerate'];
 246          $ThisFileInfo['ogg']['samples']          = 0; // filled in later
 247          $ThisFileInfo['ogg']['bitrate_average']  = 0; // filled in later
 248          $ThisFileInfo['ogg']['bitrate_max']      = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 249          $filedataoffset += 4;
 250          $ThisFileInfo['ogg']['bitrate_nominal']  = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 251          $filedataoffset += 4;
 252          $ThisFileInfo['ogg']['bitrate_min']      = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 253          $filedataoffset += 4;
 254          $ThisFileInfo['ogg']['blocksize_small']  = pow(2,  getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0x0F);
 255          $ThisFileInfo['ogg']['blocksize_large']  = pow(2, (getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0xF0) >> 4);
 256          $ThisFileInfo['ogg']['stop_bit']         = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); // must be 1, marks end of packet
 257  
 258          $ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; // overridden if actually abr
 259          if ($ThisFileInfo['ogg']['bitrate_max'] == 0xFFFFFFFF) {
 260              unset($ThisFileInfo['ogg']['bitrate_max']);
 261              $ThisFileInfo['audio']['bitrate_mode'] = 'abr';
 262          }
 263          if ($ThisFileInfo['ogg']['bitrate_nominal'] == 0xFFFFFFFF) {
 264              unset($ThisFileInfo['ogg']['bitrate_nominal']);
 265          }
 266          if ($ThisFileInfo['ogg']['bitrate_min'] == 0xFFFFFFFF) {
 267              unset($ThisFileInfo['ogg']['bitrate_min']);
 268              $ThisFileInfo['audio']['bitrate_mode'] = 'abr';
 269          }
 270          return true;
 271      }
 272  
 273  	function ParseOggPageHeader(&$fd) {
 274          // http://xiph.org/ogg/vorbis/doc/framing.html
 275          $oggheader['page_start_offset'] = ftell($fd); // where we started from in the file
 276  
 277          $filedata = fread($fd, GETID3_FREAD_BUFFER_SIZE);
 278          $filedataoffset = 0;
 279          while ((substr($filedata, $filedataoffset++, 4) != 'OggS')) {
 280              if ((ftell($fd) - $oggheader['page_start_offset']) >= GETID3_FREAD_BUFFER_SIZE) {
 281                  // should be found before here
 282                  return false;
 283              }
 284              if ((($filedataoffset + 28) > strlen($filedata)) || (strlen($filedata) < 28)) {
 285                  if (feof($fd) || (($filedata .= fread($fd, GETID3_FREAD_BUFFER_SIZE)) === false)) {
 286                      // get some more data, unless eof, in which case fail
 287                      return false;
 288                  }
 289              }
 290          }
 291          $filedataoffset += strlen('OggS') - 1; // page, delimited by 'OggS'
 292  
 293          $oggheader['stream_structver']  = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 294          $filedataoffset += 1;
 295          $oggheader['flags_raw']         = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 296          $filedataoffset += 1;
 297          $oggheader['flags']['fresh']    = (bool) ($oggheader['flags_raw'] & 0x01); // fresh packet
 298          $oggheader['flags']['bos']      = (bool) ($oggheader['flags_raw'] & 0x02); // first page of logical bitstream (bos)
 299          $oggheader['flags']['eos']      = (bool) ($oggheader['flags_raw'] & 0x04); // last page of logical bitstream (eos)
 300  
 301          $oggheader['pcm_abs_position']  = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
 302          $filedataoffset += 8;
 303          $oggheader['stream_serialno']   = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 304          $filedataoffset += 4;
 305          $oggheader['page_seqno']        = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 306          $filedataoffset += 4;
 307          $oggheader['page_checksum']     = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
 308          $filedataoffset += 4;
 309          $oggheader['page_segments']     = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 310          $filedataoffset += 1;
 311          $oggheader['page_length'] = 0;
 312          for ($i = 0; $i < $oggheader['page_segments']; $i++) {
 313              $oggheader['segment_table'][$i] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
 314              $filedataoffset += 1;
 315              $oggheader['page_length'] += $oggheader['segment_table'][$i];
 316          }
 317          $oggheader['header_end_offset'] = $oggheader['page_start_offset'] + $filedataoffset;
 318          $oggheader['page_end_offset']   = $oggheader['header_end_offset'] + $oggheader['page_length'];
 319          fseek($fd, $oggheader['header_end_offset'], SEEK_SET);
 320  
 321          return $oggheader;
 322      }
 323  
 324  
 325  	function ParseVorbisCommentsFilepointer(&$fd, &$ThisFileInfo) {
 326  
 327          $OriginalOffset = ftell($fd);
 328          $CommentStartOffset = $OriginalOffset;
 329          $commentdataoffset = 0;
 330          $VorbisCommentPage = 1;
 331  
 332          switch ($ThisFileInfo['audio']['dataformat']) {
 333              case 'vorbis':
 334                  $CommentStartOffset = $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset'];  // Second Ogg page, after header block
 335                  fseek($fd, $CommentStartOffset, SEEK_SET);
 336                  $commentdataoffset = 27 + $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_segments'];
 337                  $commentdata = fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset);
 338  
 339                  $commentdataoffset += (strlen('vorbis') + 1);
 340                  break;
 341  
 342              case 'flac':
 343                  fseek($fd, $ThisFileInfo['flac']['VORBIS_COMMENT']['raw']['offset'] + 4, SEEK_SET);
 344                  $commentdata = fread($fd, $ThisFileInfo['flac']['VORBIS_COMMENT']['raw']['block_length']);
 345                  break;
 346  
 347              case 'speex':
 348                  $CommentStartOffset = $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset'];  // Second Ogg page, after header block
 349                  fseek($fd, $CommentStartOffset, SEEK_SET);
 350                  $commentdataoffset = 27 + $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_segments'];
 351                  $commentdata = fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset);
 352                  break;
 353  
 354              default:
 355                  return false;
 356                  break;
 357          }
 358  
 359          $VendorSize = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
 360          $commentdataoffset += 4;
 361  
 362          $ThisFileInfo['ogg']['vendor'] = substr($commentdata, $commentdataoffset, $VendorSize);
 363          $commentdataoffset += $VendorSize;
 364  
 365          $CommentsCount = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
 366          $commentdataoffset += 4;
 367          $ThisFileInfo['avdataoffset'] = $CommentStartOffset + $commentdataoffset;
 368  
 369          $basicfields = array('TITLE', 'ARTIST', 'ALBUM', 'TRACKNUMBER', 'GENRE', 'DATE', 'DESCRIPTION', 'COMMENT');
 370          for ($i = 0; $i < $CommentsCount; $i++) {
 371  
 372              $ThisFileInfo['ogg']['comments_raw'][$i]['dataoffset'] = $CommentStartOffset + $commentdataoffset;
 373  
 374              if (ftell($fd) < ($ThisFileInfo['ogg']['comments_raw'][$i]['dataoffset'] + 4)) {
 375                  $VorbisCommentPage++;
 376  
 377                  $oggpageinfo = getid3_ogg::ParseOggPageHeader($fd);
 378                  $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
 379  
 380                  // First, save what we haven't read yet
 381                  $AsYetUnusedData = substr($commentdata, $commentdataoffset);
 382  
 383                  // Then take that data off the end
 384                  $commentdata     = substr($commentdata, 0, $commentdataoffset);
 385  
 386                  // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct
 387                  $commentdata .= str_repeat("\x00", 27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
 388                  $commentdataoffset += (27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
 389  
 390                  // Finally, stick the unused data back on the end
 391                  $commentdata .= $AsYetUnusedData;
 392  
 393                  //$commentdata .= fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
 394                  $commentdata .= fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1));
 395  
 396              }
 397              $ThisFileInfo['ogg']['comments_raw'][$i]['size']       = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
 398  
 399              // replace avdataoffset with position just after the last vorbiscomment
 400              $ThisFileInfo['avdataoffset'] = $ThisFileInfo['ogg']['comments_raw'][$i]['dataoffset'] + $ThisFileInfo['ogg']['comments_raw'][$i]['size'] + 4;
 401  
 402              $commentdataoffset += 4;
 403              while ((strlen($commentdata) - $commentdataoffset) < $ThisFileInfo['ogg']['comments_raw'][$i]['size']) {
 404                  if (($ThisFileInfo['ogg']['comments_raw'][$i]['size'] > $ThisFileInfo['avdataend']) || ($ThisFileInfo['ogg']['comments_raw'][$i]['size'] < 0)) {
 405                      $ThisFileInfo['error'][] = 'Invalid Ogg comment size (comment #'.$i.', claims to be '.number_format($ThisFileInfo['ogg']['comments_raw'][$i]['size']).' bytes) - aborting reading comments';
 406                      break 2;
 407                  }
 408  
 409                  $VorbisCommentPage++;
 410  
 411                  $oggpageinfo = getid3_ogg::ParseOggPageHeader($fd);
 412                  $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
 413  
 414                  // First, save what we haven't read yet
 415                  $AsYetUnusedData = substr($commentdata, $commentdataoffset);
 416  
 417                  // Then take that data off the end
 418                  $commentdata     = substr($commentdata, 0, $commentdataoffset);
 419  
 420                  // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct
 421                  $commentdata .= str_repeat("\x00", 27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
 422                  $commentdataoffset += (27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
 423  
 424                  // Finally, stick the unused data back on the end
 425                  $commentdata .= $AsYetUnusedData;
 426  
 427                  //$commentdata .= fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
 428                  $commentdata .= fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1));
 429  
 430                  //$filebaseoffset += $oggpageinfo['header_end_offset'] - $oggpageinfo['page_start_offset'];
 431              }
 432              $commentstring = substr($commentdata, $commentdataoffset, $ThisFileInfo['ogg']['comments_raw'][$i]['size']);
 433              $commentdataoffset += $ThisFileInfo['ogg']['comments_raw'][$i]['size'];
 434  
 435              if (!$commentstring) {
 436  
 437                  // no comment?
 438                  $ThisFileInfo['warning'][] = 'Blank Ogg comment ['.$i.']';
 439  
 440              } elseif (strstr($commentstring, '=')) {
 441  
 442                  $commentexploded = explode('=', $commentstring, 2);
 443                  $ThisFileInfo['ogg']['comments_raw'][$i]['key']   = strtoupper($commentexploded[0]);
 444                  $ThisFileInfo['ogg']['comments_raw'][$i]['value'] = @$commentexploded[1];
 445                  $ThisFileInfo['ogg']['comments_raw'][$i]['data']  = base64_decode($ThisFileInfo['ogg']['comments_raw'][$i]['value']);
 446  
 447                  $ThisFileInfo['ogg']['comments'][strtolower($ThisFileInfo['ogg']['comments_raw'][$i]['key'])][] = $ThisFileInfo['ogg']['comments_raw'][$i]['value'];
 448  
 449                  $imageinfo = array();
 450                  $imagechunkcheck = getid3_lib::GetDataImageSize($ThisFileInfo['ogg']['comments_raw'][$i]['data'], $imageinfo);
 451                  $ThisFileInfo['ogg']['comments_raw'][$i]['image_mime'] = getid3_lib::image_type_to_mime_type($imagechunkcheck[2]);
 452                  if (!$ThisFileInfo['ogg']['comments_raw'][$i]['image_mime'] || ($ThisFileInfo['ogg']['comments_raw'][$i]['image_mime'] == 'application/octet-stream')) {
 453                      unset($ThisFileInfo['ogg']['comments_raw'][$i]['image_mime']);
 454                      unset($ThisFileInfo['ogg']['comments_raw'][$i]['data']);
 455                  }
 456  
 457              } else {
 458  
 459                  $ThisFileInfo['warning'][] = '[known problem with CDex >= v1.40, < v1.50b7] Invalid Ogg comment name/value pair ['.$i.']: '.$commentstring;
 460  
 461              }
 462          }
 463  
 464  
 465          // Replay Gain Adjustment
 466          // http://privatewww.essex.ac.uk/~djmrob/replaygain/
 467          if (isset($ThisFileInfo['ogg']['comments']) && is_array($ThisFileInfo['ogg']['comments'])) {
 468              foreach ($ThisFileInfo['ogg']['comments'] as $index => $commentvalue) {
 469                  switch ($index) {
 470                      case 'rg_audiophile':
 471                      case 'replaygain_album_gain':
 472                          $ThisFileInfo['replay_gain']['album']['adjustment'] = (double) $commentvalue[0];
 473                          unset($ThisFileInfo['ogg']['comments'][$index]);
 474                          break;
 475  
 476                      case 'rg_radio':
 477                      case 'replaygain_track_gain':
 478                          $ThisFileInfo['replay_gain']['track']['adjustment'] = (double) $commentvalue[0];
 479                          unset($ThisFileInfo['ogg']['comments'][$index]);
 480                          break;
 481  
 482                      case 'replaygain_album_peak':
 483                          $ThisFileInfo['replay_gain']['album']['peak'] = (double) $commentvalue[0];
 484                          unset($ThisFileInfo['ogg']['comments'][$index]);
 485                          break;
 486  
 487                      case 'rg_peak':
 488                      case 'replaygain_track_peak':
 489                          $ThisFileInfo['replay_gain']['track']['peak'] = (double) $commentvalue[0];
 490                          unset($ThisFileInfo['ogg']['comments'][$index]);
 491                          break;
 492  
 493  
 494                      default:
 495                          // do nothing
 496                          break;
 497                  }
 498              }
 499          }
 500  
 501          fseek($fd, $OriginalOffset, SEEK_SET);
 502  
 503          return true;
 504      }
 505  
 506  	function SpeexBandModeLookup($mode) {
 507          static $SpeexBandModeLookup = array();
 508          if (empty($SpeexBandModeLookup)) {
 509              $SpeexBandModeLookup[0] = 'narrow';
 510              $SpeexBandModeLookup[1] = 'wide';
 511              $SpeexBandModeLookup[2] = 'ultra-wide';
 512          }
 513          return (isset($SpeexBandModeLookup[$mode]) ? $SpeexBandModeLookup[$mode] : null);
 514      }
 515  
 516  
 517  	function OggPageSegmentLength($OggInfoArray, $SegmentNumber=1) {
 518          for ($i = 0; $i < $SegmentNumber; $i++) {
 519              $segmentlength = 0;
 520              foreach ($OggInfoArray['segment_table'] as $key => $value) {
 521                  $segmentlength += $value;
 522                  if ($value < 255) {
 523                      break;
 524                  }
 525              }
 526          }
 527          return $segmentlength;
 528      }
 529  
 530  
 531  	function get_quality_from_nominal_bitrate($nominal_bitrate) {
 532  
 533          // decrease precision
 534          $nominal_bitrate = $nominal_bitrate / 1000;
 535  
 536          if ($nominal_bitrate < 128) {
 537              // q-1 to q4
 538              $qval = ($nominal_bitrate - 64) / 16;
 539          } elseif ($nominal_bitrate < 256) {
 540              // q4 to q8
 541              $qval = $nominal_bitrate / 32;
 542          } elseif ($nominal_bitrate < 320) {
 543              // q8 to q9
 544              $qval = ($nominal_bitrate + 256) / 64;
 545          } else {
 546              // q9 to q10
 547              $qval = ($nominal_bitrate + 1300) / 180;
 548          }
 549          //return $qval; // 5.031324
 550          //return intval($qval); // 5
 551          return round($qval, 1); // 5 or 4.9
 552      }
 553  
 554  }
 555  
 556  ?>


Generated: Mon Jul 9 18:01:44 2012 Cross-referenced by PHPXref 0.7