annotate e2gallerypro/e2upload/Backend/Assets/getid3/module.audio.xiph.php @ 25:6a3d04638a89 judyates

mailto links.
author Robert McIntyre <rlm@mit.edu>
date Sat, 19 Jul 2014 14:35:52 -0400
parents 3f6b44aa6b35
children
rev   line source
rlm@3 1 <?php
rlm@3 2 // +----------------------------------------------------------------------+
rlm@3 3 // | PHP version 5 |
rlm@3 4 // +----------------------------------------------------------------------+
rlm@3 5 // | Copyright (c) 2002-2006 James Heinrich, Allan Hansen |
rlm@3 6 // +----------------------------------------------------------------------+
rlm@3 7 // | This source file is subject to version 2 of the GPL license, |
rlm@3 8 // | that is bundled with this package in the file license.txt and is |
rlm@3 9 // | available through the world-wide-web at the following url: |
rlm@3 10 // | http://www.gnu.org/copyleft/gpl.html |
rlm@3 11 // +----------------------------------------------------------------------+
rlm@3 12 // | getID3() - http://getid3.sourceforge.net or http://www.getid3.org |
rlm@3 13 // +----------------------------------------------------------------------+
rlm@3 14 // | Authors: James Heinrich <infoØgetid3*org> |
rlm@3 15 // | Allan Hansen <ahØartemis*dk> |
rlm@3 16 // +----------------------------------------------------------------------+
rlm@3 17 // | module.audio.xiph.php |
rlm@3 18 // | Module for analyzing Xiph.org audio file formats: |
rlm@3 19 // | Ogg Vorbis, FLAC, OggFLAC and Speex - not Ogg Theora |
rlm@3 20 // | dependencies: module.lib.image_size.php (optional) |
rlm@3 21 // +----------------------------------------------------------------------+
rlm@3 22 //
rlm@3 23 // $Id: module.audio.xiph.php,v 1.5 2006/12/03 21:12:43 ah Exp $
rlm@3 24
rlm@3 25
rlm@3 26
rlm@3 27 class getid3_xiph extends getid3_handler
rlm@3 28 {
rlm@3 29
rlm@3 30 public function Analyze() {
rlm@3 31
rlm@3 32 $getid3 = $this->getid3;
rlm@3 33
rlm@3 34 if ($getid3->option_tags_images) {
rlm@3 35 $getid3->include_module('lib.image_size');
rlm@3 36 }
rlm@3 37
rlm@3 38 fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET);
rlm@3 39
rlm@3 40 $magic = fread($getid3->fp, 4);
rlm@3 41
rlm@3 42 if ($magic == 'OggS') {
rlm@3 43 return $this->ParseOgg();
rlm@3 44 }
rlm@3 45
rlm@3 46 if ($magic == 'fLaC') {
rlm@3 47 return $this->ParseFLAC();
rlm@3 48 }
rlm@3 49
rlm@3 50 }
rlm@3 51
rlm@3 52
rlm@3 53
rlm@3 54 private function ParseOgg() {
rlm@3 55
rlm@3 56 $getid3 = $this->getid3;
rlm@3 57
rlm@3 58 fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET);
rlm@3 59
rlm@3 60 $getid3->info['audio'] = $getid3->info['ogg'] = array ();
rlm@3 61 $info_ogg = &$getid3->info['ogg'];
rlm@3 62 $info_audio = &$getid3->info['audio'];
rlm@3 63
rlm@3 64 $getid3->info['fileformat'] = 'ogg';
rlm@3 65
rlm@3 66
rlm@3 67 //// Page 1 - Stream Header
rlm@3 68
rlm@3 69 $ogg_page_info = $this->ParseOggPageHeader();
rlm@3 70 $info_ogg['pageheader'][$ogg_page_info['page_seqno']] = $ogg_page_info;
rlm@3 71
rlm@3 72 if (ftell($getid3->fp) >= getid3::FREAD_BUFFER_SIZE) {
rlm@3 73 throw new getid3_exception('Could not find start of Ogg page in the first '.getid3::FREAD_BUFFER_SIZE.' bytes (this might not be an Ogg file?)');
rlm@3 74 }
rlm@3 75
rlm@3 76 $file_data = fread($getid3->fp, $ogg_page_info['page_length']);
rlm@3 77 $file_data_offset = 0;
rlm@3 78
rlm@3 79
rlm@3 80 // OggFLAC
rlm@3 81 if (substr($file_data, 0, 4) == 'fLaC') {
rlm@3 82
rlm@3 83 $info_audio['dataformat'] = 'flac';
rlm@3 84 $info_audio['bitrate_mode'] = 'vbr';
rlm@3 85 $info_audio['lossless'] = true;
rlm@3 86
rlm@3 87 }
rlm@3 88
rlm@3 89
rlm@3 90 // Ogg Vorbis
rlm@3 91 elseif (substr($file_data, 1, 6) == 'vorbis') {
rlm@3 92
rlm@3 93 $info_audio['dataformat'] = 'vorbis';
rlm@3 94 $info_audio['lossless'] = false;
rlm@3 95
rlm@3 96 $info_ogg['pageheader'][$ogg_page_info['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int($file_data[0]);
rlm@3 97 $info_ogg['pageheader'][$ogg_page_info['page_seqno']]['stream_type'] = substr($file_data, 1, 6); // hard-coded to 'vorbis'
rlm@3 98
rlm@3 99 getid3_lib::ReadSequence('LittleEndian2Int', $info_ogg, $file_data, 7,
rlm@3 100 array (
rlm@3 101 'bitstreamversion' => 4,
rlm@3 102 'numberofchannels' => 1,
rlm@3 103 'samplerate' => 4,
rlm@3 104 'bitrate_max' => 4,
rlm@3 105 'bitrate_nominal' => 4,
rlm@3 106 'bitrate_min' => 4
rlm@3 107 )
rlm@3 108 );
rlm@3 109
rlm@3 110 $n28 = getid3_lib::LittleEndian2Int($file_data{28});
rlm@3 111 $info_ogg['blocksize_small'] = pow(2, $n28 & 0x0F);
rlm@3 112 $info_ogg['blocksize_large'] = pow(2, ($n28 & 0xF0) >> 4);
rlm@3 113 $info_ogg['stop_bit'] = $n28;
rlm@3 114
rlm@3 115 $info_audio['channels'] = $info_ogg['numberofchannels'];
rlm@3 116 $info_audio['sample_rate'] = $info_ogg['samplerate'];
rlm@3 117
rlm@3 118 $info_audio['bitrate_mode'] = 'vbr'; // overridden if actually abr
rlm@3 119
rlm@3 120 if ($info_ogg['bitrate_max'] == 0xFFFFFFFF) {
rlm@3 121 unset($info_ogg['bitrate_max']);
rlm@3 122 $info_audio['bitrate_mode'] = 'abr';
rlm@3 123 }
rlm@3 124
rlm@3 125 if ($info_ogg['bitrate_nominal'] == 0xFFFFFFFF) {
rlm@3 126 unset($info_ogg['bitrate_nominal']);
rlm@3 127 }
rlm@3 128
rlm@3 129 if ($info_ogg['bitrate_min'] == 0xFFFFFFFF) {
rlm@3 130 unset($info_ogg['bitrate_min']);
rlm@3 131 $info_audio['bitrate_mode'] = 'abr';
rlm@3 132 }
rlm@3 133 }
rlm@3 134
rlm@3 135
rlm@3 136 // Speex
rlm@3 137 elseif (substr($file_data, 0, 8) == 'Speex ') {
rlm@3 138
rlm@3 139 // http://www.speex.org/manual/node10.html
rlm@3 140
rlm@3 141 $info_audio['dataformat'] = 'speex';
rlm@3 142 $getid3->info['mime_type'] = 'audio/speex';
rlm@3 143 $info_audio['bitrate_mode'] = 'abr';
rlm@3 144 $info_audio['lossless'] = false;
rlm@3 145
rlm@3 146 getid3_lib::ReadSequence('LittleEndian2Int', $info_ogg['pageheader'][$ogg_page_info['page_seqno']], $file_data, 0,
rlm@3 147 array (
rlm@3 148 'speex_string' => -8, // hard-coded to 'Speex '
rlm@3 149 'speex_version' => -20, // string
rlm@3 150 'speex_version_id' => 4,
rlm@3 151 'header_size' => 4,
rlm@3 152 'rate' => 4,
rlm@3 153 'mode' => 4,
rlm@3 154 'mode_bitstream_version' => 4,
rlm@3 155 'nb_channels' => 4,
rlm@3 156 'bitrate' => 4,
rlm@3 157 'framesize' => 4,
rlm@3 158 'vbr' => 4,
rlm@3 159 'frames_per_packet' => 4,
rlm@3 160 'extra_headers' => 4,
rlm@3 161 'reserved1' => 4,
rlm@3 162 'reserved2' => 4
rlm@3 163 )
rlm@3 164 );
rlm@3 165
rlm@3 166 $getid3->info['speex']['speex_version'] = trim($info_ogg['pageheader'][$ogg_page_info['page_seqno']]['speex_version']);
rlm@3 167 $getid3->info['speex']['sample_rate'] = $info_ogg['pageheader'][$ogg_page_info['page_seqno']]['rate'];
rlm@3 168 $getid3->info['speex']['channels'] = $info_ogg['pageheader'][$ogg_page_info['page_seqno']]['nb_channels'];
rlm@3 169 $getid3->info['speex']['vbr'] = (bool)$info_ogg['pageheader'][$ogg_page_info['page_seqno']]['vbr'];
rlm@3 170 $getid3->info['speex']['band_type'] = getid3_xiph::SpeexBandModeLookup($info_ogg['pageheader'][$ogg_page_info['page_seqno']]['mode']);
rlm@3 171
rlm@3 172 $info_audio['sample_rate'] = $getid3->info['speex']['sample_rate'];
rlm@3 173 $info_audio['channels'] = $getid3->info['speex']['channels'];
rlm@3 174
rlm@3 175 if ($getid3->info['speex']['vbr']) {
rlm@3 176 $info_audio['bitrate_mode'] = 'vbr';
rlm@3 177 }
rlm@3 178 }
rlm@3 179
rlm@3 180 // Unsupported Ogg file
rlm@3 181 else {
rlm@3 182
rlm@3 183 throw new getid3_exception('Expecting either "Speex " or "vorbis" identifier strings, found neither');
rlm@3 184 }
rlm@3 185
rlm@3 186
rlm@3 187 //// Page 2 - Comment Header
rlm@3 188
rlm@3 189 $ogg_page_info = $this->ParseOggPageHeader();
rlm@3 190 $info_ogg['pageheader'][$ogg_page_info['page_seqno']] = $ogg_page_info;
rlm@3 191
rlm@3 192 switch ($info_audio['dataformat']) {
rlm@3 193
rlm@3 194 case 'vorbis':
rlm@3 195 $file_data = fread($getid3->fp, $info_ogg['pageheader'][$ogg_page_info['page_seqno']]['page_length']);
rlm@3 196 $info_ogg['pageheader'][$ogg_page_info['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($file_data, 0, 1));
rlm@3 197 $info_ogg['pageheader'][$ogg_page_info['page_seqno']]['stream_type'] = substr($file_data, 1, 6); // hard-coded to 'vorbis'
rlm@3 198 $this->ParseVorbisCommentsFilepointer();
rlm@3 199 break;
rlm@3 200
rlm@3 201 case 'flac':
rlm@3 202 if (!$this->FLACparseMETAdata()) {
rlm@3 203 throw new getid3_exception('Failed to parse FLAC headers');
rlm@3 204 }
rlm@3 205 break;
rlm@3 206
rlm@3 207 case 'speex':
rlm@3 208 fseek($getid3->fp, $info_ogg['pageheader'][$ogg_page_info['page_seqno']]['page_length'], SEEK_CUR);
rlm@3 209 $this->ParseVorbisCommentsFilepointer();
rlm@3 210 break;
rlm@3 211 }
rlm@3 212
rlm@3 213
rlm@3 214 //// Last Page - Number of Samples
rlm@3 215
rlm@3 216 fseek($getid3->fp, max($getid3->info['avdataend'] - getid3::FREAD_BUFFER_SIZE, 0), SEEK_SET);
rlm@3 217 $last_chunk_of_ogg = strrev(fread($getid3->fp, getid3::FREAD_BUFFER_SIZE));
rlm@3 218
rlm@3 219 if ($last_OggS_postion = strpos($last_chunk_of_ogg, 'SggO')) {
rlm@3 220 fseek($getid3->fp, $getid3->info['avdataend'] - ($last_OggS_postion + strlen('SggO')), SEEK_SET);
rlm@3 221 $getid3->info['avdataend'] = ftell($getid3->fp);
rlm@3 222 $info_ogg['pageheader']['eos'] = $this->ParseOggPageHeader();
rlm@3 223 $info_ogg['samples'] = $info_ogg['pageheader']['eos']['pcm_abs_position'];
rlm@3 224 $info_ogg['bitrate_average'] = (($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8) / ($info_ogg['samples'] / $info_audio['sample_rate']);
rlm@3 225 }
rlm@3 226
rlm@3 227 if (!empty($info_ogg['bitrate_average'])) {
rlm@3 228 $info_audio['bitrate'] = $info_ogg['bitrate_average'];
rlm@3 229 } elseif (!empty($info_ogg['bitrate_nominal'])) {
rlm@3 230 $info_audio['bitrate'] = $info_ogg['bitrate_nominal'];
rlm@3 231 } elseif (!empty($info_ogg['bitrate_min']) && !empty($info_ogg['bitrate_max'])) {
rlm@3 232 $info_audio['bitrate'] = ($info_ogg['bitrate_min'] + $info_ogg['bitrate_max']) / 2;
rlm@3 233 }
rlm@3 234 if (isset($info_audio['bitrate']) && !isset($getid3->info['playtime_seconds'])) {
rlm@3 235 $getid3->info['playtime_seconds'] = (float)((($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8) / $info_audio['bitrate']);
rlm@3 236 }
rlm@3 237
rlm@3 238 if (isset($info_ogg['vendor'])) {
rlm@3 239 $info_audio['encoder'] = preg_replace('/^Encoded with /', '', $info_ogg['vendor']);
rlm@3 240
rlm@3 241 // Vorbis only
rlm@3 242 if ($info_audio['dataformat'] == 'vorbis') {
rlm@3 243
rlm@3 244 // Vorbis 1.0 starts with Xiph.Org
rlm@3 245 if (preg_match('/^Xiph.Org/', $info_audio['encoder'])) {
rlm@3 246
rlm@3 247 if ($info_audio['bitrate_mode'] == 'abr') {
rlm@3 248
rlm@3 249 // Set -b 128 on abr files
rlm@3 250 $info_audio['encoder_options'] = '-b '.round($info_ogg['bitrate_nominal'] / 1000);
rlm@3 251
rlm@3 252 } elseif (($info_audio['bitrate_mode'] == 'vbr') && ($info_audio['channels'] == 2) && ($info_audio['sample_rate'] >= 44100) && ($info_audio['sample_rate'] <= 48000)) {
rlm@3 253 // Set -q N on vbr files
rlm@3 254 $info_audio['encoder_options'] = '-q '.getid3_xiph::GetQualityFromNominalBitrate($info_ogg['bitrate_nominal']);
rlm@3 255 }
rlm@3 256 }
rlm@3 257
rlm@3 258 if (empty($info_audio['encoder_options']) && !empty($info_ogg['bitrate_nominal'])) {
rlm@3 259 $info_audio['encoder_options'] = 'Nominal bitrate: '.intval(round($info_ogg['bitrate_nominal'] / 1000)).'kbps';
rlm@3 260 }
rlm@3 261 }
rlm@3 262 }
rlm@3 263
rlm@3 264 return true;
rlm@3 265 }
rlm@3 266
rlm@3 267
rlm@3 268
rlm@3 269 private function ParseOggPageHeader() {
rlm@3 270
rlm@3 271 $getid3 = $this->getid3;
rlm@3 272
rlm@3 273 // http://xiph.org/ogg/vorbis/doc/framing.html
rlm@3 274 $ogg_header['page_start_offset'] = ftell($getid3->fp); // where we started from in the file
rlm@3 275
rlm@3 276 $file_data = fread($getid3->fp, getid3::FREAD_BUFFER_SIZE);
rlm@3 277 $file_data_offset = 0;
rlm@3 278
rlm@3 279 while ((substr($file_data, $file_data_offset++, 4) != 'OggS')) {
rlm@3 280 if ((ftell($getid3->fp) - $ogg_header['page_start_offset']) >= getid3::FREAD_BUFFER_SIZE) {
rlm@3 281 // should be found before here
rlm@3 282 return false;
rlm@3 283 }
rlm@3 284 if ((($file_data_offset + 28) > strlen($file_data)) || (strlen($file_data) < 28)) {
rlm@3 285 if (feof($getid3->fp) || (($file_data .= fread($getid3->fp, getid3::FREAD_BUFFER_SIZE)) === false)) {
rlm@3 286 // get some more data, unless eof, in which case fail
rlm@3 287 return false;
rlm@3 288 }
rlm@3 289 }
rlm@3 290 }
rlm@3 291
rlm@3 292 $file_data_offset += 3; // page, delimited by 'OggS'
rlm@3 293
rlm@3 294 getid3_lib::ReadSequence('LittleEndian2Int', $ogg_header, $file_data, $file_data_offset,
rlm@3 295 array (
rlm@3 296 'stream_structver' => 1,
rlm@3 297 'flags_raw' => 1,
rlm@3 298 'pcm_abs_position' => 8,
rlm@3 299 'stream_serialno' => 4,
rlm@3 300 'page_seqno' => 4,
rlm@3 301 'page_checksum' => 4,
rlm@3 302 'page_segments' => 1
rlm@3 303 )
rlm@3 304 );
rlm@3 305
rlm@3 306 $file_data_offset += 23;
rlm@3 307
rlm@3 308 $ogg_header['flags']['fresh'] = (bool)($ogg_header['flags_raw'] & 0x01); // fresh packet
rlm@3 309 $ogg_header['flags']['bos'] = (bool)($ogg_header['flags_raw'] & 0x02); // first page of logical bitstream (bos)
rlm@3 310 $ogg_header['flags']['eos'] = (bool)($ogg_header['flags_raw'] & 0x04); // last page of logical bitstream (eos)
rlm@3 311
rlm@3 312 $ogg_header['page_length'] = 0;
rlm@3 313 for ($i = 0; $i < $ogg_header['page_segments']; $i++) {
rlm@3 314 $ogg_header['segment_table'][$i] = getid3_lib::LittleEndian2Int($file_data{$file_data_offset++});
rlm@3 315 $ogg_header['page_length'] += $ogg_header['segment_table'][$i];
rlm@3 316 }
rlm@3 317 $ogg_header['header_end_offset'] = $ogg_header['page_start_offset'] + $file_data_offset;
rlm@3 318 $ogg_header['page_end_offset'] = $ogg_header['header_end_offset'] + $ogg_header['page_length'];
rlm@3 319 fseek($getid3->fp, $ogg_header['header_end_offset'], SEEK_SET);
rlm@3 320
rlm@3 321 return $ogg_header;
rlm@3 322 }
rlm@3 323
rlm@3 324
rlm@3 325
rlm@3 326 private function ParseVorbisCommentsFilepointer() {
rlm@3 327
rlm@3 328 $getid3 = $this->getid3;
rlm@3 329
rlm@3 330 $original_offset = ftell($getid3->fp);
rlm@3 331 $comment_start_offset = $original_offset;
rlm@3 332 $comment_data_offset = 0;
rlm@3 333 $vorbis_comment_page = 1;
rlm@3 334
rlm@3 335 switch ($getid3->info['audio']['dataformat']) {
rlm@3 336
rlm@3 337 case 'vorbis':
rlm@3 338 $comment_start_offset = $getid3->info['ogg']['pageheader'][$vorbis_comment_page]['page_start_offset']; // Second Ogg page, after header block
rlm@3 339 fseek($getid3->fp, $comment_start_offset, SEEK_SET);
rlm@3 340 $comment_data_offset = 27 + $getid3->info['ogg']['pageheader'][$vorbis_comment_page]['page_segments'];
rlm@3 341 $comment_data = fread($getid3->fp, getid3_xiph::OggPageSegmentLength($getid3->info['ogg']['pageheader'][$vorbis_comment_page], 1) + $comment_data_offset);
rlm@3 342 $comment_data_offset += (strlen('vorbis') + 1);
rlm@3 343 break;
rlm@3 344
rlm@3 345
rlm@3 346 case 'flac':
rlm@3 347 fseek($getid3->fp, $getid3->info['flac']['VORBIS_COMMENT']['raw']['offset'] + 4, SEEK_SET);
rlm@3 348 $comment_data = fread($getid3->fp, $getid3->info['flac']['VORBIS_COMMENT']['raw']['block_length']);
rlm@3 349 break;
rlm@3 350
rlm@3 351
rlm@3 352 case 'speex':
rlm@3 353 $comment_start_offset = $getid3->info['ogg']['pageheader'][$vorbis_comment_page]['page_start_offset']; // Second Ogg page, after header block
rlm@3 354 fseek($getid3->fp, $comment_start_offset, SEEK_SET);
rlm@3 355 $comment_data_offset = 27 + $getid3->info['ogg']['pageheader'][$vorbis_comment_page]['page_segments'];
rlm@3 356 $comment_data = fread($getid3->fp, getid3_xiph::OggPageSegmentLength($getid3->info['ogg']['pageheader'][$vorbis_comment_page], 1) + $comment_data_offset);
rlm@3 357 break;
rlm@3 358
rlm@3 359
rlm@3 360 default:
rlm@3 361 return false;
rlm@3 362 }
rlm@3 363
rlm@3 364 $vendor_size = getid3_lib::LittleEndian2Int(substr($comment_data, $comment_data_offset, 4));
rlm@3 365 $comment_data_offset += 4;
rlm@3 366
rlm@3 367 $getid3->info['ogg']['vendor'] = substr($comment_data, $comment_data_offset, $vendor_size);
rlm@3 368 $comment_data_offset += $vendor_size;
rlm@3 369
rlm@3 370 $comments_count = getid3_lib::LittleEndian2Int(substr($comment_data, $comment_data_offset, 4));
rlm@3 371 $comment_data_offset += 4;
rlm@3 372
rlm@3 373 $getid3->info['avdataoffset'] = $comment_start_offset + $comment_data_offset;
rlm@3 374
rlm@3 375 for ($i = 0; $i < $comments_count; $i++) {
rlm@3 376
rlm@3 377 $getid3->info['ogg']['comments_raw'][$i]['dataoffset'] = $comment_start_offset + $comment_data_offset;
rlm@3 378
rlm@3 379 if (ftell($getid3->fp) < ($getid3->info['ogg']['comments_raw'][$i]['dataoffset'] + 4)) {
rlm@3 380 $vorbis_comment_page++;
rlm@3 381
rlm@3 382 $ogg_page_info = $this->ParseOggPageHeader();
rlm@3 383 $getid3->info['ogg']['pageheader'][$ogg_page_info['page_seqno']] = $ogg_page_info;
rlm@3 384
rlm@3 385 // First, save what we haven't read yet
rlm@3 386 $as_yet_unused_data = substr($comment_data, $comment_data_offset);
rlm@3 387
rlm@3 388 // Then take that data off the end
rlm@3 389 $comment_data = substr($comment_data, 0, $comment_data_offset);
rlm@3 390
rlm@3 391 // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct
rlm@3 392 $comment_data .= str_repeat("\x00", 27 + $getid3->info['ogg']['pageheader'][$ogg_page_info['page_seqno']]['page_segments']);
rlm@3 393 $comment_data_offset += (27 + $getid3->info['ogg']['pageheader'][$ogg_page_info['page_seqno']]['page_segments']);
rlm@3 394
rlm@3 395 // Finally, stick the unused data back on the end
rlm@3 396 $comment_data .= $as_yet_unused_data;
rlm@3 397
rlm@3 398 $comment_data .= fread($getid3->fp, getid3_xiph::OggPageSegmentLength($getid3->info['ogg']['pageheader'][$vorbis_comment_page], 1));
rlm@3 399 }
rlm@3 400 $getid3->info['ogg']['comments_raw'][$i]['size'] = getid3_lib::LittleEndian2Int(substr($comment_data, $comment_data_offset, 4));
rlm@3 401
rlm@3 402 // replace avdataoffset with position just after the last vorbiscomment
rlm@3 403 $getid3->info['avdataoffset'] = $getid3->info['ogg']['comments_raw'][$i]['dataoffset'] + $getid3->info['ogg']['comments_raw'][$i]['size'] + 4;
rlm@3 404
rlm@3 405 $comment_data_offset += 4;
rlm@3 406 while ((strlen($comment_data) - $comment_data_offset) < $getid3->info['ogg']['comments_raw'][$i]['size']) {
rlm@3 407
rlm@3 408 if (($getid3->info['ogg']['comments_raw'][$i]['size'] > $getid3->info['avdataend']) || ($getid3->info['ogg']['comments_raw'][$i]['size'] < 0)) {
rlm@3 409 throw new getid3_exception('Invalid Ogg comment size (comment #'.$i.', claims to be '.number_format($getid3->info['ogg']['comments_raw'][$i]['size']).' bytes) - aborting reading comments');
rlm@3 410 }
rlm@3 411
rlm@3 412 $vorbis_comment_page++;
rlm@3 413
rlm@3 414 $ogg_page_info = $this->ParseOggPageHeader();
rlm@3 415 $getid3->info['ogg']['pageheader'][$ogg_page_info['page_seqno']] = $ogg_page_info;
rlm@3 416
rlm@3 417 // First, save what we haven't read yet
rlm@3 418 $as_yet_unused_data = substr($comment_data, $comment_data_offset);
rlm@3 419
rlm@3 420 // Then take that data off the end
rlm@3 421 $comment_data = substr($comment_data, 0, $comment_data_offset);
rlm@3 422
rlm@3 423 // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct
rlm@3 424 $comment_data .= str_repeat("\x00", 27 + $getid3->info['ogg']['pageheader'][$ogg_page_info['page_seqno']]['page_segments']);
rlm@3 425 $comment_data_offset += (27 + $getid3->info['ogg']['pageheader'][$ogg_page_info['page_seqno']]['page_segments']);
rlm@3 426
rlm@3 427 // Finally, stick the unused data back on the end
rlm@3 428 $comment_data .= $as_yet_unused_data;
rlm@3 429
rlm@3 430 //$comment_data .= fread($getid3->fp, $getid3->info['ogg']['pageheader'][$ogg_page_info['page_seqno']]['page_length']);
rlm@3 431 $comment_data .= fread($getid3->fp, getid3_xiph::OggPageSegmentLength($getid3->info['ogg']['pageheader'][$vorbis_comment_page], 1));
rlm@3 432
rlm@3 433 //$filebaseoffset += $ogg_page_info['header_end_offset'] - $ogg_page_info['page_start_offset'];
rlm@3 434 }
rlm@3 435 $comment_string = substr($comment_data, $comment_data_offset, $getid3->info['ogg']['comments_raw'][$i]['size']);
rlm@3 436 $comment_data_offset += $getid3->info['ogg']['comments_raw'][$i]['size'];
rlm@3 437
rlm@3 438 if (!$comment_string) {
rlm@3 439
rlm@3 440 // no comment?
rlm@3 441 $getid3->warning('Blank Ogg comment ['.$i.']');
rlm@3 442
rlm@3 443 } elseif (strstr($comment_string, '=')) {
rlm@3 444
rlm@3 445 $comment_exploded = explode('=', $comment_string, 2);
rlm@3 446 $getid3->info['ogg']['comments_raw'][$i]['key'] = strtoupper($comment_exploded[0]);
rlm@3 447 $getid3->info['ogg']['comments_raw'][$i]['value'] = @$comment_exploded[1];
rlm@3 448 $getid3->info['ogg']['comments_raw'][$i]['data'] = base64_decode($getid3->info['ogg']['comments_raw'][$i]['value']);
rlm@3 449
rlm@3 450 $getid3->info['ogg']['comments'][strtolower($getid3->info['ogg']['comments_raw'][$i]['key'])][] = $getid3->info['ogg']['comments_raw'][$i]['value'];
rlm@3 451
rlm@3 452 if ($getid3->option_tags_images) {
rlm@3 453 $image_chunk_check = getid3_lib_image_size::get($getid3->info['ogg']['comments_raw'][$i]['data']);
rlm@3 454 $getid3->info['ogg']['comments_raw'][$i]['image_mime'] = image_type_to_mime_type($image_chunk_check[2]);
rlm@3 455 }
rlm@3 456
rlm@3 457 if (!@$getid3->info['ogg']['comments_raw'][$i]['image_mime'] || ($getid3->info['ogg']['comments_raw'][$i]['image_mime'] == 'application/octet-stream')) {
rlm@3 458 unset($getid3->info['ogg']['comments_raw'][$i]['image_mime']);
rlm@3 459 unset($getid3->info['ogg']['comments_raw'][$i]['data']);
rlm@3 460 }
rlm@3 461
rlm@3 462
rlm@3 463 } else {
rlm@3 464
rlm@3 465 $getid3->warning('[known problem with CDex >= v1.40, < v1.50b7] Invalid Ogg comment name/value pair ['.$i.']: '.$comment_string);
rlm@3 466 }
rlm@3 467 }
rlm@3 468
rlm@3 469
rlm@3 470 // Replay Gain Adjustment
rlm@3 471 // http://privatewww.essex.ac.uk/~djmrob/replaygain/
rlm@3 472 if (isset($getid3->info['ogg']['comments']) && is_array($getid3->info['ogg']['comments'])) {
rlm@3 473 foreach ($getid3->info['ogg']['comments'] as $index => $commentvalue) {
rlm@3 474 switch ($index) {
rlm@3 475 case 'rg_audiophile':
rlm@3 476 case 'replaygain_album_gain':
rlm@3 477 $getid3->info['replay_gain']['album']['adjustment'] = (float)$commentvalue[0];
rlm@3 478 unset($getid3->info['ogg']['comments'][$index]);
rlm@3 479 break;
rlm@3 480
rlm@3 481 case 'rg_radio':
rlm@3 482 case 'replaygain_track_gain':
rlm@3 483 $getid3->info['replay_gain']['track']['adjustment'] = (float)$commentvalue[0];
rlm@3 484 unset($getid3->info['ogg']['comments'][$index]);
rlm@3 485 break;
rlm@3 486
rlm@3 487 case 'replaygain_album_peak':
rlm@3 488 $getid3->info['replay_gain']['album']['peak'] = (float)$commentvalue[0];
rlm@3 489 unset($getid3->info['ogg']['comments'][$index]);
rlm@3 490 break;
rlm@3 491
rlm@3 492 case 'rg_peak':
rlm@3 493 case 'replaygain_track_peak':
rlm@3 494 $getid3->info['replay_gain']['track']['peak'] = (float)$commentvalue[0];
rlm@3 495 unset($getid3->info['ogg']['comments'][$index]);
rlm@3 496 break;
rlm@3 497
rlm@3 498 case 'replaygain_reference_loudness':
rlm@3 499 $getid3->info['replay_gain']['reference_volume'] = (float)$commentvalue[0];
rlm@3 500 unset($getid3->info['ogg']['comments'][$index]);
rlm@3 501 break;
rlm@3 502 }
rlm@3 503 }
rlm@3 504 }
rlm@3 505
rlm@3 506 fseek($getid3->fp, $original_offset, SEEK_SET);
rlm@3 507
rlm@3 508 return true;
rlm@3 509 }
rlm@3 510
rlm@3 511
rlm@3 512
rlm@3 513 private function ParseFLAC() {
rlm@3 514
rlm@3 515 $getid3 = $this->getid3;
rlm@3 516
rlm@3 517 // http://flac.sourceforge.net/format.html
rlm@3 518
rlm@3 519 $getid3->info['fileformat'] = 'flac';
rlm@3 520 $getid3->info['audio']['dataformat'] = 'flac';
rlm@3 521 $getid3->info['audio']['bitrate_mode'] = 'vbr';
rlm@3 522 $getid3->info['audio']['lossless'] = true;
rlm@3 523
rlm@3 524 return $this->FLACparseMETAdata();
rlm@3 525 }
rlm@3 526
rlm@3 527
rlm@3 528
rlm@3 529 private function FLACparseMETAdata() {
rlm@3 530
rlm@3 531 $getid3 = $this->getid3;
rlm@3 532
rlm@3 533 do {
rlm@3 534
rlm@3 535 $meta_data_block_offset = ftell($getid3->fp);
rlm@3 536 $meta_data_block_header = fread($getid3->fp, 4);
rlm@3 537 $meta_data_last_block_flag = (bool)(getid3_lib::BigEndian2Int($meta_data_block_header[0]) & 0x80);
rlm@3 538 $meta_data_block_type = getid3_lib::BigEndian2Int($meta_data_block_header[0]) & 0x7F;
rlm@3 539 $meta_data_block_length = getid3_lib::BigEndian2Int(substr($meta_data_block_header, 1, 3));
rlm@3 540 $meta_data_block_type_text = getid3_xiph::FLACmetaBlockTypeLookup($meta_data_block_type);
rlm@3 541
rlm@3 542 if ($meta_data_block_length < 0) {
rlm@3 543 throw new getid3_exception('corrupt or invalid METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$meta_data_block_type.') at offset '.$meta_data_block_offset);
rlm@3 544 }
rlm@3 545
rlm@3 546 $getid3->info['flac'][$meta_data_block_type_text]['raw'] = array (
rlm@3 547 'offset' => $meta_data_block_offset,
rlm@3 548 'last_meta_block' => $meta_data_last_block_flag,
rlm@3 549 'block_type' => $meta_data_block_type,
rlm@3 550 'block_type_text' => $meta_data_block_type_text,
rlm@3 551 'block_length' => $meta_data_block_length,
rlm@3 552 'block_data' => @fread($getid3->fp, $meta_data_block_length)
rlm@3 553 );
rlm@3 554 $getid3->info['avdataoffset'] = ftell($getid3->fp);
rlm@3 555
rlm@3 556 switch ($meta_data_block_type_text) {
rlm@3 557
rlm@3 558 case 'STREAMINFO':
rlm@3 559 if (!$this->FLACparseSTREAMINFO($getid3->info['flac'][$meta_data_block_type_text]['raw']['block_data'])) {
rlm@3 560 return false;
rlm@3 561 }
rlm@3 562 break;
rlm@3 563
rlm@3 564 case 'PADDING':
rlm@3 565 // ignore
rlm@3 566 break;
rlm@3 567
rlm@3 568 case 'APPLICATION':
rlm@3 569 if (!$this->FLACparseAPPLICATION($getid3->info['flac'][$meta_data_block_type_text]['raw']['block_data'])) {
rlm@3 570 return false;
rlm@3 571 }
rlm@3 572 break;
rlm@3 573
rlm@3 574 case 'SEEKTABLE':
rlm@3 575 if (!$this->FLACparseSEEKTABLE($getid3->info['flac'][$meta_data_block_type_text]['raw']['block_data'])) {
rlm@3 576 return false;
rlm@3 577 }
rlm@3 578 break;
rlm@3 579
rlm@3 580 case 'VORBIS_COMMENT':
rlm@3 581 $old_offset = ftell($getid3->fp);
rlm@3 582 fseek($getid3->fp, 0 - $meta_data_block_length, SEEK_CUR);
rlm@3 583 $this->ParseVorbisCommentsFilepointer($getid3->fp, $getid3->info);
rlm@3 584 fseek($getid3->fp, $old_offset, SEEK_SET);
rlm@3 585 break;
rlm@3 586
rlm@3 587 case 'CUESHEET':
rlm@3 588 if (!$this->FLACparseCUESHEET($getid3->info['flac'][$meta_data_block_type_text]['raw']['block_data'])) {
rlm@3 589 return false;
rlm@3 590 }
rlm@3 591 break;
rlm@3 592
rlm@3 593 case 'PICTURE':
rlm@3 594 if (!$this->FLACparsePICTURE($getid3->info['flac'][$meta_data_block_type_text]['raw']['block_data'])) {
rlm@3 595 return false;
rlm@3 596 }
rlm@3 597 break;
rlm@3 598
rlm@3 599 default:
rlm@3 600 $getid3->warning('Unhandled METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$meta_data_block_type.') at offset '.$meta_data_block_offset);
rlm@3 601 }
rlm@3 602
rlm@3 603 } while ($meta_data_last_block_flag === false);
rlm@3 604
rlm@3 605
rlm@3 606 if (isset($getid3->info['flac']['STREAMINFO'])) {
rlm@3 607 $getid3->info['flac']['compressed_audio_bytes'] = $getid3->info['avdataend'] - $getid3->info['avdataoffset'];
rlm@3 608 $getid3->info['flac']['uncompressed_audio_bytes'] = $getid3->info['flac']['STREAMINFO']['samples_stream'] * $getid3->info['flac']['STREAMINFO']['channels'] * ($getid3->info['flac']['STREAMINFO']['bits_per_sample'] / 8);
rlm@3 609 $getid3->info['flac']['compression_ratio'] = $getid3->info['flac']['compressed_audio_bytes'] / $getid3->info['flac']['uncompressed_audio_bytes'];
rlm@3 610 }
rlm@3 611
rlm@3 612 // set md5_data_source - built into flac 0.5+
rlm@3 613 if (isset($getid3->info['flac']['STREAMINFO']['audio_signature'])) {
rlm@3 614
rlm@3 615 if ($getid3->info['flac']['STREAMINFO']['audio_signature'] === str_repeat("\x00", 16)) {
rlm@3 616 $getid3->warning('FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)');
rlm@3 617
rlm@3 618 } else {
rlm@3 619
rlm@3 620 $getid3->info['md5_data_source'] = '';
rlm@3 621 $md5 = $getid3->info['flac']['STREAMINFO']['audio_signature'];
rlm@3 622 for ($i = 0; $i < strlen($md5); $i++) {
rlm@3 623 $getid3->info['md5_data_source'] .= str_pad(dechex(ord($md5{$i})), 2, '00', STR_PAD_LEFT);
rlm@3 624 }
rlm@3 625 if (!preg_match('/^[0-9a-f]{32}$/', $getid3->info['md5_data_source'])) {
rlm@3 626 unset($getid3->info['md5_data_source']);
rlm@3 627 }
rlm@3 628
rlm@3 629 }
rlm@3 630
rlm@3 631 }
rlm@3 632
rlm@3 633 $getid3->info['audio']['bits_per_sample'] = $getid3->info['flac']['STREAMINFO']['bits_per_sample'];
rlm@3 634 if ($getid3->info['audio']['bits_per_sample'] == 8) {
rlm@3 635 // special case
rlm@3 636 // must invert sign bit on all data bytes before MD5'ing to match FLAC's calculated value
rlm@3 637 // MD5sum calculates on unsigned bytes, but FLAC calculated MD5 on 8-bit audio data as signed
rlm@3 638 $getid3->warning('FLAC calculates MD5 data strangely on 8-bit audio, so the stored md5_data_source value will not match the decoded WAV file');
rlm@3 639 }
rlm@3 640 if (!empty($getid3->info['ogg']['vendor'])) {
rlm@3 641 $getid3->info['audio']['encoder'] = $getid3->info['ogg']['vendor'];
rlm@3 642 }
rlm@3 643
rlm@3 644 return true;
rlm@3 645 }
rlm@3 646
rlm@3 647
rlm@3 648
rlm@3 649 private function FLACparseSTREAMINFO($meta_data_block_data) {
rlm@3 650
rlm@3 651 $getid3 = $this->getid3;
rlm@3 652
rlm@3 653 getid3_lib::ReadSequence('BigEndian2Int', $getid3->info['flac']['STREAMINFO'], $meta_data_block_data, 0,
rlm@3 654 array (
rlm@3 655 'min_block_size' => 2,
rlm@3 656 'max_block_size' => 2,
rlm@3 657 'min_frame_size' => 3,
rlm@3 658 'max_frame_size' => 3
rlm@3 659 )
rlm@3 660 );
rlm@3 661
rlm@3 662 $sample_rate_channels_sample_bits_stream_samples = getid3_lib::BigEndian2Bin(substr($meta_data_block_data, 10, 8));
rlm@3 663
rlm@3 664 $getid3->info['flac']['STREAMINFO']['sample_rate'] = bindec(substr($sample_rate_channels_sample_bits_stream_samples, 0, 20));
rlm@3 665 $getid3->info['flac']['STREAMINFO']['channels'] = bindec(substr($sample_rate_channels_sample_bits_stream_samples, 20, 3)) + 1;
rlm@3 666 $getid3->info['flac']['STREAMINFO']['bits_per_sample'] = bindec(substr($sample_rate_channels_sample_bits_stream_samples, 23, 5)) + 1;
rlm@3 667 $getid3->info['flac']['STREAMINFO']['samples_stream'] = bindec(substr($sample_rate_channels_sample_bits_stream_samples, 28, 36)); // bindec() returns float in case of int overrun
rlm@3 668 $getid3->info['flac']['STREAMINFO']['audio_signature'] = substr($meta_data_block_data, 18, 16);
rlm@3 669
rlm@3 670 if (!empty($getid3->info['flac']['STREAMINFO']['sample_rate'])) {
rlm@3 671
rlm@3 672 $getid3->info['audio']['bitrate_mode'] = 'vbr';
rlm@3 673 $getid3->info['audio']['sample_rate'] = $getid3->info['flac']['STREAMINFO']['sample_rate'];
rlm@3 674 $getid3->info['audio']['channels'] = $getid3->info['flac']['STREAMINFO']['channels'];
rlm@3 675 $getid3->info['audio']['bits_per_sample'] = $getid3->info['flac']['STREAMINFO']['bits_per_sample'];
rlm@3 676 $getid3->info['playtime_seconds'] = $getid3->info['flac']['STREAMINFO']['samples_stream'] / $getid3->info['flac']['STREAMINFO']['sample_rate'];
rlm@3 677 $getid3->info['audio']['bitrate'] = (($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8) / $getid3->info['playtime_seconds'];
rlm@3 678
rlm@3 679 } else {
rlm@3 680
rlm@3 681 throw new getid3_exception('Corrupt METAdata block: STREAMINFO');
rlm@3 682 }
rlm@3 683
rlm@3 684 unset($getid3->info['flac']['STREAMINFO']['raw']);
rlm@3 685
rlm@3 686 return true;
rlm@3 687 }
rlm@3 688
rlm@3 689
rlm@3 690
rlm@3 691 private function FLACparseAPPLICATION($meta_data_block_data) {
rlm@3 692
rlm@3 693 $getid3 = $this->getid3;
rlm@3 694
rlm@3 695 $application_id = getid3_lib::BigEndian2Int(substr($meta_data_block_data, 0, 4));
rlm@3 696
rlm@3 697 $getid3->info['flac']['APPLICATION'][$application_id]['name'] = getid3_xiph::FLACapplicationIDLookup($application_id);
rlm@3 698 $getid3->info['flac']['APPLICATION'][$application_id]['data'] = substr($meta_data_block_data, 4);
rlm@3 699
rlm@3 700 unset($getid3->info['flac']['APPLICATION']['raw']);
rlm@3 701
rlm@3 702 return true;
rlm@3 703 }
rlm@3 704
rlm@3 705
rlm@3 706
rlm@3 707 private function FLACparseSEEKTABLE($meta_data_block_data) {
rlm@3 708
rlm@3 709 $getid3 = $this->getid3;
rlm@3 710
rlm@3 711 $offset = 0;
rlm@3 712 $meta_data_block_length = strlen($meta_data_block_data);
rlm@3 713 while ($offset < $meta_data_block_length) {
rlm@3 714 $sample_number_string = substr($meta_data_block_data, $offset, 8);
rlm@3 715 $offset += 8;
rlm@3 716 if ($sample_number_string == "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF") {
rlm@3 717
rlm@3 718 // placeholder point
rlm@3 719 @$getid3->info['flac']['SEEKTABLE']['placeholders']++;
rlm@3 720 $offset += 10;
rlm@3 721
rlm@3 722 } else {
rlm@3 723
rlm@3 724 $sample_number = getid3_lib::BigEndian2Int($sample_number_string);
rlm@3 725
rlm@3 726 $getid3->info['flac']['SEEKTABLE'][$sample_number]['offset'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 8));
rlm@3 727 $offset += 8;
rlm@3 728
rlm@3 729 $getid3->info['flac']['SEEKTABLE'][$sample_number]['samples'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 2));
rlm@3 730 $offset += 2;
rlm@3 731
rlm@3 732 }
rlm@3 733 }
rlm@3 734
rlm@3 735 unset($getid3->info['flac']['SEEKTABLE']['raw']);
rlm@3 736
rlm@3 737 return true;
rlm@3 738 }
rlm@3 739
rlm@3 740
rlm@3 741
rlm@3 742 private function FLACparseCUESHEET($meta_data_block_data) {
rlm@3 743
rlm@3 744 $getid3 = $this->getid3;
rlm@3 745
rlm@3 746 $getid3->info['flac']['CUESHEET']['media_catalog_number'] = trim(substr($meta_data_block_data, 0, 128), "\0");
rlm@3 747 $getid3->info['flac']['CUESHEET']['lead_in_samples'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, 128, 8));
rlm@3 748 $getid3->info['flac']['CUESHEET']['flags']['is_cd'] = (bool)(getid3_lib::BigEndian2Int($meta_data_block_data[136]) & 0x80);
rlm@3 749 $getid3->info['flac']['CUESHEET']['number_tracks'] = getid3_lib::BigEndian2Int($meta_data_block_data[395]);
rlm@3 750
rlm@3 751 $offset = 396;
rlm@3 752
rlm@3 753 for ($track = 0; $track < $getid3->info['flac']['CUESHEET']['number_tracks']; $track++) {
rlm@3 754
rlm@3 755 $track_sample_offset = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 8));
rlm@3 756 $offset += 8;
rlm@3 757
rlm@3 758 $track_number = getid3_lib::BigEndian2Int($meta_data_block_data{$offset++});
rlm@3 759
rlm@3 760 $getid3->info['flac']['CUESHEET']['tracks'][$track_number]['sample_offset'] = $track_sample_offset;
rlm@3 761 $getid3->info['flac']['CUESHEET']['tracks'][$track_number]['isrc'] = substr($meta_data_block_data, $offset, 12);
rlm@3 762 $offset += 12;
rlm@3 763
rlm@3 764 $track_flags_raw = getid3_lib::BigEndian2Int($meta_data_block_data{$offset++});
rlm@3 765 $getid3->info['flac']['CUESHEET']['tracks'][$track_number]['flags']['is_audio'] = (bool)($track_flags_raw & 0x80);
rlm@3 766 $getid3->info['flac']['CUESHEET']['tracks'][$track_number]['flags']['pre_emphasis'] = (bool)($track_flags_raw & 0x40);
rlm@3 767
rlm@3 768 $offset += 13; // reserved
rlm@3 769
rlm@3 770 $getid3->info['flac']['CUESHEET']['tracks'][$track_number]['index_points'] = getid3_lib::BigEndian2Int($meta_data_block_data{$offset++});
rlm@3 771
rlm@3 772 for ($index = 0; $index < $getid3->info['flac']['CUESHEET']['tracks'][$track_number]['index_points']; $index++) {
rlm@3 773
rlm@3 774 $index_sample_offset = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 8));
rlm@3 775 $offset += 8;
rlm@3 776
rlm@3 777 $index_number = getid3_lib::BigEndian2Int($meta_data_block_data{$offset++});
rlm@3 778 $getid3->info['flac']['CUESHEET']['tracks'][$track_number]['indexes'][$index_number] = $index_sample_offset;
rlm@3 779
rlm@3 780 $offset += 3; // reserved
rlm@3 781 }
rlm@3 782 }
rlm@3 783
rlm@3 784 unset($getid3->info['flac']['CUESHEET']['raw']);
rlm@3 785
rlm@3 786 return true;
rlm@3 787 }
rlm@3 788
rlm@3 789
rlm@3 790
rlm@3 791 private function FLACparsePICTURE($meta_data_block_data) {
rlm@3 792
rlm@3 793 $getid3 = $this->getid3;
rlm@3 794
rlm@3 795 $picture = &$getid3->info['flac']['PICTURE'][sizeof($getid3->info['flac']['PICTURE']) - 1];
rlm@3 796
rlm@3 797 $offset = 0;
rlm@3 798
rlm@3 799 $picture['type'] = $this->FLACpictureTypeLookup(getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4)));
rlm@3 800 $offset += 4;
rlm@3 801
rlm@3 802 $length = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
rlm@3 803 $offset += 4;
rlm@3 804
rlm@3 805 $picture['mime_type'] = substr($meta_data_block_data, $offset, $length);
rlm@3 806 $offset += $length;
rlm@3 807
rlm@3 808 $length = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
rlm@3 809 $offset += 4;
rlm@3 810
rlm@3 811 $picture['description'] = substr($meta_data_block_data, $offset, $length);
rlm@3 812 $offset += $length;
rlm@3 813
rlm@3 814 $picture['width'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
rlm@3 815 $offset += 4;
rlm@3 816
rlm@3 817 $picture['height'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
rlm@3 818 $offset += 4;
rlm@3 819
rlm@3 820 $picture['color_depth'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
rlm@3 821 $offset += 4;
rlm@3 822
rlm@3 823 $picture['colors_indexed'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
rlm@3 824 $offset += 4;
rlm@3 825
rlm@3 826 $length = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
rlm@3 827 $offset += 4;
rlm@3 828
rlm@3 829 $picture['image_data'] = substr($meta_data_block_data, $offset, $length);
rlm@3 830 $offset += $length;
rlm@3 831
rlm@3 832 unset($getid3->info['flac']['PICTURE']['raw']);
rlm@3 833
rlm@3 834 return true;
rlm@3 835 }
rlm@3 836
rlm@3 837
rlm@3 838
rlm@3 839 public static function SpeexBandModeLookup($mode) {
rlm@3 840
rlm@3 841 static $lookup = array (
rlm@3 842 0 => 'narrow',
rlm@3 843 1 => 'wide',
rlm@3 844 2 => 'ultra-wide'
rlm@3 845 );
rlm@3 846 return (isset($lookup[$mode]) ? $lookup[$mode] : null);
rlm@3 847 }
rlm@3 848
rlm@3 849
rlm@3 850
rlm@3 851 public static function OggPageSegmentLength($ogg_info_array, $segment_number=1) {
rlm@3 852
rlm@3 853 for ($i = 0; $i < $segment_number; $i++) {
rlm@3 854 $segment_length = 0;
rlm@3 855 foreach ($ogg_info_array['segment_table'] as $key => $value) {
rlm@3 856 $segment_length += $value;
rlm@3 857 if ($value < 255) {
rlm@3 858 break;
rlm@3 859 }
rlm@3 860 }
rlm@3 861 }
rlm@3 862 return $segment_length;
rlm@3 863 }
rlm@3 864
rlm@3 865
rlm@3 866
rlm@3 867 public static function GetQualityFromNominalBitrate($nominal_bitrate) {
rlm@3 868
rlm@3 869 // decrease precision
rlm@3 870 $nominal_bitrate = $nominal_bitrate / 1000;
rlm@3 871
rlm@3 872 if ($nominal_bitrate < 128) {
rlm@3 873 // q-1 to q4
rlm@3 874 $qval = ($nominal_bitrate - 64) / 16;
rlm@3 875 } elseif ($nominal_bitrate < 256) {
rlm@3 876 // q4 to q8
rlm@3 877 $qval = $nominal_bitrate / 32;
rlm@3 878 } elseif ($nominal_bitrate < 320) {
rlm@3 879 // q8 to q9
rlm@3 880 $qval = ($nominal_bitrate + 256) / 64;
rlm@3 881 } else {
rlm@3 882 // q9 to q10
rlm@3 883 $qval = ($nominal_bitrate + 1300) / 180;
rlm@3 884 }
rlm@3 885 return round($qval, 1); // 5 or 4.9
rlm@3 886 }
rlm@3 887
rlm@3 888
rlm@3 889
rlm@3 890 public static function FLACmetaBlockTypeLookup($block_type) {
rlm@3 891
rlm@3 892 static $lookup = array (
rlm@3 893 0 => 'STREAMINFO',
rlm@3 894 1 => 'PADDING',
rlm@3 895 2 => 'APPLICATION',
rlm@3 896 3 => 'SEEKTABLE',
rlm@3 897 4 => 'VORBIS_COMMENT',
rlm@3 898 5 => 'CUESHEET',
rlm@3 899 6 => 'PICTURE'
rlm@3 900 );
rlm@3 901 return (isset($lookup[$block_type]) ? $lookup[$block_type] : 'reserved');
rlm@3 902 }
rlm@3 903
rlm@3 904
rlm@3 905
rlm@3 906 public static function FLACapplicationIDLookup($application_id) {
rlm@3 907
rlm@3 908 // http://flac.sourceforge.net/id.html
rlm@3 909
rlm@3 910 static $lookup = array (
rlm@3 911 0x46746F6C => 'flac-tools', // 'Ftol'
rlm@3 912 0x46746F6C => 'Sound Font FLAC', // 'SFFL'
rlm@3 913 0x7065656D => 'Parseable Embedded Extensible Metadata (specification)', // 'peem'
rlm@3 914 0x786D6364 => 'xmcd'
rlm@3 915
rlm@3 916 );
rlm@3 917 return (isset($lookup[$application_id]) ? $lookup[$application_id] : 'reserved');
rlm@3 918 }
rlm@3 919
rlm@3 920
rlm@3 921 public static function FLACpictureTypeLookup($type_id) {
rlm@3 922
rlm@3 923 static $lookup = array (
rlm@3 924
rlm@3 925 0 => 'Other',
rlm@3 926 1 => "32x32 pixels 'file icon' (PNG only)",
rlm@3 927 2 => 'Other file icon',
rlm@3 928 3 => 'Cover (front)',
rlm@3 929 4 => 'Cover (back)',
rlm@3 930 5 => 'Leaflet page',
rlm@3 931 6 => 'Media (e.g. label side of CD)',
rlm@3 932 7 => 'Lead artist/lead performer/soloist',
rlm@3 933 8 => 'Artist/performer',
rlm@3 934 9 => 'Conductor',
rlm@3 935 10 => 'Band/Orchestra',
rlm@3 936 11 => 'Composer',
rlm@3 937 12 => 'Lyricist/text writer',
rlm@3 938 13 => 'Recording Location',
rlm@3 939 14 => 'During recording',
rlm@3 940 15 => 'During performance',
rlm@3 941 16 => 'Movie/video screen capture',
rlm@3 942 17 => 'A bright coloured fish',
rlm@3 943 18 => 'Illustration',
rlm@3 944 19 => 'Band/artist logotype',
rlm@3 945 20 => 'Publisher/Studio logotype'
rlm@3 946 );
rlm@3 947 return (isset($lookup[$type_id]) ? $lookup[$type_id] : 'reserved');
rlm@3 948 }
rlm@3 949
rlm@3 950 }
rlm@3 951
rlm@3 952 ?>