~canonical-sysadmins/wordpress/4.7.4

« back to all changes in this revision

Viewing changes to wp-includes/ID3/module.audio.mp3.php

  • Committer: Jacek Nykis
  • Date: 2015-01-05 16:17:05 UTC
  • Revision ID: jacek.nykis@canonical.com-20150105161705-w544l1h5mcg7u4w9
Initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
/////////////////////////////////////////////////////////////////
 
3
/// getID3() by James Heinrich <info@getid3.org>               //
 
4
//  available at http://getid3.sourceforge.net                 //
 
5
//            or http://www.getid3.org                         //
 
6
/////////////////////////////////////////////////////////////////
 
7
// See readme.txt for more details                             //
 
8
/////////////////////////////////////////////////////////////////
 
9
//                                                             //
 
10
// module.audio.mp3.php                                        //
 
11
// module for analyzing MP3 files                              //
 
12
// dependencies: NONE                                          //
 
13
//                                                            ///
 
14
/////////////////////////////////////////////////////////////////
 
15
 
 
16
 
 
17
// number of frames to scan to determine if MPEG-audio sequence is valid
 
18
// Lower this number to 5-20 for faster scanning
 
19
// Increase this number to 50+ for most accurate detection of valid VBR/CBR
 
20
// mpeg-audio streams
 
21
define('GETID3_MP3_VALID_CHECK_FRAMES', 35);
 
22
 
 
23
 
 
24
class getid3_mp3 extends getid3_handler
 
25
{
 
26
 
 
27
        public $allow_bruteforce = false; // forces getID3() to scan the file byte-by-byte and log all the valid audio frame headers - extremely slow, unrecommended, but may provide data from otherwise-unusuable files
 
28
 
 
29
        public function Analyze() {
 
30
                $info = &$this->getid3->info;
 
31
 
 
32
                $initialOffset = $info['avdataoffset'];
 
33
 
 
34
                if (!$this->getOnlyMPEGaudioInfo($info['avdataoffset'])) {
 
35
                        if ($this->allow_bruteforce) {
 
36
                                $info['error'][] = 'Rescanning file in BruteForce mode';
 
37
                                $this->getOnlyMPEGaudioInfoBruteForce($this->getid3->fp, $info);
 
38
                        }
 
39
                }
 
40
 
 
41
 
 
42
                if (isset($info['mpeg']['audio']['bitrate_mode'])) {
 
43
                        $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
 
44
                }
 
45
 
 
46
                if (((isset($info['id3v2']['headerlength']) && ($info['avdataoffset'] > $info['id3v2']['headerlength'])) || (!isset($info['id3v2']) && ($info['avdataoffset'] > 0) && ($info['avdataoffset'] != $initialOffset)))) {
 
47
 
 
48
                        $synchoffsetwarning = 'Unknown data before synch ';
 
49
                        if (isset($info['id3v2']['headerlength'])) {
 
50
                                $synchoffsetwarning .= '(ID3v2 header ends at '.$info['id3v2']['headerlength'].', then '.($info['avdataoffset'] - $info['id3v2']['headerlength']).' bytes garbage, ';
 
51
                        } elseif ($initialOffset > 0) {
 
52
                                $synchoffsetwarning .= '(should be at '.$initialOffset.', ';
 
53
                        } else {
 
54
                                $synchoffsetwarning .= '(should be at beginning of file, ';
 
55
                        }
 
56
                        $synchoffsetwarning .= 'synch detected at '.$info['avdataoffset'].')';
 
57
                        if (isset($info['audio']['bitrate_mode']) && ($info['audio']['bitrate_mode'] == 'cbr')) {
 
58
 
 
59
                                if (!empty($info['id3v2']['headerlength']) && (($info['avdataoffset'] - $info['id3v2']['headerlength']) == $info['mpeg']['audio']['framelength'])) {
 
60
 
 
61
                                        $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90-3.92) DLL in CBR mode.';
 
62
                                        $info['audio']['codec'] = 'LAME';
 
63
                                        $CurrentDataLAMEversionString = 'LAME3.';
 
64
 
 
65
                                } elseif (empty($info['id3v2']['headerlength']) && ($info['avdataoffset'] == $info['mpeg']['audio']['framelength'])) {
 
66
 
 
67
                                        $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90 - 3.92) DLL in CBR mode.';
 
68
                                        $info['audio']['codec'] = 'LAME';
 
69
                                        $CurrentDataLAMEversionString = 'LAME3.';
 
70
 
 
71
                                }
 
72
 
 
73
                        }
 
74
                        $info['warning'][] = $synchoffsetwarning;
 
75
 
 
76
                }
 
77
 
 
78
                if (isset($info['mpeg']['audio']['LAME'])) {
 
79
                        $info['audio']['codec'] = 'LAME';
 
80
                        if (!empty($info['mpeg']['audio']['LAME']['long_version'])) {
 
81
                                $info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['long_version'], "\x00");
 
82
                        } elseif (!empty($info['mpeg']['audio']['LAME']['short_version'])) {
 
83
                                $info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['short_version'], "\x00");
 
84
                        }
 
85
                }
 
86
 
 
87
                $CurrentDataLAMEversionString = (!empty($CurrentDataLAMEversionString) ? $CurrentDataLAMEversionString : (isset($info['audio']['encoder']) ? $info['audio']['encoder'] : ''));
 
88
                if (!empty($CurrentDataLAMEversionString) && (substr($CurrentDataLAMEversionString, 0, 6) == 'LAME3.') && !preg_match('[0-9\)]', substr($CurrentDataLAMEversionString, -1))) {
 
89
                        // a version number of LAME that does not end with a number like "LAME3.92"
 
90
                        // or with a closing parenthesis like "LAME3.88 (alpha)"
 
91
                        // or a version of LAME with the LAMEtag-not-filled-in-DLL-mode bug (3.90-3.92)
 
92
 
 
93
                        // not sure what the actual last frame length will be, but will be less than or equal to 1441
 
94
                        $PossiblyLongerLAMEversion_FrameLength = 1441;
 
95
 
 
96
                        // Not sure what version of LAME this is - look in padding of last frame for longer version string
 
97
                        $PossibleLAMEversionStringOffset = $info['avdataend'] - $PossiblyLongerLAMEversion_FrameLength;
 
98
                        fseek($this->getid3->fp, $PossibleLAMEversionStringOffset);
 
99
                        $PossiblyLongerLAMEversion_Data = fread($this->getid3->fp, $PossiblyLongerLAMEversion_FrameLength);
 
100
                        switch (substr($CurrentDataLAMEversionString, -1)) {
 
101
                                case 'a':
 
102
                                case 'b':
 
103
                                        // "LAME3.94a" will have a longer version string of "LAME3.94 (alpha)" for example
 
104
                                        // need to trim off "a" to match longer string
 
105
                                        $CurrentDataLAMEversionString = substr($CurrentDataLAMEversionString, 0, -1);
 
106
                                        break;
 
107
                        }
 
108
                        if (($PossiblyLongerLAMEversion_String = strstr($PossiblyLongerLAMEversion_Data, $CurrentDataLAMEversionString)) !== false) {
 
109
                                if (substr($PossiblyLongerLAMEversion_String, 0, strlen($CurrentDataLAMEversionString)) == $CurrentDataLAMEversionString) {
 
110
                                        $PossiblyLongerLAMEversion_NewString = substr($PossiblyLongerLAMEversion_String, 0, strspn($PossiblyLongerLAMEversion_String, 'LAME0123456789., (abcdefghijklmnopqrstuvwxyzJFSOND)')); //"LAME3.90.3"  "LAME3.87 (beta 1, Sep 27 2000)" "LAME3.88 (beta)"
 
111
                                        if (empty($info['audio']['encoder']) || (strlen($PossiblyLongerLAMEversion_NewString) > strlen($info['audio']['encoder']))) {
 
112
                                                $info['audio']['encoder'] = $PossiblyLongerLAMEversion_NewString;
 
113
                                        }
 
114
                                }
 
115
                        }
 
116
                }
 
117
                if (!empty($info['audio']['encoder'])) {
 
118
                        $info['audio']['encoder'] = rtrim($info['audio']['encoder'], "\x00 ");
 
119
                }
 
120
 
 
121
                switch (isset($info['mpeg']['audio']['layer']) ? $info['mpeg']['audio']['layer'] : '') {
 
122
                        case 1:
 
123
                        case 2:
 
124
                                $info['audio']['dataformat'] = 'mp'.$info['mpeg']['audio']['layer'];
 
125
                                break;
 
126
                }
 
127
                if (isset($info['fileformat']) && ($info['fileformat'] == 'mp3')) {
 
128
                        switch ($info['audio']['dataformat']) {
 
129
                                case 'mp1':
 
130
                                case 'mp2':
 
131
                                case 'mp3':
 
132
                                        $info['fileformat'] = $info['audio']['dataformat'];
 
133
                                        break;
 
134
 
 
135
                                default:
 
136
                                        $info['warning'][] = 'Expecting [audio][dataformat] to be mp1/mp2/mp3 when fileformat == mp3, [audio][dataformat] actually "'.$info['audio']['dataformat'].'"';
 
137
                                        break;
 
138
                        }
 
139
                }
 
140
 
 
141
                if (empty($info['fileformat'])) {
 
142
                        unset($info['fileformat']);
 
143
                        unset($info['audio']['bitrate_mode']);
 
144
                        unset($info['avdataoffset']);
 
145
                        unset($info['avdataend']);
 
146
                        return false;
 
147
                }
 
148
 
 
149
                $info['mime_type']         = 'audio/mpeg';
 
150
                $info['audio']['lossless'] = false;
 
151
 
 
152
                // Calculate playtime
 
153
                if (!isset($info['playtime_seconds']) && isset($info['audio']['bitrate']) && ($info['audio']['bitrate'] > 0)) {
 
154
                        $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['audio']['bitrate'];
 
155
                }
 
156
 
 
157
                $info['audio']['encoder_options'] = $this->GuessEncoderOptions();
 
158
 
 
159
                return true;
 
160
        }
 
161
 
 
162
 
 
163
        public function GuessEncoderOptions() {
 
164
                // shortcuts
 
165
                $info = &$this->getid3->info;
 
166
                if (!empty($info['mpeg']['audio'])) {
 
167
                        $thisfile_mpeg_audio = &$info['mpeg']['audio'];
 
168
                        if (!empty($thisfile_mpeg_audio['LAME'])) {
 
169
                                $thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME'];
 
170
                        }
 
171
                }
 
172
 
 
173
                $encoder_options = '';
 
174
                static $NamedPresetBitrates = array(16, 24, 40, 56, 112, 128, 160, 192, 256);
 
175
 
 
176
                if (isset($thisfile_mpeg_audio['VBR_method']) && ($thisfile_mpeg_audio['VBR_method'] == 'Fraunhofer') && !empty($thisfile_mpeg_audio['VBR_quality'])) {
 
177
 
 
178
                        $encoder_options = 'VBR q'.$thisfile_mpeg_audio['VBR_quality'];
 
179
 
 
180
                } elseif (!empty($thisfile_mpeg_audio_lame['preset_used']) && (!in_array($thisfile_mpeg_audio_lame['preset_used_id'], $NamedPresetBitrates))) {
 
181
 
 
182
                        $encoder_options = $thisfile_mpeg_audio_lame['preset_used'];
 
183
 
 
184
                } elseif (!empty($thisfile_mpeg_audio_lame['vbr_quality'])) {
 
185
 
 
186
                        static $KnownEncoderValues = array();
 
187
                        if (empty($KnownEncoderValues)) {
 
188
 
 
189
                                //$KnownEncoderValues[abrbitrate_minbitrate][vbr_quality][raw_vbr_method][raw_noise_shaping][raw_stereo_mode][ath_type][lowpass_frequency] = 'preset name';
 
190
                                $KnownEncoderValues[0xFF][58][1][1][3][2][20500] = '--alt-preset insane';        // 3.90,   3.90.1, 3.92
 
191
                                $KnownEncoderValues[0xFF][58][1][1][3][2][20600] = '--alt-preset insane';        // 3.90.2, 3.90.3, 3.91
 
192
                                $KnownEncoderValues[0xFF][57][1][1][3][4][20500] = '--alt-preset insane';        // 3.94,   3.95
 
193
                                $KnownEncoderValues['**'][78][3][2][3][2][19500] = '--alt-preset extreme';       // 3.90,   3.90.1, 3.92
 
194
                                $KnownEncoderValues['**'][78][3][2][3][2][19600] = '--alt-preset extreme';       // 3.90.2, 3.91
 
195
                                $KnownEncoderValues['**'][78][3][1][3][2][19600] = '--alt-preset extreme';       // 3.90.3
 
196
                                $KnownEncoderValues['**'][78][4][2][3][2][19500] = '--alt-preset fast extreme';  // 3.90,   3.90.1, 3.92
 
197
                                $KnownEncoderValues['**'][78][4][2][3][2][19600] = '--alt-preset fast extreme';  // 3.90.2, 3.90.3, 3.91
 
198
                                $KnownEncoderValues['**'][78][3][2][3][4][19000] = '--alt-preset standard';      // 3.90,   3.90.1, 3.90.2, 3.91, 3.92
 
199
                                $KnownEncoderValues['**'][78][3][1][3][4][19000] = '--alt-preset standard';      // 3.90.3
 
200
                                $KnownEncoderValues['**'][78][4][2][3][4][19000] = '--alt-preset fast standard'; // 3.90,   3.90.1, 3.90.2, 3.91, 3.92
 
201
                                $KnownEncoderValues['**'][78][4][1][3][4][19000] = '--alt-preset fast standard'; // 3.90.3
 
202
                                $KnownEncoderValues['**'][88][4][1][3][3][19500] = '--r3mix';                    // 3.90,   3.90.1, 3.92
 
203
                                $KnownEncoderValues['**'][88][4][1][3][3][19600] = '--r3mix';                    // 3.90.2, 3.90.3, 3.91
 
204
                                $KnownEncoderValues['**'][67][4][1][3][4][18000] = '--r3mix';                    // 3.94,   3.95
 
205
                                $KnownEncoderValues['**'][68][3][2][3][4][18000] = '--alt-preset medium';        // 3.90.3
 
206
                                $KnownEncoderValues['**'][68][4][2][3][4][18000] = '--alt-preset fast medium';   // 3.90.3
 
207
 
 
208
                                $KnownEncoderValues[0xFF][99][1][1][1][2][0]     = '--preset studio';            // 3.90,   3.90.1, 3.90.2, 3.91, 3.92
 
209
                                $KnownEncoderValues[0xFF][58][2][1][3][2][20600] = '--preset studio';            // 3.90.3, 3.93.1
 
210
                                $KnownEncoderValues[0xFF][58][2][1][3][2][20500] = '--preset studio';            // 3.93
 
211
                                $KnownEncoderValues[0xFF][57][2][1][3][4][20500] = '--preset studio';            // 3.94,   3.95
 
212
                                $KnownEncoderValues[0xC0][88][1][1][1][2][0]     = '--preset cd';                // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
 
213
                                $KnownEncoderValues[0xC0][58][2][2][3][2][19600] = '--preset cd';                // 3.90.3, 3.93.1
 
214
                                $KnownEncoderValues[0xC0][58][2][2][3][2][19500] = '--preset cd';                // 3.93
 
215
                                $KnownEncoderValues[0xC0][57][2][1][3][4][19500] = '--preset cd';                // 3.94,   3.95
 
216
                                $KnownEncoderValues[0xA0][78][1][1][3][2][18000] = '--preset hifi';              // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
 
217
                                $KnownEncoderValues[0xA0][58][2][2][3][2][18000] = '--preset hifi';              // 3.90.3, 3.93,   3.93.1
 
218
                                $KnownEncoderValues[0xA0][57][2][1][3][4][18000] = '--preset hifi';              // 3.94,   3.95
 
219
                                $KnownEncoderValues[0x80][67][1][1][3][2][18000] = '--preset tape';              // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
 
220
                                $KnownEncoderValues[0x80][67][1][1][3][2][15000] = '--preset radio';             // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
 
221
                                $KnownEncoderValues[0x70][67][1][1][3][2][15000] = '--preset fm';                // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
 
222
                                $KnownEncoderValues[0x70][58][2][2][3][2][16000] = '--preset tape/radio/fm';     // 3.90.3, 3.93,   3.93.1
 
223
                                $KnownEncoderValues[0x70][57][2][1][3][4][16000] = '--preset tape/radio/fm';     // 3.94,   3.95
 
224
                                $KnownEncoderValues[0x38][58][2][2][0][2][10000] = '--preset voice';             // 3.90.3, 3.93,   3.93.1
 
225
                                $KnownEncoderValues[0x38][57][2][1][0][4][15000] = '--preset voice';             // 3.94,   3.95
 
226
                                $KnownEncoderValues[0x38][57][2][1][0][4][16000] = '--preset voice';             // 3.94a14
 
227
                                $KnownEncoderValues[0x28][65][1][1][0][2][7500]  = '--preset mw-us';             // 3.90,   3.90.1, 3.92
 
228
                                $KnownEncoderValues[0x28][65][1][1][0][2][7600]  = '--preset mw-us';             // 3.90.2, 3.91
 
229
                                $KnownEncoderValues[0x28][58][2][2][0][2][7000]  = '--preset mw-us';             // 3.90.3, 3.93,   3.93.1
 
230
                                $KnownEncoderValues[0x28][57][2][1][0][4][10500] = '--preset mw-us';             // 3.94,   3.95
 
231
                                $KnownEncoderValues[0x28][57][2][1][0][4][11200] = '--preset mw-us';             // 3.94a14
 
232
                                $KnownEncoderValues[0x28][57][2][1][0][4][8800]  = '--preset mw-us';             // 3.94a15
 
233
                                $KnownEncoderValues[0x18][58][2][2][0][2][4000]  = '--preset phon+/lw/mw-eu/sw'; // 3.90.3, 3.93.1
 
234
                                $KnownEncoderValues[0x18][58][2][2][0][2][3900]  = '--preset phon+/lw/mw-eu/sw'; // 3.93
 
235
                                $KnownEncoderValues[0x18][57][2][1][0][4][5900]  = '--preset phon+/lw/mw-eu/sw'; // 3.94,   3.95
 
236
                                $KnownEncoderValues[0x18][57][2][1][0][4][6200]  = '--preset phon+/lw/mw-eu/sw'; // 3.94a14
 
237
                                $KnownEncoderValues[0x18][57][2][1][0][4][3200]  = '--preset phon+/lw/mw-eu/sw'; // 3.94a15
 
238
                                $KnownEncoderValues[0x10][58][2][2][0][2][3800]  = '--preset phone';             // 3.90.3, 3.93.1
 
239
                                $KnownEncoderValues[0x10][58][2][2][0][2][3700]  = '--preset phone';             // 3.93
 
240
                                $KnownEncoderValues[0x10][57][2][1][0][4][5600]  = '--preset phone';             // 3.94,   3.95
 
241
                        }
 
242
 
 
243
                        if (isset($KnownEncoderValues[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) {
 
244
 
 
245
                                $encoder_options = $KnownEncoderValues[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']];
 
246
 
 
247
                        } elseif (isset($KnownEncoderValues['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) {
 
248
 
 
249
                                $encoder_options = $KnownEncoderValues['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']];
 
250
 
 
251
                        } elseif ($info['audio']['bitrate_mode'] == 'vbr') {
 
252
 
 
253
                                // http://gabriel.mp3-tech.org/mp3infotag.html
 
254
                                // int    Quality = (100 - 10 * gfp->VBR_q - gfp->quality)h
 
255
 
 
256
 
 
257
                                $LAME_V_value = 10 - ceil($thisfile_mpeg_audio_lame['vbr_quality'] / 10);
 
258
                                $LAME_q_value = 100 - $thisfile_mpeg_audio_lame['vbr_quality'] - ($LAME_V_value * 10);
 
259
                                $encoder_options = '-V'.$LAME_V_value.' -q'.$LAME_q_value;
 
260
 
 
261
                        } elseif ($info['audio']['bitrate_mode'] == 'cbr') {
 
262
 
 
263
                                $encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
 
264
 
 
265
                        } else {
 
266
 
 
267
                                $encoder_options = strtoupper($info['audio']['bitrate_mode']);
 
268
 
 
269
                        }
 
270
 
 
271
                } elseif (!empty($thisfile_mpeg_audio_lame['bitrate_abr'])) {
 
272
 
 
273
                        $encoder_options = 'ABR'.$thisfile_mpeg_audio_lame['bitrate_abr'];
 
274
 
 
275
                } elseif (!empty($info['audio']['bitrate'])) {
 
276
 
 
277
                        if ($info['audio']['bitrate_mode'] == 'cbr') {
 
278
                                $encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
 
279
                        } else {
 
280
                                $encoder_options = strtoupper($info['audio']['bitrate_mode']);
 
281
                        }
 
282
 
 
283
                }
 
284
                if (!empty($thisfile_mpeg_audio_lame['bitrate_min'])) {
 
285
                        $encoder_options .= ' -b'.$thisfile_mpeg_audio_lame['bitrate_min'];
 
286
                }
 
287
 
 
288
                if (!empty($thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev']) || !empty($thisfile_mpeg_audio_lame['encoding_flags']['nogap_next'])) {
 
289
                        $encoder_options .= ' --nogap';
 
290
                }
 
291
 
 
292
                if (!empty($thisfile_mpeg_audio_lame['lowpass_frequency'])) {
 
293
                        $ExplodedOptions = explode(' ', $encoder_options, 4);
 
294
                        if ($ExplodedOptions[0] == '--r3mix') {
 
295
                                $ExplodedOptions[1] = 'r3mix';
 
296
                        }
 
297
                        switch ($ExplodedOptions[0]) {
 
298
                                case '--preset':
 
299
                                case '--alt-preset':
 
300
                                case '--r3mix':
 
301
                                        if ($ExplodedOptions[1] == 'fast') {
 
302
                                                $ExplodedOptions[1] .= ' '.$ExplodedOptions[2];
 
303
                                        }
 
304
                                        switch ($ExplodedOptions[1]) {
 
305
                                                case 'portable':
 
306
                                                case 'medium':
 
307
                                                case 'standard':
 
308
                                                case 'extreme':
 
309
                                                case 'insane':
 
310
                                                case 'fast portable':
 
311
                                                case 'fast medium':
 
312
                                                case 'fast standard':
 
313
                                                case 'fast extreme':
 
314
                                                case 'fast insane':
 
315
                                                case 'r3mix':
 
316
                                                        static $ExpectedLowpass = array(
 
317
                                                                        'insane|20500'        => 20500,
 
318
                                                                        'insane|20600'        => 20600,  // 3.90.2, 3.90.3, 3.91
 
319
                                                                        'medium|18000'        => 18000,
 
320
                                                                        'fast medium|18000'   => 18000,
 
321
                                                                        'extreme|19500'       => 19500,  // 3.90,   3.90.1, 3.92, 3.95
 
322
                                                                        'extreme|19600'       => 19600,  // 3.90.2, 3.90.3, 3.91, 3.93.1
 
323
                                                                        'fast extreme|19500'  => 19500,  // 3.90,   3.90.1, 3.92, 3.95
 
324
                                                                        'fast extreme|19600'  => 19600,  // 3.90.2, 3.90.3, 3.91, 3.93.1
 
325
                                                                        'standard|19000'      => 19000,
 
326
                                                                        'fast standard|19000' => 19000,
 
327
                                                                        'r3mix|19500'         => 19500,  // 3.90,   3.90.1, 3.92
 
328
                                                                        'r3mix|19600'         => 19600,  // 3.90.2, 3.90.3, 3.91
 
329
                                                                        'r3mix|18000'         => 18000,  // 3.94,   3.95
 
330
                                                                );
 
331
                                                        if (!isset($ExpectedLowpass[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio_lame['lowpass_frequency']]) && ($thisfile_mpeg_audio_lame['lowpass_frequency'] < 22050) && (round($thisfile_mpeg_audio_lame['lowpass_frequency'] / 1000) < round($thisfile_mpeg_audio['sample_rate'] / 2000))) {
 
332
                                                                $encoder_options .= ' --lowpass '.$thisfile_mpeg_audio_lame['lowpass_frequency'];
 
333
                                                        }
 
334
                                                        break;
 
335
 
 
336
                                                default:
 
337
                                                        break;
 
338
                                        }
 
339
                                        break;
 
340
                        }
 
341
                }
 
342
 
 
343
                if (isset($thisfile_mpeg_audio_lame['raw']['source_sample_freq'])) {
 
344
                        if (($thisfile_mpeg_audio['sample_rate'] == 44100) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 1)) {
 
345
                                $encoder_options .= ' --resample 44100';
 
346
                        } elseif (($thisfile_mpeg_audio['sample_rate'] == 48000) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 2)) {
 
347
                                $encoder_options .= ' --resample 48000';
 
348
                        } elseif ($thisfile_mpeg_audio['sample_rate'] < 44100) {
 
349
                                switch ($thisfile_mpeg_audio_lame['raw']['source_sample_freq']) {
 
350
                                        case 0: // <= 32000
 
351
                                                // may or may not be same as source frequency - ignore
 
352
                                                break;
 
353
                                        case 1: // 44100
 
354
                                        case 2: // 48000
 
355
                                        case 3: // 48000+
 
356
                                                $ExplodedOptions = explode(' ', $encoder_options, 4);
 
357
                                                switch ($ExplodedOptions[0]) {
 
358
                                                        case '--preset':
 
359
                                                        case '--alt-preset':
 
360
                                                                switch ($ExplodedOptions[1]) {
 
361
                                                                        case 'fast':
 
362
                                                                        case 'portable':
 
363
                                                                        case 'medium':
 
364
                                                                        case 'standard':
 
365
                                                                        case 'extreme':
 
366
                                                                        case 'insane':
 
367
                                                                                $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
 
368
                                                                                break;
 
369
 
 
370
                                                                        default:
 
371
                                                                                static $ExpectedResampledRate = array(
 
372
                                                                                                'phon+/lw/mw-eu/sw|16000' => 16000,
 
373
                                                                                                'mw-us|24000'             => 24000, // 3.95
 
374
                                                                                                'mw-us|32000'             => 32000, // 3.93
 
375
                                                                                                'mw-us|16000'             => 16000, // 3.92
 
376
                                                                                                'phone|16000'             => 16000,
 
377
                                                                                                'phone|11025'             => 11025, // 3.94a15
 
378
                                                                                                'radio|32000'             => 32000, // 3.94a15
 
379
                                                                                                'fm/radio|32000'          => 32000, // 3.92
 
380
                                                                                                'fm|32000'                => 32000, // 3.90
 
381
                                                                                                'voice|32000'             => 32000);
 
382
                                                                                if (!isset($ExpectedResampledRate[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio['sample_rate']])) {
 
383
                                                                                        $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
 
384
                                                                                }
 
385
                                                                                break;
 
386
                                                                }
 
387
                                                                break;
 
388
 
 
389
                                                        case '--r3mix':
 
390
                                                        default:
 
391
                                                                $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
 
392
                                                                break;
 
393
                                                }
 
394
                                                break;
 
395
                                }
 
396
                        }
 
397
                }
 
398
                if (empty($encoder_options) && !empty($info['audio']['bitrate']) && !empty($info['audio']['bitrate_mode'])) {
 
399
                        //$encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
 
400
                        $encoder_options = strtoupper($info['audio']['bitrate_mode']);
 
401
                }
 
402
 
 
403
                return $encoder_options;
 
404
        }
 
405
 
 
406
 
 
407
        public function decodeMPEGaudioHeader($offset, &$info, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false) {
 
408
                static $MPEGaudioVersionLookup;
 
409
                static $MPEGaudioLayerLookup;
 
410
                static $MPEGaudioBitrateLookup;
 
411
                static $MPEGaudioFrequencyLookup;
 
412
                static $MPEGaudioChannelModeLookup;
 
413
                static $MPEGaudioModeExtensionLookup;
 
414
                static $MPEGaudioEmphasisLookup;
 
415
                if (empty($MPEGaudioVersionLookup)) {
 
416
                        $MPEGaudioVersionLookup       = self::MPEGaudioVersionArray();
 
417
                        $MPEGaudioLayerLookup         = self::MPEGaudioLayerArray();
 
418
                        $MPEGaudioBitrateLookup       = self::MPEGaudioBitrateArray();
 
419
                        $MPEGaudioFrequencyLookup     = self::MPEGaudioFrequencyArray();
 
420
                        $MPEGaudioChannelModeLookup   = self::MPEGaudioChannelModeArray();
 
421
                        $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
 
422
                        $MPEGaudioEmphasisLookup      = self::MPEGaudioEmphasisArray();
 
423
                }
 
424
 
 
425
                if (fseek($this->getid3->fp, $offset, SEEK_SET) != 0) {
 
426
                        $info['error'][] = 'decodeMPEGaudioHeader() failed to seek to next offset at '.$offset;
 
427
                        return false;
 
428
                }
 
429
                //$headerstring = fread($this->getid3->fp, 1441); // worst-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame
 
430
                $headerstring = fread($this->getid3->fp, 226); // LAME header at offset 36 + 190 bytes of Xing/LAME data
 
431
 
 
432
                // MP3 audio frame structure:
 
433
                // $aa $aa $aa $aa [$bb $bb] $cc...
 
434
                // where $aa..$aa is the four-byte mpeg-audio header (below)
 
435
                // $bb $bb is the optional 2-byte CRC
 
436
                // and $cc... is the audio data
 
437
 
 
438
                $head4 = substr($headerstring, 0, 4);
 
439
 
 
440
                static $MPEGaudioHeaderDecodeCache = array();
 
441
                if (isset($MPEGaudioHeaderDecodeCache[$head4])) {
 
442
                        $MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4];
 
443
                } else {
 
444
                        $MPEGheaderRawArray = self::MPEGaudioHeaderDecode($head4);
 
445
                        $MPEGaudioHeaderDecodeCache[$head4] = $MPEGheaderRawArray;
 
446
                }
 
447
 
 
448
                static $MPEGaudioHeaderValidCache = array();
 
449
                if (!isset($MPEGaudioHeaderValidCache[$head4])) { // Not in cache
 
450
                        //$MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, true);  // allow badly-formatted freeformat (from LAME 3.90 - 3.93.1)
 
451
                        $MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, false);
 
452
                }
 
453
 
 
454
                // shortcut
 
455
                if (!isset($info['mpeg']['audio'])) {
 
456
                        $info['mpeg']['audio'] = array();
 
457
                }
 
458
                $thisfile_mpeg_audio = &$info['mpeg']['audio'];
 
459
 
 
460
 
 
461
                if ($MPEGaudioHeaderValidCache[$head4]) {
 
462
                        $thisfile_mpeg_audio['raw'] = $MPEGheaderRawArray;
 
463
                } else {
 
464
                        $info['error'][] = 'Invalid MPEG audio header ('.getid3_lib::PrintHexBytes($head4).') at offset '.$offset;
 
465
                        return false;
 
466
                }
 
467
 
 
468
                if (!$FastMPEGheaderScan) {
 
469
                        $thisfile_mpeg_audio['version']       = $MPEGaudioVersionLookup[$thisfile_mpeg_audio['raw']['version']];
 
470
                        $thisfile_mpeg_audio['layer']         = $MPEGaudioLayerLookup[$thisfile_mpeg_audio['raw']['layer']];
 
471
 
 
472
                        $thisfile_mpeg_audio['channelmode']   = $MPEGaudioChannelModeLookup[$thisfile_mpeg_audio['raw']['channelmode']];
 
473
                        $thisfile_mpeg_audio['channels']      = (($thisfile_mpeg_audio['channelmode'] == 'mono') ? 1 : 2);
 
474
                        $thisfile_mpeg_audio['sample_rate']   = $MPEGaudioFrequencyLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['raw']['sample_rate']];
 
475
                        $thisfile_mpeg_audio['protection']    = !$thisfile_mpeg_audio['raw']['protection'];
 
476
                        $thisfile_mpeg_audio['private']       = (bool) $thisfile_mpeg_audio['raw']['private'];
 
477
                        $thisfile_mpeg_audio['modeextension'] = $MPEGaudioModeExtensionLookup[$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['modeextension']];
 
478
                        $thisfile_mpeg_audio['copyright']     = (bool) $thisfile_mpeg_audio['raw']['copyright'];
 
479
                        $thisfile_mpeg_audio['original']      = (bool) $thisfile_mpeg_audio['raw']['original'];
 
480
                        $thisfile_mpeg_audio['emphasis']      = $MPEGaudioEmphasisLookup[$thisfile_mpeg_audio['raw']['emphasis']];
 
481
 
 
482
                        $info['audio']['channels']    = $thisfile_mpeg_audio['channels'];
 
483
                        $info['audio']['sample_rate'] = $thisfile_mpeg_audio['sample_rate'];
 
484
 
 
485
                        if ($thisfile_mpeg_audio['protection']) {
 
486
                                $thisfile_mpeg_audio['crc'] = getid3_lib::BigEndian2Int(substr($headerstring, 4, 2));
 
487
                        }
 
488
                }
 
489
 
 
490
                if ($thisfile_mpeg_audio['raw']['bitrate'] == 15) {
 
491
                        // http://www.hydrogenaudio.org/?act=ST&f=16&t=9682&st=0
 
492
                        $info['warning'][] = 'Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1';
 
493
                        $thisfile_mpeg_audio['raw']['bitrate'] = 0;
 
494
                }
 
495
                $thisfile_mpeg_audio['padding'] = (bool) $thisfile_mpeg_audio['raw']['padding'];
 
496
                $thisfile_mpeg_audio['bitrate'] = $MPEGaudioBitrateLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['bitrate']];
 
497
 
 
498
                if (($thisfile_mpeg_audio['bitrate'] == 'free') && ($offset == $info['avdataoffset'])) {
 
499
                        // only skip multiple frame check if free-format bitstream found at beginning of file
 
500
                        // otherwise is quite possibly simply corrupted data
 
501
                        $recursivesearch = false;
 
502
                }
 
503
 
 
504
                // For Layer 2 there are some combinations of bitrate and mode which are not allowed.
 
505
                if (!$FastMPEGheaderScan && ($thisfile_mpeg_audio['layer'] == '2')) {
 
506
 
 
507
                        $info['audio']['dataformat'] = 'mp2';
 
508
                        switch ($thisfile_mpeg_audio['channelmode']) {
 
509
 
 
510
                                case 'mono':
 
511
                                        if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] <= 192000)) {
 
512
                                                // these are ok
 
513
                                        } else {
 
514
                                                $info['error'][] = $thisfile_mpeg_audio['bitrate'].'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.';
 
515
                                                return false;
 
516
                                        }
 
517
                                        break;
 
518
 
 
519
                                case 'stereo':
 
520
                                case 'joint stereo':
 
521
                                case 'dual channel':
 
522
                                        if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] == 64000) || ($thisfile_mpeg_audio['bitrate'] >= 96000)) {
 
523
                                                // these are ok
 
524
                                        } else {
 
525
                                                $info['error'][] = intval(round($thisfile_mpeg_audio['bitrate'] / 1000)).'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.';
 
526
                                                return false;
 
527
                                        }
 
528
                                        break;
 
529
 
 
530
                        }
 
531
 
 
532
                }
 
533
 
 
534
 
 
535
                if ($info['audio']['sample_rate'] > 0) {
 
536
                        $thisfile_mpeg_audio['framelength'] = self::MPEGaudioFrameLength($thisfile_mpeg_audio['bitrate'], $thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['layer'], (int) $thisfile_mpeg_audio['padding'], $info['audio']['sample_rate']);
 
537
                }
 
538
 
 
539
                $nextframetestoffset = $offset + 1;
 
540
                if ($thisfile_mpeg_audio['bitrate'] != 'free') {
 
541
 
 
542
                        $info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
 
543
 
 
544
                        if (isset($thisfile_mpeg_audio['framelength'])) {
 
545
                                $nextframetestoffset = $offset + $thisfile_mpeg_audio['framelength'];
 
546
                        } else {
 
547
                                $info['error'][] = 'Frame at offset('.$offset.') is has an invalid frame length.';
 
548
                                return false;
 
549
                        }
 
550
 
 
551
                }
 
552
 
 
553
                $ExpectedNumberOfAudioBytes = 0;
 
554
 
 
555
                ////////////////////////////////////////////////////////////////////////////////////
 
556
                // Variable-bitrate headers
 
557
 
 
558
                if (substr($headerstring, 4 + 32, 4) == 'VBRI') {
 
559
                        // Fraunhofer VBR header is hardcoded 'VBRI' at offset 0x24 (36)
 
560
                        // specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html
 
561
 
 
562
                        $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
 
563
                        $thisfile_mpeg_audio['VBR_method']   = 'Fraunhofer';
 
564
                        $info['audio']['codec']                = 'Fraunhofer';
 
565
 
 
566
                        $SideInfoData = substr($headerstring, 4 + 2, 32);
 
567
 
 
568
                        $FraunhoferVBROffset = 36;
 
569
 
 
570
                        $thisfile_mpeg_audio['VBR_encoder_version']     = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset +  4, 2)); // VbriVersion
 
571
                        $thisfile_mpeg_audio['VBR_encoder_delay']       = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset +  6, 2)); // VbriDelay
 
572
                        $thisfile_mpeg_audio['VBR_quality']             = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset +  8, 2)); // VbriQuality
 
573
                        $thisfile_mpeg_audio['VBR_bytes']               = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 10, 4)); // VbriStreamBytes
 
574
                        $thisfile_mpeg_audio['VBR_frames']              = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 14, 4)); // VbriStreamFrames
 
575
                        $thisfile_mpeg_audio['VBR_seek_offsets']        = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 18, 2)); // VbriTableSize
 
576
                        $thisfile_mpeg_audio['VBR_seek_scale']          = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 20, 2)); // VbriTableScale
 
577
                        $thisfile_mpeg_audio['VBR_entry_bytes']         = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 22, 2)); // VbriEntryBytes
 
578
                        $thisfile_mpeg_audio['VBR_entry_frames']        = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 24, 2)); // VbriEntryFrames
 
579
 
 
580
                        $ExpectedNumberOfAudioBytes = $thisfile_mpeg_audio['VBR_bytes'];
 
581
 
 
582
                        $previousbyteoffset = $offset;
 
583
                        for ($i = 0; $i < $thisfile_mpeg_audio['VBR_seek_offsets']; $i++) {
 
584
                                $Fraunhofer_OffsetN = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset, $thisfile_mpeg_audio['VBR_entry_bytes']));
 
585
                                $FraunhoferVBROffset += $thisfile_mpeg_audio['VBR_entry_bytes'];
 
586
                                $thisfile_mpeg_audio['VBR_offsets_relative'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']);
 
587
                                $thisfile_mpeg_audio['VBR_offsets_absolute'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']) + $previousbyteoffset;
 
588
                                $previousbyteoffset += $Fraunhofer_OffsetN;
 
589
                        }
 
590
 
 
591
 
 
592
                } else {
 
593
 
 
594
                        // Xing VBR header is hardcoded 'Xing' at a offset 0x0D (13), 0x15 (21) or 0x24 (36)
 
595
                        // depending on MPEG layer and number of channels
 
596
 
 
597
                        $VBRidOffset = self::XingVBRidOffset($thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['channelmode']);
 
598
                        $SideInfoData = substr($headerstring, 4 + 2, $VBRidOffset - 4);
 
599
 
 
600
                        if ((substr($headerstring, $VBRidOffset, strlen('Xing')) == 'Xing') || (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Info')) {
 
601
                                // 'Xing' is traditional Xing VBR frame
 
602
                                // 'Info' is LAME-encoded CBR (This was done to avoid CBR files to be recognized as traditional Xing VBR files by some decoders.)
 
603
                                // 'Info' *can* legally be used to specify a VBR file as well, however.
 
604
 
 
605
                                // http://www.multiweb.cz/twoinches/MP3inside.htm
 
606
                                //00..03 = "Xing" or "Info"
 
607
                                //04..07 = Flags:
 
608
                                //  0x01  Frames Flag     set if value for number of frames in file is stored
 
609
                                //  0x02  Bytes Flag      set if value for filesize in bytes is stored
 
610
                                //  0x04  TOC Flag        set if values for TOC are stored
 
611
                                //  0x08  VBR Scale Flag  set if values for VBR scale is stored
 
612
                                //08..11  Frames: Number of frames in file (including the first Xing/Info one)
 
613
                                //12..15  Bytes:  File length in Bytes
 
614
                                //16..115  TOC (Table of Contents):
 
615
                                //  Contains of 100 indexes (one Byte length) for easier lookup in file. Approximately solves problem with moving inside file.
 
616
                                //  Each Byte has a value according this formula:
 
617
                                //  (TOC[i] / 256) * fileLenInBytes
 
618
                                //  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:
 
619
                                //  TOC[(60/240)*100] = TOC[25]
 
620
                                //  and corresponding Byte in file is then approximately at:
 
621
                                //  (TOC[25]/256) * 5000000
 
622
                                //116..119  VBR Scale
 
623
 
 
624
 
 
625
                                // should be safe to leave this at 'vbr' and let it be overriden to 'cbr' if a CBR preset/mode is used by LAME
 
626
//                              if (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Xing') {
 
627
                                        $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
 
628
                                        $thisfile_mpeg_audio['VBR_method']   = 'Xing';
 
629
//                              } else {
 
630
//                                      $ScanAsCBR = true;
 
631
//                                      $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
 
632
//                              }
 
633
 
 
634
                                $thisfile_mpeg_audio['xing_flags_raw'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 4, 4));
 
635
 
 
636
                                $thisfile_mpeg_audio['xing_flags']['frames']    = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000001);
 
637
                                $thisfile_mpeg_audio['xing_flags']['bytes']     = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000002);
 
638
                                $thisfile_mpeg_audio['xing_flags']['toc']       = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000004);
 
639
                                $thisfile_mpeg_audio['xing_flags']['vbr_scale'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000008);
 
640
 
 
641
                                if ($thisfile_mpeg_audio['xing_flags']['frames']) {
 
642
                                        $thisfile_mpeg_audio['VBR_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset +  8, 4));
 
643
                                        //$thisfile_mpeg_audio['VBR_frames']--; // don't count header Xing/Info frame
 
644
                                }
 
645
                                if ($thisfile_mpeg_audio['xing_flags']['bytes']) {
 
646
                                        $thisfile_mpeg_audio['VBR_bytes']  = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 12, 4));
 
647
                                }
 
648
 
 
649
                                //if (($thisfile_mpeg_audio['bitrate'] == 'free') && !empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
 
650
                                if (!empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
 
651
 
 
652
                                        $framelengthfloat = $thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames'];
 
653
 
 
654
                                        if ($thisfile_mpeg_audio['layer'] == '1') {
 
655
                                                // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
 
656
                                                //$info['audio']['bitrate'] = ((($framelengthfloat / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
 
657
                                                $info['audio']['bitrate'] = ($framelengthfloat / 4) * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 12;
 
658
                                        } else {
 
659
                                                // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
 
660
                                                //$info['audio']['bitrate'] = (($framelengthfloat - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
 
661
                                                $info['audio']['bitrate'] = $framelengthfloat * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 144;
 
662
                                        }
 
663
                                        $thisfile_mpeg_audio['framelength'] = floor($framelengthfloat);
 
664
                                }
 
665
 
 
666
                                if ($thisfile_mpeg_audio['xing_flags']['toc']) {
 
667
                                        $LAMEtocData = substr($headerstring, $VBRidOffset + 16, 100);
 
668
                                        for ($i = 0; $i < 100; $i++) {
 
669
                                                $thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData{$i});
 
670
                                        }
 
671
                                }
 
672
                                if ($thisfile_mpeg_audio['xing_flags']['vbr_scale']) {
 
673
                                        $thisfile_mpeg_audio['VBR_scale'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 116, 4));
 
674
                                }
 
675
 
 
676
 
 
677
                                // http://gabriel.mp3-tech.org/mp3infotag.html
 
678
                                if (substr($headerstring, $VBRidOffset + 120, 4) == 'LAME') {
 
679
 
 
680
                                        // shortcut
 
681
                                        $thisfile_mpeg_audio['LAME'] = array();
 
682
                                        $thisfile_mpeg_audio_lame    = &$thisfile_mpeg_audio['LAME'];
 
683
 
 
684
 
 
685
                                        $thisfile_mpeg_audio_lame['long_version']  = substr($headerstring, $VBRidOffset + 120, 20);
 
686
                                        $thisfile_mpeg_audio_lame['short_version'] = substr($thisfile_mpeg_audio_lame['long_version'], 0, 9);
 
687
 
 
688
                                        if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.90') {
 
689
 
 
690
                                                // extra 11 chars are not part of version string when LAMEtag present
 
691
                                                unset($thisfile_mpeg_audio_lame['long_version']);
 
692
 
 
693
                                                // It the LAME tag was only introduced in LAME v3.90
 
694
                                                // http://www.hydrogenaudio.org/?act=ST&f=15&t=9933
 
695
 
 
696
                                                // Offsets of various bytes in http://gabriel.mp3-tech.org/mp3infotag.html
 
697
                                                // are assuming a 'Xing' identifier offset of 0x24, which is the case for
 
698
                                                // MPEG-1 non-mono, but not for other combinations
 
699
                                                $LAMEtagOffsetContant = $VBRidOffset - 0x24;
 
700
 
 
701
                                                // shortcuts
 
702
                                                $thisfile_mpeg_audio_lame['RGAD']    = array('track'=>array(), 'album'=>array());
 
703
                                                $thisfile_mpeg_audio_lame_RGAD       = &$thisfile_mpeg_audio_lame['RGAD'];
 
704
                                                $thisfile_mpeg_audio_lame_RGAD_track = &$thisfile_mpeg_audio_lame_RGAD['track'];
 
705
                                                $thisfile_mpeg_audio_lame_RGAD_album = &$thisfile_mpeg_audio_lame_RGAD['album'];
 
706
                                                $thisfile_mpeg_audio_lame['raw'] = array();
 
707
                                                $thisfile_mpeg_audio_lame_raw    = &$thisfile_mpeg_audio_lame['raw'];
 
708
 
 
709
                                                // byte $9B  VBR Quality
 
710
                                                // This field is there to indicate a quality level, although the scale was not precised in the original Xing specifications.
 
711
                                                // Actually overwrites original Xing bytes
 
712
                                                unset($thisfile_mpeg_audio['VBR_scale']);
 
713
                                                $thisfile_mpeg_audio_lame['vbr_quality'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0x9B, 1));
 
714
 
 
715
                                                // bytes $9C-$A4  Encoder short VersionString
 
716
                                                $thisfile_mpeg_audio_lame['short_version'] = substr($headerstring, $LAMEtagOffsetContant + 0x9C, 9);
 
717
 
 
718
                                                // byte $A5  Info Tag revision + VBR method
 
719
                                                $LAMEtagRevisionVBRmethod = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA5, 1));
 
720
 
 
721
                                                $thisfile_mpeg_audio_lame['tag_revision']   = ($LAMEtagRevisionVBRmethod & 0xF0) >> 4;
 
722
                                                $thisfile_mpeg_audio_lame_raw['vbr_method'] =  $LAMEtagRevisionVBRmethod & 0x0F;
 
723
                                                $thisfile_mpeg_audio_lame['vbr_method']     = self::LAMEvbrMethodLookup($thisfile_mpeg_audio_lame_raw['vbr_method']);
 
724
                                                $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'
 
725
 
 
726
                                                // byte $A6  Lowpass filter value
 
727
                                                $thisfile_mpeg_audio_lame['lowpass_frequency'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA6, 1)) * 100;
 
728
 
 
729
                                                // bytes $A7-$AE  Replay Gain
 
730
                                                // http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html
 
731
                                                // bytes $A7-$AA : 32 bit floating point "Peak signal amplitude"
 
732
                                                if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.94b') {
 
733
                                                        // LAME 3.94a16 and later - 9.23 fixed point
 
734
                                                        // ie 0x0059E2EE / (2^23) = 5890798 / 8388608 = 0.7022378444671630859375
 
735
                                                        $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = (float) ((getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4))) / 8388608);
 
736
                                                } else {
 
737
                                                        // LAME 3.94a15 and earlier - 32-bit floating point
 
738
                                                        // Actually 3.94a16 will fall in here too and be WRONG, but is hard to detect 3.94a16 vs 3.94a15
 
739
                                                        $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = getid3_lib::LittleEndian2Float(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4));
 
740
                                                }
 
741
                                                if ($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] == 0) {
 
742
                                                        unset($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
 
743
                                                } else {
 
744
                                                        $thisfile_mpeg_audio_lame_RGAD['peak_db'] = getid3_lib::RGADamplitude2dB($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
 
745
                                                }
 
746
 
 
747
                                                $thisfile_mpeg_audio_lame_raw['RGAD_track']      =   getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAB, 2));
 
748
                                                $thisfile_mpeg_audio_lame_raw['RGAD_album']      =   getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAD, 2));
 
749
 
 
750
 
 
751
                                                if ($thisfile_mpeg_audio_lame_raw['RGAD_track'] != 0) {
 
752
 
 
753
                                                        $thisfile_mpeg_audio_lame_RGAD_track['raw']['name']        = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0xE000) >> 13;
 
754
                                                        $thisfile_mpeg_audio_lame_RGAD_track['raw']['originator']  = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x1C00) >> 10;
 
755
                                                        $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit']    = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x0200) >> 9;
 
756
                                                        $thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'] =  $thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x01FF;
 
757
                                                        $thisfile_mpeg_audio_lame_RGAD_track['name']       = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['name']);
 
758
                                                        $thisfile_mpeg_audio_lame_RGAD_track['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['originator']);
 
759
                                                        $thisfile_mpeg_audio_lame_RGAD_track['gain_db']    = getid3_lib::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit']);
 
760
 
 
761
                                                        if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) {
 
762
                                                                $info['replay_gain']['track']['peak']   = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
 
763
                                                        }
 
764
                                                        $info['replay_gain']['track']['originator'] = $thisfile_mpeg_audio_lame_RGAD_track['originator'];
 
765
                                                        $info['replay_gain']['track']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_track['gain_db'];
 
766
                                                } else {
 
767
                                                        unset($thisfile_mpeg_audio_lame_RGAD['track']);
 
768
                                                }
 
769
                                                if ($thisfile_mpeg_audio_lame_raw['RGAD_album'] != 0) {
 
770
 
 
771
                                                        $thisfile_mpeg_audio_lame_RGAD_album['raw']['name']        = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0xE000) >> 13;
 
772
                                                        $thisfile_mpeg_audio_lame_RGAD_album['raw']['originator']  = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x1C00) >> 10;
 
773
                                                        $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit']    = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x0200) >> 9;
 
774
                                                        $thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x01FF;
 
775
                                                        $thisfile_mpeg_audio_lame_RGAD_album['name']       = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['name']);
 
776
                                                        $thisfile_mpeg_audio_lame_RGAD_album['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['originator']);
 
777
                                                        $thisfile_mpeg_audio_lame_RGAD_album['gain_db']    = getid3_lib::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit']);
 
778
 
 
779
                                                        if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) {
 
780
                                                                $info['replay_gain']['album']['peak']   = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
 
781
                                                        }
 
782
                                                        $info['replay_gain']['album']['originator'] = $thisfile_mpeg_audio_lame_RGAD_album['originator'];
 
783
                                                        $info['replay_gain']['album']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_album['gain_db'];
 
784
                                                } else {
 
785
                                                        unset($thisfile_mpeg_audio_lame_RGAD['album']);
 
786
                                                }
 
787
                                                if (empty($thisfile_mpeg_audio_lame_RGAD)) {
 
788
                                                        unset($thisfile_mpeg_audio_lame['RGAD']);
 
789
                                                }
 
790
 
 
791
 
 
792
                                                // byte $AF  Encoding flags + ATH Type
 
793
                                                $EncodingFlagsATHtype = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAF, 1));
 
794
                                                $thisfile_mpeg_audio_lame['encoding_flags']['nspsytune']   = (bool) ($EncodingFlagsATHtype & 0x10);
 
795
                                                $thisfile_mpeg_audio_lame['encoding_flags']['nssafejoint'] = (bool) ($EncodingFlagsATHtype & 0x20);
 
796
                                                $thisfile_mpeg_audio_lame['encoding_flags']['nogap_next']  = (bool) ($EncodingFlagsATHtype & 0x40);
 
797
                                                $thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev']  = (bool) ($EncodingFlagsATHtype & 0x80);
 
798
                                                $thisfile_mpeg_audio_lame['ath_type']                      =         $EncodingFlagsATHtype & 0x0F;
 
799
 
 
800
                                                // byte $B0  if ABR {specified bitrate} else {minimal bitrate}
 
801
                                                $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB0, 1));
 
802
                                                if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 2) { // Average BitRate (ABR)
 
803
                                                        $thisfile_mpeg_audio_lame['bitrate_abr'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
 
804
                                                } elseif ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) { // Constant BitRate (CBR)
 
805
                                                        // ignore
 
806
                                                } elseif ($thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] > 0) { // Variable BitRate (VBR) - minimum bitrate
 
807
                                                        $thisfile_mpeg_audio_lame['bitrate_min'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
 
808
                                                }
 
809
 
 
810
                                                // bytes $B1-$B3  Encoder delays
 
811
                                                $EncoderDelays = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB1, 3));
 
812
                                                $thisfile_mpeg_audio_lame['encoder_delay'] = ($EncoderDelays & 0xFFF000) >> 12;
 
813
                                                $thisfile_mpeg_audio_lame['end_padding']   =  $EncoderDelays & 0x000FFF;
 
814
 
 
815
                                                // byte $B4  Misc
 
816
                                                $MiscByte = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB4, 1));
 
817
                                                $thisfile_mpeg_audio_lame_raw['noise_shaping']       = ($MiscByte & 0x03);
 
818
                                                $thisfile_mpeg_audio_lame_raw['stereo_mode']         = ($MiscByte & 0x1C) >> 2;
 
819
                                                $thisfile_mpeg_audio_lame_raw['not_optimal_quality'] = ($MiscByte & 0x20) >> 5;
 
820
                                                $thisfile_mpeg_audio_lame_raw['source_sample_freq']  = ($MiscByte & 0xC0) >> 6;
 
821
                                                $thisfile_mpeg_audio_lame['noise_shaping']       = $thisfile_mpeg_audio_lame_raw['noise_shaping'];
 
822
                                                $thisfile_mpeg_audio_lame['stereo_mode']         = self::LAMEmiscStereoModeLookup($thisfile_mpeg_audio_lame_raw['stereo_mode']);
 
823
                                                $thisfile_mpeg_audio_lame['not_optimal_quality'] = (bool) $thisfile_mpeg_audio_lame_raw['not_optimal_quality'];
 
824
                                                $thisfile_mpeg_audio_lame['source_sample_freq']  = self::LAMEmiscSourceSampleFrequencyLookup($thisfile_mpeg_audio_lame_raw['source_sample_freq']);
 
825
 
 
826
                                                // byte $B5  MP3 Gain
 
827
                                                $thisfile_mpeg_audio_lame_raw['mp3_gain'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB5, 1), false, true);
 
828
                                                $thisfile_mpeg_audio_lame['mp3_gain_db']     = (getid3_lib::RGADamplitude2dB(2) / 4) * $thisfile_mpeg_audio_lame_raw['mp3_gain'];
 
829
                                                $thisfile_mpeg_audio_lame['mp3_gain_factor'] = pow(2, ($thisfile_mpeg_audio_lame['mp3_gain_db'] / 6));
 
830
 
 
831
                                                // bytes $B6-$B7  Preset and surround info
 
832
                                                $PresetSurroundBytes = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB6, 2));
 
833
                                                // Reserved                                                    = ($PresetSurroundBytes & 0xC000);
 
834
                                                $thisfile_mpeg_audio_lame_raw['surround_info'] = ($PresetSurroundBytes & 0x3800);
 
835
                                                $thisfile_mpeg_audio_lame['surround_info']     = self::LAMEsurroundInfoLookup($thisfile_mpeg_audio_lame_raw['surround_info']);
 
836
                                                $thisfile_mpeg_audio_lame['preset_used_id']    = ($PresetSurroundBytes & 0x07FF);
 
837
                                                $thisfile_mpeg_audio_lame['preset_used']       = self::LAMEpresetUsedLookup($thisfile_mpeg_audio_lame);
 
838
                                                if (!empty($thisfile_mpeg_audio_lame['preset_used_id']) && empty($thisfile_mpeg_audio_lame['preset_used'])) {
 
839
                                                        $info['warning'][] = 'Unknown LAME preset used ('.$thisfile_mpeg_audio_lame['preset_used_id'].') - please report to info@getid3.org';
 
840
                                                }
 
841
                                                if (($thisfile_mpeg_audio_lame['short_version'] == 'LAME3.90.') && !empty($thisfile_mpeg_audio_lame['preset_used_id'])) {
 
842
                                                        // this may change if 3.90.4 ever comes out
 
843
                                                        $thisfile_mpeg_audio_lame['short_version'] = 'LAME3.90.3';
 
844
                                                }
 
845
 
 
846
                                                // bytes $B8-$BB  MusicLength
 
847
                                                $thisfile_mpeg_audio_lame['audio_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB8, 4));
 
848
                                                $ExpectedNumberOfAudioBytes = (($thisfile_mpeg_audio_lame['audio_bytes'] > 0) ? $thisfile_mpeg_audio_lame['audio_bytes'] : $thisfile_mpeg_audio['VBR_bytes']);
 
849
 
 
850
                                                // bytes $BC-$BD  MusicCRC
 
851
                                                $thisfile_mpeg_audio_lame['music_crc']    = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBC, 2));
 
852
 
 
853
                                                // bytes $BE-$BF  CRC-16 of Info Tag
 
854
                                                $thisfile_mpeg_audio_lame['lame_tag_crc'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBE, 2));
 
855
 
 
856
 
 
857
                                                // LAME CBR
 
858
                                                if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) {
 
859
 
 
860
                                                        $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
 
861
                                                        $thisfile_mpeg_audio['bitrate'] = self::ClosestStandardMP3Bitrate($thisfile_mpeg_audio['bitrate']);
 
862
                                                        $info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
 
863
                                                        //if (empty($thisfile_mpeg_audio['bitrate']) || (!empty($thisfile_mpeg_audio_lame['bitrate_min']) && ($thisfile_mpeg_audio_lame['bitrate_min'] != 255))) {
 
864
                                                        //      $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio_lame['bitrate_min'];
 
865
                                                        //}
 
866
 
 
867
                                                }
 
868
 
 
869
                                        }
 
870
                                }
 
871
 
 
872
                        } else {
 
873
 
 
874
                                // not Fraunhofer or Xing VBR methods, most likely CBR (but could be VBR with no header)
 
875
                                $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
 
876
                                if ($recursivesearch) {
 
877
                                        $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
 
878
                                        if ($this->RecursiveFrameScanning($offset, $nextframetestoffset, true)) {
 
879
                                                $recursivesearch = false;
 
880
                                                $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
 
881
                                        }
 
882
                                        if ($thisfile_mpeg_audio['bitrate_mode'] == 'vbr') {
 
883
                                                $info['warning'][] = 'VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.';
 
884
                                        }
 
885
                                }
 
886
 
 
887
                        }
 
888
 
 
889
                }
 
890
 
 
891
                if (($ExpectedNumberOfAudioBytes > 0) && ($ExpectedNumberOfAudioBytes != ($info['avdataend'] - $info['avdataoffset']))) {
 
892
                        if ($ExpectedNumberOfAudioBytes > ($info['avdataend'] - $info['avdataoffset'])) {
 
893
                                if (isset($info['fileformat']) && ($info['fileformat'] == 'riff')) {
 
894
                                        // ignore, audio data is broken into chunks so will always be data "missing"
 
895
                                } elseif (($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])) == 1) {
 
896
                                        $info['warning'][] = 'Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)';
 
897
                                } else {
 
898
                                        $info['warning'][] = 'Probable truncated file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, only found '.($info['avdataend'] - $info['avdataoffset']).' (short by '.($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])).' bytes)';
 
899
                                }
 
900
                        } else {
 
901
                                if ((($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes) == 1) {
 
902
                                //      $prenullbytefileoffset = ftell($this->getid3->fp);
 
903
                                //      fseek($this->getid3->fp, $info['avdataend'], SEEK_SET);
 
904
                                //      $PossibleNullByte = fread($this->getid3->fp, 1);
 
905
                                //      fseek($this->getid3->fp, $prenullbytefileoffset, SEEK_SET);
 
906
                                //      if ($PossibleNullByte === "\x00") {
 
907
                                                $info['avdataend']--;
 
908
                                //              $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
 
909
                                //      } else {
 
910
                                //              $info['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)';
 
911
                                //      }
 
912
                                } else {
 
913
                                        $info['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)';
 
914
                                }
 
915
                        }
 
916
                }
 
917
 
 
918
                if (($thisfile_mpeg_audio['bitrate'] == 'free') && empty($info['audio']['bitrate'])) {
 
919
                        if (($offset == $info['avdataoffset']) && empty($thisfile_mpeg_audio['VBR_frames'])) {
 
920
                                $framebytelength = $this->FreeFormatFrameLength($offset, true);
 
921
                                if ($framebytelength > 0) {
 
922
                                        $thisfile_mpeg_audio['framelength'] = $framebytelength;
 
923
                                        if ($thisfile_mpeg_audio['layer'] == '1') {
 
924
                                                // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
 
925
                                                $info['audio']['bitrate'] = ((($framebytelength / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
 
926
                                        } else {
 
927
                                                // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
 
928
                                                $info['audio']['bitrate'] = (($framebytelength - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
 
929
                                        }
 
930
                                } else {
 
931
                                        $info['error'][] = 'Error calculating frame length of free-format MP3 without Xing/LAME header';
 
932
                                }
 
933
                        }
 
934
                }
 
935
 
 
936
                if (isset($thisfile_mpeg_audio['VBR_frames']) ? $thisfile_mpeg_audio['VBR_frames'] : '') {
 
937
                        switch ($thisfile_mpeg_audio['bitrate_mode']) {
 
938
                                case 'vbr':
 
939
                                case 'abr':
 
940
                                        $bytes_per_frame = 1152;
 
941
                                        if (($thisfile_mpeg_audio['version'] == '1') && ($thisfile_mpeg_audio['layer'] == 1)) {
 
942
                                                $bytes_per_frame = 384;
 
943
                                        } elseif ((($thisfile_mpeg_audio['version'] == '2') || ($thisfile_mpeg_audio['version'] == '2.5')) && ($thisfile_mpeg_audio['layer'] == 3)) {
 
944
                                                $bytes_per_frame = 576;
 
945
                                        }
 
946
                                        $thisfile_mpeg_audio['VBR_bitrate'] = (isset($thisfile_mpeg_audio['VBR_bytes']) ? (($thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($info['audio']['sample_rate'] / $bytes_per_frame) : 0);
 
947
                                        if ($thisfile_mpeg_audio['VBR_bitrate'] > 0) {
 
948
                                                $info['audio']['bitrate']         = $thisfile_mpeg_audio['VBR_bitrate'];
 
949
                                                $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; // to avoid confusion
 
950
                                        }
 
951
                                        break;
 
952
                        }
 
953
                }
 
954
 
 
955
                // End variable-bitrate headers
 
956
                ////////////////////////////////////////////////////////////////////////////////////
 
957
 
 
958
                if ($recursivesearch) {
 
959
 
 
960
                        if (!$this->RecursiveFrameScanning($offset, $nextframetestoffset, $ScanAsCBR)) {
 
961
                                return false;
 
962
                        }
 
963
 
 
964
                }
 
965
 
 
966
 
 
967
                //if (false) {
 
968
                //    // experimental side info parsing section - not returning anything useful yet
 
969
                //
 
970
                //    $SideInfoBitstream = getid3_lib::BigEndian2Bin($SideInfoData);
 
971
                //    $SideInfoOffset = 0;
 
972
                //
 
973
                //    if ($thisfile_mpeg_audio['version'] == '1') {
 
974
                //        if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
 
975
                //            // MPEG-1 (mono)
 
976
                //            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
 
977
                //            $SideInfoOffset += 9;
 
978
                //            $SideInfoOffset += 5;
 
979
                //        } else {
 
980
                //            // MPEG-1 (stereo, joint-stereo, dual-channel)
 
981
                //            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
 
982
                //            $SideInfoOffset += 9;
 
983
                //            $SideInfoOffset += 3;
 
984
                //        }
 
985
                //    } else { // 2 or 2.5
 
986
                //        if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
 
987
                //            // MPEG-2, MPEG-2.5 (mono)
 
988
                //            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
 
989
                //            $SideInfoOffset += 8;
 
990
                //            $SideInfoOffset += 1;
 
991
                //        } else {
 
992
                //            // MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel)
 
993
                //            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
 
994
                //            $SideInfoOffset += 8;
 
995
                //            $SideInfoOffset += 2;
 
996
                //        }
 
997
                //    }
 
998
                //
 
999
                //    if ($thisfile_mpeg_audio['version'] == '1') {
 
1000
                //        for ($channel = 0; $channel < $info['audio']['channels']; $channel++) {
 
1001
                //            for ($scfsi_band = 0; $scfsi_band < 4; $scfsi_band++) {
 
1002
                //                $thisfile_mpeg_audio['scfsi'][$channel][$scfsi_band] = substr($SideInfoBitstream, $SideInfoOffset, 1);
 
1003
                //                $SideInfoOffset += 2;
 
1004
                //            }
 
1005
                //        }
 
1006
                //    }
 
1007
                //    for ($granule = 0; $granule < (($thisfile_mpeg_audio['version'] == '1') ? 2 : 1); $granule++) {
 
1008
                //        for ($channel = 0; $channel < $info['audio']['channels']; $channel++) {
 
1009
                //            $thisfile_mpeg_audio['part2_3_length'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 12);
 
1010
                //            $SideInfoOffset += 12;
 
1011
                //            $thisfile_mpeg_audio['big_values'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
 
1012
                //            $SideInfoOffset += 9;
 
1013
                //            $thisfile_mpeg_audio['global_gain'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 8);
 
1014
                //            $SideInfoOffset += 8;
 
1015
                //            if ($thisfile_mpeg_audio['version'] == '1') {
 
1016
                //                $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
 
1017
                //                $SideInfoOffset += 4;
 
1018
                //            } else {
 
1019
                //                $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
 
1020
                //                $SideInfoOffset += 9;
 
1021
                //            }
 
1022
                //            $thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
 
1023
                //            $SideInfoOffset += 1;
 
1024
                //
 
1025
                //            if ($thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] == '1') {
 
1026
                //
 
1027
                //                $thisfile_mpeg_audio['block_type'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 2);
 
1028
                //                $SideInfoOffset += 2;
 
1029
                //                $thisfile_mpeg_audio['mixed_block_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
 
1030
                //                $SideInfoOffset += 1;
 
1031
                //
 
1032
                //                for ($region = 0; $region < 2; $region++) {
 
1033
                //                    $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
 
1034
                //                    $SideInfoOffset += 5;
 
1035
                //                }
 
1036
                //                $thisfile_mpeg_audio['table_select'][$granule][$channel][2] = 0;
 
1037
                //
 
1038
                //                for ($window = 0; $window < 3; $window++) {
 
1039
                //                    $thisfile_mpeg_audio['subblock_gain'][$granule][$channel][$window] = substr($SideInfoBitstream, $SideInfoOffset, 3);
 
1040
                //                    $SideInfoOffset += 3;
 
1041
                //                }
 
1042
                //
 
1043
                //            } else {
 
1044
                //
 
1045
                //                for ($region = 0; $region < 3; $region++) {
 
1046
                //                    $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
 
1047
                //                    $SideInfoOffset += 5;
 
1048
                //                }
 
1049
                //
 
1050
                //                $thisfile_mpeg_audio['region0_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
 
1051
                //                $SideInfoOffset += 4;
 
1052
                //                $thisfile_mpeg_audio['region1_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 3);
 
1053
                //                $SideInfoOffset += 3;
 
1054
                //                $thisfile_mpeg_audio['block_type'][$granule][$channel] = 0;
 
1055
                //            }
 
1056
                //
 
1057
                //            if ($thisfile_mpeg_audio['version'] == '1') {
 
1058
                //                $thisfile_mpeg_audio['preflag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
 
1059
                //                $SideInfoOffset += 1;
 
1060
                //            }
 
1061
                //            $thisfile_mpeg_audio['scalefac_scale'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
 
1062
                //            $SideInfoOffset += 1;
 
1063
                //            $thisfile_mpeg_audio['count1table_select'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
 
1064
                //            $SideInfoOffset += 1;
 
1065
                //        }
 
1066
                //    }
 
1067
                //}
 
1068
 
 
1069
                return true;
 
1070
        }
 
1071
 
 
1072
        public function RecursiveFrameScanning(&$offset, &$nextframetestoffset, $ScanAsCBR) {
 
1073
                $info = &$this->getid3->info;
 
1074
                $firstframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
 
1075
                $this->decodeMPEGaudioHeader($offset, $firstframetestarray, false);
 
1076
 
 
1077
                for ($i = 0; $i < GETID3_MP3_VALID_CHECK_FRAMES; $i++) {
 
1078
                        // check next GETID3_MP3_VALID_CHECK_FRAMES frames for validity, to make sure we haven't run across a false synch
 
1079
                        if (($nextframetestoffset + 4) >= $info['avdataend']) {
 
1080
                                // end of file
 
1081
                                return true;
 
1082
                        }
 
1083
 
 
1084
                        $nextframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
 
1085
                        if ($this->decodeMPEGaudioHeader($nextframetestoffset, $nextframetestarray, false)) {
 
1086
                                if ($ScanAsCBR) {
 
1087
                                        // force CBR mode, used for trying to pick out invalid audio streams with valid(?) VBR headers, or VBR streams with no VBR header
 
1088
                                        if (!isset($nextframetestarray['mpeg']['audio']['bitrate']) || !isset($firstframetestarray['mpeg']['audio']['bitrate']) || ($nextframetestarray['mpeg']['audio']['bitrate'] != $firstframetestarray['mpeg']['audio']['bitrate'])) {
 
1089
                                                return false;
 
1090
                                        }
 
1091
                                }
 
1092
 
 
1093
 
 
1094
                                // next frame is OK, get ready to check the one after that
 
1095
                                if (isset($nextframetestarray['mpeg']['audio']['framelength']) && ($nextframetestarray['mpeg']['audio']['framelength'] > 0)) {
 
1096
                                        $nextframetestoffset += $nextframetestarray['mpeg']['audio']['framelength'];
 
1097
                                } else {
 
1098
                                        $info['error'][] = 'Frame at offset ('.$offset.') is has an invalid frame length.';
 
1099
                                        return false;
 
1100
                                }
 
1101
 
 
1102
                        } elseif (!empty($firstframetestarray['mpeg']['audio']['framelength']) && (($nextframetestoffset + $firstframetestarray['mpeg']['audio']['framelength']) > $info['avdataend'])) {
 
1103
 
 
1104
                                // it's not the end of the file, but there's not enough data left for another frame, so assume it's garbage/padding and return OK
 
1105
                                return true;
 
1106
 
 
1107
                        } else {
 
1108
 
 
1109
                                // next frame is not valid, note the error and fail, so scanning can contiue for a valid frame sequence
 
1110
                                $info['warning'][] = 'Frame at offset ('.$offset.') is valid, but the next one at ('.$nextframetestoffset.') is not.';
 
1111
 
 
1112
                                return false;
 
1113
                        }
 
1114
                }
 
1115
                return true;
 
1116
        }
 
1117
 
 
1118
        public function FreeFormatFrameLength($offset, $deepscan=false) {
 
1119
                $info = &$this->getid3->info;
 
1120
 
 
1121
                fseek($this->getid3->fp, $offset, SEEK_SET);
 
1122
                $MPEGaudioData = fread($this->getid3->fp, 32768);
 
1123
 
 
1124
                $SyncPattern1 = substr($MPEGaudioData, 0, 4);
 
1125
                // may be different pattern due to padding
 
1126
                $SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) | 0x02).$SyncPattern1{3};
 
1127
                if ($SyncPattern2 === $SyncPattern1) {
 
1128
                        $SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) & 0xFD).$SyncPattern1{3};
 
1129
                }
 
1130
 
 
1131
                $framelength = false;
 
1132
                $framelength1 = strpos($MPEGaudioData, $SyncPattern1, 4);
 
1133
                $framelength2 = strpos($MPEGaudioData, $SyncPattern2, 4);
 
1134
                if ($framelength1 > 4) {
 
1135
                        $framelength = $framelength1;
 
1136
                }
 
1137
                if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
 
1138
                        $framelength = $framelength2;
 
1139
                }
 
1140
                if (!$framelength) {
 
1141
 
 
1142
                        // LAME 3.88 has a different value for modeextension on the first frame vs the rest
 
1143
                        $framelength1 = strpos($MPEGaudioData, substr($SyncPattern1, 0, 3), 4);
 
1144
                        $framelength2 = strpos($MPEGaudioData, substr($SyncPattern2, 0, 3), 4);
 
1145
 
 
1146
                        if ($framelength1 > 4) {
 
1147
                                $framelength = $framelength1;
 
1148
                        }
 
1149
                        if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
 
1150
                                $framelength = $framelength2;
 
1151
                        }
 
1152
                        if (!$framelength) {
 
1153
                                $info['error'][] = 'Cannot find next free-format synch pattern ('.getid3_lib::PrintHexBytes($SyncPattern1).' or '.getid3_lib::PrintHexBytes($SyncPattern2).') after offset '.$offset;
 
1154
                                return false;
 
1155
                        } else {
 
1156
                                $info['warning'][] = 'ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)';
 
1157
                                $info['audio']['codec']   = 'LAME';
 
1158
                                $info['audio']['encoder'] = 'LAME3.88';
 
1159
                                $SyncPattern1 = substr($SyncPattern1, 0, 3);
 
1160
                                $SyncPattern2 = substr($SyncPattern2, 0, 3);
 
1161
                        }
 
1162
                }
 
1163
 
 
1164
                if ($deepscan) {
 
1165
 
 
1166
                        $ActualFrameLengthValues = array();
 
1167
                        $nextoffset = $offset + $framelength;
 
1168
                        while ($nextoffset < ($info['avdataend'] - 6)) {
 
1169
                                fseek($this->getid3->fp, $nextoffset - 1, SEEK_SET);
 
1170
                                $NextSyncPattern = fread($this->getid3->fp, 6);
 
1171
                                if ((substr($NextSyncPattern, 1, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 1, strlen($SyncPattern2)) == $SyncPattern2)) {
 
1172
                                        // good - found where expected
 
1173
                                        $ActualFrameLengthValues[] = $framelength;
 
1174
                                } elseif ((substr($NextSyncPattern, 0, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 0, strlen($SyncPattern2)) == $SyncPattern2)) {
 
1175
                                        // ok - found one byte earlier than expected (last frame wasn't padded, first frame was)
 
1176
                                        $ActualFrameLengthValues[] = ($framelength - 1);
 
1177
                                        $nextoffset--;
 
1178
                                } elseif ((substr($NextSyncPattern, 2, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 2, strlen($SyncPattern2)) == $SyncPattern2)) {
 
1179
                                        // ok - found one byte later than expected (last frame was padded, first frame wasn't)
 
1180
                                        $ActualFrameLengthValues[] = ($framelength + 1);
 
1181
                                        $nextoffset++;
 
1182
                                } else {
 
1183
                                        $info['error'][] = 'Did not find expected free-format sync pattern at offset '.$nextoffset;
 
1184
                                        return false;
 
1185
                                }
 
1186
                                $nextoffset += $framelength;
 
1187
                        }
 
1188
                        if (count($ActualFrameLengthValues) > 0) {
 
1189
                                $framelength = intval(round(array_sum($ActualFrameLengthValues) / count($ActualFrameLengthValues)));
 
1190
                        }
 
1191
                }
 
1192
                return $framelength;
 
1193
        }
 
1194
 
 
1195
        public function getOnlyMPEGaudioInfoBruteForce() {
 
1196
                $MPEGaudioHeaderDecodeCache   = array();
 
1197
                $MPEGaudioHeaderValidCache    = array();
 
1198
                $MPEGaudioHeaderLengthCache   = array();
 
1199
                $MPEGaudioVersionLookup       = self::MPEGaudioVersionArray();
 
1200
                $MPEGaudioLayerLookup         = self::MPEGaudioLayerArray();
 
1201
                $MPEGaudioBitrateLookup       = self::MPEGaudioBitrateArray();
 
1202
                $MPEGaudioFrequencyLookup     = self::MPEGaudioFrequencyArray();
 
1203
                $MPEGaudioChannelModeLookup   = self::MPEGaudioChannelModeArray();
 
1204
                $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
 
1205
                $MPEGaudioEmphasisLookup      = self::MPEGaudioEmphasisArray();
 
1206
                $LongMPEGversionLookup        = array();
 
1207
                $LongMPEGlayerLookup          = array();
 
1208
                $LongMPEGbitrateLookup        = array();
 
1209
                $LongMPEGpaddingLookup        = array();
 
1210
                $LongMPEGfrequencyLookup      = array();
 
1211
                $Distribution['bitrate']      = array();
 
1212
                $Distribution['frequency']    = array();
 
1213
                $Distribution['layer']        = array();
 
1214
                $Distribution['version']      = array();
 
1215
                $Distribution['padding']      = array();
 
1216
 
 
1217
                $info = &$this->getid3->info;
 
1218
                fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
 
1219
 
 
1220
                $max_frames_scan = 5000;
 
1221
                $frames_scanned  = 0;
 
1222
 
 
1223
                $previousvalidframe = $info['avdataoffset'];
 
1224
                while (ftell($this->getid3->fp) < $info['avdataend']) {
 
1225
                        set_time_limit(30);
 
1226
                        $head4 = fread($this->getid3->fp, 4);
 
1227
                        if (strlen($head4) < 4) {
 
1228
                                break;
 
1229
                        }
 
1230
                        if ($head4{0} != "\xFF") {
 
1231
                                for ($i = 1; $i < 4; $i++) {
 
1232
                                        if ($head4{$i} == "\xFF") {
 
1233
                                                fseek($this->getid3->fp, $i - 4, SEEK_CUR);
 
1234
                                                continue 2;
 
1235
                                        }
 
1236
                                }
 
1237
                                continue;
 
1238
                        }
 
1239
                        if (!isset($MPEGaudioHeaderDecodeCache[$head4])) {
 
1240
                                $MPEGaudioHeaderDecodeCache[$head4] = self::MPEGaudioHeaderDecode($head4);
 
1241
                        }
 
1242
                        if (!isset($MPEGaudioHeaderValidCache[$head4])) {
 
1243
                                $MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$head4], false, false);
 
1244
                        }
 
1245
                        if ($MPEGaudioHeaderValidCache[$head4]) {
 
1246
 
 
1247
                                if (!isset($MPEGaudioHeaderLengthCache[$head4])) {
 
1248
                                        $LongMPEGversionLookup[$head4]   = $MPEGaudioVersionLookup[$MPEGaudioHeaderDecodeCache[$head4]['version']];
 
1249
                                        $LongMPEGlayerLookup[$head4]     = $MPEGaudioLayerLookup[$MPEGaudioHeaderDecodeCache[$head4]['layer']];
 
1250
                                        $LongMPEGbitrateLookup[$head4]   = $MPEGaudioBitrateLookup[$LongMPEGversionLookup[$head4]][$LongMPEGlayerLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['bitrate']];
 
1251
                                        $LongMPEGpaddingLookup[$head4]   = (bool) $MPEGaudioHeaderDecodeCache[$head4]['padding'];
 
1252
                                        $LongMPEGfrequencyLookup[$head4] = $MPEGaudioFrequencyLookup[$LongMPEGversionLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['sample_rate']];
 
1253
                                        $MPEGaudioHeaderLengthCache[$head4] = self::MPEGaudioFrameLength(
 
1254
                                                $LongMPEGbitrateLookup[$head4],
 
1255
                                                $LongMPEGversionLookup[$head4],
 
1256
                                                $LongMPEGlayerLookup[$head4],
 
1257
                                                $LongMPEGpaddingLookup[$head4],
 
1258
                                                $LongMPEGfrequencyLookup[$head4]);
 
1259
                                }
 
1260
                                if ($MPEGaudioHeaderLengthCache[$head4] > 4) {
 
1261
                                        $WhereWeWere = ftell($this->getid3->fp);
 
1262
                                        fseek($this->getid3->fp, $MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR);
 
1263
                                        $next4 = fread($this->getid3->fp, 4);
 
1264
                                        if ($next4{0} == "\xFF") {
 
1265
                                                if (!isset($MPEGaudioHeaderDecodeCache[$next4])) {
 
1266
                                                        $MPEGaudioHeaderDecodeCache[$next4] = self::MPEGaudioHeaderDecode($next4);
 
1267
                                                }
 
1268
                                                if (!isset($MPEGaudioHeaderValidCache[$next4])) {
 
1269
                                                        $MPEGaudioHeaderValidCache[$next4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$next4], false, false);
 
1270
                                                }
 
1271
                                                if ($MPEGaudioHeaderValidCache[$next4]) {
 
1272
                                                        fseek($this->getid3->fp, -4, SEEK_CUR);
 
1273
 
 
1274
                                                        getid3_lib::safe_inc($Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]]);
 
1275
                                                        getid3_lib::safe_inc($Distribution['layer'][$LongMPEGlayerLookup[$head4]]);
 
1276
                                                        getid3_lib::safe_inc($Distribution['version'][$LongMPEGversionLookup[$head4]]);
 
1277
                                                        getid3_lib::safe_inc($Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]);
 
1278
                                                        getid3_lib::safe_inc($Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]);
 
1279
                                                        if ($max_frames_scan && (++$frames_scanned >= $max_frames_scan)) {
 
1280
                                                                $pct_data_scanned = (ftell($this->getid3->fp) - $info['avdataoffset']) / ($info['avdataend'] - $info['avdataoffset']);
 
1281
                                                                $info['warning'][] = 'too many MPEG audio frames to scan, only scanned first '.$max_frames_scan.' frames ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.';
 
1282
                                                                foreach ($Distribution as $key1 => $value1) {
 
1283
                                                                        foreach ($value1 as $key2 => $value2) {
 
1284
                                                                                $Distribution[$key1][$key2] = round($value2 / $pct_data_scanned);
 
1285
                                                                        }
 
1286
                                                                }
 
1287
                                                                break;
 
1288
                                                        }
 
1289
                                                        continue;
 
1290
                                                }
 
1291
                                        }
 
1292
                                        unset($next4);
 
1293
                                        fseek($this->getid3->fp, $WhereWeWere - 3, SEEK_SET);
 
1294
                                }
 
1295
 
 
1296
                        }
 
1297
                }
 
1298
                foreach ($Distribution as $key => $value) {
 
1299
                        ksort($Distribution[$key], SORT_NUMERIC);
 
1300
                }
 
1301
                ksort($Distribution['version'], SORT_STRING);
 
1302
                $info['mpeg']['audio']['bitrate_distribution']   = $Distribution['bitrate'];
 
1303
                $info['mpeg']['audio']['frequency_distribution'] = $Distribution['frequency'];
 
1304
                $info['mpeg']['audio']['layer_distribution']     = $Distribution['layer'];
 
1305
                $info['mpeg']['audio']['version_distribution']   = $Distribution['version'];
 
1306
                $info['mpeg']['audio']['padding_distribution']   = $Distribution['padding'];
 
1307
                if (count($Distribution['version']) > 1) {
 
1308
                        $info['error'][] = 'Corrupt file - more than one MPEG version detected';
 
1309
                }
 
1310
                if (count($Distribution['layer']) > 1) {
 
1311
                        $info['error'][] = 'Corrupt file - more than one MPEG layer detected';
 
1312
                }
 
1313
                if (count($Distribution['frequency']) > 1) {
 
1314
                        $info['error'][] = 'Corrupt file - more than one MPEG sample rate detected';
 
1315
                }
 
1316
 
 
1317
 
 
1318
                $bittotal = 0;
 
1319
                foreach ($Distribution['bitrate'] as $bitratevalue => $bitratecount) {
 
1320
                        if ($bitratevalue != 'free') {
 
1321
                                $bittotal += ($bitratevalue * $bitratecount);
 
1322
                        }
 
1323
                }
 
1324
                $info['mpeg']['audio']['frame_count']  = array_sum($Distribution['bitrate']);
 
1325
                if ($info['mpeg']['audio']['frame_count'] == 0) {
 
1326
                        $info['error'][] = 'no MPEG audio frames found';
 
1327
                        return false;
 
1328
                }
 
1329
                $info['mpeg']['audio']['bitrate']      = ($bittotal / $info['mpeg']['audio']['frame_count']);
 
1330
                $info['mpeg']['audio']['bitrate_mode'] = ((count($Distribution['bitrate']) > 0) ? 'vbr' : 'cbr');
 
1331
                $info['mpeg']['audio']['sample_rate']  = getid3_lib::array_max($Distribution['frequency'], true);
 
1332
 
 
1333
                $info['audio']['bitrate']      = $info['mpeg']['audio']['bitrate'];
 
1334
                $info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode'];
 
1335
                $info['audio']['sample_rate']  = $info['mpeg']['audio']['sample_rate'];
 
1336
                $info['audio']['dataformat']   = 'mp'.getid3_lib::array_max($Distribution['layer'], true);
 
1337
                $info['fileformat']            = $info['audio']['dataformat'];
 
1338
 
 
1339
                return true;
 
1340
        }
 
1341
 
 
1342
 
 
1343
        public function getOnlyMPEGaudioInfo($avdataoffset, $BitrateHistogram=false) {
 
1344
                // looks for synch, decodes MPEG audio header
 
1345
 
 
1346
                $info = &$this->getid3->info;
 
1347
 
 
1348
                static $MPEGaudioVersionLookup;
 
1349
                static $MPEGaudioLayerLookup;
 
1350
                static $MPEGaudioBitrateLookup;
 
1351
                if (empty($MPEGaudioVersionLookup)) {
 
1352
                   $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
 
1353
                   $MPEGaudioLayerLookup   = self::MPEGaudioLayerArray();
 
1354
                   $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
 
1355
 
 
1356
                }
 
1357
 
 
1358
                fseek($this->getid3->fp, $avdataoffset, SEEK_SET);
 
1359
                $sync_seek_buffer_size = min(128 * 1024, $info['avdataend'] - $avdataoffset);
 
1360
                if ($sync_seek_buffer_size <= 0) {
 
1361
                        $info['error'][] = 'Invalid $sync_seek_buffer_size at offset '.$avdataoffset;
 
1362
                        return false;
 
1363
                }
 
1364
                $header = fread($this->getid3->fp, $sync_seek_buffer_size);
 
1365
                $sync_seek_buffer_size = strlen($header);
 
1366
                $SynchSeekOffset = 0;
 
1367
                while ($SynchSeekOffset < $sync_seek_buffer_size) {
 
1368
                        if ((($avdataoffset + $SynchSeekOffset)  < $info['avdataend']) && !feof($this->getid3->fp)) {
 
1369
 
 
1370
                                if ($SynchSeekOffset > $sync_seek_buffer_size) {
 
1371
                                        // if a synch's not found within the first 128k bytes, then give up
 
1372
                                        $info['error'][] = 'Could not find valid MPEG audio synch within the first '.round($sync_seek_buffer_size / 1024).'kB';
 
1373
                                        if (isset($info['audio']['bitrate'])) {
 
1374
                                                unset($info['audio']['bitrate']);
 
1375
                                        }
 
1376
                                        if (isset($info['mpeg']['audio'])) {
 
1377
                                                unset($info['mpeg']['audio']);
 
1378
                                        }
 
1379
                                        if (empty($info['mpeg'])) {
 
1380
                                                unset($info['mpeg']);
 
1381
                                        }
 
1382
                                        return false;
 
1383
 
 
1384
                                } elseif (feof($this->getid3->fp)) {
 
1385
 
 
1386
                                        $info['error'][] = 'Could not find valid MPEG audio synch before end of file';
 
1387
                                        if (isset($info['audio']['bitrate'])) {
 
1388
                                                unset($info['audio']['bitrate']);
 
1389
                                        }
 
1390
                                        if (isset($info['mpeg']['audio'])) {
 
1391
                                                unset($info['mpeg']['audio']);
 
1392
                                        }
 
1393
                                        if (isset($info['mpeg']) && (!is_array($info['mpeg']) || (count($info['mpeg']) == 0))) {
 
1394
                                                unset($info['mpeg']);
 
1395
                                        }
 
1396
                                        return false;
 
1397
                                }
 
1398
                        }
 
1399
 
 
1400
                        if (($SynchSeekOffset + 1) >= strlen($header)) {
 
1401
                                $info['error'][] = 'Could not find valid MPEG synch before end of file';
 
1402
                                return false;
 
1403
                        }
 
1404
 
 
1405
                        if (($header{$SynchSeekOffset} == "\xFF") && ($header{($SynchSeekOffset + 1)} > "\xE0")) { // synch detected
 
1406
                                if (!isset($FirstFrameThisfileInfo) && !isset($info['mpeg']['audio'])) {
 
1407
                                        $FirstFrameThisfileInfo = $info;
 
1408
                                        $FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset;
 
1409
                                        if (!$this->decodeMPEGaudioHeader($FirstFrameAVDataOffset, $FirstFrameThisfileInfo, false)) {
 
1410
                                                // if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's
 
1411
                                                // garbage between this frame and a valid sequence of MPEG-audio frames, to be restored below
 
1412
                                                unset($FirstFrameThisfileInfo);
 
1413
                                        }
 
1414
                                }
 
1415
 
 
1416
                                $dummy = $info; // only overwrite real data if valid header found
 
1417
                                if ($this->decodeMPEGaudioHeader($avdataoffset + $SynchSeekOffset, $dummy, true)) {
 
1418
                                        $info = $dummy;
 
1419
                                        $info['avdataoffset'] = $avdataoffset + $SynchSeekOffset;
 
1420
                                        switch (isset($info['fileformat']) ? $info['fileformat'] : '') {
 
1421
                                                case '':
 
1422
                                                case 'id3':
 
1423
                                                case 'ape':
 
1424
                                                case 'mp3':
 
1425
                                                        $info['fileformat']          = 'mp3';
 
1426
                                                        $info['audio']['dataformat'] = 'mp3';
 
1427
                                                        break;
 
1428
                                        }
 
1429
                                        if (isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) {
 
1430
                                                if (!(abs($info['audio']['bitrate'] - $FirstFrameThisfileInfo['audio']['bitrate']) <= 1)) {
 
1431
                                                        // If there is garbage data between a valid VBR header frame and a sequence
 
1432
                                                        // of valid MPEG-audio frames the VBR data is no longer discarded.
 
1433
                                                        $info = $FirstFrameThisfileInfo;
 
1434
                                                        $info['avdataoffset']        = $FirstFrameAVDataOffset;
 
1435
                                                        $info['fileformat']          = 'mp3';
 
1436
                                                        $info['audio']['dataformat'] = 'mp3';
 
1437
                                                        $dummy                       = $info;
 
1438
                                                        unset($dummy['mpeg']['audio']);
 
1439
                                                        $GarbageOffsetStart = $FirstFrameAVDataOffset + $FirstFrameThisfileInfo['mpeg']['audio']['framelength'];
 
1440
                                                        $GarbageOffsetEnd   = $avdataoffset + $SynchSeekOffset;
 
1441
                                                        if ($this->decodeMPEGaudioHeader($GarbageOffsetEnd, $dummy, true, true)) {
 
1442
                                                                $info = $dummy;
 
1443
                                                                $info['avdataoffset'] = $GarbageOffsetEnd;
 
1444
                                                                $info['warning'][] = 'apparently-valid VBR header not used because could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.'), but did find valid CBR stream starting at '.$GarbageOffsetEnd;
 
1445
                                                        } else {
 
1446
                                                                $info['warning'][] = 'using data from VBR header even though could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.')';
 
1447
                                                        }
 
1448
                                                }
 
1449
                                        }
 
1450
                                        if (isset($info['mpeg']['audio']['bitrate_mode']) && ($info['mpeg']['audio']['bitrate_mode'] == 'vbr') && !isset($info['mpeg']['audio']['VBR_method'])) {
 
1451
                                                // VBR file with no VBR header
 
1452
                                                $BitrateHistogram = true;
 
1453
                                        }
 
1454
 
 
1455
                                        if ($BitrateHistogram) {
 
1456
 
 
1457
                                                $info['mpeg']['audio']['stereo_distribution']  = array('stereo'=>0, 'joint stereo'=>0, 'dual channel'=>0, 'mono'=>0);
 
1458
                                                $info['mpeg']['audio']['version_distribution'] = array('1'=>0, '2'=>0, '2.5'=>0);
 
1459
 
 
1460
                                                if ($info['mpeg']['audio']['version'] == '1') {
 
1461
                                                        if ($info['mpeg']['audio']['layer'] == 3) {
 
1462
                                                                $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0);
 
1463
                                                        } elseif ($info['mpeg']['audio']['layer'] == 2) {
 
1464
                                                                $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0, 384000=>0);
 
1465
                                                        } elseif ($info['mpeg']['audio']['layer'] == 1) {
 
1466
                                                                $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 64000=>0, 96000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 288000=>0, 320000=>0, 352000=>0, 384000=>0, 416000=>0, 448000=>0);
 
1467
                                                        }
 
1468
                                                } elseif ($info['mpeg']['audio']['layer'] == 1) {
 
1469
                                                        $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0, 176000=>0, 192000=>0, 224000=>0, 256000=>0);
 
1470
                                                } else {
 
1471
                                                        $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 8000=>0, 16000=>0, 24000=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0);
 
1472
                                                }
 
1473
 
 
1474
                                                $dummy = array('error'=>$info['error'], 'warning'=>$info['warning'], 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']);
 
1475
                                                $synchstartoffset = $info['avdataoffset'];
 
1476
                                                fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
 
1477
 
 
1478
                                                // you can play with these numbers:
 
1479
                                                $max_frames_scan  = 50000;
 
1480
                                                $max_scan_segments = 10;
 
1481
 
 
1482
                                                // don't play with these numbers:
 
1483
                                                $FastMode = false;
 
1484
                                                $SynchErrorsFound = 0;
 
1485
                                                $frames_scanned   = 0;
 
1486
                                                $this_scan_segment = 0;
 
1487
                                                $frames_scan_per_segment = ceil($max_frames_scan / $max_scan_segments);
 
1488
                                                $pct_data_scanned = 0;
 
1489
                                                for ($current_segment = 0; $current_segment < $max_scan_segments; $current_segment++) {
 
1490
                                                        $frames_scanned_this_segment = 0;
 
1491
                                                        if (ftell($this->getid3->fp) >= $info['avdataend']) {
 
1492
                                                                break;
 
1493
                                                        }
 
1494
                                                        $scan_start_offset[$current_segment] = max(ftell($this->getid3->fp), $info['avdataoffset'] + round($current_segment * (($info['avdataend'] - $info['avdataoffset']) / $max_scan_segments)));
 
1495
                                                        if ($current_segment > 0) {
 
1496
                                                                fseek($this->getid3->fp, $scan_start_offset[$current_segment], SEEK_SET);
 
1497
                                                                $buffer_4k = fread($this->getid3->fp, 4096);
 
1498
                                                                for ($j = 0; $j < (strlen($buffer_4k) - 4); $j++) {
 
1499
                                                                        if (($buffer_4k{$j} == "\xFF") && ($buffer_4k{($j + 1)} > "\xE0")) { // synch detected
 
1500
                                                                                if ($this->decodeMPEGaudioHeader($scan_start_offset[$current_segment] + $j, $dummy, false, false, $FastMode)) {
 
1501
                                                                                        $calculated_next_offset = $scan_start_offset[$current_segment] + $j + $dummy['mpeg']['audio']['framelength'];
 
1502
                                                                                        if ($this->decodeMPEGaudioHeader($calculated_next_offset, $dummy, false, false, $FastMode)) {
 
1503
                                                                                                $scan_start_offset[$current_segment] += $j;
 
1504
                                                                                                break;
 
1505
                                                                                        }
 
1506
                                                                                }
 
1507
                                                                        }
 
1508
                                                                }
 
1509
                                                        }
 
1510
                                                        $synchstartoffset = $scan_start_offset[$current_segment];
 
1511
                                                        while ($this->decodeMPEGaudioHeader($synchstartoffset, $dummy, false, false, $FastMode)) {
 
1512
                                                                $FastMode = true;
 
1513
                                                                $thisframebitrate = $MPEGaudioBitrateLookup[$MPEGaudioVersionLookup[$dummy['mpeg']['audio']['raw']['version']]][$MPEGaudioLayerLookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']];
 
1514
 
 
1515
                                                                if (empty($dummy['mpeg']['audio']['framelength'])) {
 
1516
                                                                        $SynchErrorsFound++;
 
1517
                                                                        $synchstartoffset++;
 
1518
                                                                } else {
 
1519
                                                                        getid3_lib::safe_inc($info['mpeg']['audio']['bitrate_distribution'][$thisframebitrate]);
 
1520
                                                                        getid3_lib::safe_inc($info['mpeg']['audio']['stereo_distribution'][$dummy['mpeg']['audio']['channelmode']]);
 
1521
                                                                        getid3_lib::safe_inc($info['mpeg']['audio']['version_distribution'][$dummy['mpeg']['audio']['version']]);
 
1522
                                                                        $synchstartoffset += $dummy['mpeg']['audio']['framelength'];
 
1523
                                                                }
 
1524
                                                                $frames_scanned++;
 
1525
                                                                if ($frames_scan_per_segment && (++$frames_scanned_this_segment >= $frames_scan_per_segment)) {
 
1526
                                                                        $this_pct_scanned = (ftell($this->getid3->fp) - $scan_start_offset[$current_segment]) / ($info['avdataend'] - $info['avdataoffset']);
 
1527
                                                                        if (($current_segment == 0) && (($this_pct_scanned * $max_scan_segments) >= 1)) {
 
1528
                                                                                // file likely contains < $max_frames_scan, just scan as one segment
 
1529
                                                                                $max_scan_segments = 1;
 
1530
                                                                                $frames_scan_per_segment = $max_frames_scan;
 
1531
                                                                        } else {
 
1532
                                                                                $pct_data_scanned += $this_pct_scanned;
 
1533
                                                                                break;
 
1534
                                                                        }
 
1535
                                                                }
 
1536
                                                        }
 
1537
                                                }
 
1538
                                                if ($pct_data_scanned > 0) {
 
1539
                                                        $info['warning'][] = 'too many MPEG audio frames to scan, only scanned '.$frames_scanned.' frames in '.$max_scan_segments.' segments ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.';
 
1540
                                                        foreach ($info['mpeg']['audio'] as $key1 => $value1) {
 
1541
                                                                if (!preg_match('#_distribution$#i', $key1)) {
 
1542
                                                                        continue;
 
1543
                                                                }
 
1544
                                                                foreach ($value1 as $key2 => $value2) {
 
1545
                                                                        $info['mpeg']['audio'][$key1][$key2] = round($value2 / $pct_data_scanned);
 
1546
                                                                }
 
1547
                                                        }
 
1548
                                                }
 
1549
 
 
1550
                                                if ($SynchErrorsFound > 0) {
 
1551
                                                        $info['warning'][] = 'Found '.$SynchErrorsFound.' synch errors in histogram analysis';
 
1552
                                                        //return false;
 
1553
                                                }
 
1554
 
 
1555
                                                $bittotal     = 0;
 
1556
                                                $framecounter = 0;
 
1557
                                                foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitratevalue => $bitratecount) {
 
1558
                                                        $framecounter += $bitratecount;
 
1559
                                                        if ($bitratevalue != 'free') {
 
1560
                                                                $bittotal += ($bitratevalue * $bitratecount);
 
1561
                                                        }
 
1562
                                                }
 
1563
                                                if ($framecounter == 0) {
 
1564
                                                        $info['error'][] = 'Corrupt MP3 file: framecounter == zero';
 
1565
                                                        return false;
 
1566
                                                }
 
1567
                                                $info['mpeg']['audio']['frame_count'] = getid3_lib::CastAsInt($framecounter);
 
1568
                                                $info['mpeg']['audio']['bitrate']     = ($bittotal / $framecounter);
 
1569
 
 
1570
                                                $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
 
1571
 
 
1572
 
 
1573
                                                // Definitively set VBR vs CBR, even if the Xing/LAME/VBRI header says differently
 
1574
                                                $distinct_bitrates = 0;
 
1575
                                                foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitrate_value => $bitrate_count) {
 
1576
                                                        if ($bitrate_count > 0) {
 
1577
                                                                $distinct_bitrates++;
 
1578
                                                        }
 
1579
                                                }
 
1580
                                                if ($distinct_bitrates > 1) {
 
1581
                                                        $info['mpeg']['audio']['bitrate_mode'] = 'vbr';
 
1582
                                                } else {
 
1583
                                                        $info['mpeg']['audio']['bitrate_mode'] = 'cbr';
 
1584
                                                }
 
1585
                                                $info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode'];
 
1586
 
 
1587
                                        }
 
1588
 
 
1589
                                        break; // exit while()
 
1590
                                }
 
1591
                        }
 
1592
 
 
1593
                        $SynchSeekOffset++;
 
1594
                        if (($avdataoffset + $SynchSeekOffset) >= $info['avdataend']) {
 
1595
                                // end of file/data
 
1596
 
 
1597
                                if (empty($info['mpeg']['audio'])) {
 
1598
 
 
1599
                                        $info['error'][] = 'could not find valid MPEG synch before end of file';
 
1600
                                        if (isset($info['audio']['bitrate'])) {
 
1601
                                                unset($info['audio']['bitrate']);
 
1602
                                        }
 
1603
                                        if (isset($info['mpeg']['audio'])) {
 
1604
                                                unset($info['mpeg']['audio']);
 
1605
                                        }
 
1606
                                        if (isset($info['mpeg']) && (!is_array($info['mpeg']) || empty($info['mpeg']))) {
 
1607
                                                unset($info['mpeg']);
 
1608
                                        }
 
1609
                                        return false;
 
1610
 
 
1611
                                }
 
1612
                                break;
 
1613
                        }
 
1614
 
 
1615
                }
 
1616
                $info['audio']['channels']        = $info['mpeg']['audio']['channels'];
 
1617
                $info['audio']['channelmode']     = $info['mpeg']['audio']['channelmode'];
 
1618
                $info['audio']['sample_rate']     = $info['mpeg']['audio']['sample_rate'];
 
1619
                return true;
 
1620
        }
 
1621
 
 
1622
 
 
1623
        public static function MPEGaudioVersionArray() {
 
1624
                static $MPEGaudioVersion = array('2.5', false, '2', '1');
 
1625
                return $MPEGaudioVersion;
 
1626
        }
 
1627
 
 
1628
        public static function MPEGaudioLayerArray() {
 
1629
                static $MPEGaudioLayer = array(false, 3, 2, 1);
 
1630
                return $MPEGaudioLayer;
 
1631
        }
 
1632
 
 
1633
        public static function MPEGaudioBitrateArray() {
 
1634
                static $MPEGaudioBitrate;
 
1635
                if (empty($MPEGaudioBitrate)) {
 
1636
                        $MPEGaudioBitrate = array (
 
1637
                                '1'  =>  array (1 => array('free', 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000),
 
1638
                                                                2 => array('free', 32000, 48000, 56000,  64000,  80000,  96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000),
 
1639
                                                                3 => array('free', 32000, 40000, 48000,  56000,  64000,  80000,  96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000)
 
1640
                                                           ),
 
1641
 
 
1642
                                '2'  =>  array (1 => array('free', 32000, 48000, 56000,  64000,  80000,  96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000),
 
1643
                                                                2 => array('free',  8000, 16000, 24000,  32000,  40000,  48000,  56000,  64000,  80000,  96000, 112000, 128000, 144000, 160000),
 
1644
                                                           )
 
1645
                        );
 
1646
                        $MPEGaudioBitrate['2'][3] = $MPEGaudioBitrate['2'][2];
 
1647
                        $MPEGaudioBitrate['2.5']  = $MPEGaudioBitrate['2'];
 
1648
                }
 
1649
                return $MPEGaudioBitrate;
 
1650
        }
 
1651
 
 
1652
        public static function MPEGaudioFrequencyArray() {
 
1653
                static $MPEGaudioFrequency;
 
1654
                if (empty($MPEGaudioFrequency)) {
 
1655
                        $MPEGaudioFrequency = array (
 
1656
                                '1'   => array(44100, 48000, 32000),
 
1657
                                '2'   => array(22050, 24000, 16000),
 
1658
                                '2.5' => array(11025, 12000,  8000)
 
1659
                        );
 
1660
                }
 
1661
                return $MPEGaudioFrequency;
 
1662
        }
 
1663
 
 
1664
        public static function MPEGaudioChannelModeArray() {
 
1665
                static $MPEGaudioChannelMode = array('stereo', 'joint stereo', 'dual channel', 'mono');
 
1666
                return $MPEGaudioChannelMode;
 
1667
        }
 
1668
 
 
1669
        public static function MPEGaudioModeExtensionArray() {
 
1670
                static $MPEGaudioModeExtension;
 
1671
                if (empty($MPEGaudioModeExtension)) {
 
1672
                        $MPEGaudioModeExtension = array (
 
1673
                                1 => array('4-31', '8-31', '12-31', '16-31'),
 
1674
                                2 => array('4-31', '8-31', '12-31', '16-31'),
 
1675
                                3 => array('', 'IS', 'MS', 'IS+MS')
 
1676
                        );
 
1677
                }
 
1678
                return $MPEGaudioModeExtension;
 
1679
        }
 
1680
 
 
1681
        public static function MPEGaudioEmphasisArray() {
 
1682
                static $MPEGaudioEmphasis = array('none', '50/15ms', false, 'CCIT J.17');
 
1683
                return $MPEGaudioEmphasis;
 
1684
        }
 
1685
 
 
1686
        public static function MPEGaudioHeaderBytesValid($head4, $allowBitrate15=false) {
 
1687
                return self::MPEGaudioHeaderValid(self::MPEGaudioHeaderDecode($head4), false, $allowBitrate15);
 
1688
        }
 
1689
 
 
1690
        public static function MPEGaudioHeaderValid($rawarray, $echoerrors=false, $allowBitrate15=false) {
 
1691
                if (($rawarray['synch'] & 0x0FFE) != 0x0FFE) {
 
1692
                        return false;
 
1693
                }
 
1694
 
 
1695
                static $MPEGaudioVersionLookup;
 
1696
                static $MPEGaudioLayerLookup;
 
1697
                static $MPEGaudioBitrateLookup;
 
1698
                static $MPEGaudioFrequencyLookup;
 
1699
                static $MPEGaudioChannelModeLookup;
 
1700
                static $MPEGaudioModeExtensionLookup;
 
1701
                static $MPEGaudioEmphasisLookup;
 
1702
                if (empty($MPEGaudioVersionLookup)) {
 
1703
                        $MPEGaudioVersionLookup       = self::MPEGaudioVersionArray();
 
1704
                        $MPEGaudioLayerLookup         = self::MPEGaudioLayerArray();
 
1705
                        $MPEGaudioBitrateLookup       = self::MPEGaudioBitrateArray();
 
1706
                        $MPEGaudioFrequencyLookup     = self::MPEGaudioFrequencyArray();
 
1707
                        $MPEGaudioChannelModeLookup   = self::MPEGaudioChannelModeArray();
 
1708
                        $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
 
1709
                        $MPEGaudioEmphasisLookup      = self::MPEGaudioEmphasisArray();
 
1710
                }
 
1711
 
 
1712
                if (isset($MPEGaudioVersionLookup[$rawarray['version']])) {
 
1713
                        $decodedVersion = $MPEGaudioVersionLookup[$rawarray['version']];
 
1714
                } else {
 
1715
                        echo ($echoerrors ? "\n".'invalid Version ('.$rawarray['version'].')' : '');
 
1716
                        return false;
 
1717
                }
 
1718
                if (isset($MPEGaudioLayerLookup[$rawarray['layer']])) {
 
1719
                        $decodedLayer = $MPEGaudioLayerLookup[$rawarray['layer']];
 
1720
                } else {
 
1721
                        echo ($echoerrors ? "\n".'invalid Layer ('.$rawarray['layer'].')' : '');
 
1722
                        return false;
 
1723
                }
 
1724
                if (!isset($MPEGaudioBitrateLookup[$decodedVersion][$decodedLayer][$rawarray['bitrate']])) {
 
1725
                        echo ($echoerrors ? "\n".'invalid Bitrate ('.$rawarray['bitrate'].')' : '');
 
1726
                        if ($rawarray['bitrate'] == 15) {
 
1727
                                // known issue in LAME 3.90 - 3.93.1 where free-format has bitrate ID of 15 instead of 0
 
1728
                                // let it go through here otherwise file will not be identified
 
1729
                                if (!$allowBitrate15) {
 
1730
                                        return false;
 
1731
                                }
 
1732
                        } else {
 
1733
                                return false;
 
1734
                        }
 
1735
                }
 
1736
                if (!isset($MPEGaudioFrequencyLookup[$decodedVersion][$rawarray['sample_rate']])) {
 
1737
                        echo ($echoerrors ? "\n".'invalid Frequency ('.$rawarray['sample_rate'].')' : '');
 
1738
                        return false;
 
1739
                }
 
1740
                if (!isset($MPEGaudioChannelModeLookup[$rawarray['channelmode']])) {
 
1741
                        echo ($echoerrors ? "\n".'invalid ChannelMode ('.$rawarray['channelmode'].')' : '');
 
1742
                        return false;
 
1743
                }
 
1744
                if (!isset($MPEGaudioModeExtensionLookup[$decodedLayer][$rawarray['modeextension']])) {
 
1745
                        echo ($echoerrors ? "\n".'invalid Mode Extension ('.$rawarray['modeextension'].')' : '');
 
1746
                        return false;
 
1747
                }
 
1748
                if (!isset($MPEGaudioEmphasisLookup[$rawarray['emphasis']])) {
 
1749
                        echo ($echoerrors ? "\n".'invalid Emphasis ('.$rawarray['emphasis'].')' : '');
 
1750
                        return false;
 
1751
                }
 
1752
                // These are just either set or not set, you can't mess that up :)
 
1753
                // $rawarray['protection'];
 
1754
                // $rawarray['padding'];
 
1755
                // $rawarray['private'];
 
1756
                // $rawarray['copyright'];
 
1757
                // $rawarray['original'];
 
1758
 
 
1759
                return true;
 
1760
        }
 
1761
 
 
1762
        public static function MPEGaudioHeaderDecode($Header4Bytes) {
 
1763
                // AAAA AAAA  AAAB BCCD  EEEE FFGH  IIJJ KLMM
 
1764
                // A - Frame sync (all bits set)
 
1765
                // B - MPEG Audio version ID
 
1766
                // C - Layer description
 
1767
                // D - Protection bit
 
1768
                // E - Bitrate index
 
1769
                // F - Sampling rate frequency index
 
1770
                // G - Padding bit
 
1771
                // H - Private bit
 
1772
                // I - Channel Mode
 
1773
                // J - Mode extension (Only if Joint stereo)
 
1774
                // K - Copyright
 
1775
                // L - Original
 
1776
                // M - Emphasis
 
1777
 
 
1778
                if (strlen($Header4Bytes) != 4) {
 
1779
                        return false;
 
1780
                }
 
1781
 
 
1782
                $MPEGrawHeader['synch']         = (getid3_lib::BigEndian2Int(substr($Header4Bytes, 0, 2)) & 0xFFE0) >> 4;
 
1783
                $MPEGrawHeader['version']       = (ord($Header4Bytes{1}) & 0x18) >> 3; //    BB
 
1784
                $MPEGrawHeader['layer']         = (ord($Header4Bytes{1}) & 0x06) >> 1; //      CC
 
1785
                $MPEGrawHeader['protection']    = (ord($Header4Bytes{1}) & 0x01);      //        D
 
1786
                $MPEGrawHeader['bitrate']       = (ord($Header4Bytes{2}) & 0xF0) >> 4; // EEEE
 
1787
                $MPEGrawHeader['sample_rate']   = (ord($Header4Bytes{2}) & 0x0C) >> 2; //     FF
 
1788
                $MPEGrawHeader['padding']       = (ord($Header4Bytes{2}) & 0x02) >> 1; //       G
 
1789
                $MPEGrawHeader['private']       = (ord($Header4Bytes{2}) & 0x01);      //        H
 
1790
                $MPEGrawHeader['channelmode']   = (ord($Header4Bytes{3}) & 0xC0) >> 6; // II
 
1791
                $MPEGrawHeader['modeextension'] = (ord($Header4Bytes{3}) & 0x30) >> 4; //   JJ
 
1792
                $MPEGrawHeader['copyright']     = (ord($Header4Bytes{3}) & 0x08) >> 3; //     K
 
1793
                $MPEGrawHeader['original']      = (ord($Header4Bytes{3}) & 0x04) >> 2; //      L
 
1794
                $MPEGrawHeader['emphasis']      = (ord($Header4Bytes{3}) & 0x03);      //       MM
 
1795
 
 
1796
                return $MPEGrawHeader;
 
1797
        }
 
1798
 
 
1799
        public static function MPEGaudioFrameLength(&$bitrate, &$version, &$layer, $padding, &$samplerate) {
 
1800
                static $AudioFrameLengthCache = array();
 
1801
 
 
1802
                if (!isset($AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate])) {
 
1803
                        $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = false;
 
1804
                        if ($bitrate != 'free') {
 
1805
 
 
1806
                                if ($version == '1') {
 
1807
 
 
1808
                                        if ($layer == '1') {
 
1809
 
 
1810
                                                // For Layer I slot is 32 bits long
 
1811
                                                $FrameLengthCoefficient = 48;
 
1812
                                                $SlotLength = 4;
 
1813
 
 
1814
                                        } else { // Layer 2 / 3
 
1815
 
 
1816
                                                // for Layer 2 and Layer 3 slot is 8 bits long.
 
1817
                                                $FrameLengthCoefficient = 144;
 
1818
                                                $SlotLength = 1;
 
1819
 
 
1820
                                        }
 
1821
 
 
1822
                                } else { // MPEG-2 / MPEG-2.5
 
1823
 
 
1824
                                        if ($layer == '1') {
 
1825
 
 
1826
                                                // For Layer I slot is 32 bits long
 
1827
                                                $FrameLengthCoefficient = 24;
 
1828
                                                $SlotLength = 4;
 
1829
 
 
1830
                                        } elseif ($layer == '2') {
 
1831
 
 
1832
                                                // for Layer 2 and Layer 3 slot is 8 bits long.
 
1833
                                                $FrameLengthCoefficient = 144;
 
1834
                                                $SlotLength = 1;
 
1835
 
 
1836
                                        } else { // layer 3
 
1837
 
 
1838
                                                // for Layer 2 and Layer 3 slot is 8 bits long.
 
1839
                                                $FrameLengthCoefficient = 72;
 
1840
                                                $SlotLength = 1;
 
1841
 
 
1842
                                        }
 
1843
 
 
1844
                                }
 
1845
 
 
1846
                                // FrameLengthInBytes = ((Coefficient * BitRate) / SampleRate) + Padding
 
1847
                                if ($samplerate > 0) {
 
1848
                                        $NewFramelength  = ($FrameLengthCoefficient * $bitrate) / $samplerate;
 
1849
                                        $NewFramelength  = floor($NewFramelength / $SlotLength) * $SlotLength; // round to next-lower multiple of SlotLength (1 byte for Layer 2/3, 4 bytes for Layer I)
 
1850
                                        if ($padding) {
 
1851
                                                $NewFramelength += $SlotLength;
 
1852
                                        }
 
1853
                                        $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = (int) $NewFramelength;
 
1854
                                }
 
1855
                        }
 
1856
                }
 
1857
                return $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate];
 
1858
        }
 
1859
 
 
1860
        public static function ClosestStandardMP3Bitrate($bit_rate) {
 
1861
                static $standard_bit_rates = array (320000, 256000, 224000, 192000, 160000, 128000, 112000, 96000, 80000, 64000, 56000, 48000, 40000, 32000, 24000, 16000, 8000);
 
1862
                static $bit_rate_table = array (0=>'-');
 
1863
                $round_bit_rate = intval(round($bit_rate, -3));
 
1864
                if (!isset($bit_rate_table[$round_bit_rate])) {
 
1865
                        if ($round_bit_rate > max($standard_bit_rates)) {
 
1866
                                $bit_rate_table[$round_bit_rate] = round($bit_rate, 2 - strlen($bit_rate));
 
1867
                        } else {
 
1868
                                $bit_rate_table[$round_bit_rate] = max($standard_bit_rates);
 
1869
                                foreach ($standard_bit_rates as $standard_bit_rate) {
 
1870
                                        if ($round_bit_rate >= $standard_bit_rate + (($bit_rate_table[$round_bit_rate] - $standard_bit_rate) / 2)) {
 
1871
                                                break;
 
1872
                                        }
 
1873
                                        $bit_rate_table[$round_bit_rate] = $standard_bit_rate;
 
1874
                                }
 
1875
                        }
 
1876
                }
 
1877
                return $bit_rate_table[$round_bit_rate];
 
1878
        }
 
1879
 
 
1880
        public static function XingVBRidOffset($version, $channelmode) {
 
1881
                static $XingVBRidOffsetCache = array();
 
1882
                if (empty($XingVBRidOffset)) {
 
1883
                        $XingVBRidOffset = array (
 
1884
                                '1'   => array ('mono'          => 0x15, // 4 + 17 = 21
 
1885
                                                                'stereo'        => 0x24, // 4 + 32 = 36
 
1886
                                                                'joint stereo'  => 0x24,
 
1887
                                                                'dual channel'  => 0x24
 
1888
                                                           ),
 
1889
 
 
1890
                                '2'   => array ('mono'          => 0x0D, // 4 +  9 = 13
 
1891
                                                                'stereo'        => 0x15, // 4 + 17 = 21
 
1892
                                                                'joint stereo'  => 0x15,
 
1893
                                                                'dual channel'  => 0x15
 
1894
                                                           ),
 
1895
 
 
1896
                                '2.5' => array ('mono'          => 0x15,
 
1897
                                                                'stereo'        => 0x15,
 
1898
                                                                'joint stereo'  => 0x15,
 
1899
                                                                'dual channel'  => 0x15
 
1900
                                                           )
 
1901
                        );
 
1902
                }
 
1903
                return $XingVBRidOffset[$version][$channelmode];
 
1904
        }
 
1905
 
 
1906
        public static function LAMEvbrMethodLookup($VBRmethodID) {
 
1907
                static $LAMEvbrMethodLookup = array(
 
1908
                        0x00 => 'unknown',
 
1909
                        0x01 => 'cbr',
 
1910
                        0x02 => 'abr',
 
1911
                        0x03 => 'vbr-old / vbr-rh',
 
1912
                        0x04 => 'vbr-new / vbr-mtrh',
 
1913
                        0x05 => 'vbr-mt',
 
1914
                        0x06 => 'vbr (full vbr method 4)',
 
1915
                        0x08 => 'cbr (constant bitrate 2 pass)',
 
1916
                        0x09 => 'abr (2 pass)',
 
1917
                        0x0F => 'reserved'
 
1918
                );
 
1919
                return (isset($LAMEvbrMethodLookup[$VBRmethodID]) ? $LAMEvbrMethodLookup[$VBRmethodID] : '');
 
1920
        }
 
1921
 
 
1922
        public static function LAMEmiscStereoModeLookup($StereoModeID) {
 
1923
                static $LAMEmiscStereoModeLookup = array(
 
1924
                        0 => 'mono',
 
1925
                        1 => 'stereo',
 
1926
                        2 => 'dual mono',
 
1927
                        3 => 'joint stereo',
 
1928
                        4 => 'forced stereo',
 
1929
                        5 => 'auto',
 
1930
                        6 => 'intensity stereo',
 
1931
                        7 => 'other'
 
1932
                );
 
1933
                return (isset($LAMEmiscStereoModeLookup[$StereoModeID]) ? $LAMEmiscStereoModeLookup[$StereoModeID] : '');
 
1934
        }
 
1935
 
 
1936
        public static function LAMEmiscSourceSampleFrequencyLookup($SourceSampleFrequencyID) {
 
1937
                static $LAMEmiscSourceSampleFrequencyLookup = array(
 
1938
                        0 => '<= 32 kHz',
 
1939
                        1 => '44.1 kHz',
 
1940
                        2 => '48 kHz',
 
1941
                        3 => '> 48kHz'
 
1942
                );
 
1943
                return (isset($LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID]) ? $LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID] : '');
 
1944
        }
 
1945
 
 
1946
        public static function LAMEsurroundInfoLookup($SurroundInfoID) {
 
1947
                static $LAMEsurroundInfoLookup = array(
 
1948
                        0 => 'no surround info',
 
1949
                        1 => 'DPL encoding',
 
1950
                        2 => 'DPL2 encoding',
 
1951
                        3 => 'Ambisonic encoding'
 
1952
                );
 
1953
                return (isset($LAMEsurroundInfoLookup[$SurroundInfoID]) ? $LAMEsurroundInfoLookup[$SurroundInfoID] : 'reserved');
 
1954
        }
 
1955
 
 
1956
        public static function LAMEpresetUsedLookup($LAMEtag) {
 
1957
 
 
1958
                if ($LAMEtag['preset_used_id'] == 0) {
 
1959
                        // no preset used (LAME >=3.93)
 
1960
                        // no preset recorded (LAME <3.93)
 
1961
                        return '';
 
1962
                }
 
1963
                $LAMEpresetUsedLookup = array();
 
1964
 
 
1965
                /////  THIS PART CANNOT BE STATIC .
 
1966
                for ($i = 8; $i <= 320; $i++) {
 
1967
                        switch ($LAMEtag['vbr_method']) {
 
1968
                                case 'cbr':
 
1969
                                        $LAMEpresetUsedLookup[$i] = '--alt-preset '.$LAMEtag['vbr_method'].' '.$i;
 
1970
                                        break;
 
1971
                                case 'abr':
 
1972
                                default: // other VBR modes shouldn't be here(?)
 
1973
                                        $LAMEpresetUsedLookup[$i] = '--alt-preset '.$i;
 
1974
                                        break;
 
1975
                        }
 
1976
                }
 
1977
 
 
1978
                // named old-style presets (studio, phone, voice, etc) are handled in GuessEncoderOptions()
 
1979
 
 
1980
                // named alt-presets
 
1981
                $LAMEpresetUsedLookup[1000] = '--r3mix';
 
1982
                $LAMEpresetUsedLookup[1001] = '--alt-preset standard';
 
1983
                $LAMEpresetUsedLookup[1002] = '--alt-preset extreme';
 
1984
                $LAMEpresetUsedLookup[1003] = '--alt-preset insane';
 
1985
                $LAMEpresetUsedLookup[1004] = '--alt-preset fast standard';
 
1986
                $LAMEpresetUsedLookup[1005] = '--alt-preset fast extreme';
 
1987
                $LAMEpresetUsedLookup[1006] = '--alt-preset medium';
 
1988
                $LAMEpresetUsedLookup[1007] = '--alt-preset fast medium';
 
1989
 
 
1990
                // LAME 3.94 additions/changes
 
1991
                $LAMEpresetUsedLookup[1010] = '--preset portable';                                                           // 3.94a15 Oct 21 2003
 
1992
                $LAMEpresetUsedLookup[1015] = '--preset radio';                                                              // 3.94a15 Oct 21 2003
 
1993
 
 
1994
                $LAMEpresetUsedLookup[320]  = '--preset insane';                                                             // 3.94a15 Nov 12 2003
 
1995
                $LAMEpresetUsedLookup[410]  = '-V9';
 
1996
                $LAMEpresetUsedLookup[420]  = '-V8';
 
1997
                $LAMEpresetUsedLookup[440]  = '-V6';
 
1998
                $LAMEpresetUsedLookup[430]  = '--preset radio';                                                              // 3.94a15 Nov 12 2003
 
1999
                $LAMEpresetUsedLookup[450]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'portable';  // 3.94a15 Nov 12 2003
 
2000
                $LAMEpresetUsedLookup[460]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'medium';    // 3.94a15 Nov 12 2003
 
2001
                $LAMEpresetUsedLookup[470]  = '--r3mix';                                                                     // 3.94b1  Dec 18 2003
 
2002
                $LAMEpresetUsedLookup[480]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'standard';  // 3.94a15 Nov 12 2003
 
2003
                $LAMEpresetUsedLookup[490]  = '-V1';
 
2004
                $LAMEpresetUsedLookup[500]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'extreme';   // 3.94a15 Nov 12 2003
 
2005
 
 
2006
                return (isset($LAMEpresetUsedLookup[$LAMEtag['preset_used_id']]) ? $LAMEpresetUsedLookup[$LAMEtag['preset_used_id']] : 'new/unknown preset: '.$LAMEtag['preset_used_id'].' - report to info@getid3.org');
 
2007
        }
 
2008
 
 
2009
}