ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
GetId3\Module\Audio\Mp3 Class Reference

GetId3() by James Heinrich info@.nosp@m.geti.nosp@m.d3.or.nosp@m.g //. More...

+ Inheritance diagram for GetId3\Module\Audio\Mp3:
+ Collaboration diagram for GetId3\Module\Audio\Mp3:

Public Member Functions

 analyze ()
 
 GuessEncoderOptions ()
 array $NamedPresetBitrates array $KnownEncoderValues array $ExpectedLowpass array $ExpectedResampledRate More...
 
 decodeMPEGaudioHeader ($offset, &$info, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false)
 type $MPEGaudioVersionLookup type $MPEGaudioLayerLookup type $MPEGaudioBitrateLookup type $MPEGaudioFrequencyLookup type $MPEGaudioChannelModeLookup type $MPEGaudioModeExtensionLookup type $MPEGaudioEmphasisLookup array $MPEGaudioHeaderDecodeCache array $MPEGaudioHeaderValidCache More...
 
 RecursiveFrameScanning (&$offset, &$nextframetestoffset, $ScanAsCBR)
 
 FreeFormatFrameLength ($offset, $deepscan=false)
 
 getOnlyMPEGaudioInfoBruteForce ()
 
 getOnlyMPEGaudioInfo ($avdataoffset, $BitrateHistogram=false)
 type $MPEGaudioVersionLookup type $MPEGaudioLayerLookup type $MPEGaudioBitrateLookup More...
 
- Public Member Functions inherited from GetId3\Handler\BaseHandler
 __construct (GetId3Core $getid3, $call_module=null)
 
 analyze ()
 Analyze from file pointer. More...
 
 AnalyzeString (&$string)
 Analyze from string instead. More...
 
 saveAttachment (&$ThisFileInfoIndex, $filename, $offset, $length)
 

Static Public Member Functions

static MPEGaudioVersionArray ()
 array $MPEGaudioVersion More...
 
static MPEGaudioLayerArray ()
 array $MPEGaudioLayer More...
 
static MPEGaudioBitrateArray ()
 type $MPEGaudioBitrate More...
 
static MPEGaudioFrequencyArray ()
 array $MPEGaudioFrequency More...
 
static MPEGaudioChannelModeArray ()
 array $MPEGaudioChannelMode More...
 
static MPEGaudioModeExtensionArray ()
 array $MPEGaudioModeExtension More...
 
static MPEGaudioEmphasisArray ()
 array $MPEGaudioEmphasis More...
 
static MPEGaudioHeaderBytesValid ($head4, $allowBitrate15=false)
 
static MPEGaudioHeaderValid ($rawarray, $echoerrors=false, $allowBitrate15=false)
 type $MPEGaudioVersionLookup type $MPEGaudioLayerLookup type $MPEGaudioBitrateLookup type $MPEGaudioFrequencyLookup type $MPEGaudioChannelModeLookup type $MPEGaudioModeExtensionLookup type $MPEGaudioEmphasisLookup More...
 
static MPEGaudioHeaderDecode ($Header4Bytes)
 
static MPEGaudioFrameLength (&$bitrate, &$version, &$layer, $padding, &$samplerate)
 array $AudioFrameLengthCache More...
 
static ClosestStandardMP3Bitrate ($bit_rate)
 array $standard_bit_rates array $bit_rate_table More...
 
static XingVBRidOffset ($version, $channelmode)
 array $XingVBRidOffsetCache More...
 
static LAMEvbrMethodLookup ($VBRmethodID)
 array $LAMEvbrMethodLookup More...
 
static LAMEmiscStereoModeLookup ($StereoModeID)
 array $LAMEmiscStereoModeLookup More...
 
static LAMEmiscSourceSampleFrequencyLookup ($SourceSampleFrequencyID)
 array $LAMEmiscSourceSampleFrequencyLookup More...
 
static LAMEsurroundInfoLookup ($SurroundInfoID)
 array $LAMEsurroundInfoLookup More...
 
static LAMEpresetUsedLookup ($LAMEtag)
 

Data Fields

 $allow_bruteforce = false
 
const GETID3_MP3_VALID_CHECK_FRAMES = 35
 

Additional Inherited Members

- Protected Member Functions inherited from GetId3\Handler\BaseHandler
 ftell ()
 
 fread ($bytes)
 
 fseek ($bytes, $whence=SEEK_SET)
 
 feof ()
 
 isDependencyFor ($module)
 
 error ($text)
 
 warning ($text)
 
- Protected Attributes inherited from GetId3\Handler\BaseHandler
 $getid3
 
 $data_string_flag = false
 
 $data_string = ''
 
 $data_string_position = 0
 
 $data_string_length = 0
 

Detailed Description

GetId3() by James Heinrich info@.nosp@m.geti.nosp@m.d3.or.nosp@m.g //.

module for analyzing MP3 files

number of frames to scan to determine if MPEG-audio sequence is valid Lower this number to 5-20 for faster scanning Increase this number to 50+ for most accurate detection of valid VBR/CBR mpeg-audio streams

Author
James Heinrich info@.nosp@m.geti.nosp@m.d3.or.nosp@m.g http://www.getid3.org

Definition at line 38 of file Mp3.php.

Member Function Documentation

◆ analyze()

GetId3\Module\Audio\Mp3::analyze ( )
Returns
boolean

Definition at line 52 of file Mp3.php.

References $info, GetId3\Handler\BaseHandler\fread(), GetId3\Handler\BaseHandler\fseek(), GetId3\Module\Audio\Mp3\getOnlyMPEGaudioInfo(), GetId3\Module\Audio\Mp3\getOnlyMPEGaudioInfoBruteForce(), and GetId3\Module\Audio\Mp3\GuessEncoderOptions().

53  {
54  $info = &$this->getid3->info;
55 
56  $initialOffset = $info['avdataoffset'];
57 
58  if (!$this->getOnlyMPEGaudioInfo($info['avdataoffset'])) {
59  if ($this->allow_bruteforce) {
60  $info['error'][] = 'Rescanning file in BruteForce mode';
61  $this->getOnlyMPEGaudioInfoBruteForce($this->getid3->fp, $info);
62  }
63  }
64 
65  if (isset($info['mpeg']['audio']['bitrate_mode'])) {
66  $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
67  }
68 
69  if (((isset($info['id3v2']['headerlength']) && ($info['avdataoffset'] > $info['id3v2']['headerlength'])) || (!isset($info['id3v2']) && ($info['avdataoffset'] > 0) && ($info['avdataoffset'] != $initialOffset)))) {
70 
71  $synchoffsetwarning = 'Unknown data before synch ';
72  if (isset($info['id3v2']['headerlength'])) {
73  $synchoffsetwarning .= '(ID3v2 header ends at ' . $info['id3v2']['headerlength'] . ', then ' . ($info['avdataoffset'] - $info['id3v2']['headerlength']) . ' bytes garbage, ';
74  } elseif ($initialOffset > 0) {
75  $synchoffsetwarning .= '(should be at ' . $initialOffset . ', ';
76  } else {
77  $synchoffsetwarning .= '(should be at beginning of file, ';
78  }
79  $synchoffsetwarning .= 'synch detected at ' . $info['avdataoffset'] . ')';
80  if (isset($info['audio']['bitrate_mode']) && ($info['audio']['bitrate_mode'] == 'cbr')) {
81 
82  if (!empty($info['id3v2']['headerlength']) && (($info['avdataoffset'] - $info['id3v2']['headerlength']) == $info['mpeg']['audio']['framelength'])) {
83 
84  $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90-3.92) DLL in CBR mode.';
85  $info['audio']['codec'] = 'LAME';
86  $CurrentDataLAMEversionString = 'LAME3.';
87  } elseif (empty($info['id3v2']['headerlength']) && ($info['avdataoffset'] == $info['mpeg']['audio']['framelength'])) {
88 
89  $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90 - 3.92) DLL in CBR mode.';
90  $info['audio']['codec'] = 'LAME';
91  $CurrentDataLAMEversionString = 'LAME3.';
92  }
93  }
94  $info['warning'][] = $synchoffsetwarning;
95  }
96 
97  if (isset($info['mpeg']['audio']['LAME'])) {
98  $info['audio']['codec'] = 'LAME';
99  if (!empty($info['mpeg']['audio']['LAME']['long_version'])) {
100  $info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['long_version'],
101  "\x00");
102  } elseif (!empty($info['mpeg']['audio']['LAME']['short_version'])) {
103  $info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['short_version'],
104  "\x00");
105  }
106  }
107 
108  $CurrentDataLAMEversionString = (!empty($CurrentDataLAMEversionString) ? $CurrentDataLAMEversionString : (isset($info['audio']['encoder']) ? $info['audio']['encoder'] : ''));
109  if (!empty($CurrentDataLAMEversionString) && (substr($CurrentDataLAMEversionString,
110  0, 6) == 'LAME3.') && !preg_match('[0-9\)]',
111  substr($CurrentDataLAMEversionString,
112  -1))) {
113  // a version number of LAME that does not end with a number like "LAME3.92"
114  // or with a closing parenthesis like "LAME3.88 (alpha)"
115  // or a version of LAME with the LAMEtag-not-filled-in-DLL-mode bug (3.90-3.92)
116  // not sure what the actual last frame length will be, but will be less than or equal to 1441
117  $PossiblyLongerLAMEversion_FrameLength = 1441;
118 
119  // Not sure what version of LAME this is - look in padding of last frame for longer version string
120  $PossibleLAMEversionStringOffset = $info['avdataend'] - $PossiblyLongerLAMEversion_FrameLength;
121  fseek($this->getid3->fp, $PossibleLAMEversionStringOffset);
122  $PossiblyLongerLAMEversion_Data = fread($this->getid3->fp,
123  $PossiblyLongerLAMEversion_FrameLength);
124  switch (substr($CurrentDataLAMEversionString, -1)) {
125  case 'a':
126  case 'b':
127  // "LAME3.94a" will have a longer version string of "LAME3.94 (alpha)" for example
128  // need to trim off "a" to match longer string
129  $CurrentDataLAMEversionString = substr($CurrentDataLAMEversionString,
130  0, -1);
131  break;
132  }
133  if (($PossiblyLongerLAMEversion_String = strstr($PossiblyLongerLAMEversion_Data,
134  $CurrentDataLAMEversionString)) !== false) {
135  if (substr($PossiblyLongerLAMEversion_String, 0,
136  strlen($CurrentDataLAMEversionString)) == $CurrentDataLAMEversionString) {
137  $PossiblyLongerLAMEversion_NewString = substr($PossiblyLongerLAMEversion_String,
138  0,
139  strspn($PossiblyLongerLAMEversion_String,
140  'LAME0123456789., (abcdefghijklmnopqrstuvwxyzJFSOND)')); //"LAME3.90.3" "LAME3.87 (beta 1, Sep 27 2000)" "LAME3.88 (beta)"
141  if (empty($info['audio']['encoder']) || (strlen($PossiblyLongerLAMEversion_NewString) > strlen($info['audio']['encoder']))) {
142  $info['audio']['encoder'] = $PossiblyLongerLAMEversion_NewString;
143  }
144  }
145  }
146  }
147  if (!empty($info['audio']['encoder'])) {
148  $info['audio']['encoder'] = rtrim($info['audio']['encoder'], "\x00 ");
149  }
150 
151  switch (isset($info['mpeg']['audio']['layer']) ? $info['mpeg']['audio']['layer'] : '') {
152  case 1:
153  case 2:
154  $info['audio']['dataformat'] = 'mp' . $info['mpeg']['audio']['layer'];
155  break;
156  }
157  if (isset($info['fileformat']) && ($info['fileformat'] == 'mp3')) {
158  switch ($info['audio']['dataformat']) {
159  case 'mp1':
160  case 'mp2':
161  case 'mp3':
162  $info['fileformat'] = $info['audio']['dataformat'];
163  break;
164 
165  default:
166  $info['warning'][] = 'Expecting [audio][dataformat] to be mp1/mp2/mp3 when fileformat == mp3, [audio][dataformat] actually "' . $info['audio']['dataformat'] . '"';
167  break;
168  }
169  }
170 
171  if (empty($info['fileformat'])) {
172  unset($info['fileformat']);
173  unset($info['audio']['bitrate_mode']);
174  unset($info['avdataoffset']);
175  unset($info['avdataend']);
176 
177  return false;
178  }
179 
180  $info['mime_type'] = 'audio/mpeg';
181  $info['audio']['lossless'] = false;
182 
183  // Calculate playtime
184  if (!isset($info['playtime_seconds']) && isset($info['audio']['bitrate']) && ($info['audio']['bitrate'] > 0)) {
185  $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['audio']['bitrate'];
186  }
187 
188  $info['audio']['encoder_options'] = $this->GuessEncoderOptions();
189 
190  return true;
191  }
GuessEncoderOptions()
array $NamedPresetBitrates array $KnownEncoderValues array $ExpectedLowpass array $ExpectedResampl...
Definition: Mp3.php:201
fseek($bytes, $whence=SEEK_SET)
getOnlyMPEGaudioInfo($avdataoffset, $BitrateHistogram=false)
type $MPEGaudioVersionLookup type $MPEGaudioLayerLookup type $MPEGaudioBitrateLookup ...
Definition: Mp3.php:1510
$info
Definition: example_052.php:80
getOnlyMPEGaudioInfoBruteForce()
Definition: Mp3.php:1345
+ Here is the call graph for this function:

◆ ClosestStandardMP3Bitrate()

static GetId3\Module\Audio\Mp3::ClosestStandardMP3Bitrate (   $bit_rate)
static

array $standard_bit_rates array $bit_rate_table

Parameters
type$bit_rate
Returns
int

Definition at line 2146 of file Mp3.php.

References array.

2147  {
2148  static $standard_bit_rates = array(320000, 256000, 224000, 192000, 160000, 128000, 112000, 96000, 80000, 64000, 56000, 48000, 40000, 32000, 24000, 16000, 8000);
2149  static $bit_rate_table = array(0 => '-');
2150  $round_bit_rate = intval(round($bit_rate, -3));
2151  if (!isset($bit_rate_table[$round_bit_rate])) {
2152  if ($round_bit_rate > max($standard_bit_rates)) {
2153  $bit_rate_table[$round_bit_rate] = round($bit_rate,
2154  2 - strlen($bit_rate));
2155  } else {
2156  $bit_rate_table[$round_bit_rate] = max($standard_bit_rates);
2157  foreach ($standard_bit_rates as $standard_bit_rate) {
2158  if ($round_bit_rate >= $standard_bit_rate + (($bit_rate_table[$round_bit_rate] - $standard_bit_rate) / 2)) {
2159  break;
2160  }
2161  $bit_rate_table[$round_bit_rate] = $standard_bit_rate;
2162  }
2163  }
2164  }
2165 
2166  return $bit_rate_table[$round_bit_rate];
2167  }
Create styles array
The data for the language used.

◆ decodeMPEGaudioHeader()

GetId3\Module\Audio\Mp3::decodeMPEGaudioHeader (   $offset,
$info,
  $recursivesearch = true,
  $ScanAsCBR = false,
  $FastMPEGheaderScan = false 
)

type $MPEGaudioVersionLookup type $MPEGaudioLayerLookup type $MPEGaudioBitrateLookup type $MPEGaudioFrequencyLookup type $MPEGaudioChannelModeLookup type $MPEGaudioModeExtensionLookup type $MPEGaudioEmphasisLookup array $MPEGaudioHeaderDecodeCache array $MPEGaudioHeaderValidCache

Parameters
type$offset
type$info
type$recursivesearch
type$ScanAsCBR
type$FastMPEGheaderScan
Returns
boolean

Definition at line 453 of file Mp3.php.

References $info, array, GetId3\Lib\Helper\BigEndian2Int(), GetId3\Handler\BaseHandler\fread(), GetId3\Module\Audio\Mp3\FreeFormatFrameLength(), GetId3\Handler\BaseHandler\fseek(), GetId3\Lib\Helper\LittleEndian2Float(), GetId3\Lib\Helper\PrintHexBytes(), GetId3\Module\Audio\Mp3\RecursiveFrameScanning(), GetId3\Lib\Helper\RGADadjustmentLookup(), GetId3\Lib\Helper\RGADamplitude2dB(), GetId3\Lib\Helper\RGADnameLookup(), and GetId3\Lib\Helper\RGADoriginatorLookup().

Referenced by GetId3\Module\Audio\Mp3\getOnlyMPEGaudioInfo(), and GetId3\Module\Audio\Mp3\RecursiveFrameScanning().

457  {
458  static $MPEGaudioVersionLookup;
459  static $MPEGaudioLayerLookup;
460  static $MPEGaudioBitrateLookup;
461  static $MPEGaudioFrequencyLookup;
462  static $MPEGaudioChannelModeLookup;
463  static $MPEGaudioModeExtensionLookup;
464  static $MPEGaudioEmphasisLookup;
465  if (empty($MPEGaudioVersionLookup)) {
466  $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
467  $MPEGaudioLayerLookup = self::MPEGaudioLayerArray();
468  $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
469  $MPEGaudioFrequencyLookup = self::MPEGaudioFrequencyArray();
470  $MPEGaudioChannelModeLookup = self::MPEGaudioChannelModeArray();
471  $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
472  $MPEGaudioEmphasisLookup = self::MPEGaudioEmphasisArray();
473  }
474 
475  if (fseek($this->getid3->fp, $offset, SEEK_SET) != 0) {
476  $info['error'][] = 'decodeMPEGaudioHeader() failed to seek to next offset at ' . $offset;
477 
478  return false;
479  }
480  //$headerstring = fread($this->getid3->fp, 1441); // worst-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame
481  $headerstring = fread($this->getid3->fp, 226); // LAME header at offset 36 + 190 bytes of Xing/LAME data
482  // MP3 audio frame structure:
483  // $aa $aa $aa $aa [$bb $bb] $cc...
484  // where $aa..$aa is the four-byte mpeg-audio header (below)
485  // $bb $bb is the optional 2-byte CRC
486  // and $cc... is the audio data
487 
488  $head4 = substr($headerstring, 0, 4);
489 
490  static $MPEGaudioHeaderDecodeCache = array();
491  if (isset($MPEGaudioHeaderDecodeCache[$head4])) {
492  $MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4];
493  } else {
494  $MPEGheaderRawArray = self::MPEGaudioHeaderDecode($head4);
495  $MPEGaudioHeaderDecodeCache[$head4] = $MPEGheaderRawArray;
496  }
497 
498  static $MPEGaudioHeaderValidCache = array();
499  if (!isset($MPEGaudioHeaderValidCache[$head4])) { // Not in cache
500  //$MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, true); // allow badly-formatted freeformat (from LAME 3.90 - 3.93.1)
501  $MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray,
502  false,
503  false);
504  }
505 
506  // shortcut
507  if (!isset($info['mpeg']['audio'])) {
508  $info['mpeg']['audio'] = array();
509  }
510  $thisfile_mpeg_audio = &$info['mpeg']['audio'];
511 
512  if ($MPEGaudioHeaderValidCache[$head4]) {
513  $thisfile_mpeg_audio['raw'] = $MPEGheaderRawArray;
514  } else {
515  $info['error'][] = 'Invalid MPEG audio header (' . Helper::PrintHexBytes($head4) . ') at offset ' . $offset;
516 
517  return false;
518  }
519 
520  if (!$FastMPEGheaderScan) {
521  $thisfile_mpeg_audio['version'] = $MPEGaudioVersionLookup[$thisfile_mpeg_audio['raw']['version']];
522  $thisfile_mpeg_audio['layer'] = $MPEGaudioLayerLookup[$thisfile_mpeg_audio['raw']['layer']];
523 
524  $thisfile_mpeg_audio['channelmode'] = $MPEGaudioChannelModeLookup[$thisfile_mpeg_audio['raw']['channelmode']];
525  $thisfile_mpeg_audio['channels'] = (($thisfile_mpeg_audio['channelmode'] == 'mono') ? 1 : 2);
526  $thisfile_mpeg_audio['sample_rate'] = $MPEGaudioFrequencyLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['raw']['sample_rate']];
527  $thisfile_mpeg_audio['protection'] = !$thisfile_mpeg_audio['raw']['protection'];
528  $thisfile_mpeg_audio['private'] = (bool) $thisfile_mpeg_audio['raw']['private'];
529  $thisfile_mpeg_audio['modeextension'] = $MPEGaudioModeExtensionLookup[$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['modeextension']];
530  $thisfile_mpeg_audio['copyright'] = (bool) $thisfile_mpeg_audio['raw']['copyright'];
531  $thisfile_mpeg_audio['original'] = (bool) $thisfile_mpeg_audio['raw']['original'];
532  $thisfile_mpeg_audio['emphasis'] = $MPEGaudioEmphasisLookup[$thisfile_mpeg_audio['raw']['emphasis']];
533 
534  $info['audio']['channels'] = $thisfile_mpeg_audio['channels'];
535  $info['audio']['sample_rate'] = $thisfile_mpeg_audio['sample_rate'];
536 
537  if ($thisfile_mpeg_audio['protection']) {
538  $thisfile_mpeg_audio['crc'] = Helper::BigEndian2Int(substr($headerstring,
539  4,
540  2));
541  }
542  }
543 
544  if ($thisfile_mpeg_audio['raw']['bitrate'] == 15) {
545  // http://www.hydrogenaudio.org/?act=ST&f=16&t=9682&st=0
546  $info['warning'][] = 'Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1';
547  $thisfile_mpeg_audio['raw']['bitrate'] = 0;
548  }
549  $thisfile_mpeg_audio['padding'] = (bool) $thisfile_mpeg_audio['raw']['padding'];
550  $thisfile_mpeg_audio['bitrate'] = $MPEGaudioBitrateLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['bitrate']];
551 
552  if (($thisfile_mpeg_audio['bitrate'] == 'free') && ($offset == $info['avdataoffset'])) {
553  // only skip multiple frame check if free-format bitstream found at beginning of file
554  // otherwise is quite possibly simply corrupted data
555  $recursivesearch = false;
556  }
557 
558  // For Layer 2 there are some combinations of bitrate and mode which are not allowed.
559  if (!$FastMPEGheaderScan && ($thisfile_mpeg_audio['layer'] == '2')) {
560 
561  $info['audio']['dataformat'] = 'mp2';
562  switch ($thisfile_mpeg_audio['channelmode']) {
563 
564  case 'mono':
565  if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] <= 192000)) {
566  // these are ok
567  } else {
568  $info['error'][] = $thisfile_mpeg_audio['bitrate'] . 'kbps not allowed in Layer 2, ' . $thisfile_mpeg_audio['channelmode'] . '.';
569 
570  return false;
571  }
572  break;
573 
574  case 'stereo':
575  case 'joint stereo':
576  case 'dual channel':
577  if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] == 64000) || ($thisfile_mpeg_audio['bitrate'] >= 96000)) {
578  // these are ok
579  } else {
580  $info['error'][] = intval(round($thisfile_mpeg_audio['bitrate'] / 1000)) . 'kbps not allowed in Layer 2, ' . $thisfile_mpeg_audio['channelmode'] . '.';
581 
582  return false;
583  }
584  break;
585  }
586  }
587 
588  if ($info['audio']['sample_rate'] > 0) {
589  $thisfile_mpeg_audio['framelength'] = self::MPEGaudioFrameLength($thisfile_mpeg_audio['bitrate'],
590  $thisfile_mpeg_audio['version'],
591  $thisfile_mpeg_audio['layer'],
592  (int) $thisfile_mpeg_audio['padding'],
593  $info['audio']['sample_rate']);
594  }
595 
596  $nextframetestoffset = $offset + 1;
597  if ($thisfile_mpeg_audio['bitrate'] != 'free') {
598 
599  $info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
600 
601  if (isset($thisfile_mpeg_audio['framelength'])) {
602  $nextframetestoffset = $offset + $thisfile_mpeg_audio['framelength'];
603  } else {
604  $info['error'][] = 'Frame at offset(' . $offset . ') is has an invalid frame length.';
605 
606  return false;
607  }
608  }
609 
610  $ExpectedNumberOfAudioBytes = 0;
611 
613  // Variable-bitrate headers
614 
615  if (substr($headerstring, 4 + 32, 4) == 'VBRI') {
616  // Fraunhofer VBR header is hardcoded 'VBRI' at offset 0x24 (36)
617  // specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html
618 
619  $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
620  $thisfile_mpeg_audio['VBR_method'] = 'Fraunhofer';
621  $info['audio']['codec'] = 'Fraunhofer';
622 
623  $SideInfoData = substr($headerstring, 4 + 2, 32);
624 
625  $FraunhoferVBROffset = 36;
626 
627  $thisfile_mpeg_audio['VBR_encoder_version'] = Helper::BigEndian2Int(substr($headerstring,
628  $FraunhoferVBROffset + 4,
629  2)); // VbriVersion
630  $thisfile_mpeg_audio['VBR_encoder_delay'] = Helper::BigEndian2Int(substr($headerstring,
631  $FraunhoferVBROffset + 6,
632  2)); // VbriDelay
633  $thisfile_mpeg_audio['VBR_quality'] = Helper::BigEndian2Int(substr($headerstring,
634  $FraunhoferVBROffset + 8,
635  2)); // VbriQuality
636  $thisfile_mpeg_audio['VBR_bytes'] = Helper::BigEndian2Int(substr($headerstring,
637  $FraunhoferVBROffset + 10,
638  4)); // VbriStreamBytes
639  $thisfile_mpeg_audio['VBR_frames'] = Helper::BigEndian2Int(substr($headerstring,
640  $FraunhoferVBROffset + 14,
641  4)); // VbriStreamFrames
642  $thisfile_mpeg_audio['VBR_seek_offsets'] = Helper::BigEndian2Int(substr($headerstring,
643  $FraunhoferVBROffset + 18,
644  2)); // VbriTableSize
645  $thisfile_mpeg_audio['VBR_seek_scale'] = Helper::BigEndian2Int(substr($headerstring,
646  $FraunhoferVBROffset + 20,
647  2)); // VbriTableScale
648  $thisfile_mpeg_audio['VBR_entry_bytes'] = Helper::BigEndian2Int(substr($headerstring,
649  $FraunhoferVBROffset + 22,
650  2)); // VbriEntryBytes
651  $thisfile_mpeg_audio['VBR_entry_frames'] = Helper::BigEndian2Int(substr($headerstring,
652  $FraunhoferVBROffset + 24,
653  2)); // VbriEntryFrames
654 
655  $ExpectedNumberOfAudioBytes = $thisfile_mpeg_audio['VBR_bytes'];
656 
657  $previousbyteoffset = $offset;
658  for ($i = 0; $i < $thisfile_mpeg_audio['VBR_seek_offsets']; $i++) {
659  $Fraunhofer_OffsetN = Helper::BigEndian2Int(substr($headerstring,
660  $FraunhoferVBROffset,
661  $thisfile_mpeg_audio['VBR_entry_bytes']));
662  $FraunhoferVBROffset += $thisfile_mpeg_audio['VBR_entry_bytes'];
663  $thisfile_mpeg_audio['VBR_offsets_relative'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']);
664  $thisfile_mpeg_audio['VBR_offsets_absolute'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']) + $previousbyteoffset;
665  $previousbyteoffset += $Fraunhofer_OffsetN;
666  }
667  } else {
668 
669  // Xing VBR header is hardcoded 'Xing' at a offset 0x0D (13), 0x15 (21) or 0x24 (36)
670  // depending on MPEG layer and number of channels
671 
672  $VBRidOffset = self::XingVBRidOffset($thisfile_mpeg_audio['version'],
673  $thisfile_mpeg_audio['channelmode']);
674  $SideInfoData = substr($headerstring, 4 + 2, $VBRidOffset - 4);
675 
676  if ((substr($headerstring, $VBRidOffset, strlen('Xing')) == 'Xing') || (substr($headerstring,
677  $VBRidOffset,
678  strlen('Info')) == 'Info')) {
679  // 'Xing' is traditional Xing VBR frame
680  // 'Info' is LAME-encoded CBR (This was done to avoid CBR files to be recognized as traditional Xing VBR files by some decoders.)
681  // 'Info' *can* legally be used to specify a VBR file as well, however.
682  // http://www.multiweb.cz/twoinches/MP3inside.htm
683  //00..03 = "Xing" or "Info"
684  //04..07 = Flags:
685  // 0x01 Frames Flag set if value for number of frames in file is stored
686  // 0x02 Bytes Flag set if value for filesize in bytes is stored
687  // 0x04 TOC Flag set if values for TOC are stored
688  // 0x08 VBR Scale Flag set if values for VBR scale is stored
689  //08..11 Frames: Number of frames in file (including the first Xing/Info one)
690  //12..15 Bytes: File length in Bytes
691  //16..115 TOC (Table of Contents):
692  // Contains of 100 indexes (one Byte length) for easier lookup in file. Approximately solves problem with moving inside file.
693  // Each Byte has a value according this formula:
694  // (TOC[i] / 256) * fileLenInBytes
695  // 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:
696  // TOC[(60/240)*100] = TOC[25]
697  // and corresponding Byte in file is then approximately at:
698  // (TOC[25]/256) * 5000000
699  //116..119 VBR Scale
700  // should be safe to leave this at 'vbr' and let it be overriden to 'cbr' if a CBR preset/mode is used by LAME
701 // if (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Xing') {
702  $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
703  $thisfile_mpeg_audio['VBR_method'] = 'Xing';
704 // } else {
705 // $ScanAsCBR = true;
706 // $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
707 // }
708 
709  $thisfile_mpeg_audio['xing_flags_raw'] = Helper::BigEndian2Int(substr($headerstring,
710  $VBRidOffset + 4,
711  4));
712 
713  $thisfile_mpeg_audio['xing_flags']['frames'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000001);
714  $thisfile_mpeg_audio['xing_flags']['bytes'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000002);
715  $thisfile_mpeg_audio['xing_flags']['toc'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000004);
716  $thisfile_mpeg_audio['xing_flags']['vbr_scale'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000008);
717 
718  if ($thisfile_mpeg_audio['xing_flags']['frames']) {
719  $thisfile_mpeg_audio['VBR_frames'] = Helper::BigEndian2Int(substr($headerstring,
720  $VBRidOffset + 8,
721  4));
722  //$thisfile_mpeg_audio['VBR_frames']--; // don't count header Xing/Info frame
723  }
724  if ($thisfile_mpeg_audio['xing_flags']['bytes']) {
725  $thisfile_mpeg_audio['VBR_bytes'] = Helper::BigEndian2Int(substr($headerstring,
726  $VBRidOffset + 12,
727  4));
728  }
729 
730  //if (($thisfile_mpeg_audio['bitrate'] == 'free') && !empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
731  if (!empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
732 
733  $framelengthfloat = $thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames'];
734 
735  if ($thisfile_mpeg_audio['layer'] == '1') {
736  // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
737  //$info['audio']['bitrate'] = ((($framelengthfloat / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
738  $info['audio']['bitrate'] = ($framelengthfloat / 4) * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 12;
739  } else {
740  // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
741  //$info['audio']['bitrate'] = (($framelengthfloat - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
742  $info['audio']['bitrate'] = $framelengthfloat * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 144;
743  }
744  $thisfile_mpeg_audio['framelength'] = floor($framelengthfloat);
745  }
746 
747  if ($thisfile_mpeg_audio['xing_flags']['toc']) {
748  $LAMEtocData = substr($headerstring, $VBRidOffset + 16, 100);
749  for ($i = 0; $i < 100; $i++) {
750  $thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData{$i});
751  }
752  }
753  if ($thisfile_mpeg_audio['xing_flags']['vbr_scale']) {
754  $thisfile_mpeg_audio['VBR_scale'] = Helper::BigEndian2Int(substr($headerstring,
755  $VBRidOffset + 116,
756  4));
757  }
758 
759 
760  // http://gabriel.mp3-tech.org/mp3infotag.html
761  if (substr($headerstring, $VBRidOffset + 120, 4) == 'LAME') {
762 
763  // shortcut
764  $thisfile_mpeg_audio['LAME'] = array();
765  $thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME'];
766 
767 
768  $thisfile_mpeg_audio_lame['long_version'] = substr($headerstring,
769  $VBRidOffset + 120,
770  20);
771  $thisfile_mpeg_audio_lame['short_version'] = substr($thisfile_mpeg_audio_lame['long_version'],
772  0, 9);
773 
774  if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.90') {
775 
776  // extra 11 chars are not part of version string when LAMEtag present
777  unset($thisfile_mpeg_audio_lame['long_version']);
778 
779  // It the LAME tag was only introduced in LAME v3.90
780  // http://www.hydrogenaudio.org/?act=ST&f=15&t=9933
781  // Offsets of various bytes in http://gabriel.mp3-tech.org/mp3infotag.html
782  // are assuming a 'Xing' identifier offset of 0x24, which is the case for
783  // MPEG-1 non-mono, but not for other combinations
784  $LAMEtagOffsetContant = $VBRidOffset - 0x24;
785 
786  // shortcuts
787  $thisfile_mpeg_audio_lame['RGAD'] = array('track' => array(), 'album' => array());
788  $thisfile_mpeg_audio_lame_RGAD = &$thisfile_mpeg_audio_lame['RGAD'];
789  $thisfile_mpeg_audio_lame_RGAD_track = &$thisfile_mpeg_audio_lame_RGAD['track'];
790  $thisfile_mpeg_audio_lame_RGAD_album = &$thisfile_mpeg_audio_lame_RGAD['album'];
791  $thisfile_mpeg_audio_lame['raw'] = array();
792  $thisfile_mpeg_audio_lame_raw = &$thisfile_mpeg_audio_lame['raw'];
793 
794  // byte $9B VBR Quality
795  // This field is there to indicate a quality level, although the scale was not precised in the original Xing specifications.
796  // Actually overwrites original Xing bytes
797  unset($thisfile_mpeg_audio['VBR_scale']);
798  $thisfile_mpeg_audio_lame['vbr_quality'] = Helper::BigEndian2Int(substr($headerstring,
799  $LAMEtagOffsetContant + 0x9B,
800  1));
801 
802  // bytes $9C-$A4 Encoder short VersionString
803  $thisfile_mpeg_audio_lame['short_version'] = substr($headerstring,
804  $LAMEtagOffsetContant + 0x9C,
805  9);
806 
807  // byte $A5 Info Tag revision + VBR method
808  $LAMEtagRevisionVBRmethod = Helper::BigEndian2Int(substr($headerstring,
809  $LAMEtagOffsetContant + 0xA5,
810  1));
811 
812  $thisfile_mpeg_audio_lame['tag_revision'] = ($LAMEtagRevisionVBRmethod & 0xF0) >> 4;
813  $thisfile_mpeg_audio_lame_raw['vbr_method'] = $LAMEtagRevisionVBRmethod & 0x0F;
814  $thisfile_mpeg_audio_lame['vbr_method'] = self::LAMEvbrMethodLookup($thisfile_mpeg_audio_lame_raw['vbr_method']);
815  $thisfile_mpeg_audio['bitrate_mode'] = substr($thisfile_mpeg_audio_lame['vbr_method'],
816  0, 3); // usually either 'cbr' or 'vbr', but truncates 'vbr-old / vbr-rh' to 'vbr'
817  // byte $A6 Lowpass filter value
818  $thisfile_mpeg_audio_lame['lowpass_frequency'] = Helper::BigEndian2Int(substr($headerstring,
819  $LAMEtagOffsetContant + 0xA6,
820  1)) * 100;
821 
822  // bytes $A7-$AE Replay Gain
823  // http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html
824  // bytes $A7-$AA : 32 bit floating point "Peak signal amplitude"
825  if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.94b') {
826  // LAME 3.94a16 and later - 9.23 fixed point
827  // ie 0x0059E2EE / (2^23) = 5890798 / 8388608 = 0.7022378444671630859375
828  $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = (float) ((Helper::BigEndian2Int(substr($headerstring,
829  $LAMEtagOffsetContant + 0xA7,
830  4))) / 8388608);
831  } else {
832  // LAME 3.94a15 and earlier - 32-bit floating point
833  // Actually 3.94a16 will fall in here too and be WRONG, but is hard to detect 3.94a16 vs 3.94a15
834  $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = Helper::LittleEndian2Float(substr($headerstring,
835  $LAMEtagOffsetContant + 0xA7,
836  4));
837  }
838  if ($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] == 0) {
839  unset($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
840  } else {
841  $thisfile_mpeg_audio_lame_RGAD['peak_db'] = Helper::RGADamplitude2dB($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
842  }
843 
844  $thisfile_mpeg_audio_lame_raw['RGAD_track'] = Helper::BigEndian2Int(substr($headerstring,
845  $LAMEtagOffsetContant + 0xAB,
846  2));
847  $thisfile_mpeg_audio_lame_raw['RGAD_album'] = Helper::BigEndian2Int(substr($headerstring,
848  $LAMEtagOffsetContant + 0xAD,
849  2));
850 
851 
852  if ($thisfile_mpeg_audio_lame_raw['RGAD_track'] != 0) {
853 
854  $thisfile_mpeg_audio_lame_RGAD_track['raw']['name'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0xE000) >> 13;
855  $thisfile_mpeg_audio_lame_RGAD_track['raw']['originator'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x1C00) >> 10;
856  $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x0200) >> 9;
857  $thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x01FF;
858  $thisfile_mpeg_audio_lame_RGAD_track['name'] = Helper::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['name']);
859  $thisfile_mpeg_audio_lame_RGAD_track['originator'] = Helper::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['originator']);
860  $thisfile_mpeg_audio_lame_RGAD_track['gain_db'] = Helper::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'],
861  $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit']);
862 
863  if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) {
864  $info['replay_gain']['track']['peak'] = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
865  }
866  $info['replay_gain']['track']['originator'] = $thisfile_mpeg_audio_lame_RGAD_track['originator'];
867  $info['replay_gain']['track']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_track['gain_db'];
868  } else {
869  unset($thisfile_mpeg_audio_lame_RGAD['track']);
870  }
871  if ($thisfile_mpeg_audio_lame_raw['RGAD_album'] != 0) {
872 
873  $thisfile_mpeg_audio_lame_RGAD_album['raw']['name'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0xE000) >> 13;
874  $thisfile_mpeg_audio_lame_RGAD_album['raw']['originator'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x1C00) >> 10;
875  $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x0200) >> 9;
876  $thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x01FF;
877  $thisfile_mpeg_audio_lame_RGAD_album['name'] = Helper::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['name']);
878  $thisfile_mpeg_audio_lame_RGAD_album['originator'] = Helper::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['originator']);
879  $thisfile_mpeg_audio_lame_RGAD_album['gain_db'] = Helper::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'],
880  $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit']);
881 
882  if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) {
883  $info['replay_gain']['album']['peak'] = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
884  }
885  $info['replay_gain']['album']['originator'] = $thisfile_mpeg_audio_lame_RGAD_album['originator'];
886  $info['replay_gain']['album']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_album['gain_db'];
887  } else {
888  unset($thisfile_mpeg_audio_lame_RGAD['album']);
889  }
890  if (empty($thisfile_mpeg_audio_lame_RGAD)) {
891  unset($thisfile_mpeg_audio_lame['RGAD']);
892  }
893 
894 
895  // byte $AF Encoding flags + ATH Type
896  $EncodingFlagsATHtype = Helper::BigEndian2Int(substr($headerstring,
897  $LAMEtagOffsetContant + 0xAF,
898  1));
899  $thisfile_mpeg_audio_lame['encoding_flags']['nspsytune'] = (bool) ($EncodingFlagsATHtype & 0x10);
900  $thisfile_mpeg_audio_lame['encoding_flags']['nssafejoint'] = (bool) ($EncodingFlagsATHtype & 0x20);
901  $thisfile_mpeg_audio_lame['encoding_flags']['nogap_next'] = (bool) ($EncodingFlagsATHtype & 0x40);
902  $thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev'] = (bool) ($EncodingFlagsATHtype & 0x80);
903  $thisfile_mpeg_audio_lame['ath_type'] = $EncodingFlagsATHtype & 0x0F;
904 
905  // byte $B0 if ABR {specified bitrate} else {minimal bitrate}
906  $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] = Helper::BigEndian2Int(substr($headerstring,
907  $LAMEtagOffsetContant + 0xB0,
908  1));
909  if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 2) { // Average BitRate (ABR)
910  $thisfile_mpeg_audio_lame['bitrate_abr'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
911  } elseif ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) { // Constant BitRate (CBR)
912  // ignore
913  } elseif ($thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] > 0) { // Variable BitRate (VBR) - minimum bitrate
914  $thisfile_mpeg_audio_lame['bitrate_min'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
915  }
916 
917  // bytes $B1-$B3 Encoder delays
918  $EncoderDelays = Helper::BigEndian2Int(substr($headerstring,
919  $LAMEtagOffsetContant + 0xB1,
920  3));
921  $thisfile_mpeg_audio_lame['encoder_delay'] = ($EncoderDelays & 0xFFF000) >> 12;
922  $thisfile_mpeg_audio_lame['end_padding'] = $EncoderDelays & 0x000FFF;
923 
924  // byte $B4 Misc
925  $MiscByte = Helper::BigEndian2Int(substr($headerstring,
926  $LAMEtagOffsetContant + 0xB4,
927  1));
928  $thisfile_mpeg_audio_lame_raw['noise_shaping'] = ($MiscByte & 0x03);
929  $thisfile_mpeg_audio_lame_raw['stereo_mode'] = ($MiscByte & 0x1C) >> 2;
930  $thisfile_mpeg_audio_lame_raw['not_optimal_quality'] = ($MiscByte & 0x20) >> 5;
931  $thisfile_mpeg_audio_lame_raw['source_sample_freq'] = ($MiscByte & 0xC0) >> 6;
932  $thisfile_mpeg_audio_lame['noise_shaping'] = $thisfile_mpeg_audio_lame_raw['noise_shaping'];
933  $thisfile_mpeg_audio_lame['stereo_mode'] = self::LAMEmiscStereoModeLookup($thisfile_mpeg_audio_lame_raw['stereo_mode']);
934  $thisfile_mpeg_audio_lame['not_optimal_quality'] = (bool) $thisfile_mpeg_audio_lame_raw['not_optimal_quality'];
935  $thisfile_mpeg_audio_lame['source_sample_freq'] = self::LAMEmiscSourceSampleFrequencyLookup($thisfile_mpeg_audio_lame_raw['source_sample_freq']);
936 
937  // byte $B5 MP3 Gain
938  $thisfile_mpeg_audio_lame_raw['mp3_gain'] = Helper::BigEndian2Int(substr($headerstring,
939  $LAMEtagOffsetContant + 0xB5,
940  1),
941  false,
942  true);
943  $thisfile_mpeg_audio_lame['mp3_gain_db'] = (Helper::RGADamplitude2dB(2) / 4) * $thisfile_mpeg_audio_lame_raw['mp3_gain'];
944  $thisfile_mpeg_audio_lame['mp3_gain_factor'] = pow(2,
945  ($thisfile_mpeg_audio_lame['mp3_gain_db'] / 6));
946 
947  // bytes $B6-$B7 Preset and surround info
948  $PresetSurroundBytes = Helper::BigEndian2Int(substr($headerstring,
949  $LAMEtagOffsetContant + 0xB6,
950  2));
951  // Reserved = ($PresetSurroundBytes & 0xC000);
952  $thisfile_mpeg_audio_lame_raw['surround_info'] = ($PresetSurroundBytes & 0x3800);
953  $thisfile_mpeg_audio_lame['surround_info'] = self::LAMEsurroundInfoLookup($thisfile_mpeg_audio_lame_raw['surround_info']);
954  $thisfile_mpeg_audio_lame['preset_used_id'] = ($PresetSurroundBytes & 0x07FF);
955  $thisfile_mpeg_audio_lame['preset_used'] = self::LAMEpresetUsedLookup($thisfile_mpeg_audio_lame);
956  if (!empty($thisfile_mpeg_audio_lame['preset_used_id']) && empty($thisfile_mpeg_audio_lame['preset_used'])) {
957  $info['warning'][] = 'Unknown LAME preset used (' . $thisfile_mpeg_audio_lame['preset_used_id'] . ') - please report to info@getid3.org';
958  }
959  if (($thisfile_mpeg_audio_lame['short_version'] == 'LAME3.90.') && !empty($thisfile_mpeg_audio_lame['preset_used_id'])) {
960  // this may change if 3.90.4 ever comes out
961  $thisfile_mpeg_audio_lame['short_version'] = 'LAME3.90.3';
962  }
963 
964  // bytes $B8-$BB MusicLength
965  $thisfile_mpeg_audio_lame['audio_bytes'] = Helper::BigEndian2Int(substr($headerstring,
966  $LAMEtagOffsetContant + 0xB8,
967  4));
968  $ExpectedNumberOfAudioBytes = (($thisfile_mpeg_audio_lame['audio_bytes'] > 0) ? $thisfile_mpeg_audio_lame['audio_bytes'] : $thisfile_mpeg_audio['VBR_bytes']);
969 
970  // bytes $BC-$BD MusicCRC
971  $thisfile_mpeg_audio_lame['music_crc'] = Helper::BigEndian2Int(substr($headerstring,
972  $LAMEtagOffsetContant + 0xBC,
973  2));
974 
975  // bytes $BE-$BF CRC-16 of Info Tag
976  $thisfile_mpeg_audio_lame['lame_tag_crc'] = Helper::BigEndian2Int(substr($headerstring,
977  $LAMEtagOffsetContant + 0xBE,
978  2));
979 
980 
981  // LAME CBR
982  if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) {
983 
984  $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
985  $thisfile_mpeg_audio['bitrate'] = self::ClosestStandardMP3Bitrate($thisfile_mpeg_audio['bitrate']);
986  $info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
987  //if (empty($thisfile_mpeg_audio['bitrate']) || (!empty($thisfile_mpeg_audio_lame['bitrate_min']) && ($thisfile_mpeg_audio_lame['bitrate_min'] != 255))) {
988  // $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio_lame['bitrate_min'];
989  //}
990  }
991  }
992  }
993  } else {
994 
995  // not Fraunhofer or Xing VBR methods, most likely CBR (but could be VBR with no header)
996  $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
997  if ($recursivesearch) {
998  $thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
999  if ($this->RecursiveFrameScanning($offset,
1000  $nextframetestoffset, true)) {
1001  $recursivesearch = false;
1002  $thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
1003  }
1004  if ($thisfile_mpeg_audio['bitrate_mode'] == 'vbr') {
1005  $info['warning'][] = 'VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.';
1006  }
1007  }
1008  }
1009  }
1010 
1011  if (($ExpectedNumberOfAudioBytes > 0) && ($ExpectedNumberOfAudioBytes != ($info['avdataend'] - $info['avdataoffset']))) {
1012  if ($ExpectedNumberOfAudioBytes > ($info['avdataend'] - $info['avdataoffset'])) {
1013  if (isset($info['fileformat']) && ($info['fileformat'] == 'riff')) {
1014  // ignore, audio data is broken into chunks so will always be data "missing"
1015  } elseif (($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])) == 1) {
1016  $info['warning'][] = 'Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)';
1017  } else {
1018  $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)';
1019  }
1020  } else {
1021  if ((($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes) == 1) {
1022  // $prenullbytefileoffset = ftell($this->getid3->fp);
1023  // fseek($this->getid3->fp, $info['avdataend'], SEEK_SET);
1024  // $PossibleNullByte = fread($this->getid3->fp, 1);
1025  // fseek($this->getid3->fp, $prenullbytefileoffset, SEEK_SET);
1026  // if ($PossibleNullByte === "\x00") {
1027  $info['avdataend']--;
1028  // $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
1029  // } else {
1030  // $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)';
1031  // }
1032  } else {
1033  $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)';
1034  }
1035  }
1036  }
1037 
1038  if (($thisfile_mpeg_audio['bitrate'] == 'free') && empty($info['audio']['bitrate'])) {
1039  if (($offset == $info['avdataoffset']) && empty($thisfile_mpeg_audio['VBR_frames'])) {
1040  $framebytelength = $this->FreeFormatFrameLength($offset, true);
1041  if ($framebytelength > 0) {
1042  $thisfile_mpeg_audio['framelength'] = $framebytelength;
1043  if ($thisfile_mpeg_audio['layer'] == '1') {
1044  // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
1045  $info['audio']['bitrate'] = ((($framebytelength / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
1046  } else {
1047  // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
1048  $info['audio']['bitrate'] = (($framebytelength - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
1049  }
1050  } else {
1051  $info['error'][] = 'Error calculating frame length of free-format MP3 without Xing/LAME header';
1052  }
1053  }
1054  }
1055 
1056  if (isset($thisfile_mpeg_audio['VBR_frames']) ? $thisfile_mpeg_audio['VBR_frames'] : '') {
1057  switch ($thisfile_mpeg_audio['bitrate_mode']) {
1058  case 'vbr':
1059  case 'abr':
1060  $bytes_per_frame = 1152;
1061  if (($thisfile_mpeg_audio['version'] == '1') && ($thisfile_mpeg_audio['layer'] == 1)) {
1062  $bytes_per_frame = 384;
1063  } elseif ((($thisfile_mpeg_audio['version'] == '2') || ($thisfile_mpeg_audio['version'] == '2.5')) && ($thisfile_mpeg_audio['layer'] == 3)) {
1064  $bytes_per_frame = 576;
1065  }
1066  $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);
1067  if ($thisfile_mpeg_audio['VBR_bitrate'] > 0) {
1068  $info['audio']['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate'];
1069  $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; // to avoid confusion
1070  }
1071  break;
1072  }
1073  }
1074 
1075  // End variable-bitrate headers
1077 
1078  if ($recursivesearch) {
1079 
1080  if (!$this->RecursiveFrameScanning($offset, $nextframetestoffset,
1081  $ScanAsCBR)) {
1082  return false;
1083  }
1084  }
1085 
1086 
1087  //if (false) {
1088  // // experimental side info parsing section - not returning anything useful yet
1089  //
1090  // $SideInfoBitstream = GetId3_lib::BigEndian2Bin($SideInfoData);
1091  // $SideInfoOffset = 0;
1092  //
1093  // if ($thisfile_mpeg_audio['version'] == '1') {
1094  // if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
1095  // // MPEG-1 (mono)
1096  // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
1097  // $SideInfoOffset += 9;
1098  // $SideInfoOffset += 5;
1099  // } else {
1100  // // MPEG-1 (stereo, joint-stereo, dual-channel)
1101  // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
1102  // $SideInfoOffset += 9;
1103  // $SideInfoOffset += 3;
1104  // }
1105  // } else { // 2 or 2.5
1106  // if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
1107  // // MPEG-2, MPEG-2.5 (mono)
1108  // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
1109  // $SideInfoOffset += 8;
1110  // $SideInfoOffset += 1;
1111  // } else {
1112  // // MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel)
1113  // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
1114  // $SideInfoOffset += 8;
1115  // $SideInfoOffset += 2;
1116  // }
1117  // }
1118  //
1119  // if ($thisfile_mpeg_audio['version'] == '1') {
1120  // for ($channel = 0; $channel < $info['audio']['channels']; $channel++) {
1121  // for ($scfsi_band = 0; $scfsi_band < 4; $scfsi_band++) {
1122  // $thisfile_mpeg_audio['scfsi'][$channel][$scfsi_band] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1123  // $SideInfoOffset += 2;
1124  // }
1125  // }
1126  // }
1127  // for ($granule = 0; $granule < (($thisfile_mpeg_audio['version'] == '1') ? 2 : 1); $granule++) {
1128  // for ($channel = 0; $channel < $info['audio']['channels']; $channel++) {
1129  // $thisfile_mpeg_audio['part2_3_length'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 12);
1130  // $SideInfoOffset += 12;
1131  // $thisfile_mpeg_audio['big_values'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
1132  // $SideInfoOffset += 9;
1133  // $thisfile_mpeg_audio['global_gain'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 8);
1134  // $SideInfoOffset += 8;
1135  // if ($thisfile_mpeg_audio['version'] == '1') {
1136  // $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
1137  // $SideInfoOffset += 4;
1138  // } else {
1139  // $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
1140  // $SideInfoOffset += 9;
1141  // }
1142  // $thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1143  // $SideInfoOffset += 1;
1144  //
1145  // if ($thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] == '1') {
1146  //
1147  // $thisfile_mpeg_audio['block_type'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 2);
1148  // $SideInfoOffset += 2;
1149  // $thisfile_mpeg_audio['mixed_block_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1150  // $SideInfoOffset += 1;
1151  //
1152  // for ($region = 0; $region < 2; $region++) {
1153  // $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
1154  // $SideInfoOffset += 5;
1155  // }
1156  // $thisfile_mpeg_audio['table_select'][$granule][$channel][2] = 0;
1157  //
1158  // for ($window = 0; $window < 3; $window++) {
1159  // $thisfile_mpeg_audio['subblock_gain'][$granule][$channel][$window] = substr($SideInfoBitstream, $SideInfoOffset, 3);
1160  // $SideInfoOffset += 3;
1161  // }
1162  //
1163  // } else {
1164  //
1165  // for ($region = 0; $region < 3; $region++) {
1166  // $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
1167  // $SideInfoOffset += 5;
1168  // }
1169  //
1170  // $thisfile_mpeg_audio['region0_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
1171  // $SideInfoOffset += 4;
1172  // $thisfile_mpeg_audio['region1_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 3);
1173  // $SideInfoOffset += 3;
1174  // $thisfile_mpeg_audio['block_type'][$granule][$channel] = 0;
1175  // }
1176  //
1177  // if ($thisfile_mpeg_audio['version'] == '1') {
1178  // $thisfile_mpeg_audio['preflag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1179  // $SideInfoOffset += 1;
1180  // }
1181  // $thisfile_mpeg_audio['scalefac_scale'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1182  // $SideInfoOffset += 1;
1183  // $thisfile_mpeg_audio['count1table_select'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
1184  // $SideInfoOffset += 1;
1185  // }
1186  // }
1187  //}
1188  return true;
1189  }
FreeFormatFrameLength($offset, $deepscan=false)
Definition: Mp3.php:1252
static PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8')
Definition: Helper.php:36
static RGADamplitude2dB($amplitude)
Definition: Helper.php:1610
fseek($bytes, $whence=SEEK_SET)
$info
Definition: example_052.php:80
static RGADadjustmentLookup($rawadjustment, $signbit)
Definition: Helper.php:1571
static RGADnameLookup($namecode)
array $RGADname
Definition: Helper.php:1534
Create styles array
The data for the language used.
static RGADoriginatorLookup($originatorcode)
array $RGADoriginator
Definition: Helper.php:1552
static BigEndian2Int($byteword, $synchsafe=false, $signed=false)
Definition: Helper.php:374
RecursiveFrameScanning(&$offset, &$nextframetestoffset, $ScanAsCBR)
Definition: Mp3.php:1198
static LittleEndian2Float($byteword)
Definition: Helper.php:277
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ FreeFormatFrameLength()

GetId3\Module\Audio\Mp3::FreeFormatFrameLength (   $offset,
  $deepscan = false 
)
Parameters
type$offset
type$deepscan
Returns
boolean

Definition at line 1252 of file Mp3.php.

References $info, array, GetId3\Handler\BaseHandler\fread(), GetId3\Handler\BaseHandler\fseek(), and GetId3\Lib\Helper\PrintHexBytes().

Referenced by GetId3\Module\Audio\Mp3\decodeMPEGaudioHeader().

1253  {
1254  $info = &$this->getid3->info;
1255 
1256  fseek($this->getid3->fp, $offset, SEEK_SET);
1257  $MPEGaudioData = fread($this->getid3->fp, 32768);
1258 
1259  $SyncPattern1 = substr($MPEGaudioData, 0, 4);
1260  // may be different pattern due to padding
1261  $SyncPattern2 = $SyncPattern1{0} . $SyncPattern1{1} . chr(ord($SyncPattern1{2}) | 0x02) . $SyncPattern1{3};
1262  if ($SyncPattern2 === $SyncPattern1) {
1263  $SyncPattern2 = $SyncPattern1{0} . $SyncPattern1{1} . chr(ord($SyncPattern1{2}) & 0xFD) . $SyncPattern1{3};
1264  }
1265 
1266  $framelength = false;
1267  $framelength1 = strpos($MPEGaudioData, $SyncPattern1, 4);
1268  $framelength2 = strpos($MPEGaudioData, $SyncPattern2, 4);
1269  if ($framelength1 > 4) {
1270  $framelength = $framelength1;
1271  }
1272  if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
1273  $framelength = $framelength2;
1274  }
1275  if (!$framelength) {
1276 
1277  // LAME 3.88 has a different value for modeextension on the first frame vs the rest
1278  $framelength1 = strpos($MPEGaudioData, substr($SyncPattern1, 0, 3),
1279  4);
1280  $framelength2 = strpos($MPEGaudioData, substr($SyncPattern2, 0, 3),
1281  4);
1282 
1283  if ($framelength1 > 4) {
1284  $framelength = $framelength1;
1285  }
1286  if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
1287  $framelength = $framelength2;
1288  }
1289  if (!$framelength) {
1290  $info['error'][] = 'Cannot find next free-format synch pattern (' . Helper::PrintHexBytes($SyncPattern1) . ' or ' . Helper::PrintHexBytes($SyncPattern2) . ') after offset ' . $offset;
1291 
1292  return false;
1293  } else {
1294  $info['warning'][] = 'ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)';
1295  $info['audio']['codec'] = 'LAME';
1296  $info['audio']['encoder'] = 'LAME3.88';
1297  $SyncPattern1 = substr($SyncPattern1, 0, 3);
1298  $SyncPattern2 = substr($SyncPattern2, 0, 3);
1299  }
1300  }
1301 
1302  if ($deepscan) {
1303 
1304  $ActualFrameLengthValues = array();
1305  $nextoffset = $offset + $framelength;
1306  while ($nextoffset < ($info['avdataend'] - 6)) {
1307  fseek($this->getid3->fp, $nextoffset - 1, SEEK_SET);
1308  $NextSyncPattern = fread($this->getid3->fp, 6);
1309  if ((substr($NextSyncPattern, 1, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern,
1310  1,
1311  strlen($SyncPattern2)) == $SyncPattern2)) {
1312  // good - found where expected
1313  $ActualFrameLengthValues[] = $framelength;
1314  } elseif ((substr($NextSyncPattern, 0, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern,
1315  0,
1316  strlen($SyncPattern2)) == $SyncPattern2)) {
1317  // ok - found one byte earlier than expected (last frame wasn't padded, first frame was)
1318  $ActualFrameLengthValues[] = ($framelength - 1);
1319  $nextoffset--;
1320  } elseif ((substr($NextSyncPattern, 2, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern,
1321  2,
1322  strlen($SyncPattern2)) == $SyncPattern2)) {
1323  // ok - found one byte later than expected (last frame was padded, first frame wasn't)
1324  $ActualFrameLengthValues[] = ($framelength + 1);
1325  $nextoffset++;
1326  } else {
1327  $info['error'][] = 'Did not find expected free-format sync pattern at offset ' . $nextoffset;
1328 
1329  return false;
1330  }
1331  $nextoffset += $framelength;
1332  }
1333  if (count($ActualFrameLengthValues) > 0) {
1334  $framelength = intval(round(array_sum($ActualFrameLengthValues) / count($ActualFrameLengthValues)));
1335  }
1336  }
1337 
1338  return $framelength;
1339  }
static PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8')
Definition: Helper.php:36
fseek($bytes, $whence=SEEK_SET)
$info
Definition: example_052.php:80
Create styles array
The data for the language used.
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ getOnlyMPEGaudioInfo()

GetId3\Module\Audio\Mp3::getOnlyMPEGaudioInfo (   $avdataoffset,
  $BitrateHistogram = false 
)

type $MPEGaudioVersionLookup type $MPEGaudioLayerLookup type $MPEGaudioBitrateLookup

Parameters
type$avdataoffset
boolean$BitrateHistogram
Returns
boolean

Definition at line 1510 of file Mp3.php.

References $header, $info, array, GetId3\Lib\Helper\CastAsInt(), GetId3\Module\Audio\Mp3\decodeMPEGaudioHeader(), GetId3\Handler\BaseHandler\feof(), GetId3\Handler\BaseHandler\fread(), GetId3\Handler\BaseHandler\fseek(), GetId3\Handler\BaseHandler\ftell(), and GetId3\Lib\Helper\safe_inc().

Referenced by GetId3\Module\Audio\Mp3\analyze().

1511  {
1512  // looks for synch, decodes MPEG audio header
1513 
1514  $info = &$this->getid3->info;
1515 
1516  static $MPEGaudioVersionLookup;
1517  static $MPEGaudioLayerLookup;
1518  static $MPEGaudioBitrateLookup;
1519  if (empty($MPEGaudioVersionLookup)) {
1520  $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
1521  $MPEGaudioLayerLookup = self::MPEGaudioLayerArray();
1522  $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
1523  }
1524 
1525  fseek($this->getid3->fp, $avdataoffset, SEEK_SET);
1526  $sync_seek_buffer_size = min(128 * 1024,
1527  $info['avdataend'] - $avdataoffset);
1528  if ($sync_seek_buffer_size <= 0) {
1529  $info['error'][] = 'Invalid $sync_seek_buffer_size at offset ' . $avdataoffset;
1530 
1531  return false;
1532  }
1533  $header = fread($this->getid3->fp, $sync_seek_buffer_size);
1534  $sync_seek_buffer_size = strlen($header);
1535  $SynchSeekOffset = 0;
1536  while ($SynchSeekOffset < $sync_seek_buffer_size) {
1537  if ((($avdataoffset + $SynchSeekOffset) < $info['avdataend']) && !feof($this->getid3->fp)) {
1538 
1539  if ($SynchSeekOffset > $sync_seek_buffer_size) {
1540  // if a synch's not found within the first 128k bytes, then give up
1541  $info['error'][] = 'Could not find valid MPEG audio synch within the first ' . round($sync_seek_buffer_size / 1024) . 'kB';
1542  if (isset($info['audio']['bitrate'])) {
1543  unset($info['audio']['bitrate']);
1544  }
1545  if (isset($info['mpeg']['audio'])) {
1546  unset($info['mpeg']['audio']);
1547  }
1548  if (empty($info['mpeg'])) {
1549  unset($info['mpeg']);
1550  }
1551 
1552  return false;
1553  } elseif (feof($this->getid3->fp)) {
1554 
1555  $info['error'][] = 'Could not find valid MPEG audio synch before end of file';
1556  if (isset($info['audio']['bitrate'])) {
1557  unset($info['audio']['bitrate']);
1558  }
1559  if (isset($info['mpeg']['audio'])) {
1560  unset($info['mpeg']['audio']);
1561  }
1562  if (isset($info['mpeg']) && (!is_array($info['mpeg']) || (count($info['mpeg']) == 0))) {
1563  unset($info['mpeg']);
1564  }
1565 
1566  return false;
1567  }
1568  }
1569 
1570  if (($SynchSeekOffset + 1) >= strlen($header)) {
1571  $info['error'][] = 'Could not find valid MPEG synch before end of file';
1572 
1573  return false;
1574  }
1575 
1576  if (($header{$SynchSeekOffset} == "\xFF") && ($header{($SynchSeekOffset + 1)} > "\xE0")) { // synch detected
1577  if (!isset($FirstFrameThisfileInfo) && !isset($info['mpeg']['audio'])) {
1578  $FirstFrameThisfileInfo = $info;
1579  $FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset;
1580  if (!$this->decodeMPEGaudioHeader($FirstFrameAVDataOffset,
1581  $FirstFrameThisfileInfo,
1582  false)) {
1583  // if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's
1584  // garbage between this frame and a valid sequence of MPEG-audio frames, to be restored below
1585  unset($FirstFrameThisfileInfo);
1586  }
1587  }
1588 
1589  $dummy = $info; // only overwrite real data if valid header found
1590  if ($this->decodeMPEGaudioHeader($avdataoffset + $SynchSeekOffset,
1591  $dummy, true)) {
1592  $info = $dummy;
1593  $info['avdataoffset'] = $avdataoffset + $SynchSeekOffset;
1594  switch (isset($info['fileformat']) ? $info['fileformat'] : '') {
1595  case '':
1596  case 'id3':
1597  case 'ape':
1598  case 'mp3':
1599  $info['fileformat'] = 'mp3';
1600  $info['audio']['dataformat'] = 'mp3';
1601  break;
1602  }
1603  if (isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) {
1604  if (!(abs($info['audio']['bitrate'] - $FirstFrameThisfileInfo['audio']['bitrate']) <= 1)) {
1605  // If there is garbage data between a valid VBR header frame and a sequence
1606  // of valid MPEG-audio frames the VBR data is no longer discarded.
1607  $info = $FirstFrameThisfileInfo;
1608  $info['avdataoffset'] = $FirstFrameAVDataOffset;
1609  $info['fileformat'] = 'mp3';
1610  $info['audio']['dataformat'] = 'mp3';
1611  $dummy = $info;
1612  unset($dummy['mpeg']['audio']);
1613  $GarbageOffsetStart = $FirstFrameAVDataOffset + $FirstFrameThisfileInfo['mpeg']['audio']['framelength'];
1614  $GarbageOffsetEnd = $avdataoffset + $SynchSeekOffset;
1615  if ($this->decodeMPEGaudioHeader($GarbageOffsetEnd,
1616  $dummy, true, true)) {
1617  $info = $dummy;
1618  $info['avdataoffset'] = $GarbageOffsetEnd;
1619  $info['warning'][] = 'apparently-valid VBR header not used because could not find ' . self::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;
1620  } else {
1621  $info['warning'][] = 'using data from VBR header even though could not find ' . self::GETID3_MP3_VALID_CHECK_FRAMES . ' consecutive MPEG-audio frames immediately after VBR header (garbage data for ' . ($GarbageOffsetEnd - $GarbageOffsetStart) . ' bytes between ' . $GarbageOffsetStart . ' and ' . $GarbageOffsetEnd . ')';
1622  }
1623  }
1624  }
1625  if (isset($info['mpeg']['audio']['bitrate_mode']) && ($info['mpeg']['audio']['bitrate_mode'] == 'vbr') && !isset($info['mpeg']['audio']['VBR_method'])) {
1626  // VBR file with no VBR header
1627  $BitrateHistogram = true;
1628  }
1629 
1630  if ($BitrateHistogram) {
1631 
1632  $info['mpeg']['audio']['stereo_distribution'] = array('stereo' => 0, 'joint stereo' => 0, 'dual channel' => 0, 'mono' => 0);
1633  $info['mpeg']['audio']['version_distribution'] = array('1' => 0, '2' => 0, '2.5' => 0);
1634 
1635  if ($info['mpeg']['audio']['version'] == '1') {
1636  if ($info['mpeg']['audio']['layer'] == 3) {
1637  $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);
1638  } elseif ($info['mpeg']['audio']['layer'] == 2) {
1639  $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);
1640  } elseif ($info['mpeg']['audio']['layer'] == 1) {
1641  $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);
1642  }
1643  } elseif ($info['mpeg']['audio']['layer'] == 1) {
1644  $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);
1645  } else {
1646  $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);
1647  }
1648 
1649  $dummy = array('error' => $info['error'], 'warning' => $info['warning'], 'avdataend' => $info['avdataend'], 'avdataoffset' => $info['avdataoffset']);
1650  $synchstartoffset = $info['avdataoffset'];
1651  fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
1652 
1653  // you can play with these numbers:
1654  $max_frames_scan = 50000;
1655  $max_scan_segments = 10;
1656 
1657  // don't play with these numbers:
1658  $FastMode = false;
1659  $SynchErrorsFound = 0;
1660  $frames_scanned = 0;
1661  $this_scan_segment = 0;
1662  $frames_scan_per_segment = ceil($max_frames_scan / $max_scan_segments);
1663  $pct_data_scanned = 0;
1664  for ($current_segment = 0;
1665  $current_segment < $max_scan_segments;
1666  $current_segment++) {
1667  $frames_scanned_this_segment = 0;
1668  if (ftell($this->getid3->fp) >= $info['avdataend']) {
1669  break;
1670  }
1671  $scan_start_offset[$current_segment] = max(ftell($this->getid3->fp),
1672  $info['avdataoffset'] + round($current_segment * (($info['avdataend'] - $info['avdataoffset']) / $max_scan_segments)));
1673  if ($current_segment > 0) {
1674  fseek($this->getid3->fp,
1675  $scan_start_offset[$current_segment],
1676  SEEK_SET);
1677  $buffer_4k = fread($this->getid3->fp, 4096);
1678  for ($j = 0; $j < (strlen($buffer_4k) - 4); $j++) {
1679  if (($buffer_4k{$j} == "\xFF") && ($buffer_4k{($j + 1)} > "\xE0")) { // synch detected
1680  if ($this->decodeMPEGaudioHeader($scan_start_offset[$current_segment] + $j,
1681  $dummy,
1682  false,
1683  false,
1684  $FastMode)) {
1685  $calculated_next_offset = $scan_start_offset[$current_segment] + $j + $dummy['mpeg']['audio']['framelength'];
1686  if ($this->decodeMPEGaudioHeader($calculated_next_offset,
1687  $dummy,
1688  false,
1689  false,
1690  $FastMode)) {
1691  $scan_start_offset[$current_segment] += $j;
1692  break;
1693  }
1694  }
1695  }
1696  }
1697  }
1698  $synchstartoffset = $scan_start_offset[$current_segment];
1699  while ($this->decodeMPEGaudioHeader($synchstartoffset,
1700  $dummy, false,
1701  false, $FastMode)) {
1702  $FastMode = true;
1703  $thisframebitrate = $MPEGaudioBitrateLookup[$MPEGaudioVersionLookup[$dummy['mpeg']['audio']['raw']['version']]][$MPEGaudioLayerLookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']];
1704 
1705  if (empty($dummy['mpeg']['audio']['framelength'])) {
1706  $SynchErrorsFound++;
1707  $synchstartoffset++;
1708  } else {
1709  Helper::safe_inc($info['mpeg']['audio']['bitrate_distribution'][$thisframebitrate]);
1710  Helper::safe_inc($info['mpeg']['audio']['stereo_distribution'][$dummy['mpeg']['audio']['channelmode']]);
1711  Helper::safe_inc($info['mpeg']['audio']['version_distribution'][$dummy['mpeg']['audio']['version']]);
1712  $synchstartoffset += $dummy['mpeg']['audio']['framelength'];
1713  }
1714  $frames_scanned++;
1715  if ($frames_scan_per_segment && (++$frames_scanned_this_segment >= $frames_scan_per_segment)) {
1716  $this_pct_scanned = (ftell($this->getid3->fp) - $scan_start_offset[$current_segment]) / ($info['avdataend'] - $info['avdataoffset']);
1717  if (($current_segment == 0) && (($this_pct_scanned * $max_scan_segments) >= 1)) {
1718  // file likely contains < $max_frames_scan, just scan as one segment
1719  $max_scan_segments = 1;
1720  $frames_scan_per_segment = $max_frames_scan;
1721  } else {
1722  $pct_data_scanned += $this_pct_scanned;
1723  break;
1724  }
1725  }
1726  }
1727  }
1728  if ($pct_data_scanned > 0) {
1729  $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,
1730  1) . '% of file) and extrapolated distribution, playtime and bitrate may be incorrect.';
1731  foreach ($info['mpeg']['audio'] as $key1 => $value1) {
1732  if (!preg_match('#_distribution$#i', $key1)) {
1733  continue;
1734  }
1735  foreach ($value1 as $key2 => $value2) {
1736  $info['mpeg']['audio'][$key1][$key2] = round($value2 / $pct_data_scanned);
1737  }
1738  }
1739  }
1740 
1741  if ($SynchErrorsFound > 0) {
1742  $info['warning'][] = 'Found ' . $SynchErrorsFound . ' synch errors in histogram analysis';
1743  //return false;
1744  }
1745 
1746  $bittotal = 0;
1747  $framecounter = 0;
1748  foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitratevalue => $bitratecount) {
1749  $framecounter += $bitratecount;
1750  if ($bitratevalue != 'free') {
1751  $bittotal += ($bitratevalue * $bitratecount);
1752  }
1753  }
1754  if ($framecounter == 0) {
1755  $info['error'][] = 'Corrupt MP3 file: framecounter == zero';
1756 
1757  return false;
1758  }
1759  $info['mpeg']['audio']['frame_count'] = Helper::CastAsInt($framecounter);
1760  $info['mpeg']['audio']['bitrate'] = ($bittotal / $framecounter);
1761 
1762  $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
1763 
1764 
1765  // Definitively set VBR vs CBR, even if the Xing/LAME/VBRI header says differently
1766  $distinct_bitrates = 0;
1767  foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitrate_value => $bitrate_count) {
1768  if ($bitrate_count > 0) {
1769  $distinct_bitrates++;
1770  }
1771  }
1772  if ($distinct_bitrates > 1) {
1773  $info['mpeg']['audio']['bitrate_mode'] = 'vbr';
1774  } else {
1775  $info['mpeg']['audio']['bitrate_mode'] = 'cbr';
1776  }
1777  $info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode'];
1778  }
1779 
1780  break; // exit while()
1781  }
1782  }
1783 
1784  $SynchSeekOffset++;
1785  if (($avdataoffset + $SynchSeekOffset) >= $info['avdataend']) {
1786  // end of file/data
1787 
1788  if (empty($info['mpeg']['audio'])) {
1789 
1790  $info['error'][] = 'could not find valid MPEG synch before end of file';
1791  if (isset($info['audio']['bitrate'])) {
1792  unset($info['audio']['bitrate']);
1793  }
1794  if (isset($info['mpeg']['audio'])) {
1795  unset($info['mpeg']['audio']);
1796  }
1797  if (isset($info['mpeg']) && (!is_array($info['mpeg']) || empty($info['mpeg']))) {
1798  unset($info['mpeg']);
1799  }
1800 
1801  return false;
1802  }
1803  break;
1804  }
1805  }
1806  $info['audio']['channels'] = $info['mpeg']['audio']['channels'];
1807  $info['audio']['channelmode'] = $info['mpeg']['audio']['channelmode'];
1808  $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
1809 
1810  return true;
1811  }
decodeMPEGaudioHeader($offset, &$info, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false)
type $MPEGaudioVersionLookup type $MPEGaudioLayerLookup type $MPEGaudioBitrateLookup type $MPEGaud...
Definition: Mp3.php:453
fseek($bytes, $whence=SEEK_SET)
$info
Definition: example_052.php:80
$header
static CastAsInt($floatnum)
Definition: Helper.php:107
Create styles array
The data for the language used.
static safe_inc(&$variable, $increment=1)
Definition: Helper.php:91
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ getOnlyMPEGaudioInfoBruteForce()

GetId3\Module\Audio\Mp3::getOnlyMPEGaudioInfoBruteForce ( )
Returns
boolean

Definition at line 1345 of file Mp3.php.

References $info, array, GetId3\Lib\Helper\array_max(), GetId3\Handler\BaseHandler\fread(), GetId3\Handler\BaseHandler\fseek(), GetId3\Handler\BaseHandler\ftell(), and GetId3\Lib\Helper\safe_inc().

Referenced by GetId3\Module\Audio\Mp3\analyze().

1346  {
1347  $MPEGaudioHeaderDecodeCache = array();
1348  $MPEGaudioHeaderValidCache = array();
1349  $MPEGaudioHeaderLengthCache = array();
1350  $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
1351  $MPEGaudioLayerLookup = self::MPEGaudioLayerArray();
1352  $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
1353  $MPEGaudioFrequencyLookup = self::MPEGaudioFrequencyArray();
1354  $MPEGaudioChannelModeLookup = self::MPEGaudioChannelModeArray();
1355  $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
1356  $MPEGaudioEmphasisLookup = self::MPEGaudioEmphasisArray();
1357  $LongMPEGversionLookup = array();
1358  $LongMPEGlayerLookup = array();
1359  $LongMPEGbitrateLookup = array();
1360  $LongMPEGpaddingLookup = array();
1361  $LongMPEGfrequencyLookup = array();
1362  $Distribution['bitrate'] = array();
1363  $Distribution['frequency'] = array();
1364  $Distribution['layer'] = array();
1365  $Distribution['version'] = array();
1366  $Distribution['padding'] = array();
1367 
1368  $info = &$this->getid3->info;
1369  fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
1370 
1371  $max_frames_scan = 5000;
1372  $frames_scanned = 0;
1373 
1374  $previousvalidframe = $info['avdataoffset'];
1375  while (ftell($this->getid3->fp) < $info['avdataend']) {
1376  set_time_limit(30);
1377  $head4 = fread($this->getid3->fp, 4);
1378  if (strlen($head4) < 4) {
1379  break;
1380  }
1381  if ($head4{0} != "\xFF") {
1382  for ($i = 1; $i < 4; $i++) {
1383  if ($head4{$i} == "\xFF") {
1384  fseek($this->getid3->fp, $i - 4, SEEK_CUR);
1385  continue 2;
1386  }
1387  }
1388  continue;
1389  }
1390  if (!isset($MPEGaudioHeaderDecodeCache[$head4])) {
1391  $MPEGaudioHeaderDecodeCache[$head4] = self::MPEGaudioHeaderDecode($head4);
1392  }
1393  if (!isset($MPEGaudioHeaderValidCache[$head4])) {
1394  $MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$head4],
1395  false,
1396  false);
1397  }
1398  if ($MPEGaudioHeaderValidCache[$head4]) {
1399 
1400  if (!isset($MPEGaudioHeaderLengthCache[$head4])) {
1401  $LongMPEGversionLookup[$head4] = $MPEGaudioVersionLookup[$MPEGaudioHeaderDecodeCache[$head4]['version']];
1402  $LongMPEGlayerLookup[$head4] = $MPEGaudioLayerLookup[$MPEGaudioHeaderDecodeCache[$head4]['layer']];
1403  $LongMPEGbitrateLookup[$head4] = $MPEGaudioBitrateLookup[$LongMPEGversionLookup[$head4]][$LongMPEGlayerLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['bitrate']];
1404  $LongMPEGpaddingLookup[$head4] = (bool) $MPEGaudioHeaderDecodeCache[$head4]['padding'];
1405  $LongMPEGfrequencyLookup[$head4] = $MPEGaudioFrequencyLookup[$LongMPEGversionLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['sample_rate']];
1406  $MPEGaudioHeaderLengthCache[$head4] = self::MPEGaudioFrameLength(
1407  $LongMPEGbitrateLookup[$head4],
1408  $LongMPEGversionLookup[$head4],
1409  $LongMPEGlayerLookup[$head4],
1410  $LongMPEGpaddingLookup[$head4],
1411  $LongMPEGfrequencyLookup[$head4]);
1412  }
1413  if ($MPEGaudioHeaderLengthCache[$head4] > 4) {
1414  $WhereWeWere = ftell($this->getid3->fp);
1415  fseek($this->getid3->fp,
1416  $MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR);
1417  $next4 = fread($this->getid3->fp, 4);
1418  if ($next4{0} == "\xFF") {
1419  if (!isset($MPEGaudioHeaderDecodeCache[$next4])) {
1420  $MPEGaudioHeaderDecodeCache[$next4] = self::MPEGaudioHeaderDecode($next4);
1421  }
1422  if (!isset($MPEGaudioHeaderValidCache[$next4])) {
1423  $MPEGaudioHeaderValidCache[$next4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$next4],
1424  false,
1425  false);
1426  }
1427  if ($MPEGaudioHeaderValidCache[$next4]) {
1428  fseek($this->getid3->fp, -4, SEEK_CUR);
1429 
1430  Helper::safe_inc($Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]]);
1431  Helper::safe_inc($Distribution['layer'][$LongMPEGlayerLookup[$head4]]);
1432  Helper::safe_inc($Distribution['version'][$LongMPEGversionLookup[$head4]]);
1433  Helper::safe_inc($Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]);
1434  Helper::safe_inc($Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]);
1435  if ($max_frames_scan && (++$frames_scanned >= $max_frames_scan)) {
1436  $pct_data_scanned = (ftell($this->getid3->fp) - $info['avdataoffset']) / ($info['avdataend'] - $info['avdataoffset']);
1437  $info['warning'][] = 'too many MPEG audio frames to scan, only scanned first ' . $max_frames_scan . ' frames (' . number_format($pct_data_scanned * 100,
1438  1) . '% of file) and extrapolated distribution, playtime and bitrate may be incorrect.';
1439  foreach ($Distribution as $key1 => $value1) {
1440  foreach ($value1 as $key2 => $value2) {
1441  $Distribution[$key1][$key2] = round($value2 / $pct_data_scanned);
1442  }
1443  }
1444  break;
1445  }
1446  continue;
1447  }
1448  }
1449  unset($next4);
1450  fseek($this->getid3->fp, $WhereWeWere - 3, SEEK_SET);
1451  }
1452  }
1453  }
1454  foreach ($Distribution as $key => $value) {
1455  ksort($Distribution[$key], SORT_NUMERIC);
1456  }
1457  ksort($Distribution['version'], SORT_STRING);
1458  $info['mpeg']['audio']['bitrate_distribution'] = $Distribution['bitrate'];
1459  $info['mpeg']['audio']['frequency_distribution'] = $Distribution['frequency'];
1460  $info['mpeg']['audio']['layer_distribution'] = $Distribution['layer'];
1461  $info['mpeg']['audio']['version_distribution'] = $Distribution['version'];
1462  $info['mpeg']['audio']['padding_distribution'] = $Distribution['padding'];
1463  if (count($Distribution['version']) > 1) {
1464  $info['error'][] = 'Corrupt file - more than one MPEG version detected';
1465  }
1466  if (count($Distribution['layer']) > 1) {
1467  $info['error'][] = 'Corrupt file - more than one MPEG layer detected';
1468  }
1469  if (count($Distribution['frequency']) > 1) {
1470  $info['error'][] = 'Corrupt file - more than one MPEG sample rate detected';
1471  }
1472 
1473 
1474  $bittotal = 0;
1475  foreach ($Distribution['bitrate'] as $bitratevalue => $bitratecount) {
1476  if ($bitratevalue != 'free') {
1477  $bittotal += ($bitratevalue * $bitratecount);
1478  }
1479  }
1480  $info['mpeg']['audio']['frame_count'] = array_sum($Distribution['bitrate']);
1481  if ($info['mpeg']['audio']['frame_count'] == 0) {
1482  $info['error'][] = 'no MPEG audio frames found';
1483 
1484  return false;
1485  }
1486  $info['mpeg']['audio']['bitrate'] = ($bittotal / $info['mpeg']['audio']['frame_count']);
1487  $info['mpeg']['audio']['bitrate_mode'] = ((count($Distribution['bitrate']) > 0) ? 'vbr' : 'cbr');
1488  $info['mpeg']['audio']['sample_rate'] = Helper::array_max($Distribution['frequency'],
1489  true);
1490 
1491  $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
1492  $info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode'];
1493  $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
1494  $info['audio']['dataformat'] = 'mp' . Helper::array_max($Distribution['layer'],
1495  true);
1496  $info['fileformat'] = $info['audio']['dataformat'];
1497 
1498  return true;
1499  }
fseek($bytes, $whence=SEEK_SET)
$info
Definition: example_052.php:80
Create styles array
The data for the language used.
static safe_inc(&$variable, $increment=1)
Definition: Helper.php:91
static array_max($arraydata, $returnkey=false)
Definition: Helper.php:755
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ GuessEncoderOptions()

GetId3\Module\Audio\Mp3::GuessEncoderOptions ( )

array $NamedPresetBitrates array $KnownEncoderValues array $ExpectedLowpass array $ExpectedResampledRate

Returns
type

Definition at line 201 of file Mp3.php.

References $info, and array.

Referenced by GetId3\Module\Audio\Mp3\analyze().

202  {
203  // shortcuts
204  $info = &$this->getid3->info;
205  if (!empty($info['mpeg']['audio'])) {
206  $thisfile_mpeg_audio = &$info['mpeg']['audio'];
207  if (!empty($thisfile_mpeg_audio['LAME'])) {
208  $thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME'];
209  }
210  }
211 
212  $encoder_options = '';
213  static $NamedPresetBitrates = array(16, 24, 40, 56, 112, 128, 160, 192, 256);
214 
215  if (isset($thisfile_mpeg_audio['VBR_method']) && ($thisfile_mpeg_audio['VBR_method'] == 'Fraunhofer') && !empty($thisfile_mpeg_audio['VBR_quality'])) {
216 
217  $encoder_options = 'VBR q' . $thisfile_mpeg_audio['VBR_quality'];
218  } elseif (!empty($thisfile_mpeg_audio_lame['preset_used']) && (!in_array($thisfile_mpeg_audio_lame['preset_used_id'],
219  $NamedPresetBitrates))) {
220 
221  $encoder_options = $thisfile_mpeg_audio_lame['preset_used'];
222  } elseif (!empty($thisfile_mpeg_audio_lame['vbr_quality'])) {
223 
224  static $KnownEncoderValues = array();
225  if (empty($KnownEncoderValues)) {
226 
227  //$KnownEncoderValues[abrbitrate_minbitrate][vbr_quality][raw_vbr_method][raw_noise_shaping][raw_stereo_mode][ath_type][lowpass_frequency] = 'preset name';
228  $KnownEncoderValues[0xFF][58][1][1][3][2][20500] = '--alt-preset insane'; // 3.90, 3.90.1, 3.92
229  $KnownEncoderValues[0xFF][58][1][1][3][2][20600] = '--alt-preset insane'; // 3.90.2, 3.90.3, 3.91
230  $KnownEncoderValues[0xFF][57][1][1][3][4][20500] = '--alt-preset insane'; // 3.94, 3.95
231  $KnownEncoderValues['**'][78][3][2][3][2][19500] = '--alt-preset extreme'; // 3.90, 3.90.1, 3.92
232  $KnownEncoderValues['**'][78][3][2][3][2][19600] = '--alt-preset extreme'; // 3.90.2, 3.91
233  $KnownEncoderValues['**'][78][3][1][3][2][19600] = '--alt-preset extreme'; // 3.90.3
234  $KnownEncoderValues['**'][78][4][2][3][2][19500] = '--alt-preset fast extreme'; // 3.90, 3.90.1, 3.92
235  $KnownEncoderValues['**'][78][4][2][3][2][19600] = '--alt-preset fast extreme'; // 3.90.2, 3.90.3, 3.91
236  $KnownEncoderValues['**'][78][3][2][3][4][19000] = '--alt-preset standard'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
237  $KnownEncoderValues['**'][78][3][1][3][4][19000] = '--alt-preset standard'; // 3.90.3
238  $KnownEncoderValues['**'][78][4][2][3][4][19000] = '--alt-preset fast standard'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
239  $KnownEncoderValues['**'][78][4][1][3][4][19000] = '--alt-preset fast standard'; // 3.90.3
240  $KnownEncoderValues['**'][88][4][1][3][3][19500] = '--r3mix'; // 3.90, 3.90.1, 3.92
241  $KnownEncoderValues['**'][88][4][1][3][3][19600] = '--r3mix'; // 3.90.2, 3.90.3, 3.91
242  $KnownEncoderValues['**'][67][4][1][3][4][18000] = '--r3mix'; // 3.94, 3.95
243  $KnownEncoderValues['**'][68][3][2][3][4][18000] = '--alt-preset medium'; // 3.90.3
244  $KnownEncoderValues['**'][68][4][2][3][4][18000] = '--alt-preset fast medium'; // 3.90.3
245 
246  $KnownEncoderValues[0xFF][99][1][1][1][2][0] = '--preset studio'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
247  $KnownEncoderValues[0xFF][58][2][1][3][2][20600] = '--preset studio'; // 3.90.3, 3.93.1
248  $KnownEncoderValues[0xFF][58][2][1][3][2][20500] = '--preset studio'; // 3.93
249  $KnownEncoderValues[0xFF][57][2][1][3][4][20500] = '--preset studio'; // 3.94, 3.95
250  $KnownEncoderValues[0xC0][88][1][1][1][2][0] = '--preset cd'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
251  $KnownEncoderValues[0xC0][58][2][2][3][2][19600] = '--preset cd'; // 3.90.3, 3.93.1
252  $KnownEncoderValues[0xC0][58][2][2][3][2][19500] = '--preset cd'; // 3.93
253  $KnownEncoderValues[0xC0][57][2][1][3][4][19500] = '--preset cd'; // 3.94, 3.95
254  $KnownEncoderValues[0xA0][78][1][1][3][2][18000] = '--preset hifi'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
255  $KnownEncoderValues[0xA0][58][2][2][3][2][18000] = '--preset hifi'; // 3.90.3, 3.93, 3.93.1
256  $KnownEncoderValues[0xA0][57][2][1][3][4][18000] = '--preset hifi'; // 3.94, 3.95
257  $KnownEncoderValues[0x80][67][1][1][3][2][18000] = '--preset tape'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
258  $KnownEncoderValues[0x80][67][1][1][3][2][15000] = '--preset radio'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
259  $KnownEncoderValues[0x70][67][1][1][3][2][15000] = '--preset fm'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92
260  $KnownEncoderValues[0x70][58][2][2][3][2][16000] = '--preset tape/radio/fm'; // 3.90.3, 3.93, 3.93.1
261  $KnownEncoderValues[0x70][57][2][1][3][4][16000] = '--preset tape/radio/fm'; // 3.94, 3.95
262  $KnownEncoderValues[0x38][58][2][2][0][2][10000] = '--preset voice'; // 3.90.3, 3.93, 3.93.1
263  $KnownEncoderValues[0x38][57][2][1][0][4][15000] = '--preset voice'; // 3.94, 3.95
264  $KnownEncoderValues[0x38][57][2][1][0][4][16000] = '--preset voice'; // 3.94a14
265  $KnownEncoderValues[0x28][65][1][1][0][2][7500] = '--preset mw-us'; // 3.90, 3.90.1, 3.92
266  $KnownEncoderValues[0x28][65][1][1][0][2][7600] = '--preset mw-us'; // 3.90.2, 3.91
267  $KnownEncoderValues[0x28][58][2][2][0][2][7000] = '--preset mw-us'; // 3.90.3, 3.93, 3.93.1
268  $KnownEncoderValues[0x28][57][2][1][0][4][10500] = '--preset mw-us'; // 3.94, 3.95
269  $KnownEncoderValues[0x28][57][2][1][0][4][11200] = '--preset mw-us'; // 3.94a14
270  $KnownEncoderValues[0x28][57][2][1][0][4][8800] = '--preset mw-us'; // 3.94a15
271  $KnownEncoderValues[0x18][58][2][2][0][2][4000] = '--preset phon+/lw/mw-eu/sw'; // 3.90.3, 3.93.1
272  $KnownEncoderValues[0x18][58][2][2][0][2][3900] = '--preset phon+/lw/mw-eu/sw'; // 3.93
273  $KnownEncoderValues[0x18][57][2][1][0][4][5900] = '--preset phon+/lw/mw-eu/sw'; // 3.94, 3.95
274  $KnownEncoderValues[0x18][57][2][1][0][4][6200] = '--preset phon+/lw/mw-eu/sw'; // 3.94a14
275  $KnownEncoderValues[0x18][57][2][1][0][4][3200] = '--preset phon+/lw/mw-eu/sw'; // 3.94a15
276  $KnownEncoderValues[0x10][58][2][2][0][2][3800] = '--preset phone'; // 3.90.3, 3.93.1
277  $KnownEncoderValues[0x10][58][2][2][0][2][3700] = '--preset phone'; // 3.93
278  $KnownEncoderValues[0x10][57][2][1][0][4][5600] = '--preset phone'; // 3.94, 3.95
279  }
280 
281  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']])) {
282 
283  $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']];
284  } 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']])) {
285 
286  $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']];
287  } elseif ($info['audio']['bitrate_mode'] == 'vbr') {
288 
289  // http://gabriel.mp3-tech.org/mp3infotag.html
290  // int Quality = (100 - 10 * gfp->VBR_q - gfp->quality)h
291 
292  $LAME_V_value = 10 - ceil($thisfile_mpeg_audio_lame['vbr_quality'] / 10);
293  $LAME_q_value = 100 - $thisfile_mpeg_audio_lame['vbr_quality'] - ($LAME_V_value * 10);
294  $encoder_options = '-V' . $LAME_V_value . ' -q' . $LAME_q_value;
295  } elseif ($info['audio']['bitrate_mode'] == 'cbr') {
296 
297  $encoder_options = strtoupper($info['audio']['bitrate_mode']) . ceil($info['audio']['bitrate'] / 1000);
298  } else {
299 
300  $encoder_options = strtoupper($info['audio']['bitrate_mode']);
301  }
302  } elseif (!empty($thisfile_mpeg_audio_lame['bitrate_abr'])) {
303 
304  $encoder_options = 'ABR' . $thisfile_mpeg_audio_lame['bitrate_abr'];
305  } elseif (!empty($info['audio']['bitrate'])) {
306 
307  if ($info['audio']['bitrate_mode'] == 'cbr') {
308  $encoder_options = strtoupper($info['audio']['bitrate_mode']) . ceil($info['audio']['bitrate'] / 1000);
309  } else {
310  $encoder_options = strtoupper($info['audio']['bitrate_mode']);
311  }
312  }
313  if (!empty($thisfile_mpeg_audio_lame['bitrate_min'])) {
314  $encoder_options .= ' -b' . $thisfile_mpeg_audio_lame['bitrate_min'];
315  }
316 
317  if (!empty($thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev']) || !empty($thisfile_mpeg_audio_lame['encoding_flags']['nogap_next'])) {
318  $encoder_options .= ' --nogap';
319  }
320 
321  if (!empty($thisfile_mpeg_audio_lame['lowpass_frequency'])) {
322  $ExplodedOptions = explode(' ', $encoder_options, 4);
323  if ($ExplodedOptions[0] == '--r3mix') {
324  $ExplodedOptions[1] = 'r3mix';
325  }
326  switch ($ExplodedOptions[0]) {
327  case '--preset':
328  case '--alt-preset':
329  case '--r3mix':
330  if ($ExplodedOptions[1] == 'fast') {
331  $ExplodedOptions[1] .= ' ' . $ExplodedOptions[2];
332  }
333  switch ($ExplodedOptions[1]) {
334  case 'portable':
335  case 'medium':
336  case 'standard':
337  case 'extreme':
338  case 'insane':
339  case 'fast portable':
340  case 'fast medium':
341  case 'fast standard':
342  case 'fast extreme':
343  case 'fast insane':
344  case 'r3mix':
345  static $ExpectedLowpass = array(
346  'insane|20500' => 20500,
347  'insane|20600' => 20600, // 3.90.2, 3.90.3, 3.91
348  'medium|18000' => 18000,
349  'fast medium|18000' => 18000,
350  'extreme|19500' => 19500, // 3.90, 3.90.1, 3.92, 3.95
351  'extreme|19600' => 19600, // 3.90.2, 3.90.3, 3.91, 3.93.1
352  'fast extreme|19500' => 19500, // 3.90, 3.90.1, 3.92, 3.95
353  'fast extreme|19600' => 19600, // 3.90.2, 3.90.3, 3.91, 3.93.1
354  'standard|19000' => 19000,
355  'fast standard|19000' => 19000,
356  'r3mix|19500' => 19500, // 3.90, 3.90.1, 3.92
357  'r3mix|19600' => 19600, // 3.90.2, 3.90.3, 3.91
358  'r3mix|18000' => 18000, // 3.94, 3.95
359  );
360  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))) {
361  $encoder_options .= ' --lowpass ' . $thisfile_mpeg_audio_lame['lowpass_frequency'];
362  }
363  break;
364 
365  default:
366  break;
367  }
368  break;
369  }
370  }
371 
372  if (isset($thisfile_mpeg_audio_lame['raw']['source_sample_freq'])) {
373  if (($thisfile_mpeg_audio['sample_rate'] == 44100) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 1)) {
374  $encoder_options .= ' --resample 44100';
375  } elseif (($thisfile_mpeg_audio['sample_rate'] == 48000) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 2)) {
376  $encoder_options .= ' --resample 48000';
377  } elseif ($thisfile_mpeg_audio['sample_rate'] < 44100) {
378  switch ($thisfile_mpeg_audio_lame['raw']['source_sample_freq']) {
379  case 0: // <= 32000
380  // may or may not be same as source frequency - ignore
381  break;
382  case 1: // 44100
383  case 2: // 48000
384  case 3: // 48000+
385  $ExplodedOptions = explode(' ', $encoder_options, 4);
386  switch ($ExplodedOptions[0]) {
387  case '--preset':
388  case '--alt-preset':
389  switch ($ExplodedOptions[1]) {
390  case 'fast':
391  case 'portable':
392  case 'medium':
393  case 'standard':
394  case 'extreme':
395  case 'insane':
396  $encoder_options .= ' --resample ' . $thisfile_mpeg_audio['sample_rate'];
397  break;
398 
399  default:
400  static $ExpectedResampledRate = array(
401  'phon+/lw/mw-eu/sw|16000' => 16000,
402  'mw-us|24000' => 24000, // 3.95
403  'mw-us|32000' => 32000, // 3.93
404  'mw-us|16000' => 16000, // 3.92
405  'phone|16000' => 16000,
406  'phone|11025' => 11025, // 3.94a15
407  'radio|32000' => 32000, // 3.94a15
408  'fm/radio|32000' => 32000, // 3.92
409  'fm|32000' => 32000, // 3.90
410  'voice|32000' => 32000);
411  if (!isset($ExpectedResampledRate[$ExplodedOptions[1] . '|' . $thisfile_mpeg_audio['sample_rate']])) {
412  $encoder_options .= ' --resample ' . $thisfile_mpeg_audio['sample_rate'];
413  }
414  break;
415  }
416  break;
417 
418  case '--r3mix':
419  default:
420  $encoder_options .= ' --resample ' . $thisfile_mpeg_audio['sample_rate'];
421  break;
422  }
423  break;
424  }
425  }
426  }
427  if (empty($encoder_options) && !empty($info['audio']['bitrate']) && !empty($info['audio']['bitrate_mode'])) {
428  //$encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000);
429  $encoder_options = strtoupper($info['audio']['bitrate_mode']);
430  }
431 
432  return $encoder_options;
433  }
$info
Definition: example_052.php:80
Create styles array
The data for the language used.
+ Here is the caller graph for this function:

◆ LAMEmiscSourceSampleFrequencyLookup()

static GetId3\Module\Audio\Mp3::LAMEmiscSourceSampleFrequencyLookup (   $SourceSampleFrequencyID)
static

array $LAMEmiscSourceSampleFrequencyLookup

Parameters
type$SourceSampleFrequencyID
Returns
type

Definition at line 2254 of file Mp3.php.

References array.

2255  {
2256  static $LAMEmiscSourceSampleFrequencyLookup = array(
2257  0 => '<= 32 kHz',
2258  1 => '44.1 kHz',
2259  2 => '48 kHz',
2260  3 => '> 48kHz'
2261  );
2262 
2263  return (isset($LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID]) ? $LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID] : '');
2264  }
Create styles array
The data for the language used.

◆ LAMEmiscStereoModeLookup()

static GetId3\Module\Audio\Mp3::LAMEmiscStereoModeLookup (   $StereoModeID)
static

array $LAMEmiscStereoModeLookup

Parameters
type$StereoModeID
Returns
type

Definition at line 2232 of file Mp3.php.

References array.

2233  {
2234  static $LAMEmiscStereoModeLookup = array(
2235  0 => 'mono',
2236  1 => 'stereo',
2237  2 => 'dual mono',
2238  3 => 'joint stereo',
2239  4 => 'forced stereo',
2240  5 => 'auto',
2241  6 => 'intensity stereo',
2242  7 => 'other'
2243  );
2244 
2245  return (isset($LAMEmiscStereoModeLookup[$StereoModeID]) ? $LAMEmiscStereoModeLookup[$StereoModeID] : '');
2246  }
Create styles array
The data for the language used.

◆ LAMEpresetUsedLookup()

static GetId3\Module\Audio\Mp3::LAMEpresetUsedLookup (   $LAMEtag)
static
Parameters
type$LAMEtag
Returns
string

Definition at line 2289 of file Mp3.php.

References array.

2290  {
2291 
2292  if ($LAMEtag['preset_used_id'] == 0) {
2293  // no preset used (LAME >=3.93)
2294  // no preset recorded (LAME <3.93)
2295  return '';
2296  }
2297  $LAMEpresetUsedLookup = array();
2298 
2300  for ($i = 8; $i <= 320; $i++) {
2301  switch ($LAMEtag['vbr_method']) {
2302  case 'cbr':
2303  $LAMEpresetUsedLookup[$i] = '--alt-preset ' . $LAMEtag['vbr_method'] . ' ' . $i;
2304  break;
2305  case 'abr':
2306  default: // other VBR modes shouldn't be here(?)
2307  $LAMEpresetUsedLookup[$i] = '--alt-preset ' . $i;
2308  break;
2309  }
2310  }
2311 
2312  // named old-style presets (studio, phone, voice, etc) are handled in GuessEncoderOptions()
2313  // named alt-presets
2314  $LAMEpresetUsedLookup[1000] = '--r3mix';
2315  $LAMEpresetUsedLookup[1001] = '--alt-preset standard';
2316  $LAMEpresetUsedLookup[1002] = '--alt-preset extreme';
2317  $LAMEpresetUsedLookup[1003] = '--alt-preset insane';
2318  $LAMEpresetUsedLookup[1004] = '--alt-preset fast standard';
2319  $LAMEpresetUsedLookup[1005] = '--alt-preset fast extreme';
2320  $LAMEpresetUsedLookup[1006] = '--alt-preset medium';
2321  $LAMEpresetUsedLookup[1007] = '--alt-preset fast medium';
2322 
2323  // LAME 3.94 additions/changes
2324  $LAMEpresetUsedLookup[1010] = '--preset portable'; // 3.94a15 Oct 21 2003
2325  $LAMEpresetUsedLookup[1015] = '--preset radio'; // 3.94a15 Oct 21 2003
2326 
2327  $LAMEpresetUsedLookup[320] = '--preset insane'; // 3.94a15 Nov 12 2003
2328  $LAMEpresetUsedLookup[410] = '-V9';
2329  $LAMEpresetUsedLookup[420] = '-V8';
2330  $LAMEpresetUsedLookup[440] = '-V6';
2331  $LAMEpresetUsedLookup[430] = '--preset radio'; // 3.94a15 Nov 12 2003
2332  $LAMEpresetUsedLookup[450] = '--preset ' . (($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '') . 'portable'; // 3.94a15 Nov 12 2003
2333  $LAMEpresetUsedLookup[460] = '--preset ' . (($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '') . 'medium'; // 3.94a15 Nov 12 2003
2334  $LAMEpresetUsedLookup[470] = '--r3mix'; // 3.94b1 Dec 18 2003
2335  $LAMEpresetUsedLookup[480] = '--preset ' . (($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '') . 'standard'; // 3.94a15 Nov 12 2003
2336  $LAMEpresetUsedLookup[490] = '-V1';
2337  $LAMEpresetUsedLookup[500] = '--preset ' . (($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '') . 'extreme'; // 3.94a15 Nov 12 2003
2338 
2339  return (isset($LAMEpresetUsedLookup[$LAMEtag['preset_used_id']]) ? $LAMEpresetUsedLookup[$LAMEtag['preset_used_id']] : 'new/unknown preset: ' . $LAMEtag['preset_used_id'] . ' - report to info@getid3.org');
2340  }
Create styles array
The data for the language used.

◆ LAMEsurroundInfoLookup()

static GetId3\Module\Audio\Mp3::LAMEsurroundInfoLookup (   $SurroundInfoID)
static

array $LAMEsurroundInfoLookup

Parameters
type$SurroundInfoID
Returns
type

Definition at line 2272 of file Mp3.php.

References array.

2273  {
2274  static $LAMEsurroundInfoLookup = array(
2275  0 => 'no surround info',
2276  1 => 'DPL encoding',
2277  2 => 'DPL2 encoding',
2278  3 => 'Ambisonic encoding'
2279  );
2280 
2281  return (isset($LAMEsurroundInfoLookup[$SurroundInfoID]) ? $LAMEsurroundInfoLookup[$SurroundInfoID] : 'reserved');
2282  }
Create styles array
The data for the language used.

◆ LAMEvbrMethodLookup()

static GetId3\Module\Audio\Mp3::LAMEvbrMethodLookup (   $VBRmethodID)
static

array $LAMEvbrMethodLookup

Parameters
type$VBRmethodID
Returns
type

Definition at line 2208 of file Mp3.php.

References array.

2209  {
2210  static $LAMEvbrMethodLookup = array(
2211  0x00 => 'unknown',
2212  0x01 => 'cbr',
2213  0x02 => 'abr',
2214  0x03 => 'vbr-old / vbr-rh',
2215  0x04 => 'vbr-new / vbr-mtrh',
2216  0x05 => 'vbr-mt',
2217  0x06 => 'vbr (full vbr method 4)',
2218  0x08 => 'cbr (constant bitrate 2 pass)',
2219  0x09 => 'abr (2 pass)',
2220  0x0F => 'reserved'
2221  );
2222 
2223  return (isset($LAMEvbrMethodLookup[$VBRmethodID]) ? $LAMEvbrMethodLookup[$VBRmethodID] : '');
2224  }
Create styles array
The data for the language used.

◆ MPEGaudioBitrateArray()

static GetId3\Module\Audio\Mp3::MPEGaudioBitrateArray ( )
static

type $MPEGaudioBitrate

Returns
type

Definition at line 1842 of file Mp3.php.

References array.

1843  {
1844  static $MPEGaudioBitrate;
1845  if (empty($MPEGaudioBitrate)) {
1846  $MPEGaudioBitrate = array(
1847  '1' => array(1 => array('free', 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000),
1848  2 => array('free', 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000),
1849  3 => array('free', 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000)
1850  ),
1851  '2' => array(1 => array('free', 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000),
1852  2 => array('free', 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000),
1853  )
1854  );
1855  $MPEGaudioBitrate['2'][3] = $MPEGaudioBitrate['2'][2];
1856  $MPEGaudioBitrate['2.5'] = $MPEGaudioBitrate['2'];
1857  }
1858 
1859  return $MPEGaudioBitrate;
1860  }
Create styles array
The data for the language used.

◆ MPEGaudioChannelModeArray()

static GetId3\Module\Audio\Mp3::MPEGaudioChannelModeArray ( )
static

array $MPEGaudioChannelMode

Returns
string

Definition at line 1886 of file Mp3.php.

References array.

1887  {
1888  static $MPEGaudioChannelMode = array('stereo', 'joint stereo', 'dual channel', 'mono');
1889 
1890  return $MPEGaudioChannelMode;
1891  }
Create styles array
The data for the language used.

◆ MPEGaudioEmphasisArray()

static GetId3\Module\Audio\Mp3::MPEGaudioEmphasisArray ( )
static

array $MPEGaudioEmphasis

Returns
boolean

Definition at line 1917 of file Mp3.php.

References array.

1918  {
1919  static $MPEGaudioEmphasis = array('none', '50/15ms', false, 'CCIT J.17');
1920 
1921  return $MPEGaudioEmphasis;
1922  }
Create styles array
The data for the language used.

◆ MPEGaudioFrameLength()

static GetId3\Module\Audio\Mp3::MPEGaudioFrameLength ( $bitrate,
$version,
$layer,
  $padding,
$samplerate 
)
static

array $AudioFrameLengthCache

Parameters
type$bitrate
type$version
type$layer
type$padding
type$samplerate
Returns
type

Definition at line 2085 of file Mp3.php.

References $version, and array.

2087  {
2088  static $AudioFrameLengthCache = array();
2089 
2090  if (!isset($AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate])) {
2091  $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = false;
2092  if ($bitrate != 'free') {
2093 
2094  if ($version == '1') {
2095 
2096  if ($layer == '1') {
2097 
2098  // For Layer I slot is 32 bits long
2099  $FrameLengthCoefficient = 48;
2100  $SlotLength = 4;
2101  } else { // Layer 2 / 3
2102  // for Layer 2 and Layer 3 slot is 8 bits long.
2103  $FrameLengthCoefficient = 144;
2104  $SlotLength = 1;
2105  }
2106  } else { // MPEG-2 / MPEG-2.5
2107  if ($layer == '1') {
2108 
2109  // For Layer I slot is 32 bits long
2110  $FrameLengthCoefficient = 24;
2111  $SlotLength = 4;
2112  } elseif ($layer == '2') {
2113 
2114  // for Layer 2 and Layer 3 slot is 8 bits long.
2115  $FrameLengthCoefficient = 144;
2116  $SlotLength = 1;
2117  } else { // layer 3
2118  // for Layer 2 and Layer 3 slot is 8 bits long.
2119  $FrameLengthCoefficient = 72;
2120  $SlotLength = 1;
2121  }
2122  }
2123 
2124  // FrameLengthInBytes = ((Coefficient * BitRate) / SampleRate) + Padding
2125  if ($samplerate > 0) {
2126  $NewFramelength = ($FrameLengthCoefficient * $bitrate) / $samplerate;
2127  $NewFramelength = floor($NewFramelength / $SlotLength) * $SlotLength; // round to next-lower multiple of SlotLength (1 byte for Layer 2/3, 4 bytes for Layer I)
2128  if ($padding) {
2129  $NewFramelength += $SlotLength;
2130  }
2131  $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = (int) $NewFramelength;
2132  }
2133  }
2134  }
2135 
2136  return $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate];
2137  }
Create styles array
The data for the language used.

◆ MPEGaudioFrequencyArray()

static GetId3\Module\Audio\Mp3::MPEGaudioFrequencyArray ( )
static

array $MPEGaudioFrequency

Returns
array

Definition at line 1867 of file Mp3.php.

References array.

1868  {
1869  static $MPEGaudioFrequency;
1870  if (empty($MPEGaudioFrequency)) {
1871  $MPEGaudioFrequency = array(
1872  '1' => array(44100, 48000, 32000),
1873  '2' => array(22050, 24000, 16000),
1874  '2.5' => array(11025, 12000, 8000)
1875  );
1876  }
1877 
1878  return $MPEGaudioFrequency;
1879  }
Create styles array
The data for the language used.

◆ MPEGaudioHeaderBytesValid()

static GetId3\Module\Audio\Mp3::MPEGaudioHeaderBytesValid (   $head4,
  $allowBitrate15 = false 
)
static
Parameters
type$head4
type$allowBitrate15
Returns
type

Definition at line 1930 of file Mp3.php.

Referenced by GetId3\Module\AudioVideo\Riff\ParseRIFF().

1932  {
1933  return self::MPEGaudioHeaderValid(self::MPEGaudioHeaderDecode($head4),
1934  false,
1935  $allowBitrate15);
1936  }
+ Here is the caller graph for this function:

◆ MPEGaudioHeaderDecode()

static GetId3\Module\Audio\Mp3::MPEGaudioHeaderDecode (   $Header4Bytes)
static
Parameters
type$Header4Bytes
Returns
boolean

Definition at line 2036 of file Mp3.php.

References GetId3\Lib\Helper\BigEndian2Int().

2037  {
2038  // AAAA AAAA AAAB BCCD EEEE FFGH IIJJ KLMM
2039  // A - Frame sync (all bits set)
2040  // B - MPEG Audio version ID
2041  // C - Layer description
2042  // D - Protection bit
2043  // E - Bitrate index
2044  // F - Sampling rate frequency index
2045  // G - Padding bit
2046  // H - Private bit
2047  // I - Channel Mode
2048  // J - Mode extension (Only if Joint stereo)
2049  // K - Copyright
2050  // L - Original
2051  // M - Emphasis
2052 
2053  if (strlen($Header4Bytes) != 4) {
2054  return false;
2055  }
2056 
2057  $MPEGrawHeader['synch'] = (Helper::BigEndian2Int(substr($Header4Bytes,
2058  0, 2)) & 0xFFE0) >> 4;
2059  $MPEGrawHeader['version'] = (ord($Header4Bytes{1}) & 0x18) >> 3; // BB
2060  $MPEGrawHeader['layer'] = (ord($Header4Bytes{1}) & 0x06) >> 1; // CC
2061  $MPEGrawHeader['protection'] = (ord($Header4Bytes{1}) & 0x01); // D
2062  $MPEGrawHeader['bitrate'] = (ord($Header4Bytes{2}) & 0xF0) >> 4; // EEEE
2063  $MPEGrawHeader['sample_rate'] = (ord($Header4Bytes{2}) & 0x0C) >> 2; // FF
2064  $MPEGrawHeader['padding'] = (ord($Header4Bytes{2}) & 0x02) >> 1; // G
2065  $MPEGrawHeader['private'] = (ord($Header4Bytes{2}) & 0x01); // H
2066  $MPEGrawHeader['channelmode'] = (ord($Header4Bytes{3}) & 0xC0) >> 6; // II
2067  $MPEGrawHeader['modeextension'] = (ord($Header4Bytes{3}) & 0x30) >> 4; // JJ
2068  $MPEGrawHeader['copyright'] = (ord($Header4Bytes{3}) & 0x08) >> 3; // K
2069  $MPEGrawHeader['original'] = (ord($Header4Bytes{3}) & 0x04) >> 2; // L
2070  $MPEGrawHeader['emphasis'] = (ord($Header4Bytes{3}) & 0x03); // MM
2071 
2072  return $MPEGrawHeader;
2073  }
static BigEndian2Int($byteword, $synchsafe=false, $signed=false)
Definition: Helper.php:374
+ Here is the call graph for this function:

◆ MPEGaudioHeaderValid()

static GetId3\Module\Audio\Mp3::MPEGaudioHeaderValid (   $rawarray,
  $echoerrors = false,
  $allowBitrate15 = false 
)
static

type $MPEGaudioVersionLookup type $MPEGaudioLayerLookup type $MPEGaudioBitrateLookup type $MPEGaudioFrequencyLookup type $MPEGaudioChannelModeLookup type $MPEGaudioModeExtensionLookup type $MPEGaudioEmphasisLookup

Parameters
type$rawarray
type$echoerrors
type$allowBitrate15
Returns
boolean

Definition at line 1952 of file Mp3.php.

1954  {
1955  if (($rawarray['synch'] & 0x0FFE) != 0x0FFE) {
1956  return false;
1957  }
1958 
1959  static $MPEGaudioVersionLookup;
1960  static $MPEGaudioLayerLookup;
1961  static $MPEGaudioBitrateLookup;
1962  static $MPEGaudioFrequencyLookup;
1963  static $MPEGaudioChannelModeLookup;
1964  static $MPEGaudioModeExtensionLookup;
1965  static $MPEGaudioEmphasisLookup;
1966  if (empty($MPEGaudioVersionLookup)) {
1967  $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
1968  $MPEGaudioLayerLookup = self::MPEGaudioLayerArray();
1969  $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
1970  $MPEGaudioFrequencyLookup = self::MPEGaudioFrequencyArray();
1971  $MPEGaudioChannelModeLookup = self::MPEGaudioChannelModeArray();
1972  $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray();
1973  $MPEGaudioEmphasisLookup = self::MPEGaudioEmphasisArray();
1974  }
1975 
1976  if (isset($MPEGaudioVersionLookup[$rawarray['version']])) {
1977  $decodedVersion = $MPEGaudioVersionLookup[$rawarray['version']];
1978  } else {
1979  echo ($echoerrors ? "\n" . 'invalid Version (' . $rawarray['version'] . ')' : '');
1980 
1981  return false;
1982  }
1983  if (isset($MPEGaudioLayerLookup[$rawarray['layer']])) {
1984  $decodedLayer = $MPEGaudioLayerLookup[$rawarray['layer']];
1985  } else {
1986  echo ($echoerrors ? "\n" . 'invalid Layer (' . $rawarray['layer'] . ')' : '');
1987 
1988  return false;
1989  }
1990  if (!isset($MPEGaudioBitrateLookup[$decodedVersion][$decodedLayer][$rawarray['bitrate']])) {
1991  echo ($echoerrors ? "\n" . 'invalid Bitrate (' . $rawarray['bitrate'] . ')' : '');
1992  if ($rawarray['bitrate'] == 15) {
1993  // known issue in LAME 3.90 - 3.93.1 where free-format has bitrate ID of 15 instead of 0
1994  // let it go through here otherwise file will not be identified
1995  if (!$allowBitrate15) {
1996  return false;
1997  }
1998  } else {
1999  return false;
2000  }
2001  }
2002  if (!isset($MPEGaudioFrequencyLookup[$decodedVersion][$rawarray['sample_rate']])) {
2003  echo ($echoerrors ? "\n" . 'invalid Frequency (' . $rawarray['sample_rate'] . ')' : '');
2004 
2005  return false;
2006  }
2007  if (!isset($MPEGaudioChannelModeLookup[$rawarray['channelmode']])) {
2008  echo ($echoerrors ? "\n" . 'invalid ChannelMode (' . $rawarray['channelmode'] . ')' : '');
2009 
2010  return false;
2011  }
2012  if (!isset($MPEGaudioModeExtensionLookup[$decodedLayer][$rawarray['modeextension']])) {
2013  echo ($echoerrors ? "\n" . 'invalid Mode Extension (' . $rawarray['modeextension'] . ')' : '');
2014 
2015  return false;
2016  }
2017  if (!isset($MPEGaudioEmphasisLookup[$rawarray['emphasis']])) {
2018  echo ($echoerrors ? "\n" . 'invalid Emphasis (' . $rawarray['emphasis'] . ')' : '');
2019 
2020  return false;
2021  }
2022  // These are just either set or not set, you can't mess that up :)
2023  // $rawarray['protection'];
2024  // $rawarray['padding'];
2025  // $rawarray['private'];
2026  // $rawarray['copyright'];
2027  // $rawarray['original'];
2028  return true;
2029  }

◆ MPEGaudioLayerArray()

static GetId3\Module\Audio\Mp3::MPEGaudioLayerArray ( )
static

array $MPEGaudioLayer

Returns
boolean

Definition at line 1830 of file Mp3.php.

References array.

1831  {
1832  static $MPEGaudioLayer = array(false, 3, 2, 1);
1833 
1834  return $MPEGaudioLayer;
1835  }
Create styles array
The data for the language used.

◆ MPEGaudioModeExtensionArray()

static GetId3\Module\Audio\Mp3::MPEGaudioModeExtensionArray ( )
static

array $MPEGaudioModeExtension

Returns
array

Definition at line 1898 of file Mp3.php.

References array.

1899  {
1900  static $MPEGaudioModeExtension;
1901  if (empty($MPEGaudioModeExtension)) {
1902  $MPEGaudioModeExtension = array(
1903  1 => array('4-31', '8-31', '12-31', '16-31'),
1904  2 => array('4-31', '8-31', '12-31', '16-31'),
1905  3 => array('', 'IS', 'MS', 'IS+MS')
1906  );
1907  }
1908 
1909  return $MPEGaudioModeExtension;
1910  }
Create styles array
The data for the language used.

◆ MPEGaudioVersionArray()

static GetId3\Module\Audio\Mp3::MPEGaudioVersionArray ( )
static

array $MPEGaudioVersion

Returns
string

Definition at line 1818 of file Mp3.php.

References array.

1819  {
1820  static $MPEGaudioVersion = array('2.5', false, '2', '1');
1821 
1822  return $MPEGaudioVersion;
1823  }
Create styles array
The data for the language used.

◆ RecursiveFrameScanning()

GetId3\Module\Audio\Mp3::RecursiveFrameScanning ( $offset,
$nextframetestoffset,
  $ScanAsCBR 
)
Parameters
type$offset
type$nextframetestoffset
type$ScanAsCBR
Returns
boolean

Definition at line 1198 of file Mp3.php.

References $info, array, and GetId3\Module\Audio\Mp3\decodeMPEGaudioHeader().

Referenced by GetId3\Module\Audio\Mp3\decodeMPEGaudioHeader().

1200  {
1201  $info = &$this->getid3->info;
1202  $firstframetestarray = array('error' => '', 'warning' => '', 'avdataend' => $info['avdataend'], 'avdataoffset' => $info['avdataoffset']);
1203  $this->decodeMPEGaudioHeader($offset, $firstframetestarray, false);
1204 
1205  for ($i = 0; $i < self::GETID3_MP3_VALID_CHECK_FRAMES; $i++) {
1206  // check next self::GETID3_MP3_VALID_CHECK_FRAMES frames for validity, to make sure we haven't run across a false synch
1207  if (($nextframetestoffset + 4) >= $info['avdataend']) {
1208  // end of file
1209  return true;
1210  }
1211 
1212  $nextframetestarray = array('error' => '', 'warning' => '', 'avdataend' => $info['avdataend'], 'avdataoffset' => $info['avdataoffset']);
1213  if ($this->decodeMPEGaudioHeader($nextframetestoffset,
1214  $nextframetestarray, false)) {
1215  if ($ScanAsCBR) {
1216  // force CBR mode, used for trying to pick out invalid audio streams with valid(?) VBR headers, or VBR streams with no VBR header
1217  if (!isset($nextframetestarray['mpeg']['audio']['bitrate']) || !isset($firstframetestarray['mpeg']['audio']['bitrate']) || ($nextframetestarray['mpeg']['audio']['bitrate'] != $firstframetestarray['mpeg']['audio']['bitrate'])) {
1218  return false;
1219  }
1220  }
1221 
1222  // next frame is OK, get ready to check the one after that
1223  if (isset($nextframetestarray['mpeg']['audio']['framelength']) && ($nextframetestarray['mpeg']['audio']['framelength'] > 0)) {
1224  $nextframetestoffset += $nextframetestarray['mpeg']['audio']['framelength'];
1225  } else {
1226  $info['error'][] = 'Frame at offset (' . $offset . ') is has an invalid frame length.';
1227 
1228  return false;
1229  }
1230  } elseif (!empty($firstframetestarray['mpeg']['audio']['framelength']) && (($nextframetestoffset + $firstframetestarray['mpeg']['audio']['framelength']) > $info['avdataend'])) {
1231 
1232  // 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
1233  return true;
1234  } else {
1235 
1236  // next frame is not valid, note the error and fail, so scanning can contiue for a valid frame sequence
1237  $info['warning'][] = 'Frame at offset (' . $offset . ') is valid, but the next one at (' . $nextframetestoffset . ') is not.';
1238 
1239  return false;
1240  }
1241  }
1242 
1243  return true;
1244  }
decodeMPEGaudioHeader($offset, &$info, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false)
type $MPEGaudioVersionLookup type $MPEGaudioLayerLookup type $MPEGaudioBitrateLookup type $MPEGaud...
Definition: Mp3.php:453
$info
Definition: example_052.php:80
Create styles array
The data for the language used.
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ XingVBRidOffset()

static GetId3\Module\Audio\Mp3::XingVBRidOffset (   $version,
  $channelmode 
)
static

array $XingVBRidOffsetCache

Parameters
type$version
type$channelmode
Returns
array

Definition at line 2176 of file Mp3.php.

References $version, and array.

2177  {
2178  static $XingVBRidOffsetCache = array();
2179  if (empty($XingVBRidOffset)) {
2180  $XingVBRidOffset = array(
2181  '1' => array('mono' => 0x15, // 4 + 17 = 21
2182  'stereo' => 0x24, // 4 + 32 = 36
2183  'joint stereo' => 0x24,
2184  'dual channel' => 0x24
2185  ),
2186  '2' => array('mono' => 0x0D, // 4 + 9 = 13
2187  'stereo' => 0x15, // 4 + 17 = 21
2188  'joint stereo' => 0x15,
2189  'dual channel' => 0x15
2190  ),
2191  '2.5' => array('mono' => 0x15,
2192  'stereo' => 0x15,
2193  'joint stereo' => 0x15,
2194  'dual channel' => 0x15
2195  )
2196  );
2197  }
2198 
2199  return $XingVBRidOffset[$version][$channelmode];
2200  }
Create styles array
The data for the language used.

Field Documentation

◆ $allow_bruteforce

GetId3\Module\Audio\Mp3::$allow_bruteforce = false

Definition at line 44 of file Mp3.php.

◆ GETID3_MP3_VALID_CHECK_FRAMES

const GetId3\Module\Audio\Mp3::GETID3_MP3_VALID_CHECK_FRAMES = 35

Definition at line 46 of file Mp3.php.


The documentation for this class was generated from the following file: