diff e2gallerypro/e2upload/Backend/Assets/getid3/module.audio.xiph.php @ 3:3f6b44aa6b35 judyates

[svn r4] added ability to buy stuff, from a Prints page, but it doesn't work well with the css, and it also has not been fitted into the perl make system.
author rlm
date Mon, 22 Feb 2010 08:02:39 -0500
parents
children
line wrap: on
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/e2gallerypro/e2upload/Backend/Assets/getid3/module.audio.xiph.php	Mon Feb 22 08:02:39 2010 -0500
     1.3 @@ -0,0 +1,952 @@
     1.4 +<?php
     1.5 +// +----------------------------------------------------------------------+
     1.6 +// | PHP version 5                                                        |
     1.7 +// +----------------------------------------------------------------------+
     1.8 +// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen                 |
     1.9 +// +----------------------------------------------------------------------+
    1.10 +// | This source file is subject to version 2 of the GPL license,         |
    1.11 +// | that is bundled with this package in the file license.txt and is     |
    1.12 +// | available through the world-wide-web at the following url:           |
    1.13 +// | http://www.gnu.org/copyleft/gpl.html                                 |
    1.14 +// +----------------------------------------------------------------------+
    1.15 +// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org    |
    1.16 +// +----------------------------------------------------------------------+
    1.17 +// | Authors: James Heinrich <infoØgetid3*org>                            |
    1.18 +// |          Allan Hansen <ahØartemis*dk>                                |
    1.19 +// +----------------------------------------------------------------------+
    1.20 +// | module.audio.xiph.php                                                |
    1.21 +// | Module for analyzing Xiph.org audio file formats:                    |
    1.22 +// | Ogg Vorbis, FLAC, OggFLAC and Speex - not Ogg Theora                 |
    1.23 +// | dependencies: module.lib.image_size.php (optional)                   |
    1.24 +// +----------------------------------------------------------------------+
    1.25 +//
    1.26 +// $Id: module.audio.xiph.php,v 1.5 2006/12/03 21:12:43 ah Exp $
    1.27 +
    1.28 +        
    1.29 +        
    1.30 +class getid3_xiph extends getid3_handler
    1.31 +{
    1.32 +    
    1.33 +    public function Analyze() {
    1.34 +        
    1.35 +        $getid3 = $this->getid3;
    1.36 +        
    1.37 +        if ($getid3->option_tags_images) {        
    1.38 +            $getid3->include_module('lib.image_size');
    1.39 +        }
    1.40 +        
    1.41 +        fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET);
    1.42 +        
    1.43 +        $magic = fread($getid3->fp, 4);
    1.44 +        
    1.45 +        if ($magic == 'OggS') {
    1.46 +            return $this->ParseOgg();
    1.47 +        }
    1.48 +        
    1.49 +        if ($magic == 'fLaC') {
    1.50 +            return $this->ParseFLAC();
    1.51 +        }
    1.52 +        
    1.53 +    }
    1.54 +    
    1.55 +    
    1.56 +    
    1.57 +    private function ParseOgg() {
    1.58 +        
    1.59 +        $getid3 = $this->getid3;
    1.60 +        
    1.61 +        fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET);
    1.62 +        
    1.63 +        $getid3->info['audio'] = $getid3->info['ogg'] = array ();
    1.64 +        $info_ogg   = &$getid3->info['ogg'];				
    1.65 +        $info_audio = &$getid3->info['audio'];
    1.66 +        
    1.67 +        $getid3->info['fileformat'] = 'ogg';
    1.68 +
    1.69 +
    1.70 +        //// Page 1 - Stream Header
    1.71 +
    1.72 +        $ogg_page_info = $this->ParseOggPageHeader();
    1.73 +        $info_ogg['pageheader'][$ogg_page_info['page_seqno']] = $ogg_page_info;
    1.74 +
    1.75 +        if (ftell($getid3->fp) >= getid3::FREAD_BUFFER_SIZE) {
    1.76 +            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?)');
    1.77 +        }
    1.78 +
    1.79 +        $file_data = fread($getid3->fp, $ogg_page_info['page_length']);
    1.80 +        $file_data_offset = 0;
    1.81 +
    1.82 +
    1.83 +        // OggFLAC
    1.84 +        if (substr($file_data, 0, 4) == 'fLaC') {
    1.85 +
    1.86 +            $info_audio['dataformat']   = 'flac';
    1.87 +            $info_audio['bitrate_mode'] = 'vbr';
    1.88 +            $info_audio['lossless']     = true;
    1.89 +
    1.90 +        } 
    1.91 +    
    1.92 +    
    1.93 +        // Ogg Vorbis
    1.94 +        elseif (substr($file_data, 1, 6) == 'vorbis') {
    1.95 +
    1.96 +            $info_audio['dataformat'] = 'vorbis';
    1.97 +            $info_audio['lossless']   = false;
    1.98 +
    1.99 +            $info_ogg['pageheader'][$ogg_page_info['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int($file_data[0]);
   1.100 +            $info_ogg['pageheader'][$ogg_page_info['page_seqno']]['stream_type'] = substr($file_data, 1, 6); // hard-coded to 'vorbis'
   1.101 +            
   1.102 +            getid3_lib::ReadSequence('LittleEndian2Int', $info_ogg, $file_data, 7, 
   1.103 +                array (
   1.104 +                    'bitstreamversion' => 4,
   1.105 +                    'numberofchannels' => 1,
   1.106 +                    'samplerate'       => 4,
   1.107 +                    'bitrate_max'      => 4,
   1.108 +                    'bitrate_nominal'  => 4,
   1.109 +                    'bitrate_min'      => 4
   1.110 +                )
   1.111 +            );
   1.112 +                                                                                                                     
   1.113 +            $n28 = getid3_lib::LittleEndian2Int($file_data{28});
   1.114 +            $info_ogg['blocksize_small']  = pow(2, $n28 & 0x0F);
   1.115 +            $info_ogg['blocksize_large']  = pow(2, ($n28 & 0xF0) >> 4);
   1.116 +            $info_ogg['stop_bit']         = $n28;
   1.117 +            
   1.118 +            $info_audio['channels']       = $info_ogg['numberofchannels'];
   1.119 +            $info_audio['sample_rate']    = $info_ogg['samplerate'];
   1.120 +
   1.121 +            $info_audio['bitrate_mode'] = 'vbr';     // overridden if actually abr
   1.122 +
   1.123 +            if ($info_ogg['bitrate_max'] == 0xFFFFFFFF) {
   1.124 +                unset($info_ogg['bitrate_max']);
   1.125 +                $info_audio['bitrate_mode'] = 'abr';
   1.126 +            }
   1.127 +            
   1.128 +            if ($info_ogg['bitrate_nominal'] == 0xFFFFFFFF) {
   1.129 +                unset($info_ogg['bitrate_nominal']);
   1.130 +            }
   1.131 +            
   1.132 +            if ($info_ogg['bitrate_min'] == 0xFFFFFFFF) {
   1.133 +                unset($info_ogg['bitrate_min']);
   1.134 +                $info_audio['bitrate_mode'] = 'abr';
   1.135 +            }
   1.136 +        }
   1.137 +    
   1.138 +
   1.139 +        // Speex
   1.140 +        elseif (substr($file_data, 0, 8) == 'Speex   ') {
   1.141 +
   1.142 +            // http://www.speex.org/manual/node10.html
   1.143 +
   1.144 +            $info_audio['dataformat']   = 'speex';
   1.145 +            $getid3->info['mime_type']  = 'audio/speex';
   1.146 +            $info_audio['bitrate_mode'] = 'abr';
   1.147 +            $info_audio['lossless']     = false;
   1.148 +
   1.149 +            getid3_lib::ReadSequence('LittleEndian2Int', $info_ogg['pageheader'][$ogg_page_info['page_seqno']], $file_data, 0, 
   1.150 +                array (
   1.151 +                    'speex_string'           => -8, 		// hard-coded to 'Speex   '
   1.152 +                    'speex_version'          => -20,      	// string                  
   1.153 +                    'speex_version_id'       => 4,
   1.154 +                    'header_size'            => 4,
   1.155 +                    'rate'                   => 4,
   1.156 +                    'mode'                   => 4,
   1.157 +                    'mode_bitstream_version' => 4,
   1.158 +                    'nb_channels'            => 4,
   1.159 +                    'bitrate'                => 4,
   1.160 +                    'framesize'              => 4,
   1.161 +                    'vbr'                    => 4,
   1.162 +                    'frames_per_packet'      => 4,
   1.163 +                    'extra_headers'          => 4,
   1.164 +                    'reserved1'              => 4,
   1.165 +                    'reserved2'              => 4
   1.166 +                )
   1.167 +            );
   1.168 +                
   1.169 +            $getid3->info['speex']['speex_version'] = trim($info_ogg['pageheader'][$ogg_page_info['page_seqno']]['speex_version']);
   1.170 +            $getid3->info['speex']['sample_rate']   = $info_ogg['pageheader'][$ogg_page_info['page_seqno']]['rate'];
   1.171 +            $getid3->info['speex']['channels']      = $info_ogg['pageheader'][$ogg_page_info['page_seqno']]['nb_channels'];
   1.172 +            $getid3->info['speex']['vbr']           = (bool)$info_ogg['pageheader'][$ogg_page_info['page_seqno']]['vbr'];
   1.173 +            $getid3->info['speex']['band_type']     = getid3_xiph::SpeexBandModeLookup($info_ogg['pageheader'][$ogg_page_info['page_seqno']]['mode']);
   1.174 +
   1.175 +            $info_audio['sample_rate'] = $getid3->info['speex']['sample_rate'];
   1.176 +            $info_audio['channels']    = $getid3->info['speex']['channels'];
   1.177 +            
   1.178 +            if ($getid3->info['speex']['vbr']) {
   1.179 +                $info_audio['bitrate_mode'] = 'vbr';
   1.180 +            }
   1.181 +        }
   1.182 +
   1.183 +        // Unsupported Ogg file
   1.184 +        else {
   1.185 +
   1.186 +            throw new getid3_exception('Expecting either "Speex   " or "vorbis" identifier strings, found neither');
   1.187 +        }
   1.188 +
   1.189 +
   1.190 +        //// Page 2 - Comment Header
   1.191 +
   1.192 +        $ogg_page_info = $this->ParseOggPageHeader();
   1.193 +        $info_ogg['pageheader'][$ogg_page_info['page_seqno']] = $ogg_page_info;
   1.194 +
   1.195 +        switch ($info_audio['dataformat']) {
   1.196 +
   1.197 +            case 'vorbis':
   1.198 +                $file_data = fread($getid3->fp, $info_ogg['pageheader'][$ogg_page_info['page_seqno']]['page_length']);
   1.199 +                $info_ogg['pageheader'][$ogg_page_info['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($file_data, 0, 1));
   1.200 +                $info_ogg['pageheader'][$ogg_page_info['page_seqno']]['stream_type'] = substr($file_data, 1, 6); // hard-coded to 'vorbis'
   1.201 +                $this->ParseVorbisCommentsFilepointer();
   1.202 +                break;
   1.203 +
   1.204 +            case 'flac':
   1.205 +                if (!$this->FLACparseMETAdata()) {
   1.206 +                    throw new getid3_exception('Failed to parse FLAC headers');
   1.207 +                }
   1.208 +                break;
   1.209 +
   1.210 +            case 'speex':
   1.211 +                fseek($getid3->fp, $info_ogg['pageheader'][$ogg_page_info['page_seqno']]['page_length'], SEEK_CUR);
   1.212 +                $this->ParseVorbisCommentsFilepointer();
   1.213 +                break;
   1.214 +        }
   1.215 +
   1.216 +
   1.217 +        //// Last Page - Number of Samples
   1.218 +
   1.219 +        fseek($getid3->fp, max($getid3->info['avdataend'] - getid3::FREAD_BUFFER_SIZE, 0), SEEK_SET);
   1.220 +        $last_chunk_of_ogg = strrev(fread($getid3->fp, getid3::FREAD_BUFFER_SIZE));
   1.221 +        
   1.222 +        if ($last_OggS_postion = strpos($last_chunk_of_ogg, 'SggO')) {
   1.223 +            fseek($getid3->fp, $getid3->info['avdataend'] - ($last_OggS_postion + strlen('SggO')), SEEK_SET);
   1.224 +            $getid3->info['avdataend'] = ftell($getid3->fp);
   1.225 +            $info_ogg['pageheader']['eos'] = $this->ParseOggPageHeader();
   1.226 +            $info_ogg['samples']           = $info_ogg['pageheader']['eos']['pcm_abs_position'];
   1.227 +            $info_ogg['bitrate_average']   = (($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8) / ($info_ogg['samples'] / $info_audio['sample_rate']);
   1.228 +        }
   1.229 +
   1.230 +        if (!empty($info_ogg['bitrate_average'])) {
   1.231 +            $info_audio['bitrate'] = $info_ogg['bitrate_average'];
   1.232 +        } elseif (!empty($info_ogg['bitrate_nominal'])) {
   1.233 +            $info_audio['bitrate'] = $info_ogg['bitrate_nominal'];
   1.234 +        } elseif (!empty($info_ogg['bitrate_min']) && !empty($info_ogg['bitrate_max'])) {
   1.235 +            $info_audio['bitrate'] = ($info_ogg['bitrate_min'] + $info_ogg['bitrate_max']) / 2;
   1.236 +        }
   1.237 +        if (isset($info_audio['bitrate']) && !isset($getid3->info['playtime_seconds'])) {
   1.238 +            $getid3->info['playtime_seconds'] = (float)((($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8) / $info_audio['bitrate']);
   1.239 +        }
   1.240 +
   1.241 +        if (isset($info_ogg['vendor'])) {
   1.242 +            $info_audio['encoder'] = preg_replace('/^Encoded with /', '', $info_ogg['vendor']);
   1.243 +
   1.244 +            // Vorbis only
   1.245 +            if ($info_audio['dataformat'] == 'vorbis') {
   1.246 +
   1.247 +                // Vorbis 1.0 starts with Xiph.Org
   1.248 +                if  (preg_match('/^Xiph.Org/', $info_audio['encoder'])) {
   1.249 +
   1.250 +                    if ($info_audio['bitrate_mode'] == 'abr') {
   1.251 +
   1.252 +                        // Set -b 128 on abr files
   1.253 +                        $info_audio['encoder_options'] = '-b '.round($info_ogg['bitrate_nominal'] / 1000);
   1.254 +
   1.255 +                    } elseif (($info_audio['bitrate_mode'] == 'vbr') && ($info_audio['channels'] == 2) && ($info_audio['sample_rate'] >= 44100) && ($info_audio['sample_rate'] <= 48000)) {
   1.256 +                        // Set -q N on vbr files
   1.257 +                        $info_audio['encoder_options'] = '-q '.getid3_xiph::GetQualityFromNominalBitrate($info_ogg['bitrate_nominal']);
   1.258 +                    }
   1.259 +                }
   1.260 +
   1.261 +                if (empty($info_audio['encoder_options']) && !empty($info_ogg['bitrate_nominal'])) {
   1.262 +                    $info_audio['encoder_options'] = 'Nominal bitrate: '.intval(round($info_ogg['bitrate_nominal'] / 1000)).'kbps';
   1.263 +                }
   1.264 +            }
   1.265 +        }
   1.266 +
   1.267 +        return true;
   1.268 +    }
   1.269 +
   1.270 +
   1.271 +
   1.272 +    private function ParseOggPageHeader() {
   1.273 +        
   1.274 +        $getid3 = $this->getid3;
   1.275 +        
   1.276 +        // http://xiph.org/ogg/vorbis/doc/framing.html
   1.277 +        $ogg_header['page_start_offset'] = ftell($getid3->fp);      // where we started from in the file
   1.278 +        
   1.279 +        $file_data = fread($getid3->fp, getid3::FREAD_BUFFER_SIZE);
   1.280 +        $file_data_offset = 0;
   1.281 +        
   1.282 +        while ((substr($file_data, $file_data_offset++, 4) != 'OggS')) {
   1.283 +            if ((ftell($getid3->fp) - $ogg_header['page_start_offset']) >= getid3::FREAD_BUFFER_SIZE) {
   1.284 +                // should be found before here
   1.285 +                return false;
   1.286 +            }
   1.287 +            if ((($file_data_offset + 28) > strlen($file_data)) || (strlen($file_data) < 28)) {
   1.288 +                if (feof($getid3->fp) || (($file_data .= fread($getid3->fp, getid3::FREAD_BUFFER_SIZE)) === false)) {
   1.289 +                    // get some more data, unless eof, in which case fail
   1.290 +                    return false;
   1.291 +                }
   1.292 +            }
   1.293 +        }
   1.294 +        
   1.295 +        $file_data_offset += 3; // page, delimited by 'OggS'
   1.296 +        
   1.297 +        getid3_lib::ReadSequence('LittleEndian2Int', $ogg_header, $file_data, $file_data_offset, 
   1.298 +            array (
   1.299 +                'stream_structver' => 1,
   1.300 +                'flags_raw'        => 1,
   1.301 +                'pcm_abs_position' => 8,
   1.302 +                'stream_serialno'  => 4,
   1.303 +                'page_seqno'       => 4,
   1.304 +                'page_checksum'    => 4,
   1.305 +                'page_segments'    => 1
   1.306 +            )
   1.307 +        );
   1.308 +        
   1.309 +        $file_data_offset += 23;
   1.310 +
   1.311 +        $ogg_header['flags']['fresh'] = (bool)($ogg_header['flags_raw'] & 0x01); // fresh packet
   1.312 +        $ogg_header['flags']['bos']   = (bool)($ogg_header['flags_raw'] & 0x02); // first page of logical bitstream (bos)
   1.313 +        $ogg_header['flags']['eos']   = (bool)($ogg_header['flags_raw'] & 0x04); // last page of logical bitstream (eos)
   1.314 +
   1.315 +        $ogg_header['page_length'] = 0;
   1.316 +        for ($i = 0; $i < $ogg_header['page_segments']; $i++) {
   1.317 +            $ogg_header['segment_table'][$i] = getid3_lib::LittleEndian2Int($file_data{$file_data_offset++});
   1.318 +            $ogg_header['page_length'] += $ogg_header['segment_table'][$i];
   1.319 +        }
   1.320 +        $ogg_header['header_end_offset'] = $ogg_header['page_start_offset'] + $file_data_offset;
   1.321 +        $ogg_header['page_end_offset']   = $ogg_header['header_end_offset'] + $ogg_header['page_length'];
   1.322 +        fseek($getid3->fp, $ogg_header['header_end_offset'], SEEK_SET);
   1.323 +
   1.324 +        return $ogg_header;
   1.325 +    }
   1.326 +
   1.327 +
   1.328 +    
   1.329 +    private function ParseVorbisCommentsFilepointer() {
   1.330 +        
   1.331 +        $getid3 = $this->getid3;
   1.332 +
   1.333 +        $original_offset      = ftell($getid3->fp);
   1.334 +        $comment_start_offset = $original_offset;
   1.335 +        $comment_data_offset  = 0;
   1.336 +        $vorbis_comment_page  = 1;
   1.337 +
   1.338 +        switch ($getid3->info['audio']['dataformat']) {
   1.339 +            
   1.340 +            case 'vorbis':
   1.341 +                $comment_start_offset = $getid3->info['ogg']['pageheader'][$vorbis_comment_page]['page_start_offset'];  // Second Ogg page, after header block
   1.342 +                fseek($getid3->fp, $comment_start_offset, SEEK_SET);
   1.343 +                $comment_data_offset = 27 + $getid3->info['ogg']['pageheader'][$vorbis_comment_page]['page_segments'];
   1.344 +                $comment_data = fread($getid3->fp, getid3_xiph::OggPageSegmentLength($getid3->info['ogg']['pageheader'][$vorbis_comment_page], 1) + $comment_data_offset);
   1.345 +                $comment_data_offset += (strlen('vorbis') + 1);
   1.346 +                break;
   1.347 +                
   1.348 +
   1.349 +            case 'flac':
   1.350 +                fseek($getid3->fp, $getid3->info['flac']['VORBIS_COMMENT']['raw']['offset'] + 4, SEEK_SET);
   1.351 +                $comment_data = fread($getid3->fp, $getid3->info['flac']['VORBIS_COMMENT']['raw']['block_length']);
   1.352 +                break;
   1.353 +                
   1.354 +
   1.355 +            case 'speex':
   1.356 +                $comment_start_offset = $getid3->info['ogg']['pageheader'][$vorbis_comment_page]['page_start_offset'];  // Second Ogg page, after header block
   1.357 +                fseek($getid3->fp, $comment_start_offset, SEEK_SET);
   1.358 +                $comment_data_offset = 27 + $getid3->info['ogg']['pageheader'][$vorbis_comment_page]['page_segments'];
   1.359 +                $comment_data = fread($getid3->fp, getid3_xiph::OggPageSegmentLength($getid3->info['ogg']['pageheader'][$vorbis_comment_page], 1) + $comment_data_offset);
   1.360 +                break;
   1.361 +                
   1.362 +
   1.363 +            default:
   1.364 +                return false;
   1.365 +        }
   1.366 +
   1.367 +        $vendor_size = getid3_lib::LittleEndian2Int(substr($comment_data, $comment_data_offset, 4));
   1.368 +        $comment_data_offset += 4;
   1.369 +
   1.370 +        $getid3->info['ogg']['vendor'] = substr($comment_data, $comment_data_offset, $vendor_size);
   1.371 +        $comment_data_offset += $vendor_size;
   1.372 +
   1.373 +        $comments_count = getid3_lib::LittleEndian2Int(substr($comment_data, $comment_data_offset, 4));
   1.374 +        $comment_data_offset += 4;
   1.375 +        
   1.376 +        $getid3->info['avdataoffset'] = $comment_start_offset + $comment_data_offset;
   1.377 +
   1.378 +        for ($i = 0; $i < $comments_count; $i++) {
   1.379 +
   1.380 +            $getid3->info['ogg']['comments_raw'][$i]['dataoffset'] = $comment_start_offset + $comment_data_offset;
   1.381 +
   1.382 +            if (ftell($getid3->fp) < ($getid3->info['ogg']['comments_raw'][$i]['dataoffset'] + 4)) {
   1.383 +                $vorbis_comment_page++;
   1.384 +
   1.385 +                $ogg_page_info = $this->ParseOggPageHeader();
   1.386 +                $getid3->info['ogg']['pageheader'][$ogg_page_info['page_seqno']] = $ogg_page_info;
   1.387 +
   1.388 +                // First, save what we haven't read yet
   1.389 +                $as_yet_unused_data = substr($comment_data, $comment_data_offset);
   1.390 +
   1.391 +                // Then take that data off the end
   1.392 +                $comment_data = substr($comment_data, 0, $comment_data_offset);
   1.393 +
   1.394 +                // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct
   1.395 +                $comment_data .= str_repeat("\x00", 27 + $getid3->info['ogg']['pageheader'][$ogg_page_info['page_seqno']]['page_segments']);
   1.396 +                $comment_data_offset += (27 + $getid3->info['ogg']['pageheader'][$ogg_page_info['page_seqno']]['page_segments']);
   1.397 +
   1.398 +                // Finally, stick the unused data back on the end
   1.399 +                $comment_data .= $as_yet_unused_data;
   1.400 +
   1.401 +                $comment_data .= fread($getid3->fp, getid3_xiph::OggPageSegmentLength($getid3->info['ogg']['pageheader'][$vorbis_comment_page], 1));
   1.402 +            }
   1.403 +            $getid3->info['ogg']['comments_raw'][$i]['size'] = getid3_lib::LittleEndian2Int(substr($comment_data, $comment_data_offset, 4));
   1.404 +
   1.405 +            // replace avdataoffset with position just after the last vorbiscomment
   1.406 +            $getid3->info['avdataoffset'] = $getid3->info['ogg']['comments_raw'][$i]['dataoffset'] + $getid3->info['ogg']['comments_raw'][$i]['size'] + 4;
   1.407 +
   1.408 +            $comment_data_offset += 4;
   1.409 +            while ((strlen($comment_data) - $comment_data_offset) < $getid3->info['ogg']['comments_raw'][$i]['size']) {
   1.410 +            
   1.411 +                if (($getid3->info['ogg']['comments_raw'][$i]['size'] > $getid3->info['avdataend']) || ($getid3->info['ogg']['comments_raw'][$i]['size'] < 0)) {
   1.412 +                    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');
   1.413 +                }
   1.414 +
   1.415 +                $vorbis_comment_page++;
   1.416 +
   1.417 +                $ogg_page_info = $this->ParseOggPageHeader();
   1.418 +                $getid3->info['ogg']['pageheader'][$ogg_page_info['page_seqno']] = $ogg_page_info;
   1.419 +
   1.420 +                // First, save what we haven't read yet
   1.421 +                $as_yet_unused_data = substr($comment_data, $comment_data_offset);
   1.422 +
   1.423 +                // Then take that data off the end
   1.424 +                $comment_data     = substr($comment_data, 0, $comment_data_offset);
   1.425 +
   1.426 +                // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct
   1.427 +                $comment_data .= str_repeat("\x00", 27 + $getid3->info['ogg']['pageheader'][$ogg_page_info['page_seqno']]['page_segments']);
   1.428 +                $comment_data_offset += (27 + $getid3->info['ogg']['pageheader'][$ogg_page_info['page_seqno']]['page_segments']);
   1.429 +
   1.430 +                // Finally, stick the unused data back on the end
   1.431 +                $comment_data .= $as_yet_unused_data;
   1.432 +
   1.433 +                //$comment_data .= fread($getid3->fp, $getid3->info['ogg']['pageheader'][$ogg_page_info['page_seqno']]['page_length']);
   1.434 +                $comment_data .= fread($getid3->fp, getid3_xiph::OggPageSegmentLength($getid3->info['ogg']['pageheader'][$vorbis_comment_page], 1));
   1.435 +
   1.436 +                //$filebaseoffset += $ogg_page_info['header_end_offset'] - $ogg_page_info['page_start_offset'];
   1.437 +            }
   1.438 +            $comment_string = substr($comment_data, $comment_data_offset, $getid3->info['ogg']['comments_raw'][$i]['size']);
   1.439 +            $comment_data_offset += $getid3->info['ogg']['comments_raw'][$i]['size'];
   1.440 +
   1.441 +            if (!$comment_string) {
   1.442 +
   1.443 +                // no comment?
   1.444 +                $getid3->warning('Blank Ogg comment ['.$i.']');
   1.445 +
   1.446 +            } elseif (strstr($comment_string, '=')) {
   1.447 +
   1.448 +                $comment_exploded = explode('=', $comment_string, 2);
   1.449 +                $getid3->info['ogg']['comments_raw'][$i]['key']   = strtoupper($comment_exploded[0]);
   1.450 +                $getid3->info['ogg']['comments_raw'][$i]['value'] = @$comment_exploded[1];
   1.451 +                $getid3->info['ogg']['comments_raw'][$i]['data']  = base64_decode($getid3->info['ogg']['comments_raw'][$i]['value']);
   1.452 +
   1.453 +                $getid3->info['ogg']['comments'][strtolower($getid3->info['ogg']['comments_raw'][$i]['key'])][] = $getid3->info['ogg']['comments_raw'][$i]['value'];
   1.454 +
   1.455 +                if ($getid3->option_tags_images) {
   1.456 +                    $image_chunk_check = getid3_lib_image_size::get($getid3->info['ogg']['comments_raw'][$i]['data']);
   1.457 +                    $getid3->info['ogg']['comments_raw'][$i]['image_mime'] = image_type_to_mime_type($image_chunk_check[2]);
   1.458 +                }
   1.459 +                
   1.460 +                if (!@$getid3->info['ogg']['comments_raw'][$i]['image_mime'] || ($getid3->info['ogg']['comments_raw'][$i]['image_mime'] == 'application/octet-stream')) {
   1.461 +                    unset($getid3->info['ogg']['comments_raw'][$i]['image_mime']);
   1.462 +                    unset($getid3->info['ogg']['comments_raw'][$i]['data']);
   1.463 +                }
   1.464 +                
   1.465 +
   1.466 +            } else {
   1.467 +
   1.468 +                $getid3->warning('[known problem with CDex >= v1.40, < v1.50b7] Invalid Ogg comment name/value pair ['.$i.']: '.$comment_string);
   1.469 +            }
   1.470 +        }
   1.471 +
   1.472 +
   1.473 +        // Replay Gain Adjustment
   1.474 +        // http://privatewww.essex.ac.uk/~djmrob/replaygain/
   1.475 +        if (isset($getid3->info['ogg']['comments']) && is_array($getid3->info['ogg']['comments'])) {
   1.476 +            foreach ($getid3->info['ogg']['comments'] as $index => $commentvalue) {
   1.477 +                switch ($index) {
   1.478 +                    case 'rg_audiophile':
   1.479 +                    case 'replaygain_album_gain':
   1.480 +                        $getid3->info['replay_gain']['album']['adjustment'] = (float)$commentvalue[0];
   1.481 +                        unset($getid3->info['ogg']['comments'][$index]);
   1.482 +                        break;
   1.483 +
   1.484 +                    case 'rg_radio':
   1.485 +                    case 'replaygain_track_gain':
   1.486 +                        $getid3->info['replay_gain']['track']['adjustment'] = (float)$commentvalue[0];
   1.487 +                        unset($getid3->info['ogg']['comments'][$index]);
   1.488 +                        break;
   1.489 +
   1.490 +                    case 'replaygain_album_peak':
   1.491 +                        $getid3->info['replay_gain']['album']['peak'] = (float)$commentvalue[0];
   1.492 +                        unset($getid3->info['ogg']['comments'][$index]);
   1.493 +                        break;
   1.494 +
   1.495 +                    case 'rg_peak':
   1.496 +                    case 'replaygain_track_peak':
   1.497 +                        $getid3->info['replay_gain']['track']['peak'] = (float)$commentvalue[0];
   1.498 +                        unset($getid3->info['ogg']['comments'][$index]);
   1.499 +                        break;
   1.500 +                        
   1.501 +                    case 'replaygain_reference_loudness':
   1.502 +                        $getid3->info['replay_gain']['reference_volume'] = (float)$commentvalue[0];
   1.503 +                        unset($getid3->info['ogg']['comments'][$index]);
   1.504 +                        break;
   1.505 +                }
   1.506 +            }
   1.507 +        }
   1.508 +
   1.509 +        fseek($getid3->fp, $original_offset, SEEK_SET);
   1.510 +
   1.511 +        return true;
   1.512 +    }
   1.513 +
   1.514 +
   1.515 +
   1.516 +    private function ParseFLAC() {
   1.517 +        
   1.518 +        $getid3 = $this->getid3;
   1.519 +        
   1.520 +        // http://flac.sourceforge.net/format.html
   1.521 +
   1.522 +        $getid3->info['fileformat']            = 'flac';
   1.523 +        $getid3->info['audio']['dataformat']   = 'flac';
   1.524 +        $getid3->info['audio']['bitrate_mode'] = 'vbr';
   1.525 +        $getid3->info['audio']['lossless']     = true;
   1.526 +
   1.527 +        return $this->FLACparseMETAdata();
   1.528 +    }
   1.529 +
   1.530 +
   1.531 +
   1.532 +    private function FLACparseMETAdata() {
   1.533 +        
   1.534 +        $getid3 = $this->getid3;
   1.535 +
   1.536 +        do {
   1.537 +            
   1.538 +            $meta_data_block_offset    = ftell($getid3->fp);
   1.539 +            $meta_data_block_header    = fread($getid3->fp, 4);
   1.540 +            $meta_data_last_block_flag = (bool)(getid3_lib::BigEndian2Int($meta_data_block_header[0]) & 0x80);
   1.541 +            $meta_data_block_type      = getid3_lib::BigEndian2Int($meta_data_block_header[0]) & 0x7F;
   1.542 +            $meta_data_block_length    = getid3_lib::BigEndian2Int(substr($meta_data_block_header, 1, 3));
   1.543 +            $meta_data_block_type_text = getid3_xiph::FLACmetaBlockTypeLookup($meta_data_block_type);
   1.544 +
   1.545 +            if ($meta_data_block_length < 0) {
   1.546 +                throw new getid3_exception('corrupt or invalid METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$meta_data_block_type.') at offset '.$meta_data_block_offset);
   1.547 +            }
   1.548 +
   1.549 +            $getid3->info['flac'][$meta_data_block_type_text]['raw'] = array (
   1.550 +                'offset'          => $meta_data_block_offset,
   1.551 +                'last_meta_block' => $meta_data_last_block_flag,
   1.552 +                'block_type'      => $meta_data_block_type,
   1.553 +                'block_type_text' => $meta_data_block_type_text,
   1.554 +                'block_length'    => $meta_data_block_length,
   1.555 +                'block_data'      => @fread($getid3->fp, $meta_data_block_length)
   1.556 +            );
   1.557 +            $getid3->info['avdataoffset'] = ftell($getid3->fp);
   1.558 +
   1.559 +            switch ($meta_data_block_type_text) {
   1.560 +
   1.561 +                case 'STREAMINFO':
   1.562 +                    if (!$this->FLACparseSTREAMINFO($getid3->info['flac'][$meta_data_block_type_text]['raw']['block_data'])) {
   1.563 +                        return false;
   1.564 +                    }
   1.565 +                    break;
   1.566 +
   1.567 +                case 'PADDING':
   1.568 +                    // ignore
   1.569 +                    break;
   1.570 +
   1.571 +                case 'APPLICATION':
   1.572 +                    if (!$this->FLACparseAPPLICATION($getid3->info['flac'][$meta_data_block_type_text]['raw']['block_data'])) {
   1.573 +                        return false;
   1.574 +                    }
   1.575 +                    break;
   1.576 +
   1.577 +                case 'SEEKTABLE':
   1.578 +                    if (!$this->FLACparseSEEKTABLE($getid3->info['flac'][$meta_data_block_type_text]['raw']['block_data'])) {
   1.579 +                        return false;
   1.580 +                    }
   1.581 +                    break;
   1.582 +
   1.583 +                case 'VORBIS_COMMENT':
   1.584 +                    $old_offset = ftell($getid3->fp);
   1.585 +                    fseek($getid3->fp, 0 - $meta_data_block_length, SEEK_CUR);
   1.586 +                    $this->ParseVorbisCommentsFilepointer($getid3->fp, $getid3->info);
   1.587 +                    fseek($getid3->fp, $old_offset, SEEK_SET);
   1.588 +                    break;
   1.589 +
   1.590 +                case 'CUESHEET':
   1.591 +                    if (!$this->FLACparseCUESHEET($getid3->info['flac'][$meta_data_block_type_text]['raw']['block_data'])) {
   1.592 +                        return false;
   1.593 +                    }
   1.594 +                    break;
   1.595 +                    
   1.596 +                case 'PICTURE':
   1.597 +                    if (!$this->FLACparsePICTURE($getid3->info['flac'][$meta_data_block_type_text]['raw']['block_data'])) {
   1.598 +                        return false;
   1.599 +                    }
   1.600 +                    break;
   1.601 +
   1.602 +                default:
   1.603 +                    $getid3->warning('Unhandled METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$meta_data_block_type.') at offset '.$meta_data_block_offset);
   1.604 +            }
   1.605 +
   1.606 +        } while ($meta_data_last_block_flag === false);
   1.607 +
   1.608 +
   1.609 +        if (isset($getid3->info['flac']['STREAMINFO'])) {
   1.610 +            $getid3->info['flac']['compressed_audio_bytes']   = $getid3->info['avdataend'] - $getid3->info['avdataoffset'];
   1.611 +            $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);
   1.612 +            $getid3->info['flac']['compression_ratio']        = $getid3->info['flac']['compressed_audio_bytes'] / $getid3->info['flac']['uncompressed_audio_bytes'];
   1.613 +        }
   1.614 +
   1.615 +        // set md5_data_source - built into flac 0.5+
   1.616 +        if (isset($getid3->info['flac']['STREAMINFO']['audio_signature'])) {
   1.617 +
   1.618 +            if ($getid3->info['flac']['STREAMINFO']['audio_signature'] === str_repeat("\x00", 16)) {
   1.619 +                $getid3->warning('FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)');
   1.620 +
   1.621 +            } else {
   1.622 +
   1.623 +                $getid3->info['md5_data_source'] = '';
   1.624 +                $md5 = $getid3->info['flac']['STREAMINFO']['audio_signature'];
   1.625 +                for ($i = 0; $i < strlen($md5); $i++) {
   1.626 +                    $getid3->info['md5_data_source'] .= str_pad(dechex(ord($md5{$i})), 2, '00', STR_PAD_LEFT);
   1.627 +                }
   1.628 +                if (!preg_match('/^[0-9a-f]{32}$/', $getid3->info['md5_data_source'])) {
   1.629 +                    unset($getid3->info['md5_data_source']);
   1.630 +                }
   1.631 +
   1.632 +            }
   1.633 +
   1.634 +        }
   1.635 +
   1.636 +        $getid3->info['audio']['bits_per_sample'] = $getid3->info['flac']['STREAMINFO']['bits_per_sample'];
   1.637 +        if ($getid3->info['audio']['bits_per_sample'] == 8) {
   1.638 +            // special case
   1.639 +            // must invert sign bit on all data bytes before MD5'ing to match FLAC's calculated value
   1.640 +            // MD5sum calculates on unsigned bytes, but FLAC calculated MD5 on 8-bit audio data as signed
   1.641 +            $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');
   1.642 +        }
   1.643 +        if (!empty($getid3->info['ogg']['vendor'])) {
   1.644 +            $getid3->info['audio']['encoder'] = $getid3->info['ogg']['vendor'];
   1.645 +        }
   1.646 +
   1.647 +        return true;
   1.648 +    }
   1.649 +
   1.650 +
   1.651 +
   1.652 +    private function FLACparseSTREAMINFO($meta_data_block_data) {
   1.653 +        
   1.654 +        $getid3 = $this->getid3;
   1.655 +        
   1.656 +        getid3_lib::ReadSequence('BigEndian2Int', $getid3->info['flac']['STREAMINFO'], $meta_data_block_data, 0,
   1.657 +            array (
   1.658 +                'min_block_size' => 2,
   1.659 +                'max_block_size' => 2,
   1.660 +                'min_frame_size' => 3,
   1.661 +                'max_frame_size' => 3
   1.662 +            )
   1.663 +        );
   1.664 +
   1.665 +        $sample_rate_channels_sample_bits_stream_samples = getid3_lib::BigEndian2Bin(substr($meta_data_block_data, 10, 8));
   1.666 +        
   1.667 +        $getid3->info['flac']['STREAMINFO']['sample_rate']     = bindec(substr($sample_rate_channels_sample_bits_stream_samples,  0, 20));
   1.668 +        $getid3->info['flac']['STREAMINFO']['channels']        = bindec(substr($sample_rate_channels_sample_bits_stream_samples, 20,  3)) + 1;
   1.669 +        $getid3->info['flac']['STREAMINFO']['bits_per_sample'] = bindec(substr($sample_rate_channels_sample_bits_stream_samples, 23,  5)) + 1;
   1.670 +        $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
   1.671 +        $getid3->info['flac']['STREAMINFO']['audio_signature'] = substr($meta_data_block_data, 18, 16);
   1.672 +
   1.673 +        if (!empty($getid3->info['flac']['STREAMINFO']['sample_rate'])) {
   1.674 +
   1.675 +            $getid3->info['audio']['bitrate_mode']    = 'vbr';
   1.676 +            $getid3->info['audio']['sample_rate']     = $getid3->info['flac']['STREAMINFO']['sample_rate'];
   1.677 +            $getid3->info['audio']['channels']        = $getid3->info['flac']['STREAMINFO']['channels'];
   1.678 +            $getid3->info['audio']['bits_per_sample'] = $getid3->info['flac']['STREAMINFO']['bits_per_sample'];
   1.679 +            $getid3->info['playtime_seconds']         = $getid3->info['flac']['STREAMINFO']['samples_stream'] / $getid3->info['flac']['STREAMINFO']['sample_rate'];
   1.680 +            $getid3->info['audio']['bitrate']         = (($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8) / $getid3->info['playtime_seconds'];
   1.681 +
   1.682 +        } else {
   1.683 +
   1.684 +            throw new getid3_exception('Corrupt METAdata block: STREAMINFO');
   1.685 +        }
   1.686 +        
   1.687 +        unset($getid3->info['flac']['STREAMINFO']['raw']);
   1.688 +
   1.689 +        return true;
   1.690 +    }
   1.691 +
   1.692 +
   1.693 +
   1.694 +    private function FLACparseAPPLICATION($meta_data_block_data) {
   1.695 +        
   1.696 +        $getid3 = $this->getid3;
   1.697 +        
   1.698 +        $application_id = getid3_lib::BigEndian2Int(substr($meta_data_block_data, 0, 4));
   1.699 +        
   1.700 +        $getid3->info['flac']['APPLICATION'][$application_id]['name'] = getid3_xiph::FLACapplicationIDLookup($application_id);
   1.701 +        $getid3->info['flac']['APPLICATION'][$application_id]['data'] = substr($meta_data_block_data, 4);
   1.702 +        
   1.703 +        unset($getid3->info['flac']['APPLICATION']['raw']);
   1.704 +
   1.705 +        return true;
   1.706 +    }
   1.707 +
   1.708 +
   1.709 +
   1.710 +    private function FLACparseSEEKTABLE($meta_data_block_data) {
   1.711 +        
   1.712 +        $getid3 = $this->getid3;
   1.713 +        
   1.714 +        $offset = 0;
   1.715 +        $meta_data_block_length = strlen($meta_data_block_data);
   1.716 +        while ($offset < $meta_data_block_length) {
   1.717 +            $sample_number_string = substr($meta_data_block_data, $offset, 8);
   1.718 +            $offset += 8;
   1.719 +            if ($sample_number_string == "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF") {
   1.720 +
   1.721 +                // placeholder point
   1.722 +                @$getid3->info['flac']['SEEKTABLE']['placeholders']++;
   1.723 +                $offset += 10;
   1.724 +
   1.725 +            } else {
   1.726 +
   1.727 +                $sample_number = getid3_lib::BigEndian2Int($sample_number_string);
   1.728 +                
   1.729 +                $getid3->info['flac']['SEEKTABLE'][$sample_number]['offset']  = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 8));
   1.730 +                $offset += 8;
   1.731 +                
   1.732 +                $getid3->info['flac']['SEEKTABLE'][$sample_number]['samples'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 2));
   1.733 +                $offset += 2;
   1.734 +
   1.735 +            }
   1.736 +        }
   1.737 +        
   1.738 +        unset($getid3->info['flac']['SEEKTABLE']['raw']);
   1.739 +        
   1.740 +        return true;
   1.741 +    }
   1.742 +
   1.743 +
   1.744 +
   1.745 +    private function FLACparseCUESHEET($meta_data_block_data) {
   1.746 +        
   1.747 +        $getid3 = $this->getid3;
   1.748 +        
   1.749 +        $getid3->info['flac']['CUESHEET']['media_catalog_number'] = trim(substr($meta_data_block_data, 0, 128), "\0");
   1.750 +        $getid3->info['flac']['CUESHEET']['lead_in_samples']      = getid3_lib::BigEndian2Int(substr($meta_data_block_data, 128, 8));
   1.751 +        $getid3->info['flac']['CUESHEET']['flags']['is_cd']       = (bool)(getid3_lib::BigEndian2Int($meta_data_block_data[136]) & 0x80);
   1.752 +        $getid3->info['flac']['CUESHEET']['number_tracks']        = getid3_lib::BigEndian2Int($meta_data_block_data[395]);
   1.753 +
   1.754 +        $offset = 396;
   1.755 +        
   1.756 +        for ($track = 0; $track < $getid3->info['flac']['CUESHEET']['number_tracks']; $track++) {
   1.757 +        
   1.758 +            $track_sample_offset = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 8));
   1.759 +            $offset += 8;
   1.760 +
   1.761 +            $track_number        = getid3_lib::BigEndian2Int($meta_data_block_data{$offset++});
   1.762 +
   1.763 +            $getid3->info['flac']['CUESHEET']['tracks'][$track_number]['sample_offset'] = $track_sample_offset;
   1.764 +            $getid3->info['flac']['CUESHEET']['tracks'][$track_number]['isrc']          = substr($meta_data_block_data, $offset, 12);
   1.765 +            $offset += 12;
   1.766 +
   1.767 +            $track_flags_raw = getid3_lib::BigEndian2Int($meta_data_block_data{$offset++});
   1.768 +            $getid3->info['flac']['CUESHEET']['tracks'][$track_number]['flags']['is_audio']     = (bool)($track_flags_raw & 0x80);
   1.769 +            $getid3->info['flac']['CUESHEET']['tracks'][$track_number]['flags']['pre_emphasis'] = (bool)($track_flags_raw & 0x40);
   1.770 +
   1.771 +            $offset += 13; // reserved
   1.772 +
   1.773 +            $getid3->info['flac']['CUESHEET']['tracks'][$track_number]['index_points'] = getid3_lib::BigEndian2Int($meta_data_block_data{$offset++});
   1.774 +
   1.775 +            for ($index = 0; $index < $getid3->info['flac']['CUESHEET']['tracks'][$track_number]['index_points']; $index++) {
   1.776 +                
   1.777 +                $index_sample_offset = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 8));
   1.778 +                $offset += 8;
   1.779 +                
   1.780 +                $index_number = getid3_lib::BigEndian2Int($meta_data_block_data{$offset++});
   1.781 +                $getid3->info['flac']['CUESHEET']['tracks'][$track_number]['indexes'][$index_number] = $index_sample_offset;
   1.782 +                
   1.783 +                $offset += 3; // reserved
   1.784 +            }
   1.785 +        }
   1.786 +        
   1.787 +        unset($getid3->info['flac']['CUESHEET']['raw']);
   1.788 +        
   1.789 +        return true;
   1.790 +    }
   1.791 +    
   1.792 +    
   1.793 +    
   1.794 +    private function FLACparsePICTURE($meta_data_block_data) {
   1.795 +        
   1.796 +        $getid3 = $this->getid3;
   1.797 +        
   1.798 +        $picture = &$getid3->info['flac']['PICTURE'][sizeof($getid3->info['flac']['PICTURE']) - 1];
   1.799 +        
   1.800 +        $offset = 0;
   1.801 +        
   1.802 +        $picture['type'] = $this->FLACpictureTypeLookup(getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4)));
   1.803 +        $offset += 4;
   1.804 +        
   1.805 +        $length = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
   1.806 +        $offset += 4;
   1.807 +        
   1.808 +        $picture['mime_type'] = substr($meta_data_block_data, $offset, $length);
   1.809 +        $offset += $length;
   1.810 +        
   1.811 +        $length = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
   1.812 +        $offset += 4;
   1.813 +        
   1.814 +        $picture['description'] = substr($meta_data_block_data, $offset, $length);
   1.815 +        $offset += $length;
   1.816 +        
   1.817 +        $picture['width'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
   1.818 +        $offset += 4;
   1.819 +        
   1.820 +        $picture['height'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
   1.821 +        $offset += 4;
   1.822 +        
   1.823 +        $picture['color_depth'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
   1.824 +        $offset += 4;
   1.825 +        
   1.826 +        $picture['colors_indexed'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
   1.827 +        $offset += 4;
   1.828 +        
   1.829 +        $length = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
   1.830 +        $offset += 4;
   1.831 +        
   1.832 +        $picture['image_data'] = substr($meta_data_block_data, $offset, $length);
   1.833 +        $offset += $length;
   1.834 +        
   1.835 +        unset($getid3->info['flac']['PICTURE']['raw']);
   1.836 +        
   1.837 +        return true;
   1.838 +    }
   1.839 +    
   1.840 +    
   1.841 +    
   1.842 +    public static function SpeexBandModeLookup($mode) {
   1.843 +        
   1.844 +        static $lookup = array (
   1.845 +            0 => 'narrow',
   1.846 +            1 => 'wide',
   1.847 +            2 => 'ultra-wide'
   1.848 +        );
   1.849 +        return (isset($lookup[$mode]) ? $lookup[$mode] : null);
   1.850 +    }
   1.851 +
   1.852 +
   1.853 +
   1.854 +    public static function OggPageSegmentLength($ogg_info_array, $segment_number=1) {
   1.855 +        
   1.856 +        for ($i = 0; $i < $segment_number; $i++) {
   1.857 +            $segment_length = 0;
   1.858 +            foreach ($ogg_info_array['segment_table'] as $key => $value) {
   1.859 +                $segment_length += $value;
   1.860 +                if ($value < 255) {
   1.861 +                    break;
   1.862 +                }
   1.863 +            }
   1.864 +        }
   1.865 +        return $segment_length;
   1.866 +    }
   1.867 +
   1.868 +
   1.869 +
   1.870 +    public static function GetQualityFromNominalBitrate($nominal_bitrate) {
   1.871 +
   1.872 +        // decrease precision
   1.873 +        $nominal_bitrate = $nominal_bitrate / 1000;
   1.874 +
   1.875 +        if ($nominal_bitrate < 128) {
   1.876 +            // q-1 to q4
   1.877 +            $qval = ($nominal_bitrate - 64) / 16;
   1.878 +        } elseif ($nominal_bitrate < 256) {
   1.879 +            // q4 to q8
   1.880 +            $qval = $nominal_bitrate / 32;
   1.881 +        } elseif ($nominal_bitrate < 320) {
   1.882 +            // q8 to q9
   1.883 +            $qval = ($nominal_bitrate + 256) / 64;
   1.884 +        } else {
   1.885 +            // q9 to q10
   1.886 +            $qval = ($nominal_bitrate + 1300) / 180;
   1.887 +        }
   1.888 +        return round($qval, 1); // 5 or 4.9
   1.889 +    }
   1.890 +    
   1.891 +    
   1.892 +    
   1.893 +    public static function FLACmetaBlockTypeLookup($block_type) {
   1.894 +    
   1.895 +        static $lookup = array (
   1.896 +            0 => 'STREAMINFO',
   1.897 +            1 => 'PADDING',
   1.898 +            2 => 'APPLICATION',
   1.899 +            3 => 'SEEKTABLE',
   1.900 +            4 => 'VORBIS_COMMENT',
   1.901 +            5 => 'CUESHEET',
   1.902 +            6 => 'PICTURE'
   1.903 +        );
   1.904 +        return (isset($lookup[$block_type]) ? $lookup[$block_type] : 'reserved');
   1.905 +    }
   1.906 +
   1.907 +
   1.908 +
   1.909 +    public static function FLACapplicationIDLookup($application_id) {
   1.910 +        
   1.911 +        // http://flac.sourceforge.net/id.html
   1.912 +        
   1.913 +        static $lookup = array (
   1.914 +            0x46746F6C => 'flac-tools',                                                 // 'Ftol'
   1.915 +            0x46746F6C => 'Sound Font FLAC',                                            // 'SFFL'
   1.916 +            0x7065656D => 'Parseable Embedded Extensible Metadata (specification)',     //  'peem'
   1.917 +            0x786D6364 => 'xmcd'
   1.918 +            
   1.919 +        );
   1.920 +        return (isset($lookup[$application_id]) ? $lookup[$application_id] : 'reserved');
   1.921 +    }
   1.922 +
   1.923 +
   1.924 +    public static function FLACpictureTypeLookup($type_id) {
   1.925 +        
   1.926 +        static $lookup = array (
   1.927 +            
   1.928 +             0 => 'Other',
   1.929 +             1 => "32x32 pixels 'file icon' (PNG only)",
   1.930 +             2 => 'Other file icon',
   1.931 +             3 => 'Cover (front)',
   1.932 +             4 => 'Cover (back)',
   1.933 +             5 => 'Leaflet page',
   1.934 +             6 => 'Media (e.g. label side of CD)',
   1.935 +             7 => 'Lead artist/lead performer/soloist',
   1.936 +             8 => 'Artist/performer',
   1.937 +             9 => 'Conductor',
   1.938 +            10 => 'Band/Orchestra',
   1.939 +            11 => 'Composer',
   1.940 +            12 => 'Lyricist/text writer',
   1.941 +            13 => 'Recording Location',
   1.942 +            14 => 'During recording',
   1.943 +            15 => 'During performance',
   1.944 +            16 => 'Movie/video screen capture',
   1.945 +            17 => 'A bright coloured fish',
   1.946 +            18 => 'Illustration',
   1.947 +            19 => 'Band/artist logotype',
   1.948 +            20 => 'Publisher/Studio logotype'
   1.949 +        );
   1.950 +        return (isset($lookup[$type_id]) ? $lookup[$type_id] : 'reserved');
   1.951 +    }
   1.952 +
   1.953 +}
   1.954 +
   1.955 +?>
   1.956 \ No newline at end of file