Passed
Branch master (f2d2e3)
by Michael
18:45
created

getid3_mp3::AnalyzeMPEGaudioInfo()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
c 1
b 0
f 0
nc 1
dl 0
loc 3
rs 10
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.mp3.php                                                 |
18
// | Module for analyzing MPEG Audio Layer 1,2,3 files.                   |
19
// | dependencies: none                                                   |
20
// +----------------------------------------------------------------------+
21
//
22
// $Id: module.audio.mp3.php,v 1.10 2006/11/16 22:57:57 ah Exp $
23
24
25
26
class getid3_mp3 extends getid3_handler
27
{
28
    // Number of frames to scan to determine if MPEG-audio sequence is valid.
29
    // Lower this number to 5-20 for faster scanning
30
    // Increase this number to 50+ for most accurate detection of valid VBR/CBR mpeg-audio streams
31
    const VALID_CHECK_FRAMES = 35;
32
    
33
34
    public function Analyze() {
35
36
        $this->getAllMPEGInfo($this->getid3->fp, $this->getid3->info);
37
38
        return true;   
39
    }
40
    
41
    
42
    public function AnalyzeMPEGaudioInfo() {
43
        
44
        $this->getOnlyMPEGaudioInfo($this->getid3->fp, $this->getid3->info, $this->getid3->info['avdataoffset'], false);
45
    }
46
47
48
    public function getAllMPEGInfo(&$fd, &$info) {
49
50
        $this->getOnlyMPEGaudioInfo($fd, $info, 0 + $info['avdataoffset']);
51
52
        if (isset($info['mpeg']['audio']['bitrate_mode'])) {
53
            $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
54
        }
55
56
        if (((isset($info['id3v2']['headerlength']) && ($info['avdataoffset'] > $info['id3v2']['headerlength'])) || (!isset($info['id3v2']) && ($info['avdataoffset'] > 0)))) {
57
58
            $synch_offset_warning = 'Unknown data before synch ';
59
            if (isset($info['id3v2']['headerlength'])) {
60
                $synch_offset_warning .= '(ID3v2 header ends at '.$info['id3v2']['headerlength'].', then '.($info['avdataoffset'] - $info['id3v2']['headerlength']).' bytes garbage, ';
61
            } else {
62
                $synch_offset_warning .= '(should be at beginning of file, ';
63
            }
64
            $synch_offset_warning .= 'synch detected at '.$info['avdataoffset'].')';
65
            if ($info['audio']['bitrate_mode'] == 'cbr') {
66
67
                if (!empty($info['id3v2']['headerlength']) && (($info['avdataoffset'] - $info['id3v2']['headerlength']) == $info['mpeg']['audio']['framelength'])) {
68
69
                    $synch_offset_warning .= '. This is a known problem with some versions of LAME (3.90-3.92) DLL in CBR mode.';
70
                    $info['audio']['codec'] = 'LAME';
71
                    $current_data_lame_version_string = 'LAME3.';
72
73
                } elseif (empty($info['id3v2']['headerlength']) && ($info['avdataoffset'] == $info['mpeg']['audio']['framelength'])) {
74
75
                    $synch_offset_warning .= '. This is a known problem with some versions of LAME (3.90 - 3.92) DLL in CBR mode.';
76
                    $info['audio']['codec'] = 'LAME';
77
                    $current_data_lame_version_string = 'LAME3.';
78
79
                }
80
81
            }
82
            $this->getid3->warning($synch_offset_warning);
83
84
        }
85
86
        if (isset($info['mpeg']['audio']['LAME'])) {
87
            $info['audio']['codec'] = 'LAME';
88
            if (!empty($info['mpeg']['audio']['LAME']['long_version'])) {
89
                $info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['long_version'], "\x00");
90
            } elseif (!empty($info['mpeg']['audio']['LAME']['short_version'])) {
91
                $info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['short_version'], "\x00");
92
            }
93
        }
94
95
        $current_data_lame_version_string = (!empty($current_data_lame_version_string) ? $current_data_lame_version_string : @$info['audio']['encoder']);
96
        if (!empty($current_data_lame_version_string) && (substr($current_data_lame_version_string, 0, 6) == 'LAME3.') && !preg_match('[0-9\)]', substr($current_data_lame_version_string, -1))) {
97
            // a version number of LAME that does not end with a number like "LAME3.92"
98
            // or with a closing parenthesis like "LAME3.88 (alpha)"
99
            // or a version of LAME with the LAMEtag-not-filled-in-DLL-mode bug (3.90-3.92)
100
101
            // not sure what the actual last frame length will be, but will be less than or equal to 1441
102
            $possibly_longer_lame_version_frame_length = 1441;
103
104
            // Not sure what version of LAME this is - look in padding of last frame for longer version string
105
            $possible_lame_version_string_offset = $info['avdataend'] - $possibly_longer_lame_version_frame_length;
106
            fseek($fd, $possible_lame_version_string_offset);
107
            $possibly_longer_lame_version_data = fread($fd, $possibly_longer_lame_version_frame_length);
108
            switch (substr($current_data_lame_version_string, -1)) {
109
                case 'a':
110
                case 'b':
111
                    // "LAME3.94a" will have a longer version string of "LAME3.94 (alpha)" for example
112
                    // need to trim off "a" to match longer string
113
                    $current_data_lame_version_string = substr($current_data_lame_version_string, 0, -1);
114
                    break;
115
            }
116
            if (($possibly_longer_lame_version_string = strstr($possibly_longer_lame_version_data, $current_data_lame_version_string)) !== false) {
117
                if (substr($possibly_longer_lame_version_string, 0, strlen($current_data_lame_version_string)) == $current_data_lame_version_string) {
118
                    $possibly_longer_lame_version_new_string = substr($possibly_longer_lame_version_string, 0, strspn($possibly_longer_lame_version_string, 'LAME0123456789., (abcdefghijklmnopqrstuvwxyzJFSOND)')); //"LAME3.90.3"  "LAME3.87 (beta 1, Sep 27 2000)" "LAME3.88 (beta)"
119
                    if (strlen($possibly_longer_lame_version_new_string) > strlen(@$info['audio']['encoder'])) {
120
                        $info['audio']['encoder'] = $possibly_longer_lame_version_new_string;
121
                    }
122
                }
123
            }
124
        }
125
        if (!empty($info['audio']['encoder'])) {
126
            $info['audio']['encoder'] = rtrim($info['audio']['encoder'], "\x00 ");
127
        }
128
129
        switch (@$info['mpeg']['audio']['layer']) {
130
            case 1:
131
            case 2:
132
                $info['audio']['dataformat'] = 'mp'.$info['mpeg']['audio']['layer'];
133
                break;
134
        }
135
        if (@$info['fileformat'] == 'mp3') {
136
            switch ($info['audio']['dataformat']) {
137
                case 'mp1':
138
                case 'mp2':
139
                case 'mp3':
140
                    $info['fileformat'] = $info['audio']['dataformat'];
141
                    break;
142
143
                default:
144
                    $this->getid3->warning('Expecting [audio][dataformat] to be mp1/mp2/mp3 when fileformat == mp3, [audio][dataformat] actually "'.$info['audio']['dataformat'].'"');
145
                    break;
146
            }
147
        }
148
        
149
        $info['mime_type']         = 'audio/mpeg';
150
        $info['audio']['lossless'] = false;
151
152
        // Calculate playtime
153
        if (!isset($info['playtime_seconds']) && isset($info['audio']['bitrate']) && ($info['audio']['bitrate'] > 0)) {
154
            $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['audio']['bitrate'];
155
        }
156
157
        $info['audio']['encoder_options'] = getid3_mp3::GuessEncoderOptions($info);
158
159
        return true;
160
    }
161
162
163
164
    public static function GuessEncoderOptions(&$info) {
165
        // shortcuts
166
        if (!empty($info['mpeg']['audio'])) {
167
            $thisfile_mpeg_audio = &$info['mpeg']['audio'];
168
            if (!empty($thisfile_mpeg_audio['LAME'])) {
169
                $thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME'];
170
            }
171
        }
172
173
        $encoder_options = '';
174
        static $named_preset_bitrates = array (16, 24, 40, 56, 112, 128, 160, 192, 256);
175
176
        if ((@$thisfile_mpeg_audio['VBR_method'] == 'Fraunhofer') && !empty($thisfile_mpeg_audio['VBR_quality'])) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $thisfile_mpeg_audio does not seem to be defined for all execution paths leading up to this point.
Loading history...
177
178
            $encoder_options = 'VBR q'.$thisfile_mpeg_audio['VBR_quality'];
179
180
        } elseif (!empty($thisfile_mpeg_audio_lame['preset_used']) && (!in_array($thisfile_mpeg_audio_lame['preset_used_id'], $named_preset_bitrates))) {
181
182
            $encoder_options = $thisfile_mpeg_audio_lame['preset_used'];
183
184
        } elseif (!empty($thisfile_mpeg_audio_lame['vbr_quality'])) {
185
186
            static $known_encoder_values = array ();
187
            if (empty($known_encoder_values)) {
188
189
                //$known_encoder_values[abrbitrate_minbitrate][vbr_quality][raw_vbr_method][raw_noise_shaping][raw_stereo_mode][ath_type][lowpass_frequency] = 'preset name';
190
                $known_encoder_values[0xFF][58][1][1][3][2][20500] = '--alt-preset insane';        // 3.90,   3.90.1, 3.92
191
                $known_encoder_values[0xFF][58][1][1][3][2][20600] = '--alt-preset insane';        // 3.90.2, 3.90.3, 3.91
192
                $known_encoder_values[0xFF][57][1][1][3][4][20500] = '--alt-preset insane';        // 3.94,   3.95
193
                $known_encoder_values['**'][78][3][2][3][2][19500] = '--alt-preset extreme';       // 3.90,   3.90.1, 3.92
194
                $known_encoder_values['**'][78][3][2][3][2][19600] = '--alt-preset extreme';       // 3.90.2, 3.91
195
                $known_encoder_values['**'][78][3][1][3][2][19600] = '--alt-preset extreme';       // 3.90.3
196
                $known_encoder_values['**'][78][4][2][3][2][19500] = '--alt-preset fast extreme';  // 3.90,   3.90.1, 3.92
197
                $known_encoder_values['**'][78][4][2][3][2][19600] = '--alt-preset fast extreme';  // 3.90.2, 3.90.3, 3.91
198
                $known_encoder_values['**'][78][3][2][3][4][19000] = '--alt-preset standard';      // 3.90,   3.90.1, 3.90.2, 3.91, 3.92
199
                $known_encoder_values['**'][78][3][1][3][4][19000] = '--alt-preset standard';      // 3.90.3
200
                $known_encoder_values['**'][78][4][2][3][4][19000] = '--alt-preset fast standard'; // 3.90,   3.90.1, 3.90.2, 3.91, 3.92
201
                $known_encoder_values['**'][78][4][1][3][4][19000] = '--alt-preset fast standard'; // 3.90.3
202
                $known_encoder_values['**'][88][4][1][3][3][19500] = '--r3mix';                    // 3.90,   3.90.1, 3.92
203
                $known_encoder_values['**'][88][4][1][3][3][19600] = '--r3mix';                    // 3.90.2, 3.90.3, 3.91
204
                $known_encoder_values['**'][67][4][1][3][4][18000] = '--r3mix';                    // 3.94,   3.95
205
                $known_encoder_values['**'][68][3][2][3][4][18000] = '--alt-preset medium';        // 3.90.3
206
                $known_encoder_values['**'][68][4][2][3][4][18000] = '--alt-preset fast medium';   // 3.90.3
207
208
                $known_encoder_values[0xFF][99][1][1][1][2][0]     = '--preset studio';            // 3.90,   3.90.1, 3.90.2, 3.91, 3.92
209
                $known_encoder_values[0xFF][58][2][1][3][2][20600] = '--preset studio';            // 3.90.3, 3.93.1
210
                $known_encoder_values[0xFF][58][2][1][3][2][20500] = '--preset studio';            // 3.93
211
                $known_encoder_values[0xFF][57][2][1][3][4][20500] = '--preset studio';            // 3.94,   3.95
212
                $known_encoder_values[0xC0][88][1][1][1][2][0]     = '--preset cd';                // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
213
                $known_encoder_values[0xC0][58][2][2][3][2][19600] = '--preset cd';                // 3.90.3, 3.93.1
214
                $known_encoder_values[0xC0][58][2][2][3][2][19500] = '--preset cd';                // 3.93
215
                $known_encoder_values[0xC0][57][2][1][3][4][19500] = '--preset cd';                // 3.94,   3.95
216
                $known_encoder_values[0xA0][78][1][1][3][2][18000] = '--preset hifi';              // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
217
                $known_encoder_values[0xA0][58][2][2][3][2][18000] = '--preset hifi';              // 3.90.3, 3.93,   3.93.1
218
                $known_encoder_values[0xA0][57][2][1][3][4][18000] = '--preset hifi';              // 3.94,   3.95
219
                $known_encoder_values[0x80][67][1][1][3][2][18000] = '--preset tape';              // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
220
                $known_encoder_values[0x80][67][1][1][3][2][15000] = '--preset radio';             // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
221
                $known_encoder_values[0x70][67][1][1][3][2][15000] = '--preset fm';                // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
222
                $known_encoder_values[0x70][58][2][2][3][2][16000] = '--preset tape/radio/fm';     // 3.90.3, 3.93,   3.93.1
223
                $known_encoder_values[0x70][57][2][1][3][4][16000] = '--preset tape/radio/fm';     // 3.94,   3.95
224
                $known_encoder_values[0x38][58][2][2][0][2][10000] = '--preset voice';             // 3.90.3, 3.93,   3.93.1
225
                $known_encoder_values[0x38][57][2][1][0][4][15000] = '--preset voice';             // 3.94,   3.95
226
                $known_encoder_values[0x38][57][2][1][0][4][16000] = '--preset voice';             // 3.94a14
227
                $known_encoder_values[0x28][65][1][1][0][2][7500]  = '--preset mw-us';             // 3.90,   3.90.1, 3.92
228
                $known_encoder_values[0x28][65][1][1][0][2][7600]  = '--preset mw-us';             // 3.90.2, 3.91
229
                $known_encoder_values[0x28][58][2][2][0][2][7000]  = '--preset mw-us';             // 3.90.3, 3.93,   3.93.1
230
                $known_encoder_values[0x28][57][2][1][0][4][10500] = '--preset mw-us';             // 3.94,   3.95
231
                $known_encoder_values[0x28][57][2][1][0][4][11200] = '--preset mw-us';             // 3.94a14
232
                $known_encoder_values[0x28][57][2][1][0][4][8800]  = '--preset mw-us';             // 3.94a15
233
                $known_encoder_values[0x18][58][2][2][0][2][4000]  = '--preset phon+/lw/mw-eu/sw'; // 3.90.3, 3.93.1
234
                $known_encoder_values[0x18][58][2][2][0][2][3900]  = '--preset phon+/lw/mw-eu/sw'; // 3.93
235
                $known_encoder_values[0x18][57][2][1][0][4][5900]  = '--preset phon+/lw/mw-eu/sw'; // 3.94,   3.95
236
                $known_encoder_values[0x18][57][2][1][0][4][6200]  = '--preset phon+/lw/mw-eu/sw'; // 3.94a14
237
                $known_encoder_values[0x18][57][2][1][0][4][3200]  = '--preset phon+/lw/mw-eu/sw'; // 3.94a15
238
                $known_encoder_values[0x10][58][2][2][0][2][3800]  = '--preset phone';             // 3.90.3, 3.93.1
239
                $known_encoder_values[0x10][58][2][2][0][2][3700]  = '--preset phone';             // 3.93
240
                $known_encoder_values[0x10][57][2][1][0][4][5600]  = '--preset phone';             // 3.94,   3.95
241
            }
242
243
            if (isset($known_encoder_values[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) {
244
245
                $encoder_options = $known_encoder_values[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']];
246
247
            } elseif (isset($known_encoder_values['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) {
248
249
                $encoder_options = $known_encoder_values['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']];
250
251
            } elseif ($info['audio']['bitrate_mode'] == 'vbr') {
252
253
                // http://gabriel.mp3-tech.org/mp3infotag.html
254
                // int    Quality = (100 - 10 * gfp->VBR_q - gfp->quality)h
255
256
257
                $lame_v_value = 10 - ceil($thisfile_mpeg_audio_lame['vbr_quality'] / 10);
258
                $lame_q_value = 100 - $thisfile_mpeg_audio_lame['vbr_quality'] - ($lame_v_value * 10);
259
                $encoder_options = '-V'.$lame_v_value.' -q'.$lame_q_value;
260
261
            } elseif ($info['audio']['bitrate_mode'] == 'cbr') {
262
263
                $encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
264
265
            } else {
266
267
                $encoder_options = strtoupper($info['audio']['bitrate_mode']);
268
269
            }
270
271
        } elseif (!empty($thisfile_mpeg_audio_lame['bitrate_abr'])) {
272
273
            $encoder_options = 'ABR'.$thisfile_mpeg_audio_lame['bitrate_abr'];
274
275
        } elseif (!empty($info['audio']['bitrate'])) {
276
277
            if ($info['audio']['bitrate_mode'] == 'cbr') {
278
                $encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
279
            } else {
280
                $encoder_options = strtoupper($info['audio']['bitrate_mode']);
281
            }
282
283
        }
284
        if (!empty($thisfile_mpeg_audio_lame['bitrate_min'])) {
285
            $encoder_options .= ' -b'.$thisfile_mpeg_audio_lame['bitrate_min'];
286
        }
287
288
        if (@$thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev'] || @$thisfile_mpeg_audio_lame['encoding_flags']['nogap_next']) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $thisfile_mpeg_audio_lame does not seem to be defined for all execution paths leading up to this point.
Loading history...
289
            $encoder_options .= ' --nogap';
290
        }
291
292
        if (!empty($thisfile_mpeg_audio_lame['lowpass_frequency'])) {
293
            $exploded_options = explode(' ', $encoder_options, 4);
294
            if ($exploded_options[0] == '--r3mix') {
295
                $exploded_options[1] = 'r3mix';
296
            }
297
            switch ($exploded_options[0]) {
298
                case '--preset':
299
                case '--alt-preset':
300
                case '--r3mix':
301
                    if ($exploded_options[1] == 'fast') {
302
                        $exploded_options[1] .= ' '.$exploded_options[2];
303
                    }
304
                    switch ($exploded_options[1]) {
305
                        case 'portable':
306
                        case 'medium':
307
                        case 'standard':
308
                        case 'extreme':
309
                        case 'insane':
310
                        case 'fast portable':
311
                        case 'fast medium':
312
                        case 'fast standard':
313
                        case 'fast extreme':
314
                        case 'fast insane':
315
                        case 'r3mix':
316
                            static $expected_lowpass = array (
317
                                    'insane|20500'        => 20500,
318
                                    'insane|20600'        => 20600,  // 3.90.2, 3.90.3, 3.91
319
                                    'medium|18000'        => 18000,
320
                                    'fast medium|18000'   => 18000,
321
                                    'extreme|19500'       => 19500,  // 3.90,   3.90.1, 3.92, 3.95
322
                                    'extreme|19600'       => 19600,  // 3.90.2, 3.90.3, 3.91, 3.93.1
323
                                    'fast extreme|19500'  => 19500,  // 3.90,   3.90.1, 3.92, 3.95
324
                                    'fast extreme|19600'  => 19600,  // 3.90.2, 3.90.3, 3.91, 3.93.1
325
                                    'standard|19000'      => 19000,
326
                                    'fast standard|19000' => 19000,
327
                                    'r3mix|19500'         => 19500,  // 3.90,   3.90.1, 3.92
328
                                    'r3mix|19600'         => 19600,  // 3.90.2, 3.90.3, 3.91
329
                                    'r3mix|18000'         => 18000,  // 3.94,   3.95
330
                                );
331
                            if (!isset($expected_lowpass[$exploded_options[1].'|'.$thisfile_mpeg_audio_lame['lowpass_frequency']]) && ($thisfile_mpeg_audio_lame['lowpass_frequency'] < 22050) && (round($thisfile_mpeg_audio_lame['lowpass_frequency'] / 1000) < round($thisfile_mpeg_audio['sample_rate'] / 2000))) {
332
                                $encoder_options .= ' --lowpass '.$thisfile_mpeg_audio_lame['lowpass_frequency'];
333
                            }
334
                            break;
335
336
                        default:
337
                            break;
338
                    }
339
                    break;
340
            }
341
        }
342
343
        if (isset($thisfile_mpeg_audio_lame['raw']['source_sample_freq'])) {
344
            if (($thisfile_mpeg_audio['sample_rate'] == 44100) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 1)) {
345
                $encoder_options .= ' --resample 44100';
346
            } elseif (($thisfile_mpeg_audio['sample_rate'] == 48000) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 2)) {
347
                $encoder_options .= ' --resample 48000';
348
            } elseif ($thisfile_mpeg_audio['sample_rate'] < 44100) {
349
                switch ($thisfile_mpeg_audio_lame['raw']['source_sample_freq']) {
350
                    case 0: // <= 32000
351
                        // may or may not be same as source frequency - ignore
352
                        break;
353
                    case 1: // 44100
354
                    case 2: // 48000
355
                    case 3: // 48000+
356
                        $exploded_options = explode(' ', $encoder_options, 4);
357
                        switch ($exploded_options[0]) {
358
                            case '--preset':
359
                            case '--alt-preset':
360
                                switch ($exploded_options[1]) {
361
                                    case 'fast':
362
                                    case 'portable':
363
                                    case 'medium':
364
                                    case 'standard':
365
                                    case 'extreme':
366
                                    case 'insane':
367
                                        $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
368
                                        break;
369
370
                                    default:
371
                                        static $expected_resampled_rate = array (
372
                                                'phon+/lw/mw-eu/sw|16000' => 16000,
373
                                                'mw-us|24000'             => 24000, // 3.95
374
                                                'mw-us|32000'             => 32000, // 3.93
375
                                                'mw-us|16000'             => 16000, // 3.92
376
                                                'phone|16000'             => 16000,
377
                                                'phone|11025'             => 11025, // 3.94a15
378
                                                'radio|32000'             => 32000, // 3.94a15
379
                                                'fm/radio|32000'          => 32000, // 3.92
380
                                                'fm|32000'                => 32000, // 3.90
381
                                                'voice|32000'             => 32000);
382
                                        if (!isset($expected_resampled_rate[$exploded_options[1].'|'.$thisfile_mpeg_audio['sample_rate']])) {
383
                                            $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
384
                                        }
385
                                        break;
386
                                }
387
                                break;
388
389
                            case '--r3mix':
390
                            default:
391
                                $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
392
                                break;
393
                        }
394
                        break;
395
                }
396
            }
397
        }
398
        if (empty($encoder_options) && !empty($info['audio']['bitrate']) && !empty($info['audio']['bitrate_mode'])) {
399
            //$encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
400
            $encoder_options = strtoupper($info['audio']['bitrate_mode']);
401
        }
402
403
        return $encoder_options;
404
    }
405
406
407
    
408
    public function decodeMPEGaudioHeader($fd, $offset, &$info, $recursive_search=true, $scan_as_cbr=false, $fast_mpeg_header_scan=false) {
409
410
        static $mpeg_audio_version_lookup;
411
        static $mpeg_audio_layer_lookup;
412
        static $mpeg_audio_bitrate_lookup;
413
        static $mpeg_audio_frequency_lookup;
414
        static $mpeg_audio_channel_mode_lookup;
415
        static $mpeg_audio_mode_extension_lookup;
416
        static $mpeg_audio_emphasis_lookup;
417
        if (empty($mpeg_audio_version_lookup)) {
418
            $mpeg_audio_version_lookup        = getid3_mp3::MPEGaudioVersionarray();
419
            $mpeg_audio_layer_lookup          = getid3_mp3::MPEGaudioLayerarray();
420
            $mpeg_audio_bitrate_lookup        = getid3_mp3::MPEGaudioBitratearray();
421
            $mpeg_audio_frequency_lookup      = getid3_mp3::MPEGaudioFrequencyarray();
422
            $mpeg_audio_channel_mode_lookup   = getid3_mp3::MPEGaudioChannelModearray();
423
            $mpeg_audio_mode_extension_lookup = getid3_mp3::MPEGaudioModeExtensionarray();
424
            $mpeg_audio_emphasis_lookup       = getid3_mp3::MPEGaudioEmphasisarray();
425
        }
426
427
        if ($offset >= $info['avdataend']) {
428
429
            // non-fatal error: 'end of file encounter looking for MPEG synch'
430
            return;
431
            
432
        }
433
        fseek($fd, $offset, SEEK_SET);
434
        $header_string = fread($fd, 226); // LAME header at offset 36 + 190 bytes of Xing/LAME data
435
436
        // MP3 audio frame structure:
437
        // $aa $aa $aa $aa [$bb $bb] $cc...
438
        // where $aa..$aa is the four-byte mpeg-audio header (below)
439
        // $bb $bb is the optional 2-byte CRC
440
        // and $cc... is the audio data
441
442
        $head4 = substr($header_string, 0, 4);
443
444
        if (isset($mpeg_audio_header_decode_cache[$head4])) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $mpeg_audio_header_decode_cache seems to never exist and therefore isset should always be false.
Loading history...
445
            $mpeg_header_raw_array= $mpeg_audio_header_decode_cache[$head4];
446
        } else {
447
            $mpeg_header_raw_array = getid3_mp3::MPEGaudioHeaderDecode($head4);
448
            $mpeg_audio_header_decode_cache[$head4] = $mpeg_header_raw_array;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$mpeg_audio_header_decode_cache was never initialized. Although not strictly required by PHP, it is generally a good practice to add $mpeg_audio_header_decode_cache = array(); before regardless.
Loading history...
449
        }
450
451
        // Not in cache
452
        if (!isset($mpeg_audio_header_valid_cache[$head4])) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $mpeg_audio_header_valid_cache seems to never exist and therefore isset should always be false.
Loading history...
453
            $mpeg_audio_header_valid_cache[$head4] = getid3_mp3::MPEGaudioHeaderValid($mpeg_header_raw_array, false, false);
0 ignored issues
show
Comprehensibility Best Practice introduced by
$mpeg_audio_header_valid_cache was never initialized. Although not strictly required by PHP, it is generally a good practice to add $mpeg_audio_header_valid_cache = array(); before regardless.
Loading history...
454
        }
455
456
        // shortcut
457
        if (!isset($info['mpeg']['audio'])) {
458
            $info['mpeg']['audio'] = array ();
459
        }
460
        $thisfile_mpeg_audio = &$info['mpeg']['audio'];
461
462
463
        if ($mpeg_audio_header_valid_cache[$head4]) {
464
            $thisfile_mpeg_audio['raw'] = $mpeg_header_raw_array;
465
        } else {
466
            
467
            // non-fatal error: Invalid MPEG audio header at offset $offset
468
            return;
469
        }
470
471
        if (!$fast_mpeg_header_scan) {
472
473
            $thisfile_mpeg_audio['version']       = $mpeg_audio_version_lookup[$thisfile_mpeg_audio['raw']['version']];
474
            $thisfile_mpeg_audio['layer']         = $mpeg_audio_layer_lookup[$thisfile_mpeg_audio['raw']['layer']];
475
476
            $thisfile_mpeg_audio['channelmode']   = $mpeg_audio_channel_mode_lookup[$thisfile_mpeg_audio['raw']['channelmode']];
477
            $thisfile_mpeg_audio['channels']      = (($thisfile_mpeg_audio['channelmode'] == 'mono') ? 1 : 2);
478
            $thisfile_mpeg_audio['sample_rate']   = $mpeg_audio_frequency_lookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['raw']['sample_rate']];
479
            $thisfile_mpeg_audio['protection']    = !$thisfile_mpeg_audio['raw']['protection'];
480
            $thisfile_mpeg_audio['private']       = (bool) $thisfile_mpeg_audio['raw']['private'];
481
            $thisfile_mpeg_audio['modeextension'] = $mpeg_audio_mode_extension_lookup[$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['modeextension']];
482
            $thisfile_mpeg_audio['copyright']     = (bool) $thisfile_mpeg_audio['raw']['copyright'];
483
            $thisfile_mpeg_audio['original']      = (bool) $thisfile_mpeg_audio['raw']['original'];
484
            $thisfile_mpeg_audio['emphasis']      = $mpeg_audio_emphasis_lookup[$thisfile_mpeg_audio['raw']['emphasis']];
485
486
            $info['audio']['channels']    = $thisfile_mpeg_audio['channels'];
487
            $info['audio']['sample_rate'] = $thisfile_mpeg_audio['sample_rate'];
488
489
            if ($thisfile_mpeg_audio['protection']) {
490
                $thisfile_mpeg_audio['crc'] = getid3_lib::BigEndian2Int(substr($header_string, 4, 2));
491
            }
492
493
        }
494
495
        if ($thisfile_mpeg_audio['raw']['bitrate'] == 15) {
496
            // http://www.hydrogenaudio.org/?act=ST&f=16&t=9682&st=0
497
            $this->getid3->warning('Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1');
498
            $thisfile_mpeg_audio['raw']['bitrate'] = 0;
499
        }
500
        $thisfile_mpeg_audio['padding'] = (bool) $thisfile_mpeg_audio['raw']['padding'];
501
        $thisfile_mpeg_audio['bitrate'] = $mpeg_audio_bitrate_lookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['bitrate']];
502
503
        if (($thisfile_mpeg_audio['bitrate'] == 'free') && ($offset == $info['avdataoffset'])) {
504
            // only skip multiple frame check if free-format bitstream found at beginning of file
505
            // otherwise is quite possibly simply corrupted data
506
            $recursive_search = false;
507
        }
508
509
        // For Layer 2 there are some combinations of bitrate and mode which are not allowed.
510
        if (!$fast_mpeg_header_scan && ($thisfile_mpeg_audio['layer'] == '2')) {
511
512
            $info['audio']['dataformat'] = 'mp2';
513
            switch ($thisfile_mpeg_audio['channelmode']) {
514
515
                case 'mono':
516
                    if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] <= 192000)) {
517
                        // these are ok
518
                    } else {
519
                        
520
                        // non-fatal error: bitrate not allowed in Layer 2/mono
521
                        return;
522
                    }
523
                    break;
524
525
                case 'stereo':
526
                case 'joint stereo':
527
                case 'dual channel':
528
                    if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] == 64000) || ($thisfile_mpeg_audio['bitrate'] >= 96000)) {
529
                        // these are ok
530
                    } else {
531
                        
532
                        // non-fatal error: bitrate not allowed in Layer 2/stereo/joint stereo/dual channel
533
                        return;
534
                    }
535
                    break;
536
537
            }
538
539
        }
540
541
542
        if ($info['audio']['sample_rate'] > 0) {
543
            $thisfile_mpeg_audio['framelength'] = getid3_mp3::MPEGaudioFrameLength($thisfile_mpeg_audio['bitrate'], $thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['layer'], (int) $thisfile_mpeg_audio['padding'], $info['audio']['sample_rate']);
544
        }
545
546
        $next_frame_test_offset = $offset + 1;
547
        if ($thisfile_mpeg_audio['bitrate'] != 'free') {
548
549
            $info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
550
551
            if (isset($thisfile_mpeg_audio['framelength'])) {
552
                $next_frame_test_offset = $offset + $thisfile_mpeg_audio['framelength'];
553
            } else {
554
555
                // non-fatal error: Frame at offset('.$offset.') is has an invalid frame length.
556
                return;
557
            }
558
559
        }
560
561
        $expected_number_of_audio_bytes = 0;
562
563
        ////////////////////////////////////////////////////////////////////////////////////
564
        // Variable-bitrate headers
565
566
        if (substr($header_string, 4 + 32, 4) == 'VBRI') {
567
            // Fraunhofer VBR header is hardcoded 'VBRI' at offset 0x24 (36)
568
            // specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html
569
570
            $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
571
            $thisfile_mpeg_audio['VBR_method']   = 'Fraunhofer';
572
            $info['audio']['codec']                = 'Fraunhofer';
573
574
            $side_info_data = substr($header_string, 4 + 2, 32);
0 ignored issues
show
Unused Code introduced by
The assignment to $side_info_data is dead and can be removed.
Loading history...
575
576
            $fraunhofer_vbr_offset = 36;
577
578
            $thisfile_mpeg_audio['VBR_encoder_version']     = getid3_lib::BigEndian2Int(substr($header_string, $fraunhofer_vbr_offset +  4, 2)); // VbriVersion
579
            $thisfile_mpeg_audio['VBR_encoder_delay']       = getid3_lib::BigEndian2Int(substr($header_string, $fraunhofer_vbr_offset +  6, 2)); // VbriDelay
580
            $thisfile_mpeg_audio['VBR_quality']             = getid3_lib::BigEndian2Int(substr($header_string, $fraunhofer_vbr_offset +  8, 2)); // VbriQuality
581
            $thisfile_mpeg_audio['VBR_bytes']               = getid3_lib::BigEndian2Int(substr($header_string, $fraunhofer_vbr_offset + 10, 4)); // VbriStreamBytes
582
            $thisfile_mpeg_audio['VBR_frames']              = getid3_lib::BigEndian2Int(substr($header_string, $fraunhofer_vbr_offset + 14, 4)); // VbriStreamFrames
583
            $thisfile_mpeg_audio['VBR_seek_offsets']        = getid3_lib::BigEndian2Int(substr($header_string, $fraunhofer_vbr_offset + 18, 2)); // VbriTableSize
584
            $thisfile_mpeg_audio['VBR_seek_scale']          = getid3_lib::BigEndian2Int(substr($header_string, $fraunhofer_vbr_offset + 20, 2)); // VbriTableScale
585
            $thisfile_mpeg_audio['VBR_entry_bytes']         = getid3_lib::BigEndian2Int(substr($header_string, $fraunhofer_vbr_offset + 22, 2)); // VbriEntryBytes
586
            $thisfile_mpeg_audio['VBR_entry_frames']        = getid3_lib::BigEndian2Int(substr($header_string, $fraunhofer_vbr_offset + 24, 2)); // VbriEntryFrames
587
588
            $expected_number_of_audio_bytes = $thisfile_mpeg_audio['VBR_bytes'];
589
590
            $previous_byte_offset = $offset;
591
            for ($i = 0; $i < $thisfile_mpeg_audio['VBR_seek_offsets']; $i++) {
592
                $fraunhofer_offset_n = getid3_lib::BigEndian2Int(substr($header_string, $fraunhofer_vbr_offset, $thisfile_mpeg_audio['VBR_entry_bytes']));
593
                $fraunhofer_vbr_offset += $thisfile_mpeg_audio['VBR_entry_bytes'];
594
                $thisfile_mpeg_audio['VBR_offsets_relative'][$i] = ($fraunhofer_offset_n * $thisfile_mpeg_audio['VBR_seek_scale']);
595
                $thisfile_mpeg_audio['VBR_offsets_absolute'][$i] = ($fraunhofer_offset_n * $thisfile_mpeg_audio['VBR_seek_scale']) + $previous_byte_offset;
596
                $previous_byte_offset += $fraunhofer_offset_n;
597
            }
598
599
600
        } else {
601
602
            // Xing VBR header is hardcoded 'Xing' at a offset 0x0D (13), 0x15 (21) or 0x24 (36)
603
            // depending on MPEG layer and number of channels
604
605
            $vbr_id_offset = getid3_mp3::XingVBRidOffset($thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['channelmode']);
606
            $side_info_data = substr($header_string, 4 + 2, $vbr_id_offset - 4);
607
608
            if ((substr($header_string, $vbr_id_offset, strlen('Xing')) == 'Xing') || (substr($header_string, $vbr_id_offset, strlen('Info')) == 'Info')) {
609
                // 'Xing' is traditional Xing VBR frame
610
                // 'Info' is LAME-encoded CBR (This was done to avoid CBR files to be recognized as traditional Xing VBR files by some decoders.)
611
                // 'Info' *can* legally be used to specify a VBR file as well, however.
612
613
                // http://www.multiweb.cz/twoinches/MP3inside.htm
614
                //00..03 = "Xing" or "Info"
615
                //04..07 = Flags:
616
                //  0x01  Frames Flag     set if value for number of frames in file is stored
617
                //  0x02  Bytes Flag      set if value for filesize in bytes is stored
618
                //  0x04  TOC Flag        set if values for TOC are stored
619
                //  0x08  VBR Scale Flag  set if values for VBR scale is stored
620
                //08..11  Frames: Number of frames in file (including the first Xing/Info one)
621
                //12..15  Bytes:  File length in Bytes
622
                //16..115  TOC (Table of Contents):
623
                //  Contains of 100 indexes (one Byte length) for easier lookup in file. Approximately solves problem with moving inside file.
624
                //  Each Byte has a value according this formula:
625
                //  (TOC[i] / 256) * fileLenInBytes
626
                //  So if song lasts eg. 240 sec. and you want to jump to 60. sec. (and file is 5 000 000 Bytes length) you can use:
627
                //  TOC[(60/240)*100] = TOC[25]
628
                //  and corresponding Byte in file is then approximately at:
629
                //  (TOC[25]/256) * 5000000
630
                //116..119  VBR Scale
631
632
633
                // should be safe to leave this at 'vbr' and let it be overriden to 'cbr' if a CBR preset/mode is used by LAME
634
                $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
635
                $thisfile_mpeg_audio['VBR_method']   = 'Xing';
636
637
                $thisfile_mpeg_audio['xing_flags_raw'] = getid3_lib::BigEndian2Int(substr($header_string, $vbr_id_offset + 4, 4));
638
639
                $thisfile_mpeg_audio['xing_flags']['frames']    = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000001);
640
                $thisfile_mpeg_audio['xing_flags']['bytes']     = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000002);
641
                $thisfile_mpeg_audio['xing_flags']['toc']       = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000004);
642
                $thisfile_mpeg_audio['xing_flags']['vbr_scale'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000008);
643
644
                if ($thisfile_mpeg_audio['xing_flags']['frames']) {
645
                    $thisfile_mpeg_audio['VBR_frames'] = getid3_lib::BigEndian2Int(substr($header_string, $vbr_id_offset +  8, 4));
646
                }
647
                if ($thisfile_mpeg_audio['xing_flags']['bytes']) {
648
                    $thisfile_mpeg_audio['VBR_bytes']  = getid3_lib::BigEndian2Int(substr($header_string, $vbr_id_offset + 12, 4));
649
                }
650
651
                if (!empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
652
653
                    $frame_lengthfloat = $thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames'];
654
655
                    if ($thisfile_mpeg_audio['layer'] == '1') {
656
                        // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
657
                        $info['audio']['bitrate'] = ($frame_lengthfloat / 4) * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 12;
658
                    } else {
659
                        // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
660
                        $info['audio']['bitrate'] = $frame_lengthfloat * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 144;
661
                    }
662
                    $thisfile_mpeg_audio['framelength'] = floor($frame_lengthfloat);
663
                }
664
665
                if ($thisfile_mpeg_audio['xing_flags']['toc']) {
666
                    $lame_toc_data = substr($header_string, $vbr_id_offset + 16, 100);
667
                    for ($i = 0; $i < 100; $i++) {
668
                        $thisfile_mpeg_audio['toc'][$i] = ord($lame_toc_data{$i});
669
                    }
670
                }
671
                if ($thisfile_mpeg_audio['xing_flags']['vbr_scale']) {
672
                    $thisfile_mpeg_audio['VBR_scale'] = getid3_lib::BigEndian2Int(substr($header_string, $vbr_id_offset + 116, 4));
673
                }
674
675
676
                // http://gabriel.mp3-tech.org/mp3infotag.html
677
                if (substr($header_string, $vbr_id_offset + 120, 4) == 'LAME') {
678
679
                    // shortcut
680
                    $thisfile_mpeg_audio['LAME'] = array ();
681
                    $thisfile_mpeg_audio_lame    = &$thisfile_mpeg_audio['LAME'];
682
683
684
                    $thisfile_mpeg_audio_lame['long_version']  = substr($header_string, $vbr_id_offset + 120, 20);
685
                    $thisfile_mpeg_audio_lame['short_version'] = substr($thisfile_mpeg_audio_lame['long_version'], 0, 9);
686
687
                    if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.90') {
688
689
                        // extra 11 chars are not part of version string when LAMEtag present
690
                        unset($thisfile_mpeg_audio_lame['long_version']);
691
692
                        // It the LAME tag was only introduced in LAME v3.90
693
                        // http://www.hydrogenaudio.org/?act=ST&f=15&t=9933
694
695
                        // Offsets of various bytes in http://gabriel.mp3-tech.org/mp3infotag.html
696
                        // are assuming a 'Xing' identifier offset of 0x24, which is the case for
697
                        // MPEG-1 non-mono, but not for other combinations
698
                        $lame_tag_offset_contant = $vbr_id_offset - 0x24;
699
700
                        // shortcuts
701
                        $thisfile_mpeg_audio_lame['RGAD']    = array ('track'=>array(), 'album'=>array());
702
                        $thisfile_mpeg_audio_lame_rgad       = &$thisfile_mpeg_audio_lame['RGAD'];
703
                        $thisfile_mpeg_audio_lame_rgad_track = &$thisfile_mpeg_audio_lame_rgad['track'];
704
                        $thisfile_mpeg_audio_lame_rgad_album = &$thisfile_mpeg_audio_lame_rgad['album'];
705
                        $thisfile_mpeg_audio_lame['raw']     = array ();
706
                        $thisfile_mpeg_audio_lame_raw        = &$thisfile_mpeg_audio_lame['raw'];
707
708
                        // byte $9B  VBR Quality
709
                        // This field is there to indicate a quality level, although the scale was not precised in the original Xing specifications.
710
                        // Actually overwrites original Xing bytes
711
                        unset($thisfile_mpeg_audio['VBR_scale']);
712
                        $thisfile_mpeg_audio_lame['vbr_quality'] = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0x9B, 1));
713
714
                        // bytes $9C-$A4  Encoder short VersionString
715
                        $thisfile_mpeg_audio_lame['short_version'] = substr($header_string, $lame_tag_offset_contant + 0x9C, 9);
716
717
                        // byte $A5  Info Tag revision + VBR method
718
                        $lame_tagRevisionVBRmethod = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xA5, 1));
719
720
                        $thisfile_mpeg_audio_lame['tag_revision']      = ($lame_tagRevisionVBRmethod & 0xF0) >> 4;
721
                        $thisfile_mpeg_audio_lame_raw['vbr_method'] =  $lame_tagRevisionVBRmethod & 0x0F;
722
                        $thisfile_mpeg_audio_lame['vbr_method']        = getid3_mp3::LAMEvbrMethodLookup($thisfile_mpeg_audio_lame_raw['vbr_method']);
723
                        $thisfile_mpeg_audio['bitrate_mode']           = substr($thisfile_mpeg_audio_lame['vbr_method'], 0, 3); // usually either 'cbr' or 'vbr', but truncates 'vbr-old / vbr-rh' to 'vbr'
724
725
                        // byte $A6  Lowpass filter value
726
                        $thisfile_mpeg_audio_lame['lowpass_frequency'] = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xA6, 1)) * 100;
727
728
                        // bytes $A7-$AE  Replay Gain
729
                        // http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html
730
                        // bytes $A7-$AA : 32 bit floating point "Peak signal amplitude"
731
                        if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.94b') {
732
                            // LAME 3.94a16 and later - 9.23 fixed point
733
                            // ie 0x0059E2EE / (2^23) = 5890798 / 8388608 = 0.7022378444671630859375
734
                            $thisfile_mpeg_audio_lame_rgad['peak_amplitude'] = (float) ((getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xA7, 4))) / 8388608);
735
                        } else {
736
                            // LAME 3.94a15 and earlier - 32-bit floating point
737
                            // Actually 3.94a16 will fall in here too and be WRONG, but is hard to detect 3.94a16 vs 3.94a15
738
                            $thisfile_mpeg_audio_lame_rgad['peak_amplitude'] = getid3_lib::LittleEndian2Float(substr($header_string, $lame_tag_offset_contant + 0xA7, 4));
739
                        }
740
                        if ($thisfile_mpeg_audio_lame_rgad['peak_amplitude'] == 0) {
741
                            unset($thisfile_mpeg_audio_lame_rgad['peak_amplitude']);
742
                        } else {
743
                            $thisfile_mpeg_audio_lame_rgad['peak_db'] = 20 * log10($thisfile_mpeg_audio_lame_rgad['peak_amplitude']);
744
                        }
745
746
                        $thisfile_mpeg_audio_lame_raw['RGAD_track']      =   getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xAB, 2));
747
                        $thisfile_mpeg_audio_lame_raw['RGAD_album']      =   getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xAD, 2));
748
749
750
                        if ($thisfile_mpeg_audio_lame_raw['RGAD_track'] != 0) {
751
752
                            $thisfile_mpeg_audio_lame_rgad_track['raw']['name']        = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0xE000) >> 13;
753
                            $thisfile_mpeg_audio_lame_rgad_track['raw']['originator']  = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x1C00) >> 10;
754
                            $thisfile_mpeg_audio_lame_rgad_track['raw']['sign_bit']    = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x0200) >> 9;
755
                            $thisfile_mpeg_audio_lame_rgad_track['raw']['gain_adjust'] =  $thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x01FF;
756
                            $thisfile_mpeg_audio_lame_rgad_track['name']       = getid3_lib_replaygain::NameLookup($thisfile_mpeg_audio_lame_rgad_track['raw']['name']);
757
                            $thisfile_mpeg_audio_lame_rgad_track['originator'] = getid3_lib_replaygain::OriginatorLookup($thisfile_mpeg_audio_lame_rgad_track['raw']['originator']);
758
                            $thisfile_mpeg_audio_lame_rgad_track['gain_db']    = getid3_lib_replaygain::AdjustmentLookup($thisfile_mpeg_audio_lame_rgad_track['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_rgad_track['raw']['sign_bit']);
759
760
                            if (!empty($thisfile_mpeg_audio_lame_rgad['peak_amplitude'])) {
761
                                $info['replay_gain']['track']['peak']   = $thisfile_mpeg_audio_lame_rgad['peak_amplitude'];
762
                            }
763
                            $info['replay_gain']['track']['originator'] = $thisfile_mpeg_audio_lame_rgad_track['originator'];
764
                            $info['replay_gain']['track']['adjustment'] = $thisfile_mpeg_audio_lame_rgad_track['gain_db'];
765
                        } else {
766
                            unset($thisfile_mpeg_audio_lame_rgad['track']);
767
                        }
768
                        if ($thisfile_mpeg_audio_lame_raw['RGAD_album'] != 0) {
769
770
                            $thisfile_mpeg_audio_lame_rgad_album['raw']['name']        = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0xE000) >> 13;
771
                            $thisfile_mpeg_audio_lame_rgad_album['raw']['originator']  = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x1C00) >> 10;
772
                            $thisfile_mpeg_audio_lame_rgad_album['raw']['sign_bit']    = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x0200) >> 9;
773
                            $thisfile_mpeg_audio_lame_rgad_album['raw']['gain_adjust'] =  $thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x01FF;
774
                            $thisfile_mpeg_audio_lame_rgad_album['name']       = getid3_lib_replaygain::NameLookup($thisfile_mpeg_audio_lame_rgad_album['raw']['name']);
775
                            $thisfile_mpeg_audio_lame_rgad_album['originator'] = getid3_lib_replaygain::OriginatorLookup($thisfile_mpeg_audio_lame_rgad_album['raw']['originator']);
776
                            $thisfile_mpeg_audio_lame_rgad_album['gain_db']    = getid3_lib_replaygain::AdjustmentLookup($thisfile_mpeg_audio_lame_rgad_album['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_rgad_album['raw']['sign_bit']);
777
778
                            if (!empty($thisfile_mpeg_audio_lame_rgad['peak_amplitude'])) {
779
                                $info['replay_gain']['album']['peak']   = $thisfile_mpeg_audio_lame_rgad['peak_amplitude'];
780
                            }
781
                            $info['replay_gain']['album']['originator'] = $thisfile_mpeg_audio_lame_rgad_album['originator'];
782
                            $info['replay_gain']['album']['adjustment'] = $thisfile_mpeg_audio_lame_rgad_album['gain_db'];
783
                        } else {
784
                            unset($thisfile_mpeg_audio_lame_rgad['album']);
785
                        }
786
                        if (empty($thisfile_mpeg_audio_lame_rgad)) {
787
                            unset($thisfile_mpeg_audio_lame['RGAD']);
788
                        }
789
790
791
                        // byte $AF  Encoding flags + ATH Type
792
                        $encoding_flags_ath_type = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xAF, 1));
793
                        $thisfile_mpeg_audio_lame['encoding_flags']['nspsytune']   = (bool) ($encoding_flags_ath_type & 0x10);
794
                        $thisfile_mpeg_audio_lame['encoding_flags']['nssafejoint'] = (bool) ($encoding_flags_ath_type & 0x20);
795
                        $thisfile_mpeg_audio_lame['encoding_flags']['nogap_next']  = (bool) ($encoding_flags_ath_type & 0x40);
796
                        $thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev']  = (bool) ($encoding_flags_ath_type & 0x80);
797
                        $thisfile_mpeg_audio_lame['ath_type']                      =         $encoding_flags_ath_type & 0x0F;
798
799
                        // byte $B0  if ABR {specified bitrate} else {minimal bitrate}
800
                        $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xB0, 1));
801
                        if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 2) { // Average BitRate (ABR)
802
                            $thisfile_mpeg_audio_lame['bitrate_abr'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
803
                        } elseif ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) { // Constant BitRate (CBR)
804
                            // ignore
805
                        } elseif ($thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] > 0) { // Variable BitRate (VBR) - minimum bitrate
806
                            $thisfile_mpeg_audio_lame['bitrate_min'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
807
                        }
808
809
                        // bytes $B1-$B3  Encoder delays
810
                        $encoder_delays = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xB1, 3));
811
                        $thisfile_mpeg_audio_lame['encoder_delay'] = ($encoder_delays & 0xFFF000) >> 12;
812
                        $thisfile_mpeg_audio_lame['end_padding']   =  $encoder_delays & 0x000FFF;
813
814
                        // byte $B4  Misc
815
                        $misc_byte = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xB4, 1));
816
                        $thisfile_mpeg_audio_lame_raw['noise_shaping']       = ($misc_byte & 0x03);
817
                        $thisfile_mpeg_audio_lame_raw['stereo_mode']         = ($misc_byte & 0x1C) >> 2;
818
                        $thisfile_mpeg_audio_lame_raw['not_optimal_quality'] = ($misc_byte & 0x20) >> 5;
819
                        $thisfile_mpeg_audio_lame_raw['source_sample_freq']  = ($misc_byte & 0xC0) >> 6;
820
                        $thisfile_mpeg_audio_lame['noise_shaping']           = $thisfile_mpeg_audio_lame_raw['noise_shaping'];
821
                        $thisfile_mpeg_audio_lame['stereo_mode']             = getid3_mp3::LAMEmiscStereoModeLookup($thisfile_mpeg_audio_lame_raw['stereo_mode']);
822
                        $thisfile_mpeg_audio_lame['not_optimal_quality']     = (bool) $thisfile_mpeg_audio_lame_raw['not_optimal_quality'];
823
                        $thisfile_mpeg_audio_lame['source_sample_freq']      = getid3_mp3::LAMEmiscSourceSampleFrequencyLookup($thisfile_mpeg_audio_lame_raw['source_sample_freq']);
824
825
                        // byte $B5  MP3 Gain
826
                        $thisfile_mpeg_audio_lame_raw['mp3_gain']   = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xB5, 1), false, true);
827
                        $thisfile_mpeg_audio_lame['mp3_gain_db']     = (20 * log10(2) / 4) * $thisfile_mpeg_audio_lame_raw['mp3_gain'];
828
                        $thisfile_mpeg_audio_lame['mp3_gain_factor'] = pow(2, ($thisfile_mpeg_audio_lame['mp3_gain_db'] / 6));
829
830
                        // bytes $B6-$B7  Preset and surround info
831
                        $PresetSurroundBytes = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xB6, 2));
832
                        // Reserved                                                    = ($PresetSurroundBytes & 0xC000);
833
                        $thisfile_mpeg_audio_lame_raw['surround_info'] = ($PresetSurroundBytes & 0x3800);
834
                        $thisfile_mpeg_audio_lame['surround_info']     = getid3_mp3::LAMEsurroundInfoLookup($thisfile_mpeg_audio_lame_raw['surround_info']);
835
                        $thisfile_mpeg_audio_lame['preset_used_id']    = ($PresetSurroundBytes & 0x07FF);
836
                        $thisfile_mpeg_audio_lame['preset_used']       = getid3_mp3::LAMEpresetUsedLookup($thisfile_mpeg_audio_lame);
837
                        if (!empty($thisfile_mpeg_audio_lame['preset_used_id']) && empty($thisfile_mpeg_audio_lame['preset_used'])) {
838
                            $this->getid3->warning('Unknown LAME preset used ('.$thisfile_mpeg_audio_lame['preset_used_id'].') - please report to [email protected]');
839
                        }
840
                        if (($thisfile_mpeg_audio_lame['short_version'] == 'LAME3.90.') && !empty($thisfile_mpeg_audio_lame['preset_used_id'])) {
841
                            // this may change if 3.90.4 ever comes out
842
                            $thisfile_mpeg_audio_lame['short_version'] = 'LAME3.90.3';
843
                        }
844
845
                        // bytes $B8-$BB  MusicLength
846
                        $thisfile_mpeg_audio_lame['audio_bytes'] = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xB8, 4));
847
                        $expected_number_of_audio_bytes = (($thisfile_mpeg_audio_lame['audio_bytes'] > 0) ? $thisfile_mpeg_audio_lame['audio_bytes'] : $thisfile_mpeg_audio['VBR_bytes']);
848
849
                        // bytes $BC-$BD  MusicCRC
850
                        $thisfile_mpeg_audio_lame['music_crc']    = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xBC, 2));
851
852
                        // bytes $BE-$BF  CRC-16 of Info Tag
853
                        $thisfile_mpeg_audio_lame['lame_tag_crc'] = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xBE, 2));
854
855
856
                        // LAME CBR
857
                        if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) {
858
859
                            $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
860
                            $thisfile_mpeg_audio['bitrate'] = getid3_mp3::ClosestStandardMP3Bitrate($thisfile_mpeg_audio['bitrate']);
861
                            $info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
862
863
                        }
864
865
                    }
866
                }
867
868
            } else {
869
870
                // not Fraunhofer or Xing VBR methods, most likely CBR (but could be VBR with no header)
871
                $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
872
                if ($recursive_search) {
873
                    $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
874
                    if (getid3_mp3::RecursiveFrameScanning($fd, $info, $offset, $next_frame_test_offset, true)) {
875
                        $recursive_search = false;
876
                        $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
877
                    }
878
                    if ($thisfile_mpeg_audio['bitrate_mode'] == 'vbr') {
879
                        $this->getid3->warning('VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.');
880
                    }
881
                }
882
883
            }
884
885
        }
886
887
        if (($expected_number_of_audio_bytes > 0) && ($expected_number_of_audio_bytes != ($info['avdataend'] - $info['avdataoffset']))) {
888
            if ($expected_number_of_audio_bytes > ($info['avdataend'] - $info['avdataoffset'])) {
889
                if (($expected_number_of_audio_bytes - ($info['avdataend'] - $info['avdataoffset'])) == 1) {
890
                    $this->getid3->warning('Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)');
891
                } else {
892
                    $this->getid3->warning('Probable truncated file: expecting '.$expected_number_of_audio_bytes.' bytes of audio data, only found '.($info['avdataend'] - $info['avdataoffset']).' (short by '.($expected_number_of_audio_bytes - ($info['avdataend'] - $info['avdataoffset'])).' bytes)');
893
                }
894
            } else {
895
                if ((($info['avdataend'] - $info['avdataoffset']) - $expected_number_of_audio_bytes) == 1) {
896
                        $info['avdataend']--;
897
                } else {
898
                    $this->getid3->warning('Too much data in file: expecting '.$expected_number_of_audio_bytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $expected_number_of_audio_bytes).' bytes too many)');
899
                }
900
            }
901
        }
902
903
        if (($thisfile_mpeg_audio['bitrate'] == 'free') && empty($info['audio']['bitrate'])) {
904
            if (($offset == $info['avdataoffset']) && empty($thisfile_mpeg_audio['VBR_frames'])) {
905
                $frame_byte_length = getid3_mp3::FreeFormatFrameLength($fd, $offset, $info, true);
906
                if ($frame_byte_length > 0) {
907
                    $thisfile_mpeg_audio['framelength'] = $frame_byte_length;
908
                    if ($thisfile_mpeg_audio['layer'] == '1') {
909
                        // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
910
                        $info['audio']['bitrate'] = ((($frame_byte_length / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
911
                    } else {
912
                        // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
913
                        $info['audio']['bitrate'] = (($frame_byte_length - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
914
                    }
915
                } else {
916
                    
917
                    // non-fatal error: Error calculating frame length of free-format MP3 without Xing/LAME header.
918
                    return;
919
                }
920
            }
921
        }
922
923
        if (@$thisfile_mpeg_audio['VBR_frames']) {
924
            switch ($thisfile_mpeg_audio['bitrate_mode']) {
925
                case 'vbr':
926
                case 'abr':
927
                    if (($thisfile_mpeg_audio['version'] == '1') && ($thisfile_mpeg_audio['layer'] == 1)) {
928
                        $thisfile_mpeg_audio['VBR_bitrate'] = ((@$thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($info['audio']['sample_rate'] / 384);
929
                    } elseif ((($thisfile_mpeg_audio['version'] == '2') || ($thisfile_mpeg_audio['version'] == '2.5')) && ($thisfile_mpeg_audio['layer'] == 3)) {
930
                        $thisfile_mpeg_audio['VBR_bitrate'] = ((@$thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($info['audio']['sample_rate'] / 576);
931
                    } else {
932
                        $thisfile_mpeg_audio['VBR_bitrate'] = ((@$thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($info['audio']['sample_rate'] / 1152);
933
                    }
934
                    if ($thisfile_mpeg_audio['VBR_bitrate'] > 0) {
935
                        $info['audio']['bitrate']         = $thisfile_mpeg_audio['VBR_bitrate'];
936
                        $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; // to avoid confusion
937
                    }
938
                    break;
939
            }
940
        }
941
942
        // End variable-bitrate headers
943
        ////////////////////////////////////////////////////////////////////////////////////
944
945
        if ($recursive_search) {
946
947
            if (!getid3_mp3::RecursiveFrameScanning($fd, $info, $offset, $next_frame_test_offset, $scan_as_cbr)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression getid3_mp3::RecursiveFra...t_offset, $scan_as_cbr) of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
948
                return false;
949
            }
950
951
        }
952
953
        return true;
954
    }
955
956
    
957
    
958
    public function RecursiveFrameScanning(&$fd, &$info, &$offset, &$next_frame_test_offset, $scan_as_cbr) {
0 ignored issues
show
Unused Code introduced by
The parameter $offset is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

958
    public function RecursiveFrameScanning(&$fd, &$info, /** @scrutinizer ignore-unused */ &$offset, &$next_frame_test_offset, $scan_as_cbr) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
959
        for ($i = 0; $i < getid3_mp3::VALID_CHECK_FRAMES; $i++) {
960
            // check next getid3_mp3::VALID_CHECK_FRAMES frames for validity, to make sure we haven't run across a false synch
961
            if (($next_frame_test_offset + 4) >= $info['avdataend']) {
962
                // end of file
963
                return true;
964
            }
965
966
            $next_frame_test_array = array ('avdataend' => $info['avdataend'], 'avdataoffset' => $info['avdataoffset']);
967
            if ($this->decodeMPEGaudioHeader($fd, $next_frame_test_offset, $next_frame_test_array, false)) {
968
                if ($scan_as_cbr) {
969
                    // force CBR mode, used for trying to pick out invalid audio streams with
970
                    // valid(?) VBR headers, or VBR streams with no VBR header
971
                    if (!isset($next_frame_test_array['mpeg']['audio']['bitrate']) || !isset($info['mpeg']['audio']['bitrate']) || ($next_frame_test_array['mpeg']['audio']['bitrate'] != $info['mpeg']['audio']['bitrate'])) {
972
                        return false;
973
                    }
974
                }
975
976
977
                // next frame is OK, get ready to check the one after that
978
                if (isset($next_frame_test_array['mpeg']['audio']['framelength']) && ($next_frame_test_array['mpeg']['audio']['framelength'] > 0)) {
979
                    $next_frame_test_offset += $next_frame_test_array['mpeg']['audio']['framelength'];
980
                } else {
981
                    
982
                    // non-fatal error: Frame at offset $offset has an invalid frame length.
983
                    return;
984
                }
985
986
            } else {
987
988
                // non-fatal error: Next frame is not valid.
989
                return;
990
            }
991
        }
992
        return true;
993
    }
994
995
    
996
    
997
    public function FreeFormatFrameLength($fd, $offset, &$info, $deep_scan=false) {
998
        fseek($fd, $offset, SEEK_SET);
999
        $mpeg_audio_data = fread($fd, 32768);
1000
1001
        $sync_pattern1 = substr($mpeg_audio_data, 0, 4);
1002
        // may be different pattern due to padding
1003
        $sync_pattern2 = $sync_pattern1{0}.$sync_pattern1{1}.chr(ord($sync_pattern1{2}) | 0x02).$sync_pattern1{3};
1004
        if ($sync_pattern2 === $sync_pattern1) {
1005
            $sync_pattern2 = $sync_pattern1{0}.$sync_pattern1{1}.chr(ord($sync_pattern1{2}) & 0xFD).$sync_pattern1{3};
1006
        }
1007
1008
        $frame_length = false;
1009
        $frame_length1 = strpos($mpeg_audio_data, $sync_pattern1, 4);
1010
        $frame_length2 = strpos($mpeg_audio_data, $sync_pattern2, 4);
1011
        if ($frame_length1 > 4) {
1012
            $frame_length = $frame_length1;
1013
        }
1014
        if (($frame_length2 > 4) && ($frame_length2 < $frame_length1)) {
1015
            $frame_length = $frame_length2;
1016
        }
1017
        if (!$frame_length) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $frame_length of type false|integer is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1018
1019
            // LAME 3.88 has a different value for modeextension on the first frame vs the rest
1020
            $frame_length1 = strpos($mpeg_audio_data, substr($sync_pattern1, 0, 3), 4);
1021
            $frame_length2 = strpos($mpeg_audio_data, substr($sync_pattern2, 0, 3), 4);
1022
1023
            if ($frame_length1 > 4) {
1024
                $frame_length = $frame_length1;
1025
            }
1026
            if (($frame_length2 > 4) && ($frame_length2 < $frame_length1)) {
1027
                $frame_length = $frame_length2;
1028
            }
1029
            if (!$frame_length) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $frame_length of type false|integer is loosely compared to false; this is ambiguous if the integer can be 0. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1030
                throw new getid3_exception('Cannot find next free-format synch pattern ('.getid3_lib::PrintHexBytes($sync_pattern1).' or '.getid3_lib::PrintHexBytes($sync_pattern2).') after offset '.$offset);
1031
            } else {
1032
                $this->getid3->warning('ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)');
1033
                $info['audio']['codec']   = 'LAME';
1034
                $info['audio']['encoder'] = 'LAME3.88';
1035
                $sync_pattern1 = substr($sync_pattern1, 0, 3);
1036
                $sync_pattern2 = substr($sync_pattern2, 0, 3);
1037
            }
1038
        }
1039
1040
        if ($deep_scan) {
1041
1042
            $actual_frame_length_values = array ();
1043
            $next_offset = $offset + $frame_length;
1044
            while ($next_offset < ($info['avdataend'] - 6)) {
1045
                fseek($fd, $next_offset - 1, SEEK_SET);
1046
                $NextSyncPattern = fread($fd, 6);
1047
                if ((substr($NextSyncPattern, 1, strlen($sync_pattern1)) == $sync_pattern1) || (substr($NextSyncPattern, 1, strlen($sync_pattern2)) == $sync_pattern2)) {
1048
                    // good - found where expected
1049
                    $actual_frame_length_values[] = $frame_length;
1050
                } elseif ((substr($NextSyncPattern, 0, strlen($sync_pattern1)) == $sync_pattern1) || (substr($NextSyncPattern, 0, strlen($sync_pattern2)) == $sync_pattern2)) {
1051
                    // ok - found one byte earlier than expected (last frame wasn't padded, first frame was)
1052
                    $actual_frame_length_values[] = ($frame_length - 1);
1053
                    $next_offset--;
1054
                } elseif ((substr($NextSyncPattern, 2, strlen($sync_pattern1)) == $sync_pattern1) || (substr($NextSyncPattern, 2, strlen($sync_pattern2)) == $sync_pattern2)) {
1055
                    // ok - found one byte later than expected (last frame was padded, first frame wasn't)
1056
                    $actual_frame_length_values[] = ($frame_length + 1);
1057
                    $next_offset++;
1058
                } else {
1059
                    throw new getid3_exception('Did not find expected free-format sync pattern at offset '.$next_offset);
1060
                }
1061
                $next_offset += $frame_length;
1062
            }
1063
            if (count($actual_frame_length_values) > 0) {
1064
                $frame_length = intval(round(array_sum($actual_frame_length_values) / count($actual_frame_length_values)));
1065
            }
1066
        }
1067
        return $frame_length;
1068
    }
1069
1070
    
1071
1072
    public function getOnlyMPEGaudioInfo($fd, &$info, $avdata_offset, $bit_rate_histogram=false) {
1073
1074
        // looks for synch, decodes MPEG audio header
1075
     
1076
        fseek($fd, $avdata_offset, SEEK_SET);
1077
        
1078
        $sync_seek_buffer_size = min(128 * 1024, $info['avdataend'] - $avdata_offset);
1079
        $header = fread($fd, $sync_seek_buffer_size);
1080
        $sync_seek_buffer_size = strlen($header);
1081
        $synch_seek_offset = 0;
1082
        
1083
        static $mpeg_audio_version_lookup;
1084
        static $mpeg_audio_layer_lookup;
1085
        static $mpeg_audio_bitrate_lookup;
1086
        if (empty($mpeg_audio_version_lookup)) {
1087
            $mpeg_audio_version_lookup = getid3_mp3::MPEGaudioVersionarray();
1088
            $mpeg_audio_layer_lookup   = getid3_mp3::MPEGaudioLayerarray();
1089
            $mpeg_audio_bitrate_lookup = getid3_mp3::MPEGaudioBitratearray();
1090
1091
        }
1092
1093
        while ($synch_seek_offset < $sync_seek_buffer_size) {
1094
     
1095
            if ((($avdata_offset + $synch_seek_offset)  < $info['avdataend']) && !feof($fd)) {
1096
     
1097
                // if a synch's not found within the first 128k bytes, then give up               
1098
                if ($synch_seek_offset > $sync_seek_buffer_size) {
1099
                    throw new getid3_exception('Could not find valid MPEG audio synch within the first '.round($sync_seek_buffer_size / 1024).'kB');
1100
                } 
1101
              
1102
                if (feof($fd)) {
1103
                    throw new getid3_exception('Could not find valid MPEG audio synch before end of file');
1104
                }
1105
            }
1106
     
1107
           if (($synch_seek_offset + 1) >= strlen($header)) {
1108
                throw new getid3_exception('Could not find valid MPEG synch before end of file');
1109
           }
1110
     
1111
           if (($header{$synch_seek_offset} == "\xFF") && ($header{($synch_seek_offset + 1)} > "\xE0")) { // synch detected 
1112
1113
                if (!isset($first_frame_info) && !isset($info['mpeg']['audio'])) {
1114
                    $first_frame_info = $info;
1115
                    $first_frame_avdata_offset = $avdata_offset + $synch_seek_offset;
1116
                    if (!getid3_mp3::decodeMPEGaudioHeader($fd, $avdata_offset + $synch_seek_offset, $first_frame_info, false)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression getid3_mp3::decodeMPEGau...irst_frame_info, false) of type null|true is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
1117
                        // if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's
1118
                        // garbage between this frame and a valid sequence of MPEG-audio frames, to be restored below
1119
                        unset($first_frame_info);
1120
                    }
1121
                }
1122
1123
                $dummy = $info; // only overwrite real data if valid header found
1124
                if (getid3_mp3::decodeMPEGaudioHeader($fd, $avdata_offset + $synch_seek_offset, $dummy, true)) {
1125
                    $info = $dummy;
1126
                    $info['avdataoffset'] = $avdata_offset + $synch_seek_offset;
1127
                    
1128
                    switch (@$info['fileformat']) {
1129
                        case '':
1130
                        case 'mp3':
1131
                            $info['fileformat']          = 'mp3';
1132
                            $info['audio']['dataformat'] = 'mp3';
1133
                            break;
1134
                    }
1135
                    if (isset($first_frame_info['mpeg']['audio']['bitrate_mode']) && ($first_frame_info['mpeg']['audio']['bitrate_mode'] == 'vbr')) {
1136
                        if (!(abs($info['audio']['bitrate'] - $first_frame_info['audio']['bitrate']) <= 1)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $first_frame_info does not seem to be defined for all execution paths leading up to this point.
Loading history...
1137
                            // If there is garbage data between a valid VBR header frame and a sequence
1138
                            // of valid MPEG-audio frames the VBR data is no longer discarded.
1139
                            $info = $first_frame_info;
1140
                            $info['avdataoffset']        = $first_frame_avdata_offset;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $first_frame_avdata_offset does not seem to be defined for all execution paths leading up to this point.
Loading history...
1141
                            $info['fileformat']          = 'mp3';
1142
                            $info['audio']['dataformat'] = 'mp3';
1143
                            $dummy                               = $info;
1144
                            unset($dummy['mpeg']['audio']);
1145
                            $GarbageOffsetStart = $first_frame_avdata_offset + $first_frame_info['mpeg']['audio']['framelength'];
1146
                            $GarbageOffsetEnd   = $avdata_offset + $synch_seek_offset;
1147
                            if (getid3_mp3::decodeMPEGaudioHeader($fd, $GarbageOffsetEnd, $dummy, true, true)) {
1148
1149
                                $info = $dummy;
1150
                                $info['avdataoffset'] = $GarbageOffsetEnd;
1151
                                $this->getid3->warning('apparently-valid VBR header not used because could not find '.getid3_mp3::VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.'), but did find valid CBR stream starting at '.$GarbageOffsetEnd);
1152
1153
                            } else {
1154
1155
                                $this->getid3->warning('using data from VBR header even though could not find '.getid3_mp3::VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.')');
1156
1157
                            }
1158
                        }
1159
                    }
1160
                    if (isset($info['mpeg']['audio']['bitrate_mode']) && ($info['mpeg']['audio']['bitrate_mode'] == 'vbr') && !isset($info['mpeg']['audio']['VBR_method'])) {
1161
                        // VBR file with no VBR header
1162
                        $bit_rate_histogram = true;
1163
                    }
1164
1165
                    if ($bit_rate_histogram) {
1166
1167
                        $info['mpeg']['audio']['stereo_distribution']  = array ('stereo'=>0, 'joint stereo'=>0, 'dual channel'=>0, 'mono'=>0);
1168
                        $info['mpeg']['audio']['version_distribution'] = array ('1'=>0, '2'=>0, '2.5'=>0);
1169
1170
                        if ($info['mpeg']['audio']['version'] == '1') {
1171
                            if ($info['mpeg']['audio']['layer'] == 3) {
1172
                                $info['mpeg']['audio']['bitrate_distribution'] = array ('free'=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0);
1173
                            } elseif ($info['mpeg']['audio']['layer'] == 2) {
1174
                                $info['mpeg']['audio']['bitrate_distribution'] = array ('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0, 384000=>0);
1175
                            } elseif ($info['mpeg']['audio']['layer'] == 1) {
1176
                                $info['mpeg']['audio']['bitrate_distribution'] = array ('free'=>0, 32000=>0, 64000=>0, 96000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 288000=>0, 320000=>0, 352000=>0, 384000=>0, 416000=>0, 448000=>0);
1177
                            }
1178
                        } elseif ($info['mpeg']['audio']['layer'] == 1) {
1179
                            $info['mpeg']['audio']['bitrate_distribution'] = array ('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0, 176000=>0, 192000=>0, 224000=>0, 256000=>0);
1180
                        } else {
1181
                            $info['mpeg']['audio']['bitrate_distribution'] = array ('free'=>0, 8000=>0, 16000=>0, 24000=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0);
1182
                        }
1183
1184
                        $dummy = array ('avdataend' => $info['avdataend'], 'avdataoffset' => $info['avdataoffset']);
1185
                        $synch_start_offset = $info['avdataoffset'];
1186
1187
                        $fast_mode = false;
1188
                        $synch_errors_found = 0;
1189
                        while ($this->decodeMPEGaudioHeader($fd, $synch_start_offset, $dummy, false, false, $fast_mode)) {
1190
                            $fast_mode = true;
1191
                            $thisframebitrate = $mpeg_audio_bitrate_lookup[$mpeg_audio_version_lookup[$dummy['mpeg']['audio']['raw']['version']]][$mpeg_audio_layer_lookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']];
1192
1193
                            if (empty($dummy['mpeg']['audio']['framelength'])) {
1194
                                $synch_errors_found++;
1195
                            } 
1196
                            else {
1197
                                @$info['mpeg']['audio']['bitrate_distribution'][$thisframebitrate]++;
1198
                                @$info['mpeg']['audio']['stereo_distribution'][$dummy['mpeg']['audio']['channelmode']]++;
1199
                                @$info['mpeg']['audio']['version_distribution'][$dummy['mpeg']['audio']['version']]++;
1200
1201
                                $synch_start_offset += $dummy['mpeg']['audio']['framelength'];
1202
                            }
1203
                        }
1204
                        if ($synch_errors_found > 0) {
1205
                            $this->getid3->warning('Found '.$synch_errors_found.' synch errors in histogram analysis');
1206
                        }
1207
1208
                        $bit_total     = 0;
1209
                        $frame_counter = 0;
1210
                        foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bit_rate_value => $bit_rate_count) {
1211
                            $frame_counter += $bit_rate_count;
1212
                            if ($bit_rate_value != 'free') {
1213
                                $bit_total += ($bit_rate_value * $bit_rate_count);
1214
                            }
1215
                        }
1216
                        if ($frame_counter == 0) {
1217
                            throw new getid3_exception('Corrupt MP3 file: framecounter == zero');
1218
                        }
1219
                        $info['mpeg']['audio']['frame_count'] = $frame_counter;
1220
                        $info['mpeg']['audio']['bitrate']     = ($bit_total / $frame_counter);
1221
1222
                        $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
1223
1224
1225
                        // Definitively set VBR vs CBR, even if the Xing/LAME/VBRI header says differently
1226
                        $distinct_bit_rates = 0;
1227
                        foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bit_rate_value => $bit_rate_count) {
1228
                            if ($bit_rate_count > 0) {
1229
                                $distinct_bit_rates++;
1230
                            }
1231
                        }
1232
                        if ($distinct_bit_rates > 1) {
1233
                            $info['mpeg']['audio']['bitrate_mode'] = 'vbr';
1234
                        } else {
1235
                            $info['mpeg']['audio']['bitrate_mode'] = 'cbr';
1236
                        }
1237
                        $info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode'];
1238
1239
                    }
1240
1241
                    break; // exit while()
1242
                }
1243
            }
1244
1245
            $synch_seek_offset++;
1246
            if (($avdata_offset + $synch_seek_offset) >= $info['avdataend']) {
1247
                // end of file/data
1248
1249
                if (empty($info['mpeg']['audio'])) {
1250
1251
                    throw new getid3_exception('could not find valid MPEG synch before end of file');
1252
                }
1253
                break;
1254
            }
1255
1256
        }
1257
        
1258
        $info['audio']['channels']        = $info['mpeg']['audio']['channels'];
1259
        $info['audio']['channelmode']     = $info['mpeg']['audio']['channelmode'];
1260
        $info['audio']['sample_rate']     = $info['mpeg']['audio']['sample_rate'];
1261
        return true;
1262
    }
1263
1264
1265
1266
    public static function MPEGaudioVersionarray() {
1267
        
1268
        static $array = array ('2.5', false, '2', '1');
1269
        return $array;
1270
    }
1271
1272
1273
1274
    public static function MPEGaudioLayerarray() {
1275
        
1276
        static $array = array (false, 3, 2, 1);
1277
        return $array;
1278
    }
1279
1280
1281
1282
    public static function MPEGaudioBitratearray() {
1283
        
1284
        static $array;
1285
        if (empty($array)) {
1286
            $array = array (
1287
                '1'  =>  array (1 => array ('free', 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000),
1288
                                2 => array ('free', 32000, 48000, 56000,  64000,  80000,  96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000),
1289
                                3 => array ('free', 32000, 40000, 48000,  56000,  64000,  80000,  96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000)
1290
                               ),
1291
1292
                '2'  =>  array (1 => array ('free', 32000, 48000, 56000,  64000,  80000,  96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000),
1293
                                2 => array ('free',  8000, 16000, 24000,  32000,  40000,  48000,  56000,  64000,  80000,  96000, 112000, 128000, 144000, 160000),
1294
                               )
1295
            );
1296
            $array['2'][3] = $array['2'][2];
1297
            $array['2.5']  = $array['2'];
1298
        }
1299
        return $array;
1300
    }
1301
1302
1303
1304
    public static function MPEGaudioFrequencyarray() {
1305
        
1306
        static $array = array (
1307
                '1'   => array (44100, 48000, 32000),
1308
                '2'   => array (22050, 24000, 16000),
1309
                '2.5' => array (11025, 12000,  8000)
1310
        );
1311
        return $array;
1312
    }
1313
1314
1315
1316
    public static function MPEGaudioChannelModearray() {
1317
        
1318
        static $array = array ('stereo', 'joint stereo', 'dual channel', 'mono');
1319
        return $array;
1320
    }
1321
1322
1323
1324
    public static function MPEGaudioModeExtensionarray() {
1325
        
1326
        static $array = array (
1327
                1 => array ('4-31', '8-31', '12-31', '16-31'),
1328
                2 => array ('4-31', '8-31', '12-31', '16-31'),
1329
                3 => array ('', 'IS', 'MS', 'IS+MS')
1330
        );
1331
        return $array;
1332
    }
1333
1334
1335
1336
    public static function MPEGaudioEmphasisarray() {
1337
        
1338
        static $array = array ('none', '50/15ms', false, 'CCIT J.17');
1339
        return $array;
1340
    }
1341
1342
1343
1344
    public static function MPEGaudioHeaderBytesValid($head4, $allow_bitrate_15=false) {
1345
        
1346
        return getid3_mp3::MPEGaudioHeaderValid(getid3_mp3::MPEGaudioHeaderDecode($head4), false, $allow_bitrate_15);
1347
    }
1348
1349
1350
1351
    public static function MPEGaudioHeaderValid($raw_array, $echo_errors=false, $allow_bitrate_15=false) {
1352
        
1353
        if (($raw_array['synch'] & 0x0FFE) != 0x0FFE) {
1354
            return false;
1355
        }
1356
1357
        static $mpeg_audio_version_lookup;
1358
        static $mpeg_audio_layer_lookup;
1359
        static $mpeg_audio_bitrate_lookup;
1360
        static $mpeg_audio_frequency_lookup;
1361
        static $mpeg_audio_channel_mode_lookup;
1362
        static $mpeg_audio_mode_extension_lookup;
1363
        static $mpeg_audio_emphasis_lookup;
1364
        if (empty($mpeg_audio_version_lookup)) {
1365
            $mpeg_audio_version_lookup        = getid3_mp3::MPEGaudioVersionarray();
1366
            $mpeg_audio_layer_lookup          = getid3_mp3::MPEGaudioLayerarray();
1367
            $mpeg_audio_bitrate_lookup        = getid3_mp3::MPEGaudioBitratearray();
1368
            $mpeg_audio_frequency_lookup      = getid3_mp3::MPEGaudioFrequencyarray();
1369
            $mpeg_audio_channel_mode_lookup   = getid3_mp3::MPEGaudioChannelModearray();
1370
            $mpeg_audio_mode_extension_lookup = getid3_mp3::MPEGaudioModeExtensionarray();
1371
            $mpeg_audio_emphasis_lookup       = getid3_mp3::MPEGaudioEmphasisarray();
1372
        }
1373
1374
        if (isset($mpeg_audio_version_lookup[$raw_array['version']])) {
1375
            $decodedVersion = $mpeg_audio_version_lookup[$raw_array['version']];
1376
        } else {
1377
            echo ($echo_errors ? "\n".'invalid Version ('.$raw_array['version'].')' : '');
1378
            return false;
1379
        }
1380
        if (isset($mpeg_audio_layer_lookup[$raw_array['layer']])) {
1381
            $decodedLayer = $mpeg_audio_layer_lookup[$raw_array['layer']];
1382
        } else {
1383
            echo ($echo_errors ? "\n".'invalid Layer ('.$raw_array['layer'].')' : '');
1384
            return false;
1385
        }
1386
        if (!isset($mpeg_audio_bitrate_lookup[$decodedVersion][$decodedLayer][$raw_array['bitrate']])) {
1387
            echo ($echo_errors ? "\n".'invalid Bitrate ('.$raw_array['bitrate'].')' : '');
1388
            if ($raw_array['bitrate'] == 15) {
1389
                // known issue in LAME 3.90 - 3.93.1 where free-format has bitrate ID of 15 instead of 0
1390
                // let it go through here otherwise file will not be identified
1391
                if (!$allow_bitrate_15) {
1392
                    return false;
1393
                }
1394
            } else {
1395
                return false;
1396
            }
1397
        }
1398
        if (!isset($mpeg_audio_frequency_lookup[$decodedVersion][$raw_array['sample_rate']])) {
1399
            echo ($echo_errors ? "\n".'invalid Frequency ('.$raw_array['sample_rate'].')' : '');
1400
            return false;
1401
        }
1402
        if (!isset($mpeg_audio_channel_mode_lookup[$raw_array['channelmode']])) {
1403
            echo ($echo_errors ? "\n".'invalid ChannelMode ('.$raw_array['channelmode'].')' : '');
1404
            return false;
1405
        }
1406
        if (!isset($mpeg_audio_mode_extension_lookup[$decodedLayer][$raw_array['modeextension']])) {
1407
            echo ($echo_errors ? "\n".'invalid Mode Extension ('.$raw_array['modeextension'].')' : '');
1408
            return false;
1409
        }
1410
        if (!isset($mpeg_audio_emphasis_lookup[$raw_array['emphasis']])) {
1411
            echo ($echo_errors ? "\n".'invalid Emphasis ('.$raw_array['emphasis'].')' : '');
1412
            return false;
1413
        }
1414
        // These are just either set or not set, you can't mess that up :)
1415
        // $raw_array['protection'];
1416
        // $raw_array['padding'];
1417
        // $raw_array['private'];
1418
        // $raw_array['copyright'];
1419
        // $raw_array['original'];
1420
1421
        return true;
1422
    }
1423
1424
1425
1426
    public static function MPEGaudioHeaderDecode($header_four_bytes) {
1427
        // AAAA AAAA  AAAB BCCD  EEEE FFGH  IIJJ KLMM
1428
        // A - Frame sync (all bits set)
1429
        // B - MPEG Audio version ID
1430
        // C - Layer description
1431
        // D - Protection bit
1432
        // E - Bitrate index
1433
        // F - Sampling rate frequency index
1434
        // G - Padding bit
1435
        // H - Private bit
1436
        // I - Channel Mode
1437
        // J - Mode extension (Only if Joint stereo)
1438
        // K - Copyright
1439
        // L - Original
1440
        // M - Emphasis
1441
1442
        if (strlen($header_four_bytes) != 4) {
1443
            return false;
1444
        }
1445
1446
        $mpeg_raw_header['synch']         = (getid3_lib::BigEndian2Int(substr($header_four_bytes, 0, 2)) & 0xFFE0) >> 4;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$mpeg_raw_header was never initialized. Although not strictly required by PHP, it is generally a good practice to add $mpeg_raw_header = array(); before regardless.
Loading history...
1447
        $mpeg_raw_header['version']       = (ord($header_four_bytes{1}) & 0x18) >> 3; //    BB
1448
        $mpeg_raw_header['layer']         = (ord($header_four_bytes{1}) & 0x06) >> 1; //      CC
1449
        $mpeg_raw_header['protection']    = (ord($header_four_bytes{1}) & 0x01);      //        D
1450
        $mpeg_raw_header['bitrate']       = (ord($header_four_bytes{2}) & 0xF0) >> 4; // EEEE
1451
        $mpeg_raw_header['sample_rate']   = (ord($header_four_bytes{2}) & 0x0C) >> 2; //     FF
1452
        $mpeg_raw_header['padding']       = (ord($header_four_bytes{2}) & 0x02) >> 1; //       G
1453
        $mpeg_raw_header['private']       = (ord($header_four_bytes{2}) & 0x01);      //        H
1454
        $mpeg_raw_header['channelmode']   = (ord($header_four_bytes{3}) & 0xC0) >> 6; // II
1455
        $mpeg_raw_header['modeextension'] = (ord($header_four_bytes{3}) & 0x30) >> 4; //   JJ
1456
        $mpeg_raw_header['copyright']     = (ord($header_four_bytes{3}) & 0x08) >> 3; //     K
1457
        $mpeg_raw_header['original']      = (ord($header_four_bytes{3}) & 0x04) >> 2; //      L
1458
        $mpeg_raw_header['emphasis']      = (ord($header_four_bytes{3}) & 0x03);      //       MM
1459
1460
        return $mpeg_raw_header;
1461
    }
1462
1463
1464
1465
    public static function MPEGaudioFrameLength(&$bit_rate, &$version, &$layer, $padding, &$sample_rate) {
1466
        
1467
        if (!isset($cache[$bit_rate][$version][$layer][$padding][$sample_rate])) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $cache seems to never exist and therefore isset should always be false.
Loading history...
1468
            $cache[$bit_rate][$version][$layer][$padding][$sample_rate] = false;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$cache was never initialized. Although not strictly required by PHP, it is generally a good practice to add $cache = array(); before regardless.
Loading history...
1469
            if ($bit_rate != 'free') {
1470
1471
                if ($version == '1') {
1472
1473
                    if ($layer == '1') {
1474
1475
                        // For Layer I slot is 32 bits long
1476
                        $frame_length_coefficient = 48;
1477
                        $slot_length = 4;
1478
1479
                    } else { // Layer 2 / 3
1480
1481
                        // for Layer 2 and Layer 3 slot is 8 bits long.
1482
                        $frame_length_coefficient = 144;
1483
                        $slot_length = 1;
1484
1485
                    }
1486
1487
                } else { // MPEG-2 / MPEG-2.5
1488
1489
                    if ($layer == '1') {
1490
1491
                        // For Layer I slot is 32 bits long
1492
                        $frame_length_coefficient = 24;
1493
                        $slot_length = 4;
1494
1495
                    } elseif ($layer == '2') {
1496
1497
                        // for Layer 2 and Layer 3 slot is 8 bits long.
1498
                        $frame_length_coefficient = 144;
1499
                        $slot_length = 1;
1500
1501
                    } else { // layer 3
1502
1503
                        // for Layer 2 and Layer 3 slot is 8 bits long.
1504
                        $frame_length_coefficient = 72;
1505
                        $slot_length = 1;
1506
1507
                    }
1508
1509
                }
1510
1511
                // FrameLengthInBytes = ((Coefficient * BitRate) / SampleRate) + Padding
1512
                if ($sample_rate > 0) {
1513
                    $new_frame_length  = ($frame_length_coefficient * $bit_rate) / $sample_rate;
1514
                    $new_frame_length  = floor($new_frame_length / $slot_length) * $slot_length; // round to next-lower multiple of SlotLength (1 byte for Layer 2/3, 4 bytes for Layer I)
1515
                    if ($padding) {
1516
                        $new_frame_length += $slot_length;
1517
                    }
1518
                    $cache[$bit_rate][$version][$layer][$padding][$sample_rate] = (int) $new_frame_length;
1519
                }
1520
            }
1521
        }
1522
        return $cache[$bit_rate][$version][$layer][$padding][$sample_rate];
1523
    }
1524
1525
1526
1527
    public static function ClosestStandardMP3Bitrate($bit_rate) {
1528
        
1529
        static $standard_bit_rates = array (320000, 256000, 224000, 192000, 160000, 128000, 112000, 96000, 80000, 64000, 56000, 48000, 40000, 32000, 24000, 16000, 8000);
1530
        static $bit_rate_table = array (0=>'-');
1531
        $round_bit_rate = intval(round($bit_rate, -3));
1532
        if (!isset($bit_rate_table[$round_bit_rate])) {
1533
            if ($round_bit_rate > 320000) {
1534
                $bit_rate_table[$round_bit_rate] = round($bit_rate, -4);
1535
            } else {
1536
                $last_bit_rate = 320000;
1537
                foreach ($standard_bit_rates as $standard_bit_rate) {
1538
                    $bit_rate_table[$round_bit_rate] = $standard_bit_rate;
1539
                    if ($round_bit_rate >= $standard_bit_rate - (($last_bit_rate - $standard_bit_rate) / 2)) {
1540
                        break;
1541
                    }
1542
                    $last_bit_rate = $standard_bit_rate;
1543
                }
1544
            }
1545
        }
1546
        return $bit_rate_table[$round_bit_rate];
1547
    }
1548
1549
1550
1551
    public static function XingVBRidOffset($version, $channel_mode) {
1552
        
1553
        static $lookup = array (
1554
                '1'   => array ('mono'          => 0x15, // 4 + 17 = 21
1555
                                'stereo'        => 0x24, // 4 + 32 = 36
1556
                                'joint stereo'  => 0x24,
1557
                                'dual channel'  => 0x24
1558
                               ),
1559
1560
                '2'   => array ('mono'          => 0x0D, // 4 +  9 = 13
1561
                                'stereo'        => 0x15, // 4 + 17 = 21
1562
                                'joint stereo'  => 0x15,
1563
                                'dual channel'  => 0x15
1564
                               ),
1565
1566
                '2.5' => array ('mono'          => 0x15,
1567
                                'stereo'        => 0x15,
1568
                                'joint stereo'  => 0x15,
1569
                                'dual channel'  => 0x15
1570
                               )
1571
        );
1572
        
1573
        return $lookup[$version][$channel_mode];
1574
    }
1575
1576
1577
1578
    public static function LAMEvbrMethodLookup($vbr_method_id) {
1579
        
1580
        static $lookup = array (
1581
            0x00 => 'unknown',
1582
            0x01 => 'cbr',
1583
            0x02 => 'abr',
1584
            0x03 => 'vbr-old / vbr-rh',
1585
            0x04 => 'vbr-new / vbr-mtrh',
1586
            0x05 => 'vbr-mt',
1587
            0x06 => 'Full VBR Method 4',
1588
            0x08 => 'constant bitrate 2 pass',
1589
            0x09 => 'abr 2 pass',
1590
            0x0F => 'reserved'
1591
        );
1592
        return (isset($lookup[$vbr_method_id]) ? $lookup[$vbr_method_id] : '');
1593
    }
1594
1595
1596
1597
    public static function LAMEmiscStereoModeLookup($stereo_mode_id) {
1598
    
1599
        static $lookup = array (
1600
            0 => 'mono',
1601
            1 => 'stereo',
1602
            2 => 'dual mono',
1603
            3 => 'joint stereo',
1604
            4 => 'forced stereo',
1605
            5 => 'auto',
1606
            6 => 'intensity stereo',
1607
            7 => 'other'
1608
        );
1609
        return (isset($lookup[$stereo_mode_id]) ? $lookup[$stereo_mode_id] : '');
1610
    }
1611
1612
1613
1614
    public static function LAMEmiscSourceSampleFrequencyLookup($source_sample_frequency_id) {
1615
        
1616
        static $lookup = array (
1617
            0 => '<= 32 kHz',
1618
            1 => '44.1 kHz',
1619
            2 => '48 kHz',
1620
            3 => '> 48kHz'
1621
        );
1622
        return (isset($lookup[$source_sample_frequency_id]) ? $lookup[$source_sample_frequency_id] : '');
1623
    }
1624
1625
1626
1627
    public static function LAMEsurroundInfoLookup($surround_info_id) {
1628
        
1629
        static $lookup = array (
1630
            0 => 'no surround info',
1631
            1 => 'DPL encoding',
1632
            2 => 'DPL2 encoding',
1633
            3 => 'Ambisonic encoding'
1634
        );
1635
        return (isset($lookup[$surround_info_id]) ? $lookup[$surround_info_id] : 'reserved');
1636
    }
1637
1638
1639
1640
    public static function LAMEpresetUsedLookup($lame_tag) {
1641
        
1642
        if ($lame_tag['preset_used_id'] == 0) {
1643
            // no preset used (LAME >=3.93)
1644
            // no preset recorded (LAME <3.93)
1645
            return '';
1646
        }
1647
        
1648
        $lame_preset_used_lookup = array ();
1649
        
1650
        for ($i = 8; $i <= 320; $i++) {
1651
            switch ($lame_tag['vbr_method']) {
1652
                case 'cbr':
1653
                    $lame_preset_used_lookup[$i] = '--alt-preset '.$lame_tag['vbr_method'].' '.$i;
1654
                    break;
1655
                case 'abr':
1656
                default: // other VBR modes shouldn't be here(?)
1657
                    $lame_preset_used_lookup[$i] = '--alt-preset '.$i;
1658
                    break;
1659
            }
1660
        }
1661
1662
        // named old-style presets (studio, phone, voice, etc) are handled in GuessEncoderOptions()
1663
1664
        // named alt-presets
1665
        $lame_preset_used_lookup[1000] = '--r3mix';
1666
        $lame_preset_used_lookup[1001] = '--alt-preset standard';
1667
        $lame_preset_used_lookup[1002] = '--alt-preset extreme';
1668
        $lame_preset_used_lookup[1003] = '--alt-preset insane';
1669
        $lame_preset_used_lookup[1004] = '--alt-preset fast standard';
1670
        $lame_preset_used_lookup[1005] = '--alt-preset fast extreme';
1671
        $lame_preset_used_lookup[1006] = '--alt-preset medium';
1672
        $lame_preset_used_lookup[1007] = '--alt-preset fast medium';
1673
1674
        // LAME 3.94 additions/changes
1675
        $lame_preset_used_lookup[1010] = '--preset portable';                                                            // 3.94a15 Oct 21 2003
1676
        $lame_preset_used_lookup[1015] = '--preset radio';                                                               // 3.94a15 Oct 21 2003
1677
1678
        $lame_preset_used_lookup[320]  = '--preset insane';                                                              // 3.94a15 Nov 12 2003
1679
        $lame_preset_used_lookup[410]  = '-V9';
1680
        $lame_preset_used_lookup[420]  = '-V8';
1681
        $lame_preset_used_lookup[430]  = '--preset radio';                                                               // 3.94a15 Nov 12 2003
1682
        $lame_preset_used_lookup[440]  = '-V6';
1683
        $lame_preset_used_lookup[450]  = '--preset '.(($lame_tag['raw']['vbr_method'] == 4) ? 'fast ' : '').'portable';  // 3.94a15 Nov 12 2003
1684
        $lame_preset_used_lookup[460]  = '--preset '.(($lame_tag['raw']['vbr_method'] == 4) ? 'fast ' : '').'medium';    // 3.94a15 Nov 12 2003
1685
        $lame_preset_used_lookup[470]  = '--r3mix';                                                                      // 3.94b1  Dec 18 2003
1686
        $lame_preset_used_lookup[480]  = '--preset '.(($lame_tag['raw']['vbr_method'] == 4) ? 'fast ' : '').'standard';  // 3.94a15 Nov 12 2003
1687
        $lame_preset_used_lookup[490]  = '-V1';
1688
        $lame_preset_used_lookup[500]  = '--preset '.(($lame_tag['raw']['vbr_method'] == 4) ? 'fast ' : '').'extreme';   // 3.94a15 Nov 12 2003
1689
        
1690
        return (isset($lame_preset_used_lookup[$lame_tag['preset_used_id']]) ? $lame_preset_used_lookup[$lame_tag['preset_used_id']] : 'new/unknown preset: '.$lame_tag['preset_used_id'].' - report to [email protected]');
1691
    }
1692
	
1693
	
1694
}
1695
1696
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...