getid3_mp3::MPEGaudioLayerarray()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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

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

763
                        /** @scrutinizer ignore-call */ 
764
                        $thisfile_mpeg_audio_lame_raw['mp3_gain']    = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xB5, 1), false, true);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
764
                        $thisfile_mpeg_audio_lame['mp3_gain_db']     = (20 * log10(2) / 4) * $thisfile_mpeg_audio_lame_raw['mp3_gain'];
765
                        $thisfile_mpeg_audio_lame['mp3_gain_factor'] = pow(2, ($thisfile_mpeg_audio_lame['mp3_gain_db'] / 6));
766
767
                        // bytes $B6-$B7  Preset and surround info
768
                        $PresetSurroundBytes = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xB6, 2));
769
                        // Reserved                                                    = ($PresetSurroundBytes & 0xC000);
770
                        $thisfile_mpeg_audio_lame_raw['surround_info'] = ($PresetSurroundBytes & 0x3800);
771
                        $thisfile_mpeg_audio_lame['surround_info']     = getid3_mp3::LAMEsurroundInfoLookup($thisfile_mpeg_audio_lame_raw['surround_info']);
772
                        $thisfile_mpeg_audio_lame['preset_used_id']    = ($PresetSurroundBytes & 0x07FF);
773
                        $thisfile_mpeg_audio_lame['preset_used']       = getid3_mp3::LAMEpresetUsedLookup($thisfile_mpeg_audio_lame);
774
                        if (!empty($thisfile_mpeg_audio_lame['preset_used_id']) && empty($thisfile_mpeg_audio_lame['preset_used'])) {
775
                            $this->getid3->warning('Unknown LAME preset used (' . $thisfile_mpeg_audio_lame['preset_used_id'] . ') - please report to [email protected]');
776
                        }
777
                        if (('LAME3.90.' == $thisfile_mpeg_audio_lame['short_version']) && !empty($thisfile_mpeg_audio_lame['preset_used_id'])) {
778
                            // this may change if 3.90.4 ever comes out
779
                            $thisfile_mpeg_audio_lame['short_version'] = 'LAME3.90.3';
780
                        }
781
782
                        // bytes $B8-$BB  MusicLength
783
                        $thisfile_mpeg_audio_lame['audio_bytes'] = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xB8, 4));
784
                        $expected_number_of_audio_bytes          = (($thisfile_mpeg_audio_lame['audio_bytes'] > 0) ? $thisfile_mpeg_audio_lame['audio_bytes'] : $thisfile_mpeg_audio['VBR_bytes']);
785
786
                        // bytes $BC-$BD  MusicCRC
787
                        $thisfile_mpeg_audio_lame['music_crc'] = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xBC, 2));
788
789
                        // bytes $BE-$BF  CRC-16 of Info Tag
790
                        $thisfile_mpeg_audio_lame['lame_tag_crc'] = getid3_lib::BigEndian2Int(substr($header_string, $lame_tag_offset_contant + 0xBE, 2));
791
792
                        // LAME CBR
793
                        if (1 == $thisfile_mpeg_audio_lame_raw['vbr_method']) {
794
                            $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
795
                            $thisfile_mpeg_audio['bitrate']      = getid3_mp3::ClosestStandardMP3Bitrate($thisfile_mpeg_audio['bitrate']);
796
                            $info['audio']['bitrate']            = $thisfile_mpeg_audio['bitrate'];
797
                        }
798
                    }
799
                }
800
            } else {
801
                // not Fraunhofer or Xing VBR methods, most likely CBR (but could be VBR with no header)
802
                $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
803
                if ($recursive_search) {
804
                    $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
805
                    if (getid3_mp3::RecursiveFrameScanning($fd, $info, $offset, $next_frame_test_offset, true)) {
0 ignored issues
show
Bug Best Practice introduced by
The method getid3_mp3::RecursiveFrameScanning() is not static, but was called statically. ( Ignorable by Annotation )

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

805
                    if (getid3_mp3::/** @scrutinizer ignore-call */ RecursiveFrameScanning($fd, $info, $offset, $next_frame_test_offset, true)) {
Loading history...
806
                        $recursive_search                    = false;
807
                        $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
808
                    }
809
                    if ('vbr' == $thisfile_mpeg_audio['bitrate_mode']) {
810
                        $this->getid3->warning('VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.');
811
                    }
812
                }
813
            }
814
        }
815
816
        if (($expected_number_of_audio_bytes > 0) && ($expected_number_of_audio_bytes != ($info['avdataend'] - $info['avdataoffset']))) {
817
            if ($expected_number_of_audio_bytes > ($info['avdataend'] - $info['avdataoffset'])) {
818
                if (1 == ($expected_number_of_audio_bytes - ($info['avdataend'] - $info['avdataoffset']))) {
819
                    $this->getid3->warning('Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)');
820
                } else {
821
                    $this->getid3->warning(
822
                        '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)'
823
                    );
824
                }
825
            } else {
826
                if (1 == (($info['avdataend'] - $info['avdataoffset']) - $expected_number_of_audio_bytes)) {
827
                    $info['avdataend']--;
828
                } else {
829
                    $this->getid3->warning(
830
                        '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)'
831
                    );
832
                }
833
            }
834
        }
835
836
        if (('free' == $thisfile_mpeg_audio['bitrate']) && empty($info['audio']['bitrate'])) {
837
            if (($offset == $info['avdataoffset']) && empty($thisfile_mpeg_audio['VBR_frames'])) {
838
                $frame_byte_length = getid3_mp3::FreeFormatFrameLength($fd, $offset, $info, true);
0 ignored issues
show
Bug Best Practice introduced by
The method getid3_mp3::FreeFormatFrameLength() is not static, but was called statically. ( Ignorable by Annotation )

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

838
                /** @scrutinizer ignore-call */ 
839
                $frame_byte_length = getid3_mp3::FreeFormatFrameLength($fd, $offset, $info, true);
Loading history...
839
                if ($frame_byte_length > 0) {
840
                    $thisfile_mpeg_audio['framelength'] = $frame_byte_length;
841
                    if ('1' == $thisfile_mpeg_audio['layer']) {
842
                        // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
843
                        $info['audio']['bitrate'] = ((($frame_byte_length / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
844
                    } else {
845
                        // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
846
                        $info['audio']['bitrate'] = (($frame_byte_length - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
847
                    }
848
                } else {
849
                    // non-fatal error: Error calculating frame length of free-format MP3 without Xing/LAME header.
850
                    return;
851
                }
852
            }
853
        }
854
855
        if (@$thisfile_mpeg_audio['VBR_frames']) {
856
            switch ($thisfile_mpeg_audio['bitrate_mode']) {
857
                case 'vbr':
858
                case 'abr':
859
                    if (('1' == $thisfile_mpeg_audio['version']) && (1 == $thisfile_mpeg_audio['layer'])) {
860
                        $thisfile_mpeg_audio['VBR_bitrate'] = ((@$thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($info['audio']['sample_rate'] / 384);
861
                    } elseif ((('2' == $thisfile_mpeg_audio['version']) || ('2.5' == $thisfile_mpeg_audio['version'])) && (3 == $thisfile_mpeg_audio['layer'])) {
862
                        $thisfile_mpeg_audio['VBR_bitrate'] = ((@$thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($info['audio']['sample_rate'] / 576);
863
                    } else {
864
                        $thisfile_mpeg_audio['VBR_bitrate'] = ((@$thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($info['audio']['sample_rate'] / 1152);
865
                    }
866
                    if ($thisfile_mpeg_audio['VBR_bitrate'] > 0) {
867
                        $info['audio']['bitrate']       = $thisfile_mpeg_audio['VBR_bitrate'];
868
                        $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; // to avoid confusion
869
                    }
870
                    break;
871
            }
872
        }
873
874
        // End variable-bitrate headers
875
        ////////////////////////////////////////////////////////////////////////////////////
876
877
        if ($recursive_search) {
878
            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...
879
                return false;
880
            }
881
        }
882
883
        return true;
884
    }
885
886
    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

886
    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...
887
    {
888
        for ($i = 0; $i < getid3_mp3::VALID_CHECK_FRAMES; $i++) {
889
            // check next getid3_mp3::VALID_CHECK_FRAMES frames for validity, to make sure we haven't run across a false synch
890
            if (($next_frame_test_offset + 4) >= $info['avdataend']) {
891
                // end of file
892
                return true;
893
            }
894
895
            $next_frame_test_array = ['avdataend' => $info['avdataend'], 'avdataoffset' => $info['avdataoffset']];
896
            if ($this->decodeMPEGaudioHeader($fd, $next_frame_test_offset, $next_frame_test_array, false)) {
897
                if ($scan_as_cbr) {
898
                    // force CBR mode, used for trying to pick out invalid audio streams with
899
                    // valid(?) VBR headers, or VBR streams with no VBR header
900
                    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'])) {
901
                        return false;
902
                    }
903
                }
904
905
                // next frame is OK, get ready to check the one after that
906
                if (isset($next_frame_test_array['mpeg']['audio']['framelength']) && ($next_frame_test_array['mpeg']['audio']['framelength'] > 0)) {
907
                    $next_frame_test_offset += $next_frame_test_array['mpeg']['audio']['framelength'];
908
                } else {
909
                    // non-fatal error: Frame at offset $offset has an invalid frame length.
910
                    return;
911
                }
912
            } else {
913
                // non-fatal error: Next frame is not valid.
914
                return;
915
            }
916
        }
917
        return true;
918
    }
919
920
    public function FreeFormatFrameLength($fd, $offset, &$info, $deep_scan = false)
921
    {
922
        fseek($fd, $offset, SEEK_SET);
923
        $mpeg_audio_data = fread($fd, 32768);
924
925
        $sync_pattern1 = substr($mpeg_audio_data, 0, 4);
926
        // may be different pattern due to padding
927
        $sync_pattern2 = $sync_pattern1{0} . $sync_pattern1{1} . chr(ord($sync_pattern1{2}) | 0x02) . $sync_pattern1{3};
928
        if ($sync_pattern2 === $sync_pattern1) {
929
            $sync_pattern2 = $sync_pattern1{0} . $sync_pattern1{1} . chr(ord($sync_pattern1{2}) & 0xFD) . $sync_pattern1{3};
930
        }
931
932
        $frame_length  = false;
933
        $frame_length1 = strpos($mpeg_audio_data, $sync_pattern1, 4);
934
        $frame_length2 = strpos($mpeg_audio_data, $sync_pattern2, 4);
935
        if ($frame_length1 > 4) {
936
            $frame_length = $frame_length1;
937
        }
938
        if (($frame_length2 > 4) && ($frame_length2 < $frame_length1)) {
939
            $frame_length = $frame_length2;
940
        }
941
        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...
942
            // LAME 3.88 has a different value for modeextension on the first frame vs the rest
943
            $frame_length1 = strpos($mpeg_audio_data, substr($sync_pattern1, 0, 3), 4);
944
            $frame_length2 = strpos($mpeg_audio_data, substr($sync_pattern2, 0, 3), 4);
945
946
            if ($frame_length1 > 4) {
947
                $frame_length = $frame_length1;
948
            }
949
            if (($frame_length2 > 4) && ($frame_length2 < $frame_length1)) {
950
                $frame_length = $frame_length2;
951
            }
952
            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...
953
                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);
954
            } else {
955
                $this->getid3->warning('ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)');
956
                $info['audio']['codec']   = 'LAME';
957
                $info['audio']['encoder'] = 'LAME3.88';
958
                $sync_pattern1            = substr($sync_pattern1, 0, 3);
959
                $sync_pattern2            = substr($sync_pattern2, 0, 3);
960
            }
961
        }
962
963
        if ($deep_scan) {
964
            $actual_frame_length_values = [];
965
            $next_offset                = $offset + $frame_length;
966
            while ($next_offset < ($info['avdataend'] - 6)) {
967
                fseek($fd, $next_offset - 1, SEEK_SET);
968
                $NextSyncPattern = fread($fd, 6);
969
                if ((substr($NextSyncPattern, 1, strlen($sync_pattern1)) == $sync_pattern1) || (substr($NextSyncPattern, 1, strlen($sync_pattern2)) == $sync_pattern2)) {
970
                    // good - found where expected
971
                    $actual_frame_length_values[] = $frame_length;
972
                } elseif ((substr($NextSyncPattern, 0, strlen($sync_pattern1)) == $sync_pattern1) || (substr($NextSyncPattern, 0, strlen($sync_pattern2)) == $sync_pattern2)) {
973
                    // ok - found one byte earlier than expected (last frame wasn't padded, first frame was)
974
                    $actual_frame_length_values[] = ($frame_length - 1);
975
                    $next_offset--;
976
                } elseif ((substr($NextSyncPattern, 2, strlen($sync_pattern1)) == $sync_pattern1) || (substr($NextSyncPattern, 2, strlen($sync_pattern2)) == $sync_pattern2)) {
977
                    // ok - found one byte later than expected (last frame was padded, first frame wasn't)
978
                    $actual_frame_length_values[] = ($frame_length + 1);
979
                    $next_offset++;
980
                } else {
981
                    throw new getid3_exception('Did not find expected free-format sync pattern at offset ' . $next_offset);
982
                }
983
                $next_offset += $frame_length;
984
            }
985
            if (count($actual_frame_length_values) > 0) {
986
                $frame_length = intval(round(array_sum($actual_frame_length_values) / count($actual_frame_length_values)));
987
            }
988
        }
989
        return $frame_length;
990
    }
991
992
    public function getOnlyMPEGaudioInfo($fd, &$info, $avdata_offset, $bit_rate_histogram = false)
993
    {
994
        // looks for synch, decodes MPEG audio header
995
996
        fseek($fd, $avdata_offset, SEEK_SET);
997
998
        $sync_seek_buffer_size = min(128 * 1024, $info['avdataend'] - $avdata_offset);
999
        $header                = fread($fd, $sync_seek_buffer_size);
1000
        $sync_seek_buffer_size = strlen($header);
1001
        $synch_seek_offset     = 0;
1002
1003
        static $mpeg_audio_version_lookup;
1004
        static $mpeg_audio_layer_lookup;
1005
        static $mpeg_audio_bitrate_lookup;
1006
        if (empty($mpeg_audio_version_lookup)) {
1007
            $mpeg_audio_version_lookup = getid3_mp3::MPEGaudioVersionarray();
1008
            $mpeg_audio_layer_lookup   = getid3_mp3::MPEGaudioLayerarray();
1009
            $mpeg_audio_bitrate_lookup = getid3_mp3::MPEGaudioBitratearray();
1010
        }
1011
1012
        while ($synch_seek_offset < $sync_seek_buffer_size) {
1013
            if ((($avdata_offset + $synch_seek_offset) < $info['avdataend']) && !feof($fd)) {
1014
                // if a synch's not found within the first 128k bytes, then give up
1015
                if ($synch_seek_offset > $sync_seek_buffer_size) {
1016
                    throw new getid3_exception('Could not find valid MPEG audio synch within the first ' . round($sync_seek_buffer_size / 1024) . 'kB');
1017
                }
1018
1019
                if (feof($fd)) {
1020
                    throw new getid3_exception('Could not find valid MPEG audio synch before end of file');
1021
                }
1022
            }
1023
1024
            if (($synch_seek_offset + 1) >= strlen($header)) {
1025
                throw new getid3_exception('Could not find valid MPEG synch before end of file');
1026
            }
1027
1028
            if (("\xFF" == $header{$synch_seek_offset}) && ($header{($synch_seek_offset + 1)} > "\xE0")) { // synch detected
1029
1030
                if (!isset($first_frame_info) && !isset($info['mpeg']['audio'])) {
1031
                    $first_frame_info          = $info;
1032
                    $first_frame_avdata_offset = $avdata_offset + $synch_seek_offset;
1033
                    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...
Bug Best Practice introduced by
The method getid3_mp3::decodeMPEGaudioHeader() is not static, but was called statically. ( Ignorable by Annotation )

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

1033
                    if (!getid3_mp3::/** @scrutinizer ignore-call */ decodeMPEGaudioHeader($fd, $avdata_offset + $synch_seek_offset, $first_frame_info, false)) {
Loading history...
1034
                        // if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's
1035
                        // garbage between this frame and a valid sequence of MPEG-audio frames, to be restored below
1036
                        unset($first_frame_info);
1037
                    }
1038
                }
1039
1040
                $dummy = $info; // only overwrite real data if valid header found
1041
                if (getid3_mp3::decodeMPEGaudioHeader($fd, $avdata_offset + $synch_seek_offset, $dummy, true)) {
1042
                    $info                 = $dummy;
1043
                    $info['avdataoffset'] = $avdata_offset + $synch_seek_offset;
1044
1045
                    switch (@$info['fileformat']) {
1046
                        case '':
1047
                        case 'mp3':
1048
                            $info['fileformat']          = 'mp3';
1049
                            $info['audio']['dataformat'] = 'mp3';
1050
                            break;
1051
                    }
1052
                    if (isset($first_frame_info['mpeg']['audio']['bitrate_mode']) && ('vbr' == $first_frame_info['mpeg']['audio']['bitrate_mode'])) {
1053
                        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...
1054
                            // If there is garbage data between a valid VBR header frame and a sequence
1055
                            // of valid MPEG-audio frames the VBR data is no longer discarded.
1056
                            $info                        = $first_frame_info;
1057
                            $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...
1058
                            $info['fileformat']          = 'mp3';
1059
                            $info['audio']['dataformat'] = 'mp3';
1060
                            $dummy                       = $info;
1061
                            unset($dummy['mpeg']['audio']);
1062
                            $GarbageOffsetStart = $first_frame_avdata_offset + $first_frame_info['mpeg']['audio']['framelength'];
1063
                            $GarbageOffsetEnd   = $avdata_offset + $synch_seek_offset;
1064
                            if (getid3_mp3::decodeMPEGaudioHeader($fd, $GarbageOffsetEnd, $dummy, true, true)) {
1065
                                $info                 = $dummy;
1066
                                $info['avdataoffset'] = $GarbageOffsetEnd;
1067
                                $this->getid3->warning(
1068
                                    'apparently-valid VBR header not used because could not find '
1069
                                    . getid3_mp3::VALID_CHECK_FRAMES
1070
                                    . ' consecutive MPEG-audio frames immediately after VBR header (garbage data for '
1071
                                    . ($GarbageOffsetEnd - $GarbageOffsetStart)
1072
                                    . ' bytes between '
1073
                                    . $GarbageOffsetStart
1074
                                    . ' and '
1075
                                    . $GarbageOffsetEnd
1076
                                    . '), but did find valid CBR stream starting at '
1077
                                    . $GarbageOffsetEnd
1078
                                );
1079
                            } else {
1080
                                $this->getid3->warning(
1081
                                    'using data from VBR header even though could not find '
1082
                                    . getid3_mp3::VALID_CHECK_FRAMES
1083
                                    . ' consecutive MPEG-audio frames immediately after VBR header (garbage data for '
1084
                                    . ($GarbageOffsetEnd - $GarbageOffsetStart)
1085
                                    . ' bytes between '
1086
                                    . $GarbageOffsetStart
1087
                                    . ' and '
1088
                                    . $GarbageOffsetEnd
1089
                                    . ')'
1090
                                );
1091
                            }
1092
                        }
1093
                    }
1094
                    if (isset($info['mpeg']['audio']['bitrate_mode']) && ('vbr' == $info['mpeg']['audio']['bitrate_mode']) && !isset($info['mpeg']['audio']['VBR_method'])) {
1095
                        // VBR file with no VBR header
1096
                        $bit_rate_histogram = true;
1097
                    }
1098
1099
                    if ($bit_rate_histogram) {
1100
                        $info['mpeg']['audio']['stereo_distribution']  = ['stereo' => 0, 'joint stereo' => 0, 'dual channel' => 0, 'mono' => 0];
1101
                        $info['mpeg']['audio']['version_distribution'] = ['1' => 0, '2' => 0, '2.5' => 0];
1102
1103
                        if ('1' == $info['mpeg']['audio']['version']) {
1104
                            if (3 == $info['mpeg']['audio']['layer']) {
1105
                                $info['mpeg']['audio']['bitrate_distribution'] = ['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];
1106
                            } elseif (2 == $info['mpeg']['audio']['layer']) {
1107
                                $info['mpeg']['audio']['bitrate_distribution'] = ['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];
1108
                            } elseif (1 == $info['mpeg']['audio']['layer']) {
1109
                                $info['mpeg']['audio']['bitrate_distribution'] = ['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];
1110
                            }
1111
                        } elseif (1 == $info['mpeg']['audio']['layer']) {
1112
                            $info['mpeg']['audio']['bitrate_distribution'] = ['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];
1113
                        } else {
1114
                            $info['mpeg']['audio']['bitrate_distribution'] = ['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];
1115
                        }
1116
1117
                        $dummy              = ['avdataend' => $info['avdataend'], 'avdataoffset' => $info['avdataoffset']];
1118
                        $synch_start_offset = $info['avdataoffset'];
1119
1120
                        $fast_mode          = false;
1121
                        $synch_errors_found = 0;
1122
                        while ($this->decodeMPEGaudioHeader($fd, $synch_start_offset, $dummy, false, false, $fast_mode)) {
1123
                            $fast_mode        = true;
1124
                            $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']];
1125
1126
                            if (empty($dummy['mpeg']['audio']['framelength'])) {
1127
                                $synch_errors_found++;
1128
                            } else {
1129
                                @$info['mpeg']['audio']['bitrate_distribution'][$thisframebitrate]++;
1130
                                @$info['mpeg']['audio']['stereo_distribution'][$dummy['mpeg']['audio']['channelmode']]++;
1131
                                @$info['mpeg']['audio']['version_distribution'][$dummy['mpeg']['audio']['version']]++;
1132
1133
                                $synch_start_offset += $dummy['mpeg']['audio']['framelength'];
1134
                            }
1135
                        }
1136
                        if ($synch_errors_found > 0) {
1137
                            $this->getid3->warning('Found ' . $synch_errors_found . ' synch errors in histogram analysis');
1138
                        }
1139
1140
                        $bit_total     = 0;
1141
                        $frame_counter = 0;
1142
                        foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bit_rate_value => $bit_rate_count) {
1143
                            $frame_counter += $bit_rate_count;
1144
                            if ('free' != $bit_rate_value) {
1145
                                $bit_total += ($bit_rate_value * $bit_rate_count);
1146
                            }
1147
                        }
1148
                        if (0 == $frame_counter) {
1149
                            throw new getid3_exception('Corrupt MP3 file: framecounter == zero');
1150
                        }
1151
                        $info['mpeg']['audio']['frame_count'] = $frame_counter;
1152
                        $info['mpeg']['audio']['bitrate']     = ($bit_total / $frame_counter);
1153
1154
                        $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
1155
1156
                        // Definitively set VBR vs CBR, even if the Xing/LAME/VBRI header says differently
1157
                        $distinct_bit_rates = 0;
1158
                        foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bit_rate_value => $bit_rate_count) {
1159
                            if ($bit_rate_count > 0) {
1160
                                $distinct_bit_rates++;
1161
                            }
1162
                        }
1163
                        if ($distinct_bit_rates > 1) {
1164
                            $info['mpeg']['audio']['bitrate_mode'] = 'vbr';
1165
                        } else {
1166
                            $info['mpeg']['audio']['bitrate_mode'] = 'cbr';
1167
                        }
1168
                        $info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode'];
1169
                    }
1170
1171
                    break; // exit while()
1172
                }
1173
            }
1174
1175
            $synch_seek_offset++;
1176
            if (($avdata_offset + $synch_seek_offset) >= $info['avdataend']) {
1177
                // end of file/data
1178
1179
                if (empty($info['mpeg']['audio'])) {
1180
                    throw new getid3_exception('could not find valid MPEG synch before end of file');
1181
                }
1182
                break;
1183
            }
1184
        }
1185
1186
        $info['audio']['channels']    = $info['mpeg']['audio']['channels'];
1187
        $info['audio']['channelmode'] = $info['mpeg']['audio']['channelmode'];
1188
        $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
1189
        return true;
1190
    }
1191
1192
    public static function MPEGaudioVersionarray()
1193
    {
1194
        static $array = ['2.5', false, '2', '1'];
1195
        return $array;
1196
    }
1197
1198
    public static function MPEGaudioLayerarray()
1199
    {
1200
        static $array = [false, 3, 2, 1];
1201
        return $array;
1202
    }
1203
1204
    public static function MPEGaudioBitratearray()
1205
    {
1206
        static $array;
1207
        if (empty($array)) {
1208
            $array         = [
1209
                '1' => [
1210
                    1 => ['free', 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000],
1211
                    2 => ['free', 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000],
1212
                    3 => ['free', 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000]
1213
                ],
1214
1215
                '2' => [
1216
                    1 => ['free', 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000],
1217
                    2 => ['free', 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000],
1218
                ]
1219
            ];
1220
            $array['2'][3] = $array['2'][2];
1221
            $array['2.5']  = $array['2'];
1222
        }
1223
        return $array;
1224
    }
1225
1226
    public static function MPEGaudioFrequencyarray()
1227
    {
1228
        static $array = [
1229
            '1'   => [44100, 48000, 32000],
1230
            '2'   => [22050, 24000, 16000],
1231
            '2.5' => [11025, 12000, 8000]
1232
        ];
1233
        return $array;
1234
    }
1235
1236
    public static function MPEGaudioChannelModearray()
1237
    {
1238
        static $array = ['stereo', 'joint stereo', 'dual channel', 'mono'];
1239
        return $array;
1240
    }
1241
1242
    public static function MPEGaudioModeExtensionarray()
1243
    {
1244
        static $array = [
1245
            1 => ['4-31', '8-31', '12-31', '16-31'],
1246
            2 => ['4-31', '8-31', '12-31', '16-31'],
1247
            3 => ['', 'IS', 'MS', 'IS+MS']
1248
        ];
1249
        return $array;
1250
    }
1251
1252
    public static function MPEGaudioEmphasisarray()
1253
    {
1254
        static $array = ['none', '50/15ms', false, 'CCIT J.17'];
1255
        return $array;
1256
    }
1257
1258
    public static function MPEGaudioHeaderBytesValid($head4, $allow_bitrate_15 = false)
1259
    {
1260
        return getid3_mp3::MPEGaudioHeaderValid(getid3_mp3::MPEGaudioHeaderDecode($head4), false, $allow_bitrate_15);
1261
    }
1262
1263
    public static function MPEGaudioHeaderValid($raw_array, $echo_errors = false, $allow_bitrate_15 = false)
1264
    {
1265
        if (0x0FFE != ($raw_array['synch'] & 0x0FFE)) {
1266
            return false;
1267
        }
1268
1269
        static $mpeg_audio_version_lookup;
1270
        static $mpeg_audio_layer_lookup;
1271
        static $mpeg_audio_bitrate_lookup;
1272
        static $mpeg_audio_frequency_lookup;
1273
        static $mpeg_audio_channel_mode_lookup;
1274
        static $mpeg_audio_mode_extension_lookup;
1275
        static $mpeg_audio_emphasis_lookup;
1276
        if (empty($mpeg_audio_version_lookup)) {
1277
            $mpeg_audio_version_lookup        = getid3_mp3::MPEGaudioVersionarray();
1278
            $mpeg_audio_layer_lookup          = getid3_mp3::MPEGaudioLayerarray();
1279
            $mpeg_audio_bitrate_lookup        = getid3_mp3::MPEGaudioBitratearray();
1280
            $mpeg_audio_frequency_lookup      = getid3_mp3::MPEGaudioFrequencyarray();
1281
            $mpeg_audio_channel_mode_lookup   = getid3_mp3::MPEGaudioChannelModearray();
1282
            $mpeg_audio_mode_extension_lookup = getid3_mp3::MPEGaudioModeExtensionarray();
1283
            $mpeg_audio_emphasis_lookup       = getid3_mp3::MPEGaudioEmphasisarray();
1284
        }
1285
1286
        if (isset($mpeg_audio_version_lookup[$raw_array['version']])) {
1287
            $decodedVersion = $mpeg_audio_version_lookup[$raw_array['version']];
1288
        } else {
1289
            echo($echo_errors ? "\n" . 'invalid Version (' . $raw_array['version'] . ')' : '');
1290
            return false;
1291
        }
1292
        if (isset($mpeg_audio_layer_lookup[$raw_array['layer']])) {
1293
            $decodedLayer = $mpeg_audio_layer_lookup[$raw_array['layer']];
1294
        } else {
1295
            echo($echo_errors ? "\n" . 'invalid Layer (' . $raw_array['layer'] . ')' : '');
1296
            return false;
1297
        }
1298
        if (!isset($mpeg_audio_bitrate_lookup[$decodedVersion][$decodedLayer][$raw_array['bitrate']])) {
1299
            echo($echo_errors ? "\n" . 'invalid Bitrate (' . $raw_array['bitrate'] . ')' : '');
1300
            if (15 == $raw_array['bitrate']) {
1301
                // known issue in LAME 3.90 - 3.93.1 where free-format has bitrate ID of 15 instead of 0
1302
                // let it go through here otherwise file will not be identified
1303
                if (!$allow_bitrate_15) {
1304
                    return false;
1305
                }
1306
            } else {
1307
                return false;
1308
            }
1309
        }
1310
        if (!isset($mpeg_audio_frequency_lookup[$decodedVersion][$raw_array['sample_rate']])) {
1311
            echo($echo_errors ? "\n" . 'invalid Frequency (' . $raw_array['sample_rate'] . ')' : '');
1312
            return false;
1313
        }
1314
        if (!isset($mpeg_audio_channel_mode_lookup[$raw_array['channelmode']])) {
1315
            echo($echo_errors ? "\n" . 'invalid ChannelMode (' . $raw_array['channelmode'] . ')' : '');
1316
            return false;
1317
        }
1318
        if (!isset($mpeg_audio_mode_extension_lookup[$decodedLayer][$raw_array['modeextension']])) {
1319
            echo($echo_errors ? "\n" . 'invalid Mode Extension (' . $raw_array['modeextension'] . ')' : '');
1320
            return false;
1321
        }
1322
        if (!isset($mpeg_audio_emphasis_lookup[$raw_array['emphasis']])) {
1323
            echo($echo_errors ? "\n" . 'invalid Emphasis (' . $raw_array['emphasis'] . ')' : '');
1324
            return false;
1325
        }
1326
        // These are just either set or not set, you can't mess that up :)
1327
        // $raw_array['protection'];
1328
        // $raw_array['padding'];
1329
        // $raw_array['private'];
1330
        // $raw_array['copyright'];
1331
        // $raw_array['original'];
1332
1333
        return true;
1334
    }
1335
1336
    public static function MPEGaudioHeaderDecode($header_four_bytes)
1337
    {
1338
        // AAAA AAAA  AAAB BCCD  EEEE FFGH  IIJJ KLMM
1339
        // A - Frame sync (all bits set)
1340
        // B - MPEG Audio version ID
1341
        // C - Layer description
1342
        // D - Protection bit
1343
        // E - Bitrate index
1344
        // F - Sampling rate frequency index
1345
        // G - Padding bit
1346
        // H - Private bit
1347
        // I - Channel Mode
1348
        // J - Mode extension (Only if Joint stereo)
1349
        // K - Copyright
1350
        // L - Original
1351
        // M - Emphasis
1352
1353
        if (4 != strlen($header_four_bytes)) {
1354
            return false;
1355
        }
1356
1357
        $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...
1358
        $mpeg_raw_header['version']       = (ord($header_four_bytes{1}) & 0x18) >> 3; //    BB
1359
        $mpeg_raw_header['layer']         = (ord($header_four_bytes{1}) & 0x06) >> 1; //      CC
1360
        $mpeg_raw_header['protection']    = (ord($header_four_bytes{1}) & 0x01);      //        D
1361
        $mpeg_raw_header['bitrate']       = (ord($header_four_bytes{2}) & 0xF0) >> 4; // EEEE
1362
        $mpeg_raw_header['sample_rate']   = (ord($header_four_bytes{2}) & 0x0C) >> 2; //     FF
1363
        $mpeg_raw_header['padding']       = (ord($header_four_bytes{2}) & 0x02) >> 1; //       G
1364
        $mpeg_raw_header['private']       = (ord($header_four_bytes{2}) & 0x01);      //        H
1365
        $mpeg_raw_header['channelmode']   = (ord($header_four_bytes{3}) & 0xC0) >> 6; // II
1366
        $mpeg_raw_header['modeextension'] = (ord($header_four_bytes{3}) & 0x30) >> 4; //   JJ
1367
        $mpeg_raw_header['copyright']     = (ord($header_four_bytes{3}) & 0x08) >> 3; //     K
1368
        $mpeg_raw_header['original']      = (ord($header_four_bytes{3}) & 0x04) >> 2; //      L
1369
        $mpeg_raw_header['emphasis']      = (ord($header_four_bytes{3}) & 0x03);      //       MM
1370
1371
        return $mpeg_raw_header;
1372
    }
1373
1374
    public static function MPEGaudioFrameLength(&$bit_rate, &$version, &$layer, $padding, &$sample_rate)
1375
    {
1376
        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...
1377
            $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...
1378
            if ('free' != $bit_rate) {
1379
                if ('1' == $version) {
1380
                    if ('1' == $layer) {
1381
                        // For Layer I slot is 32 bits long
1382
                        $frame_length_coefficient = 48;
1383
                        $slot_length              = 4;
1384
                    } else { // Layer 2 / 3
1385
1386
                        // for Layer 2 and Layer 3 slot is 8 bits long.
1387
                        $frame_length_coefficient = 144;
1388
                        $slot_length              = 1;
1389
                    }
1390
                } else { // MPEG-2 / MPEG-2.5
1391
1392
                    if ('1' == $layer) {
1393
                        // For Layer I slot is 32 bits long
1394
                        $frame_length_coefficient = 24;
1395
                        $slot_length              = 4;
1396
                    } elseif ('2' == $layer) {
1397
                        // for Layer 2 and Layer 3 slot is 8 bits long.
1398
                        $frame_length_coefficient = 144;
1399
                        $slot_length              = 1;
1400
                    } else { // layer 3
1401
1402
                        // for Layer 2 and Layer 3 slot is 8 bits long.
1403
                        $frame_length_coefficient = 72;
1404
                        $slot_length              = 1;
1405
                    }
1406
                }
1407
1408
                // FrameLengthInBytes = ((Coefficient * BitRate) / SampleRate) + Padding
1409
                if ($sample_rate > 0) {
1410
                    $new_frame_length = ($frame_length_coefficient * $bit_rate) / $sample_rate;
1411
                    $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)
1412
                    if ($padding) {
1413
                        $new_frame_length += $slot_length;
1414
                    }
1415
                    $cache[$bit_rate][$version][$layer][$padding][$sample_rate] = (int)$new_frame_length;
1416
                }
1417
            }
1418
        }
1419
        return $cache[$bit_rate][$version][$layer][$padding][$sample_rate];
1420
    }
1421
1422
    public static function ClosestStandardMP3Bitrate($bit_rate)
1423
    {
1424
        static $standard_bit_rates = [320000, 256000, 224000, 192000, 160000, 128000, 112000, 96000, 80000, 64000, 56000, 48000, 40000, 32000, 24000, 16000, 8000];
1425
        static $bit_rate_table = [0 => '-'];
1426
        $round_bit_rate = intval(round($bit_rate, -3));
1427
        if (!isset($bit_rate_table[$round_bit_rate])) {
1428
            if ($round_bit_rate > 320000) {
1429
                $bit_rate_table[$round_bit_rate] = round($bit_rate, -4);
1430
            } else {
1431
                $last_bit_rate = 320000;
1432
                foreach ($standard_bit_rates as $standard_bit_rate) {
1433
                    $bit_rate_table[$round_bit_rate] = $standard_bit_rate;
1434
                    if ($round_bit_rate >= $standard_bit_rate - (($last_bit_rate - $standard_bit_rate) / 2)) {
1435
                        break;
1436
                    }
1437
                    $last_bit_rate = $standard_bit_rate;
1438
                }
1439
            }
1440
        }
1441
        return $bit_rate_table[$round_bit_rate];
1442
    }
1443
1444
    public static function XingVBRidOffset($version, $channel_mode)
1445
    {
1446
        static $lookup = [
1447
            '1' => [
1448
                'mono'         => 0x15, // 4 + 17 = 21
1449
                'stereo'       => 0x24, // 4 + 32 = 36
1450
                'joint stereo' => 0x24,
1451
                'dual channel' => 0x24
1452
            ],
1453
1454
            '2' => [
1455
                'mono'         => 0x0D, // 4 +  9 = 13
1456
                'stereo'       => 0x15, // 4 + 17 = 21
1457
                'joint stereo' => 0x15,
1458
                'dual channel' => 0x15
1459
            ],
1460
1461
            '2.5' => [
1462
                'mono'         => 0x15,
1463
                'stereo'       => 0x15,
1464
                'joint stereo' => 0x15,
1465
                'dual channel' => 0x15
1466
            ]
1467
        ];
1468
1469
        return $lookup[$version][$channel_mode];
1470
    }
1471
1472
    public static function LAMEvbrMethodLookup($vbr_method_id)
1473
    {
1474
        static $lookup = [
1475
            0x00 => 'unknown',
1476
            0x01 => 'cbr',
1477
            0x02 => 'abr',
1478
            0x03 => 'vbr-old / vbr-rh',
1479
            0x04 => 'vbr-new / vbr-mtrh',
1480
            0x05 => 'vbr-mt',
1481
            0x06 => 'Full VBR Method 4',
1482
            0x08 => 'constant bitrate 2 pass',
1483
            0x09 => 'abr 2 pass',
1484
            0x0F => 'reserved'
1485
        ];
1486
        return (isset($lookup[$vbr_method_id]) ? $lookup[$vbr_method_id] : '');
1487
    }
1488
1489
    public static function LAMEmiscStereoModeLookup($stereo_mode_id)
1490
    {
1491
        static $lookup = [
1492
            0 => 'mono',
1493
            1 => 'stereo',
1494
            2 => 'dual mono',
1495
            3 => 'joint stereo',
1496
            4 => 'forced stereo',
1497
            5 => 'auto',
1498
            6 => 'intensity stereo',
1499
            7 => 'other'
1500
        ];
1501
        return (isset($lookup[$stereo_mode_id]) ? $lookup[$stereo_mode_id] : '');
1502
    }
1503
1504
    public static function LAMEmiscSourceSampleFrequencyLookup($source_sample_frequency_id)
1505
    {
1506
        static $lookup = [
1507
            0 => '<= 32 kHz',
1508
            1 => '44.1 kHz',
1509
            2 => '48 kHz',
1510
            3 => '> 48kHz'
1511
        ];
1512
        return (isset($lookup[$source_sample_frequency_id]) ? $lookup[$source_sample_frequency_id] : '');
1513
    }
1514
1515
    public static function LAMEsurroundInfoLookup($surround_info_id)
1516
    {
1517
        static $lookup = [
1518
            0 => 'no surround info',
1519
            1 => 'DPL encoding',
1520
            2 => 'DPL2 encoding',
1521
            3 => 'Ambisonic encoding'
1522
        ];
1523
        return (isset($lookup[$surround_info_id]) ? $lookup[$surround_info_id] : 'reserved');
1524
    }
1525
1526
    public static function LAMEpresetUsedLookup($lame_tag)
1527
    {
1528
        if (0 == $lame_tag['preset_used_id']) {
1529
            // no preset used (LAME >=3.93)
1530
            // no preset recorded (LAME <3.93)
1531
            return '';
1532
        }
1533
1534
        $lame_preset_used_lookup = [];
1535
1536
        for ($i = 8; $i <= 320; $i++) {
1537
            switch ($lame_tag['vbr_method']) {
1538
                case 'cbr':
1539
                    $lame_preset_used_lookup[$i] = '--alt-preset ' . $lame_tag['vbr_method'] . ' ' . $i;
1540
                    break;
1541
                case 'abr':
1542
                default: // other VBR modes shouldn't be here(?)
1543
                    $lame_preset_used_lookup[$i] = '--alt-preset ' . $i;
1544
                    break;
1545
            }
1546
        }
1547
1548
        // named old-style presets (studio, phone, voice, etc) are handled in GuessEncoderOptions()
1549
1550
        // named alt-presets
1551
        $lame_preset_used_lookup[1000] = '--r3mix';
1552
        $lame_preset_used_lookup[1001] = '--alt-preset standard';
1553
        $lame_preset_used_lookup[1002] = '--alt-preset extreme';
1554
        $lame_preset_used_lookup[1003] = '--alt-preset insane';
1555
        $lame_preset_used_lookup[1004] = '--alt-preset fast standard';
1556
        $lame_preset_used_lookup[1005] = '--alt-preset fast extreme';
1557
        $lame_preset_used_lookup[1006] = '--alt-preset medium';
1558
        $lame_preset_used_lookup[1007] = '--alt-preset fast medium';
1559
1560
        // LAME 3.94 additions/changes
1561
        $lame_preset_used_lookup[1010] = '--preset portable';                                                            // 3.94a15 Oct 21 2003
1562
        $lame_preset_used_lookup[1015] = '--preset radio';                                                               // 3.94a15 Oct 21 2003
1563
1564
        $lame_preset_used_lookup[320] = '--preset insane';                                                              // 3.94a15 Nov 12 2003
1565
        $lame_preset_used_lookup[410] = '-V9';
1566
        $lame_preset_used_lookup[420] = '-V8';
1567
        $lame_preset_used_lookup[430] = '--preset radio';                                                               // 3.94a15 Nov 12 2003
1568
        $lame_preset_used_lookup[440] = '-V6';
1569
        $lame_preset_used_lookup[450] = '--preset ' . ((4 == $lame_tag['raw']['vbr_method']) ? 'fast ' : '') . 'portable';  // 3.94a15 Nov 12 2003
1570
        $lame_preset_used_lookup[460] = '--preset ' . ((4 == $lame_tag['raw']['vbr_method']) ? 'fast ' : '') . 'medium';    // 3.94a15 Nov 12 2003
1571
        $lame_preset_used_lookup[470] = '--r3mix';                                                                      // 3.94b1  Dec 18 2003
1572
        $lame_preset_used_lookup[480] = '--preset ' . ((4 == $lame_tag['raw']['vbr_method']) ? 'fast ' : '') . 'standard';  // 3.94a15 Nov 12 2003
1573
        $lame_preset_used_lookup[490] = '-V1';
1574
        $lame_preset_used_lookup[500] = '--preset ' . ((4 == $lame_tag['raw']['vbr_method']) ? 'fast ' : '') . 'extreme';   // 3.94a15 Nov 12 2003
1575
1576
        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]');
1577
    }
1578
1579
}
1580
1581
1582