view e2gallerypro/e2upload/Backend/Assets/getid3/module.audio.optimfrog.php @ 28:118cb614cf18 judyates tip

judy bio.
author Robert McIntyre <rlm@mit.edu>
date Tue, 29 Dec 2015 16:26:18 -0800
parents 3f6b44aa6b35
children
line wrap: on
line source
1 <?php
2 // +----------------------------------------------------------------------+
3 // | PHP version 5 |
4 // +----------------------------------------------------------------------+
5 // | Copyright (c) 2002-2006 James Heinrich, Allan Hansen |
6 // +----------------------------------------------------------------------+
7 // | This source file is subject to version 2 of the GPL license, |
8 // | that is bundled with this package in the file license.txt and is |
9 // | available through the world-wide-web at the following url: |
10 // | http://www.gnu.org/copyleft/gpl.html |
11 // +----------------------------------------------------------------------+
12 // | getID3() - http://getid3.sourceforge.net or http://www.getid3.org |
13 // +----------------------------------------------------------------------+
14 // | Authors: James Heinrich <infoØgetid3*org> |
15 // | Allan Hansen <ahØartemis*dk> |
16 // +----------------------------------------------------------------------+
17 // | module.audio.optimfrog.php |
18 // | Module for analyzing OptimFROG Audio files |
19 // | dependencies: module.audio-video.riff.php |
20 // +----------------------------------------------------------------------+
21 //
22 // $Id: module.audio.optimfrog.php,v 1.3 2006/11/02 10:48:01 ah Exp $
26 class getid3_optimfrog extends getid3_handler
27 {
29 public function Analyze() {
31 $getid3 = $this->getid3;
33 $getid3->include_module('audio-video.riff');
35 $getid3->info['audio']['dataformat'] = 'ofr';
36 $getid3->info['audio']['bitrate_mode'] = 'vbr';
37 $getid3->info['audio']['lossless'] = true;
39 fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET);
40 $ofr_header = fread($getid3->fp, 8);
42 if (substr($ofr_header, 0, 5) == '*RIFF') {
43 return $this->ParseOptimFROGheader42($getid3->fp, $getid3->info);
45 } elseif (substr($ofr_header, 0, 3) == 'OFR') {
46 return $this->ParseOptimFROGheader45($getid3->fp, $getid3->info);
47 }
49 throw new getid3_exception('Expecting "*RIFF" or "OFR " at offset '.$getid3->info['avdataoffset'].', found "'.$ofr_header.'"');
50 }
54 private function ParseOptimFROGheader42() {
56 $getid3 = $this->getid3;
58 // for fileformat of v4.21 and older
60 fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET);
62 $ofr_header_data = fread($getid3->fp, 45);
63 $getid3->info['avdataoffset'] = 45;
65 $ofr_encoder_version_raw = getid3_lib::LittleEndian2Int(substr($ofr_header_data, 0, 1));
66 $ofr_encoder_version_major = floor($ofr_encoder_version_raw / 10);
67 $ofr_encoder_version_minor = $ofr_encoder_version_raw - ($ofr_encoder_version_major * 10);
68 $riff_data = substr($ofr_header_data, 1, 44);
69 $origna_riff_header_size = getid3_lib::LittleEndian2Int(substr($riff_data, 4, 4)) + 8;
70 $origna_riff_data_size = getid3_lib::LittleEndian2Int(substr($riff_data, 40, 4)) + 44;
72 if ($origna_riff_header_size > $origna_riff_data_size) {
73 $getid3->info['avdataend'] -= ($origna_riff_header_size - $origna_riff_data_size);
74 fseek($getid3->fp, $getid3->info['avdataend'], SEEK_SET);
75 $riff_data .= fread($getid3->fp, $origna_riff_header_size - $origna_riff_data_size);
76 }
78 // move the data chunk after all other chunks (if any)
79 // so that the RIFF parser doesn't see EOF when trying
80 // to skip over the data chunk
82 $riff_data = substr($riff_data, 0, 36).substr($riff_data, 44).substr($riff_data, 36, 8);
84 // Save audio info key
85 $saved_info_audio = $getid3->info['audio'];
87 // Instantiate riff module and analyze string
88 $riff = new getid3_riff($getid3);
89 $riff->AnalyzeString($riff_data);
91 // Restore info key
92 $getid3->info['audio'] = $saved_info_audio;
94 $getid3->info['audio']['encoder'] = 'OptimFROG '.$ofr_encoder_version_major.'.'.$ofr_encoder_version_minor;
95 $getid3->info['audio']['channels'] = $getid3->info['riff']['audio'][0]['channels'];
96 $getid3->info['audio']['sample_rate'] = $getid3->info['riff']['audio'][0]['sample_rate'];
97 $getid3->info['audio']['bits_per_sample'] = $getid3->info['riff']['audio'][0]['bits_per_sample'];
98 $getid3->info['playtime_seconds'] = $origna_riff_data_size / ($getid3->info['audio']['channels'] * $getid3->info['audio']['sample_rate'] * ($getid3->info['audio']['bits_per_sample'] / 8));
99 $getid3->info['audio']['bitrate'] = (($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8) / $getid3->info['playtime_seconds'];
101 $getid3->info['fileformat'] = 'ofr';
103 return true;
104 }
108 private function ParseOptimFROGheader45() {
110 $getid3 = $this->getid3;
112 // for fileformat of v4.50a and higher
114 $riff_data = '';
115 fseek($getid3->fp, $getid3->info['avdataoffset'], SEEK_SET);
117 while (!feof($getid3->fp) && (ftell($getid3->fp) < $getid3->info['avdataend'])) {
119 $block_offset = ftell($getid3->fp);
120 $block_data = fread($getid3->fp, 8);
121 $offset = 8;
122 $block_name = substr($block_data, 0, 4);
123 $block_size = getid3_lib::LittleEndian2Int(substr($block_data, 4, 4));
125 if ($block_name == 'OFRX') {
126 $block_name = 'OFR ';
127 }
128 if (!isset($getid3->info['ofr'][$block_name])) {
129 $getid3->info['ofr'][$block_name] = array ();
130 }
131 $info_ofr_this_block = &$getid3->info['ofr'][$block_name];
133 switch ($block_name) {
134 case 'OFR ':
136 // shortcut
137 $info_ofr_this_block['offset'] = $block_offset;
138 $info_ofr_this_block['size'] = $block_size;
140 $getid3->info['audio']['encoder'] = 'OptimFROG 4.50 alpha';
141 switch ($block_size) {
142 case 12:
143 case 15:
144 // good
145 break;
147 default:
148 $getid3->warning('"'.$block_name.'" contains more data than expected (expected 12 or 15 bytes, found '.$block_size.' bytes)');
149 break;
150 }
151 $block_data .= fread($getid3->fp, $block_size);
153 $info_ofr_this_block['total_samples'] = getid3_lib::LittleEndian2Int(substr($block_data, $offset, 6));
154 $offset += 6;
156 $info_ofr_this_block['raw']['sample_type'] = getid3_lib::LittleEndian2Int($block_data{$offset++});
157 $info_ofr_this_block['sample_type'] = $this->OptimFROGsampleTypeLookup($info_ofr_this_block['raw']['sample_type']);
159 $info_ofr_this_block['channel_config'] = getid3_lib::LittleEndian2Int($block_data{$offset++});
160 $info_ofr_this_block['channels'] = $info_ofr_this_block['channel_config'];
162 $info_ofr_this_block['sample_rate'] = getid3_lib::LittleEndian2Int(substr($block_data, $offset, 4));
163 $offset += 4;
165 if ($block_size > 12) {
167 // OFR 4.504b or higher
168 $info_ofr_this_block['channels'] = $this->OptimFROGchannelConfigNumChannelsLookup($info_ofr_this_block['channel_config']);
169 $info_ofr_this_block['raw']['encoder_id'] = getid3_lib::LittleEndian2Int(substr($block_data, $offset, 2));
170 $info_ofr_this_block['encoder'] = $this->OptimFROGencoderNameLookup($info_ofr_this_block['raw']['encoder_id']);
171 $offset += 2;
173 $info_ofr_this_block['raw']['compression'] = getid3_lib::LittleEndian2Int($block_data{$offset++});
174 $info_ofr_this_block['compression'] = $this->OptimFROGcompressionLookup($info_ofr_this_block['raw']['compression']);
175 $info_ofr_this_block['speedup'] = $this->OptimFROGspeedupLookup($info_ofr_this_block['raw']['compression']);
177 $getid3->info['audio']['encoder'] = 'OptimFROG '.$info_ofr_this_block['encoder'];
178 $getid3->info['audio']['encoder_options'] = '--mode '.$info_ofr_this_block['compression'];
180 if ((($info_ofr_this_block['raw']['encoder_id'] & 0xF0) >> 4) == 7) { // v4.507
181 if (preg_match('/\.ofs$/i', $getid3->filename)) {
182 // OptimFROG DualStream format is lossy, but as of v4.507 there is no way to tell the difference
183 // between lossless and lossy other than the file extension.
184 $getid3->info['audio']['dataformat'] = 'ofs';
185 $getid3->info['audio']['lossless'] = true;
186 }
187 }
188 }
190 $getid3->info['audio']['channels'] = $info_ofr_this_block['channels'];
191 $getid3->info['audio']['sample_rate'] = $info_ofr_this_block['sample_rate'];
192 $getid3->info['audio']['bits_per_sample'] = $this->OptimFROGbitsPerSampleTypeLookup($info_ofr_this_block['raw']['sample_type']);
193 break;
196 case 'COMP':
197 // unlike other block types, there CAN be multiple COMP blocks
199 $comp_data['offset'] = $block_offset;
200 $comp_data['size'] = $block_size;
202 if ($getid3->info['avdataoffset'] == 0) {
203 $getid3->info['avdataoffset'] = $block_offset;
204 }
206 // Only interested in first 14 bytes (only first 12 needed for v4.50 alpha), not actual audio data
207 $block_data .= fread($getid3->fp, 14);
208 fseek($getid3->fp, $block_size - 14, SEEK_CUR);
210 $comp_data['crc_32'] = getid3_lib::LittleEndian2Int(substr($block_data, $offset, 4));
211 $offset += 4;
213 $comp_data['sample_count'] = getid3_lib::LittleEndian2Int(substr($block_data, $offset, 4));
214 $offset += 4;
216 $comp_data['raw']['sample_type'] = getid3_lib::LittleEndian2Int($block_data{$offset++});
217 $comp_data['sample_type'] = $this->OptimFROGsampleTypeLookup($comp_data['raw']['sample_type']);
219 $comp_data['raw']['channel_configuration'] = getid3_lib::LittleEndian2Int($block_data{$offset++});
220 $comp_data['channel_configuration'] = $this->OptimFROGchannelConfigurationLookup($comp_data['raw']['channel_configuration']);
222 $comp_data['raw']['algorithm_id'] = getid3_lib::LittleEndian2Int(substr($block_data, $offset, 2));
223 $offset += 2;
225 if ($getid3->info['ofr']['OFR ']['size'] > 12) {
227 // OFR 4.504b or higher
228 $comp_data['raw']['encoder_id'] = getid3_lib::LittleEndian2Int(substr($block_data, $offset, 2));
229 $comp_data['encoder'] = $this->OptimFROGencoderNameLookup($comp_data['raw']['encoder_id']);
230 $offset += 2;
231 }
233 if ($comp_data['crc_32'] == 0x454E4F4E) {
234 // ASCII value of 'NONE' - placeholder value in v4.50a
235 $comp_data['crc_32'] = false;
236 }
238 $info_ofr_this_block[] = $comp_data;
239 break;
241 case 'HEAD':
242 $info_ofr_this_block['offset'] = $block_offset;
243 $info_ofr_this_block['size'] = $block_size;
245 $riff_data .= fread($getid3->fp, $block_size);
246 break;
248 case 'TAIL':
249 $info_ofr_this_block['offset'] = $block_offset;
250 $info_ofr_this_block['size'] = $block_size;
252 if ($block_size > 0) {
253 $riff_data .= fread($getid3->fp, $block_size);
254 }
255 break;
257 case 'RECV':
258 // block contains no useful meta data - simply note and skip
260 $info_ofr_this_block['offset'] = $block_offset;
261 $info_ofr_this_block['size'] = $block_size;
263 fseek($getid3->fp, $block_size, SEEK_CUR);
264 break;
267 case 'APET':
268 // APEtag v2
270 $info_ofr_this_block['offset'] = $block_offset;
271 $info_ofr_this_block['size'] = $block_size;
272 $getid3->warning('APEtag processing inside OptimFROG not supported in this version ('.GETID3_VERSION.') of getID3()');
274 fseek($getid3->fp, $block_size, SEEK_CUR);
275 break;
278 case 'MD5 ':
279 // APEtag v2
281 $info_ofr_this_block['offset'] = $block_offset;
282 $info_ofr_this_block['size'] = $block_size;
284 if ($block_size == 16) {
286 $info_ofr_this_block['md5_binary'] = fread($getid3->fp, $block_size);
287 $info_ofr_this_block['md5_string'] = getid3_lib::PrintHexBytes($info_ofr_this_block['md5_binary'], true, false, false);
288 $getid3->info['md5_data_source'] = $info_ofr_this_block['md5_string'];
290 } else {
292 $getid3->warning('Expecting block size of 16 in "MD5 " chunk, found '.$block_size.' instead');
293 fseek($getid3->fp, $block_size, SEEK_CUR);
295 }
296 break;
299 default:
300 $info_ofr_this_block['offset'] = $block_offset;
301 $info_ofr_this_block['size'] = $block_size;
303 $getid3->warning('Unhandled OptimFROG block type "'.$block_name.'" at offset '.$info_ofr_this_block['offset']);
304 fseek($getid3->fp, $block_size, SEEK_CUR);
305 break;
306 }
307 }
309 if (isset($getid3->info['ofr']['TAIL']['offset'])) {
310 $getid3->info['avdataend'] = $getid3->info['ofr']['TAIL']['offset'];
311 }
313 $getid3->info['playtime_seconds'] = (float)$getid3->info['ofr']['OFR ']['total_samples'] / ($getid3->info['audio']['channels'] * $getid3->info['audio']['sample_rate']);
314 $getid3->info['audio']['bitrate'] = (($getid3->info['avdataend'] - $getid3->info['avdataoffset']) * 8) / $getid3->info['playtime_seconds'];
316 // move the data chunk after all other chunks (if any)
317 // so that the RIFF parser doesn't see EOF when trying
318 // to skip over the data chunk
320 $riff_data = substr($riff_data, 0, 36).substr($riff_data, 44).substr($riff_data, 36, 8);
322 // Save audio info key
323 $saved_info_audio = $getid3->info['audio'];
325 // Instantiate riff module and analyze string
326 $riff = new getid3_riff($getid3);
327 $riff->AnalyzeString($riff_data);
329 // Restore info key
330 $getid3->info['audio'] = $saved_info_audio;
332 $getid3->info['fileformat'] = 'ofr';
334 return true;
335 }
339 public static function OptimFROGsampleTypeLookup($sample_type) {
341 static $lookup = array (
342 0 => 'unsigned int (8-bit)',
343 1 => 'signed int (8-bit)',
344 2 => 'unsigned int (16-bit)',
345 3 => 'signed int (16-bit)',
346 4 => 'unsigned int (24-bit)',
347 5 => 'signed int (24-bit)',
348 6 => 'unsigned int (32-bit)',
349 7 => 'signed int (32-bit)',
350 8 => 'float 0.24 (32-bit)',
351 9 => 'float 16.8 (32-bit)',
352 10 => 'float 24.0 (32-bit)'
353 );
355 return @$lookup[$sample_type];
356 }
360 public static function OptimFROGbitsPerSampleTypeLookup($sample_type) {
362 static $lookup = array (
363 0 => 8,
364 1 => 8,
365 2 => 16,
366 3 => 16,
367 4 => 24,
368 5 => 24,
369 6 => 32,
370 7 => 32,
371 8 => 32,
372 9 => 32,
373 10 => 32
374 );
376 return @$lookup[$sample_type];
377 }
381 public static function OptimFROGchannelConfigurationLookup($channel_configuration) {
383 static $lookup = array (
384 0 => 'mono',
385 1 => 'stereo'
386 );
388 return @$lookup[$channel_configuration];
389 }
393 public static function OptimFROGchannelConfigNumChannelsLookup($channel_configuration) {
395 static $lookup = array (
396 0 => 1,
397 1 => 2
398 );
400 return @$lookup[$channel_configuration];
401 }
405 public static function OptimFROGencoderNameLookup($encoder_id) {
407 // version = (encoderID >> 4) + 4500
408 // system = encoderID & 0xF
410 $encoder_version = number_format(((($encoder_id & 0xF0) >> 4) + 4500) / 1000, 3);
411 $encoder_system_id = ($encoder_id & 0x0F);
413 static $lookup = array (
414 0x00 => 'Windows console',
415 0x01 => 'Linux console',
416 0x0F => 'unknown'
417 );
418 return $encoder_version.' ('.(isset($lookup[$encoder_system_id]) ? $lookup[$encoder_system_id] : 'undefined encoder type (0x'.dechex($encoder_system_id).')').')';
419 }
423 public static function OptimFROGcompressionLookup($compression_id) {
425 // mode = compression >> 3
426 // speedup = compression & 0x07
428 $compression_mode_id = ($compression_id & 0xF8) >> 3;
429 //$compression_speed_up_id = ($compression_id & 0x07);
431 static $lookup = array (
432 0x00 => 'fast',
433 0x01 => 'normal',
434 0x02 => 'high',
435 0x03 => 'extra', // extranew (some versions)
436 0x04 => 'best', // bestnew (some versions)
437 0x05 => 'ultra',
438 0x06 => 'insane',
439 0x07 => 'highnew',
440 0x08 => 'extranew',
441 0x09 => 'bestnew'
442 );
443 return (isset($lookup[$compression_mode_id]) ? $lookup[$compression_mode_id] : 'undefined mode (0x'.str_pad(dechex($compression_mode_id), 2, '0', STR_PAD_LEFT).')');
444 }
448 public static function OptimFROGspeedupLookup($compression_id) {
450 // mode = compression >> 3
451 // speedup = compression & 0x07
453 //$compression_mode_id = ($compression_id & 0xF8) >> 3;
454 $compression_speed_up_id = ($compression_id & 0x07);
456 static $lookup = array (
457 0x00 => '1x',
458 0x01 => '2x',
459 0x02 => '4x'
460 );
462 return (isset($lookup[$compression_speed_up_id]) ? $lookup[$compression_speed_up_id] : 'undefined mode (0x'.dechex($compression_speed_up_id));
463 }
465 }
468 ?>