source: extensions/charlies_content/getid3/getid3/module.audio.optimfrog.php @ 3544

Last change on this file since 3544 was 3544, checked in by vdigital, 15 years ago

Change: getid3 upgraded to -> 1.7.9

  • Property svn:keywords set to Author Date Id Revision
File size: 15.8 KB
Line 
1<?php
2/////////////////////////////////////////////////////////////////
3/// getID3() by James Heinrich <info@getid3.org>               //
4//  available at http://getid3.sourceforge.net                 //
5//            or http://www.getid3.org                         //
6/////////////////////////////////////////////////////////////////
7// See readme.txt for more details                             //
8/////////////////////////////////////////////////////////////////
9//                                                             //
10// module.audio.optimfrog.php                                  //
11// module for analyzing OptimFROG audio files                  //
12// dependencies: module.audio.riff.php                         //
13//                                                            ///
14/////////////////////////////////////////////////////////////////
15
16getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
17
18class getid3_optimfrog
19{
20
21        function getid3_optimfrog(&$fd, &$ThisFileInfo) {
22                $ThisFileInfo['fileformat']            = 'ofr';
23                $ThisFileInfo['audio']['dataformat']   = 'ofr';
24                $ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
25                $ThisFileInfo['audio']['lossless']     = true;
26
27                fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
28                $OFRheader  = fread($fd, 8);
29                if (substr($OFRheader, 0, 5) == '*RIFF') {
30
31                        return $this->ParseOptimFROGheader42($fd, $ThisFileInfo);
32
33                } elseif (substr($OFRheader, 0, 3) == 'OFR') {
34
35                        return $this->ParseOptimFROGheader45($fd, $ThisFileInfo);
36
37                }
38
39                $ThisFileInfo['error'][] = 'Expecting "*RIFF" or "OFR " at offset '.$ThisFileInfo['avdataoffset'].', found "'.$OFRheader.'"';
40                unset($ThisFileInfo['fileformat']);
41                return false;
42        }
43
44
45        function ParseOptimFROGheader42(&$fd, &$ThisFileInfo) {
46                // for fileformat of v4.21 and older
47
48                fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
49                $OptimFROGheaderData = fread($fd, 45);
50                $ThisFileInfo['avdataoffset'] = 45;
51
52                $OptimFROGencoderVersion_raw   = getid3_lib::LittleEndian2Int(substr($OptimFROGheaderData, 0, 1));
53                $OptimFROGencoderVersion_major = floor($OptimFROGencoderVersion_raw / 10);
54                $OptimFROGencoderVersion_minor = $OptimFROGencoderVersion_raw - ($OptimFROGencoderVersion_major * 10);
55                $RIFFdata                = substr($OptimFROGheaderData, 1, 44);
56                $OrignalRIFFheaderSize   = getid3_lib::LittleEndian2Int(substr($RIFFdata,  4, 4)) +  8;
57                $OrignalRIFFdataSize     = getid3_lib::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44;
58
59                if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) {
60                        $ThisFileInfo['avdataend'] -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
61                        fseek($fd, $ThisFileInfo['avdataend'], SEEK_SET);
62                        $RIFFdata .= fread($fd, $OrignalRIFFheaderSize - $OrignalRIFFdataSize);
63                }
64
65                // move the data chunk after all other chunks (if any)
66                // so that the RIFF parser doesn't see EOF when trying
67                // to skip over the data chunk
68                $RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8);
69                getid3_riff::ParseRIFFdata($RIFFdata, $ThisFileInfo);
70
71                $ThisFileInfo['audio']['encoder']         = 'OptimFROG '.$OptimFROGencoderVersion_major.'.'.$OptimFROGencoderVersion_minor;
72                $ThisFileInfo['audio']['channels']        = $ThisFileInfo['riff']['audio'][0]['channels'];
73                $ThisFileInfo['audio']['sample_rate']     = $ThisFileInfo['riff']['audio'][0]['sample_rate'];
74                $ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['riff']['audio'][0]['bits_per_sample'];
75                $ThisFileInfo['playtime_seconds']         = $OrignalRIFFdataSize / ($ThisFileInfo['audio']['channels'] * $ThisFileInfo['audio']['sample_rate'] * ($ThisFileInfo['audio']['bits_per_sample'] / 8));
76                $ThisFileInfo['audio']['bitrate']         = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
77
78                return true;
79        }
80
81
82        function ParseOptimFROGheader45(&$fd, &$ThisFileInfo) {
83                // for fileformat of v4.50a and higher
84
85                $RIFFdata = '';
86                fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
87                while (!feof($fd) && (ftell($fd) < $ThisFileInfo['avdataend'])) {
88                        $BlockOffset = ftell($fd);
89                        $BlockData   = fread($fd, 8);
90                        $offset      = 8;
91                        $BlockName   =                  substr($BlockData, 0, 4);
92                        $BlockSize   = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 4));
93
94                        if ($BlockName == 'OFRX') {
95                                $BlockName = 'OFR ';
96                        }
97                        if (!isset($ThisFileInfo['ofr'][$BlockName])) {
98                                $ThisFileInfo['ofr'][$BlockName] = array();
99                        }
100                        $thisfile_ofr_thisblock = &$ThisFileInfo['ofr'][$BlockName];
101
102                        switch ($BlockName) {
103                                case 'OFR ':
104
105                                        // shortcut
106                                        $thisfile_ofr_thisblock['offset'] = $BlockOffset;
107                                        $thisfile_ofr_thisblock['size']   = $BlockSize;
108
109                                        $ThisFileInfo['audio']['encoder'] = 'OptimFROG 4.50 alpha';
110                                        switch ($BlockSize) {
111                                                case 12:
112                                                case 15:
113                                                        // good
114                                                        break;
115
116                                                default:
117                                                        $ThisFileInfo['warning'][] = '"'.$BlockName.'" contains more data than expected (expected 12 or 15 bytes, found '.$BlockSize.' bytes)';
118                                                        break;
119                                        }
120                                        $BlockData .= fread($fd, $BlockSize);
121
122                                        $thisfile_ofr_thisblock['total_samples']      = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 6));
123                                        $offset += 6;
124                                        $thisfile_ofr_thisblock['raw']['sample_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
125                                        $thisfile_ofr_thisblock['sample_type']        = $this->OptimFROGsampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']);
126                                        $offset += 1;
127                                        $thisfile_ofr_thisblock['channel_config']     = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
128                                        $thisfile_ofr_thisblock['channels']           = $thisfile_ofr_thisblock['channel_config'];
129                                        $offset += 1;
130                                        $thisfile_ofr_thisblock['sample_rate']        = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4));
131                                        $offset += 4;
132
133                                        if ($BlockSize > 12) {
134
135                                                // OFR 4.504b or higher
136                                                $thisfile_ofr_thisblock['channels']           = $this->OptimFROGchannelConfigNumChannelsLookup($thisfile_ofr_thisblock['channel_config']);
137                                                $thisfile_ofr_thisblock['raw']['encoder_id']  = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2));
138                                                $thisfile_ofr_thisblock['encoder']            = $this->OptimFROGencoderNameLookup($thisfile_ofr_thisblock['raw']['encoder_id']);
139                                                $offset += 2;
140                                                $thisfile_ofr_thisblock['raw']['compression'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
141                                                $thisfile_ofr_thisblock['compression']        = $this->OptimFROGcompressionLookup($thisfile_ofr_thisblock['raw']['compression']);
142                                                $thisfile_ofr_thisblock['speedup']            = $this->OptimFROGspeedupLookup($thisfile_ofr_thisblock['raw']['compression']);
143                                                $offset += 1;
144
145                                                $ThisFileInfo['audio']['encoder']         = 'OptimFROG '.$thisfile_ofr_thisblock['encoder'];
146                                                $ThisFileInfo['audio']['encoder_options'] = '--mode '.$thisfile_ofr_thisblock['compression'];
147
148                                                if ((($thisfile_ofr_thisblock['raw']['encoder_id'] & 0xF0) >> 4) == 7) { // v4.507
149                                                        if (strtolower(getid3_lib::fileextension($ThisFileInfo['filename'])) == 'ofs') {
150                                                                // OptimFROG DualStream format is lossy, but as of v4.507 there is no way to tell the difference
151                                                                // between lossless and lossy other than the file extension.
152                                                                $ThisFileInfo['audio']['dataformat']   = 'ofs';
153                                                                $ThisFileInfo['audio']['lossless']     = true;
154                                                        }
155                                                }
156
157                                        }
158
159                                        $ThisFileInfo['audio']['channels']        = $thisfile_ofr_thisblock['channels'];
160                                        $ThisFileInfo['audio']['sample_rate']     = $thisfile_ofr_thisblock['sample_rate'];
161                                        $ThisFileInfo['audio']['bits_per_sample'] = $this->OptimFROGbitsPerSampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']);
162                                        break;
163
164
165                                case 'COMP':
166                                        // unlike other block types, there CAN be multiple COMP blocks
167
168                                        $COMPdata['offset'] = $BlockOffset;
169                                        $COMPdata['size']   = $BlockSize;
170
171                                        if ($ThisFileInfo['avdataoffset'] == 0) {
172                                                $ThisFileInfo['avdataoffset'] = $BlockOffset;
173                                        }
174
175                                        // Only interested in first 14 bytes (only first 12 needed for v4.50 alpha), not actual audio data
176                                        $BlockData .= fread($fd, 14);
177                                        fseek($fd, $BlockSize - 14, SEEK_CUR);
178
179                                        $COMPdata['crc_32']                       = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4));
180                                        $offset += 4;
181                                        $COMPdata['sample_count']                 = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4));
182                                        $offset += 4;
183                                        $COMPdata['raw']['sample_type']           = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
184                                        $COMPdata['sample_type']                  = $this->OptimFROGsampleTypeLookup($COMPdata['raw']['sample_type']);
185                                        $offset += 1;
186                                        $COMPdata['raw']['channel_configuration'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
187                                        $COMPdata['channel_configuration']        = $this->OptimFROGchannelConfigurationLookup($COMPdata['raw']['channel_configuration']);
188                                        $offset += 1;
189                                        $COMPdata['raw']['algorithm_id']          = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2));
190                                        //$COMPdata['algorithm']                    = OptimFROGalgorithmNameLookup($COMPdata['raw']['algorithm_id']);
191                                        $offset += 2;
192
193                                        if ($ThisFileInfo['ofr']['OFR ']['size'] > 12) {
194
195                                                // OFR 4.504b or higher
196                                                $COMPdata['raw']['encoder_id']        = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2));
197                                                $COMPdata['encoder']                  = $this->OptimFROGencoderNameLookup($COMPdata['raw']['encoder_id']);
198                                                $offset += 2;
199
200                                        }
201
202                                        if ($COMPdata['crc_32'] == 0x454E4F4E) {
203                                                // ASCII value of 'NONE' - placeholder value in v4.50a
204                                                $COMPdata['crc_32'] = false;
205                                        }
206
207                                        $thisfile_ofr_thisblock[] = $COMPdata;
208                                        break;
209
210                                case 'HEAD':
211                                        $thisfile_ofr_thisblock['offset'] = $BlockOffset;
212                                        $thisfile_ofr_thisblock['size']   = $BlockSize;
213
214                                        $RIFFdata .= fread($fd, $BlockSize);
215                                        break;
216
217                                case 'TAIL':
218                                        $thisfile_ofr_thisblock['offset'] = $BlockOffset;
219                                        $thisfile_ofr_thisblock['size']   = $BlockSize;
220
221                                        if ($BlockSize > 0) {
222                                                $RIFFdata .= fread($fd, $BlockSize);
223                                        }
224                                        break;
225
226                                case 'RECV':
227                                        // block contains no useful meta data - simply note and skip
228
229                                        $thisfile_ofr_thisblock['offset'] = $BlockOffset;
230                                        $thisfile_ofr_thisblock['size']   = $BlockSize;
231
232                                        fseek($fd, $BlockSize, SEEK_CUR);
233                                        break;
234
235
236                                case 'APET':
237                                        // APEtag v2
238
239                                        $thisfile_ofr_thisblock['offset'] = $BlockOffset;
240                                        $thisfile_ofr_thisblock['size']   = $BlockSize;
241                                        $ThisFileInfo['warning'][] = 'APEtag processing inside OptimFROG not supported in this version ('.GETID3_VERSION.') of getID3()';
242
243                                        fseek($fd, $BlockSize, SEEK_CUR);
244                                        break;
245
246
247                                case 'MD5 ':
248                                        // APEtag v2
249
250                                        $thisfile_ofr_thisblock['offset'] = $BlockOffset;
251                                        $thisfile_ofr_thisblock['size']   = $BlockSize;
252
253                                        if ($BlockSize == 16) {
254
255                                                $thisfile_ofr_thisblock['md5_binary'] = fread($fd, $BlockSize);
256                                                $thisfile_ofr_thisblock['md5_string'] = getid3_lib::PrintHexBytes($thisfile_ofr_thisblock['md5_binary'], true, false, false);
257                                                $ThisFileInfo['md5_data_source'] = $thisfile_ofr_thisblock['md5_string'];
258
259                                        } else {
260
261                                                $ThisFileInfo['warning'][] = 'Expecting block size of 16 in "MD5 " chunk, found '.$BlockSize.' instead';
262                                                fseek($fd, $BlockSize, SEEK_CUR);
263
264                                        }
265                                        break;
266
267
268                                default:
269                                        $thisfile_ofr_thisblock['offset'] = $BlockOffset;
270                                        $thisfile_ofr_thisblock['size']   = $BlockSize;
271
272                                        $ThisFileInfo['warning'][] = 'Unhandled OptimFROG block type "'.$BlockName.'" at offset '.$thisfile_ofr_thisblock['offset'];
273                                        fseek($fd, $BlockSize, SEEK_CUR);
274                                        break;
275                        }
276                }
277                if (isset($ThisFileInfo['ofr']['TAIL']['offset'])) {
278                        $ThisFileInfo['avdataend'] = $ThisFileInfo['ofr']['TAIL']['offset'];
279                }
280
281                $ThisFileInfo['playtime_seconds'] = (float) $ThisFileInfo['ofr']['OFR ']['total_samples'] / ($ThisFileInfo['audio']['channels'] * $ThisFileInfo['audio']['sample_rate']);
282                $ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
283
284                // move the data chunk after all other chunks (if any)
285                // so that the RIFF parser doesn't see EOF when trying
286                // to skip over the data chunk
287                $RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8);
288                getid3_riff::ParseRIFFdata($RIFFdata, $ThisFileInfo);
289
290                return true;
291        }
292
293
294        function OptimFROGsampleTypeLookup($SampleType) {
295                static $OptimFROGsampleTypeLookup = array(
296                        0  => 'unsigned int (8-bit)',
297                        1  => 'signed int (8-bit)',
298                        2  => 'unsigned int (16-bit)',
299                        3  => 'signed int (16-bit)',
300                        4  => 'unsigned int (24-bit)',
301                        5  => 'signed int (24-bit)',
302                        6  => 'unsigned int (32-bit)',
303                        7  => 'signed int (32-bit)',
304                        8  => 'float 0.24 (32-bit)',
305                        9  => 'float 16.8 (32-bit)',
306                        10 => 'float 24.0 (32-bit)'
307                );
308                return (isset($OptimFROGsampleTypeLookup[$SampleType]) ? $OptimFROGsampleTypeLookup[$SampleType] : false);
309        }
310
311        function OptimFROGbitsPerSampleTypeLookup($SampleType) {
312                static $OptimFROGbitsPerSampleTypeLookup = array(
313                        0  => 8,
314                        1  => 8,
315                        2  => 16,
316                        3  => 16,
317                        4  => 24,
318                        5  => 24,
319                        6  => 32,
320                        7  => 32,
321                        8  => 32,
322                        9  => 32,
323                        10 => 32
324                );
325                return (isset($OptimFROGbitsPerSampleTypeLookup[$SampleType]) ? $OptimFROGbitsPerSampleTypeLookup[$SampleType] : false);
326        }
327
328        function OptimFROGchannelConfigurationLookup($ChannelConfiguration) {
329                static $OptimFROGchannelConfigurationLookup = array(
330                        0 => 'mono',
331                        1 => 'stereo'
332                );
333                return (isset($OptimFROGchannelConfigurationLookup[$ChannelConfiguration]) ? $OptimFROGchannelConfigurationLookup[$ChannelConfiguration] : false);
334        }
335
336        function OptimFROGchannelConfigNumChannelsLookup($ChannelConfiguration) {
337                static $OptimFROGchannelConfigNumChannelsLookup = array(
338                        0 => 1,
339                        1 => 2
340                );
341                return (isset($OptimFROGchannelConfigNumChannelsLookup[$ChannelConfiguration]) ? $OptimFROGchannelConfigNumChannelsLookup[$ChannelConfiguration] : false);
342        }
343
344
345
346        // function OptimFROGalgorithmNameLookup($AlgorithID) {
347        //     static $OptimFROGalgorithmNameLookup = array();
348        //     return (isset($OptimFROGalgorithmNameLookup[$AlgorithID]) ? $OptimFROGalgorithmNameLookup[$AlgorithID] : false);
349        // }
350
351
352        function OptimFROGencoderNameLookup($EncoderID) {
353                // version = (encoderID >> 4) + 4500
354                // system  =  encoderID & 0xF
355
356                $EncoderVersion  = number_format(((($EncoderID & 0xF0) >> 4) + 4500) / 1000, 3);
357                $EncoderSystemID = ($EncoderID & 0x0F);
358
359                static $OptimFROGencoderSystemLookup = array(
360                        0x00 => 'Windows console',
361                        0x01 => 'Linux console',
362                        0x0F => 'unknown'
363                );
364                return $EncoderVersion.' ('.(isset($OptimFROGencoderSystemLookup[$EncoderSystemID]) ? $OptimFROGencoderSystemLookup[$EncoderSystemID] : 'undefined encoder type (0x'.dechex($EncoderSystemID).')').')';
365        }
366
367        function OptimFROGcompressionLookup($CompressionID) {
368                // mode    = compression >> 3
369                // speedup = compression & 0x07
370
371                $CompressionModeID    = ($CompressionID & 0xF8) >> 3;
372                //$CompressionSpeedupID = ($CompressionID & 0x07);
373
374                static $OptimFROGencoderModeLookup = array(
375                        0x00 => 'fast',
376                        0x01 => 'normal',
377                        0x02 => 'high',
378                        0x03 => 'extra', // extranew (some versions)
379                        0x04 => 'best',  // bestnew (some versions)
380                        0x05 => 'ultra',
381                        0x06 => 'insane',
382                        0x07 => 'highnew',
383                        0x08 => 'extranew',
384                        0x09 => 'bestnew'
385                );
386                return (isset($OptimFROGencoderModeLookup[$CompressionModeID]) ? $OptimFROGencoderModeLookup[$CompressionModeID] : 'undefined mode (0x'.str_pad(dechex($CompressionModeID), 2, '0', STR_PAD_LEFT).')');
387        }
388
389        function OptimFROGspeedupLookup($CompressionID) {
390                // mode    = compression >> 3
391                // speedup = compression & 0x07
392
393                //$CompressionModeID    = ($CompressionID & 0xF8) >> 3;
394                $CompressionSpeedupID = ($CompressionID & 0x07);
395
396                static $OptimFROGencoderSpeedupLookup = array(
397                        0x00 => '1x',
398                        0x01 => '2x',
399                        0x02 => '4x'
400                );
401
402                return (isset($OptimFROGencoderSpeedupLookup[$CompressionSpeedupID]) ? $OptimFROGencoderSpeedupLookup[$CompressionSpeedupID] : 'undefined mode (0x'.dechex($CompressionSpeedupID));
403        }
404
405}
406
407
408?>
Note: See TracBrowser for help on using the repository browser.