ILIAS  release_5-2 Revision v5.2.25-18-g3f80b82851
GetId3\Module\Audio\Mpc Class Reference

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

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

Public Member Functions

 analyze ()
 
 ParseMPCsv8 ()
 http://trac.musepack.net/trac/wiki/SV8Specification More...
 
 ParseMPCsv7 ()
 
 ParseMPCsv6 ()
 
 MPCprofileNameLookup ($profileid)
 array $MPCprofileNameLookup More...
 
 MPCfrequencyLookup ($frequencyid)
 array $MPCfrequencyLookup More...
 
 MPCpeakDBLookup ($intvalue)
 
 MPCencoderVersionLookup ($encoderversion)
 
 SV8variableLengthInteger ($data, &$packetLength, $maxHandledPacketLength=9)
 
 MPCsv8PacketName ($packetKey)
 array $MPCsv8PacketName 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)
 

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 Musepack/MPEG+ Audio files

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

Definition at line 29 of file Mpc.php.

Member Function Documentation

◆ analyze()

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

Definition at line 36 of file Mpc.php.

References $info, array, GetId3\Handler\BaseHandler\fread(), GetId3\Handler\BaseHandler\fseek(), GetId3\Module\Audio\Mpc\ParseMPCsv6(), GetId3\Module\Audio\Mpc\ParseMPCsv7(), GetId3\Module\Audio\Mpc\ParseMPCsv8(), and GetId3\Lib\Helper\PrintHexBytes().

37  {
38  $info = &$this->getid3->info;
39 
40  $info['mpc']['header'] = array();
41  $thisfile_mpc_header = &$info['mpc']['header'];
42 
43  $info['fileformat'] = 'mpc';
44  $info['audio']['dataformat'] = 'mpc';
45  $info['audio']['bitrate_mode'] = 'vbr';
46  $info['audio']['channels'] = 2; // up to SV7 the format appears to have been hardcoded for stereo only
47  $info['audio']['lossless'] = false;
48 
49  fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
50  $MPCheaderData = fread($this->getid3->fp, 4);
51  $info['mpc']['header']['preamble'] = substr($MPCheaderData, 0, 4); // should be 'MPCK' (SV8) or 'MP+' (SV7), otherwise possible stream data (SV4-SV6)
52  if (preg_match('#^MPCK#', $info['mpc']['header']['preamble'])) {
53 
54  // this is SV8
55  return $this->ParseMPCsv8();
56 
57  } elseif (preg_match('#^MP\+#', $info['mpc']['header']['preamble'])) {
58 
59  // this is SV7
60  return $this->ParseMPCsv7();
61 
62  } elseif (preg_match('/^[\x00\x01\x10\x11\x40\x41\x50\x51\x80\x81\x90\x91\xC0\xC1\xD0\xD1][\x20-37][\x00\x20\x40\x60\x80\xA0\xC0\xE0]/s', $MPCheaderData)) {
63 
64  // this is SV4 - SV6, handle seperately
65  return $this->ParseMPCsv6();
66 
67  } else {
68 
69  $info['error'][] = 'Expecting "MP+" or "MPCK" at offset '.$info['avdataoffset'].', found "'.Helper::PrintHexBytes(substr($MPCheaderData, 0, 4)).'"';
70  unset($info['fileformat']);
71  unset($info['mpc']);
72 
73  return false;
74 
75  }
76 
77  return false;
78  }
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.
ParseMPCsv8()
http://trac.musepack.net/trac/wiki/SV8Specification
Definition: Mpc.php:84
+ Here is the call graph for this function:

◆ MPCencoderVersionLookup()

GetId3\Module\Audio\Mpc::MPCencoderVersionLookup (   $encoderversion)
Parameters
type$encoderversion
Returns
string

Definition at line 504 of file Mpc.php.

Referenced by GetId3\Module\Audio\Mpc\ParseMPCsv7().

505  {
506  //Encoder version * 100 (106 = 1.06)
507  //EncoderVersion % 10 == 0 Release (1.0)
508  //EncoderVersion % 2 == 0 Beta (1.06)
509  //EncoderVersion % 2 == 1 Alpha (1.05a...z)
510 
511  if ($encoderversion == 0) {
512  // very old version, not known exactly which
513  return 'Buschmann v1.7.0-v1.7.9 or Klemm v0.90-v1.05';
514  }
515 
516  if (($encoderversion % 10) == 0) {
517 
518  // release version
519  return number_format($encoderversion / 100, 2);
520 
521  } elseif (($encoderversion % 2) == 0) {
522 
523  // beta version
524  return number_format($encoderversion / 100, 2).' beta';
525 
526  }
527 
528  // alpha version
529  return number_format($encoderversion / 100, 2).' alpha';
530  }
+ Here is the caller graph for this function:

◆ MPCfrequencyLookup()

GetId3\Module\Audio\Mpc::MPCfrequencyLookup (   $frequencyid)

array $MPCfrequencyLookup

Parameters
type$frequencyid
Returns
type

Definition at line 473 of file Mpc.php.

References array.

Referenced by GetId3\Module\Audio\Mpc\ParseMPCsv7(), and GetId3\Module\Audio\Mpc\ParseMPCsv8().

474  {
475  static $MPCfrequencyLookup = array(
476  0 => 44100,
477  1 => 48000,
478  2 => 37800,
479  3 => 32000
480  );
481 
482  return (isset($MPCfrequencyLookup[$frequencyid]) ? $MPCfrequencyLookup[$frequencyid] : 'invalid');
483  }
Create styles array
The data for the language used.
+ Here is the caller graph for this function:

◆ MPCpeakDBLookup()

GetId3\Module\Audio\Mpc::MPCpeakDBLookup (   $intvalue)
Parameters
type$intvalue
Returns
boolean

Definition at line 490 of file Mpc.php.

Referenced by GetId3\Module\Audio\Mpc\ParseMPCsv7().

491  {
492  if ($intvalue > 0) {
493  return ((log10($intvalue) / log10(2)) - 15) * 6;
494  }
495 
496  return false;
497  }
+ Here is the caller graph for this function:

◆ MPCprofileNameLookup()

GetId3\Module\Audio\Mpc::MPCprofileNameLookup (   $profileid)

array $MPCprofileNameLookup

Parameters
type$profileid
Returns
type

Definition at line 443 of file Mpc.php.

References array.

Referenced by GetId3\Module\Audio\Mpc\ParseMPCsv7().

444  {
445  static $MPCprofileNameLookup = array(
446  0 => 'no profile',
447  1 => 'Experimental',
448  2 => 'unused',
449  3 => 'unused',
450  4 => 'unused',
451  5 => 'below Telephone (q = 0.0)',
452  6 => 'below Telephone (q = 1.0)',
453  7 => 'Telephone (q = 2.0)',
454  8 => 'Thumb (q = 3.0)',
455  9 => 'Radio (q = 4.0)',
456  10 => 'Standard (q = 5.0)',
457  11 => 'Extreme (q = 6.0)',
458  12 => 'Insane (q = 7.0)',
459  13 => 'BrainDead (q = 8.0)',
460  14 => 'above BrainDead (q = 9.0)',
461  15 => 'above BrainDead (q = 10.0)'
462  );
463 
464  return (isset($MPCprofileNameLookup[$profileid]) ? $MPCprofileNameLookup[$profileid] : 'invalid');
465  }
Create styles array
The data for the language used.
+ Here is the caller graph for this function:

◆ MPCsv8PacketName()

GetId3\Module\Audio\Mpc::MPCsv8PacketName (   $packetKey)

array $MPCsv8PacketName

Parameters
type$packetKey
Returns
type

Definition at line 571 of file Mpc.php.

References array.

Referenced by GetId3\Module\Audio\Mpc\ParseMPCsv8().

572  {
573  static $MPCsv8PacketName = array();
574  if (empty($MPCsv8PacketName)) {
575  $MPCsv8PacketName = array(
576  'AP' => 'Audio Packet',
577  'CT' => 'Chapter Tag',
578  'EI' => 'Encoder Info',
579  'RG' => 'Replay Gain',
580  'SE' => 'Stream End',
581  'SH' => 'Stream Header',
582  'SO' => 'Seek Table Offset',
583  'ST' => 'Seek Table',
584  );
585  }
586 
587  return (isset($MPCsv8PacketName[$packetKey]) ? $MPCsv8PacketName[$packetKey] : $packetKey);
588  }
Create styles array
The data for the language used.
+ Here is the caller graph for this function:

◆ ParseMPCsv6()

GetId3\Module\Audio\Mpc::ParseMPCsv6 ( )
Returns
boolean

Definition at line 361 of file Mpc.php.

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

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

362  {
363  // this is SV4 - SV6
364 
365  $info = &$this->getid3->info;
366  $thisfile_mpc_header = &$info['mpc']['header'];
367  $offset = 0;
368 
369  $thisfile_mpc_header['size'] = 8;
370  fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
371  $MPCheaderData = fread($this->getid3->fp, $thisfile_mpc_header['size']);
372 
373  // add size of file header to avdataoffset - calc bitrate correctly + MD5 data
374  $info['avdataoffset'] += $thisfile_mpc_header['size'];
375 
376  // Most of this code adapted from Jurgen Faul's MPEGplus source code - thanks Jurgen! :)
377  $HeaderDWORD[0] = Helper::LittleEndian2Int(substr($MPCheaderData, 0, 4));
378  $HeaderDWORD[1] = Helper::LittleEndian2Int(substr($MPCheaderData, 4, 4));
379 
380  // DDDD DDDD CCCC CCCC BBBB BBBB AAAA AAAA
381  // aaaa aaaa abcd dddd dddd deee eeff ffff
382  //
383  // a = bitrate = anything
384  // b = IS = anything
385  // c = MS = anything
386  // d = streamversion = 0000000004 or 0000000005 or 0000000006
387  // e = maxband = anything
388  // f = blocksize = 000001 for SV5+, anything(?) for SV4
389 
390  $thisfile_mpc_header['target_bitrate'] = (($HeaderDWORD[0] & 0xFF800000) >> 23);
391  $thisfile_mpc_header['intensity_stereo'] = (bool) (($HeaderDWORD[0] & 0x00400000) >> 22);
392  $thisfile_mpc_header['mid_side_stereo'] = (bool) (($HeaderDWORD[0] & 0x00200000) >> 21);
393  $thisfile_mpc_header['stream_version_major'] = ($HeaderDWORD[0] & 0x001FF800) >> 11;
394  $thisfile_mpc_header['stream_version_minor'] = 0; // no sub-version numbers before SV7
395  $thisfile_mpc_header['max_band'] = ($HeaderDWORD[0] & 0x000007C0) >> 6; // related to lowpass frequency, not sure how it translates exactly
396  $thisfile_mpc_header['block_size'] = ($HeaderDWORD[0] & 0x0000003F);
397 
398  switch ($thisfile_mpc_header['stream_version_major']) {
399  case 4:
400  $thisfile_mpc_header['frame_count'] = ($HeaderDWORD[1] >> 16);
401  break;
402 
403  case 5:
404  case 6:
405  $thisfile_mpc_header['frame_count'] = $HeaderDWORD[1];
406  break;
407 
408  default:
409  $info['error'] = 'Expecting 4, 5 or 6 in version field, found '.$thisfile_mpc_header['stream_version_major'].' instead';
410  unset($info['mpc']);
411 
412  return false;
413  break;
414  }
415 
416  if (($thisfile_mpc_header['stream_version_major'] > 4) && ($thisfile_mpc_header['block_size'] != 1)) {
417  $info['warning'][] = 'Block size expected to be 1, actual value found: '.$thisfile_mpc_header['block_size'];
418  }
419 
420  $thisfile_mpc_header['sample_rate'] = 44100; // AB: used by all files up to SV7
421  $info['audio']['sample_rate'] = $thisfile_mpc_header['sample_rate'];
422  $thisfile_mpc_header['samples'] = $thisfile_mpc_header['frame_count'] * 1152 * $info['audio']['channels'];
423 
424  if ($thisfile_mpc_header['target_bitrate'] == 0) {
425  $info['audio']['bitrate_mode'] = 'vbr';
426  } else {
427  $info['audio']['bitrate_mode'] = 'cbr';
428  }
429 
430  $info['mpc']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 * 44100 / $thisfile_mpc_header['frame_count'] / 1152;
431  $info['audio']['bitrate'] = $info['mpc']['bitrate'];
432  $info['audio']['encoder'] = 'SV'.$thisfile_mpc_header['stream_version_major'];
433 
434  return true;
435  }
fseek($bytes, $whence=SEEK_SET)
$info
Definition: example_052.php:80
static LittleEndian2Int($byteword, $signed=false)
Definition: Helper.php:413
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ParseMPCsv7()

GetId3\Module\Audio\Mpc::ParseMPCsv7 ( )
Returns
boolean http://www.uni-jena.de/~pfk/mpp/sv8/header.html

Definition at line 241 of file Mpc.php.

References $info, GetId3\Lib\Helper\CastAsInt(), GetId3\Handler\BaseHandler\fread(), GetId3\Lib\Helper\LittleEndian2Int(), GetId3\Module\Audio\Mpc\MPCencoderVersionLookup(), GetId3\Module\Audio\Mpc\MPCfrequencyLookup(), GetId3\Module\Audio\Mpc\MPCpeakDBLookup(), and GetId3\Module\Audio\Mpc\MPCprofileNameLookup().

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

242  {
243  // this is SV7
244 
245  $info = &$this->getid3->info;
246  $thisfile_mpc_header = &$info['mpc']['header'];
247  $offset = 0;
248 
249  $thisfile_mpc_header['size'] = 28;
250  $MPCheaderData = $info['mpc']['header']['preamble'];
251  $MPCheaderData .= fread($this->getid3->fp, $thisfile_mpc_header['size'] - strlen($info['mpc']['header']['preamble']));
252  $offset = strlen('MP+');
253 
254  $StreamVersionByte = Helper::LittleEndian2Int(substr($MPCheaderData, $offset, 1));
255  $offset += 1;
256  $thisfile_mpc_header['stream_version_major'] = ($StreamVersionByte & 0x0F) >> 0;
257  $thisfile_mpc_header['stream_version_minor'] = ($StreamVersionByte & 0xF0) >> 4; // should always be 0, subversions no longer exist in SV8
258  $thisfile_mpc_header['frame_count'] = Helper::LittleEndian2Int(substr($MPCheaderData, $offset, 4));
259  $offset += 4;
260 
261  if ($thisfile_mpc_header['stream_version_major'] != 7) {
262  $info['error'][] = 'Only Musepack SV7 supported (this file claims to be v'.$thisfile_mpc_header['stream_version_major'].')';
263 
264  return false;
265  }
266 
267  $FlagsDWORD1 = Helper::LittleEndian2Int(substr($MPCheaderData, $offset, 4));
268  $offset += 4;
269  $thisfile_mpc_header['intensity_stereo'] = (bool) (($FlagsDWORD1 & 0x80000000) >> 31);
270  $thisfile_mpc_header['mid_side_stereo'] = (bool) (($FlagsDWORD1 & 0x40000000) >> 30);
271  $thisfile_mpc_header['max_subband'] = ($FlagsDWORD1 & 0x3F000000) >> 24;
272  $thisfile_mpc_header['raw']['profile'] = ($FlagsDWORD1 & 0x00F00000) >> 20;
273  $thisfile_mpc_header['begin_loud'] = (bool) (($FlagsDWORD1 & 0x00080000) >> 19);
274  $thisfile_mpc_header['end_loud'] = (bool) (($FlagsDWORD1 & 0x00040000) >> 18);
275  $thisfile_mpc_header['raw']['sample_rate'] = ($FlagsDWORD1 & 0x00030000) >> 16;
276  $thisfile_mpc_header['max_level'] = ($FlagsDWORD1 & 0x0000FFFF);
277 
278  $thisfile_mpc_header['raw']['title_peak'] = Helper::LittleEndian2Int(substr($MPCheaderData, $offset, 2));
279  $offset += 2;
280  $thisfile_mpc_header['raw']['title_gain'] = Helper::LittleEndian2Int(substr($MPCheaderData, $offset, 2), true);
281  $offset += 2;
282 
283  $thisfile_mpc_header['raw']['album_peak'] = Helper::LittleEndian2Int(substr($MPCheaderData, $offset, 2));
284  $offset += 2;
285  $thisfile_mpc_header['raw']['album_gain'] = Helper::LittleEndian2Int(substr($MPCheaderData, $offset, 2), true);
286  $offset += 2;
287 
288  $FlagsDWORD2 = Helper::LittleEndian2Int(substr($MPCheaderData, $offset, 4));
289  $offset += 4;
290  $thisfile_mpc_header['true_gapless'] = (bool) (($FlagsDWORD2 & 0x80000000) >> 31);
291  $thisfile_mpc_header['last_frame_length'] = ($FlagsDWORD2 & 0x7FF00000) >> 20;
292 
293  $thisfile_mpc_header['raw']['not_sure_what'] = Helper::LittleEndian2Int(substr($MPCheaderData, $offset, 3));
294  $offset += 3;
295  $thisfile_mpc_header['raw']['encoder_version'] = Helper::LittleEndian2Int(substr($MPCheaderData, $offset, 1));
296  $offset += 1;
297 
298  $thisfile_mpc_header['profile'] = $this->MPCprofileNameLookup($thisfile_mpc_header['raw']['profile']);
299  $thisfile_mpc_header['sample_rate'] = $this->MPCfrequencyLookup($thisfile_mpc_header['raw']['sample_rate']);
300  if ($thisfile_mpc_header['sample_rate'] == 0) {
301  $info['error'][] = 'Corrupt MPC file: frequency == zero';
302 
303  return false;
304  }
305  $info['audio']['sample_rate'] = $thisfile_mpc_header['sample_rate'];
306  $thisfile_mpc_header['samples'] = ((($thisfile_mpc_header['frame_count'] - 1) * 1152) + $thisfile_mpc_header['last_frame_length']) * $info['audio']['channels'];
307 
308  $info['playtime_seconds'] = ($thisfile_mpc_header['samples'] / $info['audio']['channels']) / $info['audio']['sample_rate'];
309  if ($info['playtime_seconds'] == 0) {
310  $info['error'][] = 'Corrupt MPC file: playtime_seconds == zero';
311 
312  return false;
313  }
314 
315  // add size of file header to avdataoffset - calc bitrate correctly + MD5 data
316  $info['avdataoffset'] += $thisfile_mpc_header['size'];
317 
318  $info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
319 
320  $thisfile_mpc_header['title_peak'] = $thisfile_mpc_header['raw']['title_peak'];
321  $thisfile_mpc_header['title_peak_db'] = $this->MPCpeakDBLookup($thisfile_mpc_header['title_peak']);
322  if ($thisfile_mpc_header['raw']['title_gain'] < 0) {
323  $thisfile_mpc_header['title_gain_db'] = (float) (32768 + $thisfile_mpc_header['raw']['title_gain']) / -100;
324  } else {
325  $thisfile_mpc_header['title_gain_db'] = (float) $thisfile_mpc_header['raw']['title_gain'] / 100;
326  }
327 
328  $thisfile_mpc_header['album_peak'] = $thisfile_mpc_header['raw']['album_peak'];
329  $thisfile_mpc_header['album_peak_db'] = $this->MPCpeakDBLookup($thisfile_mpc_header['album_peak']);
330  if ($thisfile_mpc_header['raw']['album_gain'] < 0) {
331  $thisfile_mpc_header['album_gain_db'] = (float) (32768 + $thisfile_mpc_header['raw']['album_gain']) / -100;
332  } else {
333  $thisfile_mpc_header['album_gain_db'] = (float) $thisfile_mpc_header['raw']['album_gain'] / 100;;
334  }
335  $thisfile_mpc_header['encoder_version'] = $this->MPCencoderVersionLookup($thisfile_mpc_header['raw']['encoder_version']);
336 
337  $info['replay_gain']['track']['adjustment'] = $thisfile_mpc_header['title_gain_db'];
338  $info['replay_gain']['album']['adjustment'] = $thisfile_mpc_header['album_gain_db'];
339 
340  if ($thisfile_mpc_header['title_peak'] > 0) {
341  $info['replay_gain']['track']['peak'] = $thisfile_mpc_header['title_peak'];
342  } elseif (round($thisfile_mpc_header['max_level'] * 1.18) > 0) {
343  $info['replay_gain']['track']['peak'] = Helper::CastAsInt(round($thisfile_mpc_header['max_level'] * 1.18)); // why? I don't know - see mppdec.c
344  }
345  if ($thisfile_mpc_header['album_peak'] > 0) {
346  $info['replay_gain']['album']['peak'] = $thisfile_mpc_header['album_peak'];
347  }
348 
349  //$info['audio']['encoder'] = 'SV'.$thisfile_mpc_header['stream_version_major'].'.'.$thisfile_mpc_header['stream_version_minor'].', '.$thisfile_mpc_header['encoder_version'];
350  $info['audio']['encoder'] = $thisfile_mpc_header['encoder_version'];
351  $info['audio']['encoder_options'] = $thisfile_mpc_header['profile'];
352  $thisfile_mpc_header['quality'] = (float) ($thisfile_mpc_header['raw']['profile'] - 5); // values can range from 0 to 15, of which 0..4 are "reserved/experimental", and 5..15 are mapped to qualities of 0.0 to 10.0
353 
354  return true;
355  }
MPCprofileNameLookup($profileid)
array $MPCprofileNameLookup
Definition: Mpc.php:443
MPCfrequencyLookup($frequencyid)
array $MPCfrequencyLookup
Definition: Mpc.php:473
MPCpeakDBLookup($intvalue)
Definition: Mpc.php:490
$info
Definition: example_052.php:80
MPCencoderVersionLookup($encoderversion)
Definition: Mpc.php:504
static CastAsInt($floatnum)
Definition: Helper.php:107
static LittleEndian2Int($byteword, $signed=false)
Definition: Helper.php:413
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ParseMPCsv8()

GetId3\Module\Audio\Mpc::ParseMPCsv8 ( )

http://trac.musepack.net/trac/wiki/SV8Specification

Definition at line 84 of file Mpc.php.

References $info, array, GetId3\Lib\Helper\BigEndian2Int(), GetId3\Handler\BaseHandler\fread(), GetId3\Handler\BaseHandler\fseek(), GetId3\Handler\BaseHandler\ftell(), GetId3\Module\Audio\Mpc\MPCfrequencyLookup(), GetId3\Module\Audio\Mpc\MPCsv8PacketName(), and GetId3\Module\Audio\Mpc\SV8variableLengthInteger().

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

85  {
86  // this is SV8
87 
88  $info = &$this->getid3->info;
89  $thisfile_mpc_header = &$info['mpc']['header'];
90 
91  $keyNameSize = 2;
92  $maxHandledPacketLength = 9; // specs say: "n*8; 0 < n < 10"
93 
94  $offset = ftell($this->getid3->fp);
95  while ($offset < $info['avdataend']) {
96  $thisPacket = array();
97  $thisPacket['offset'] = $offset;
98  $packet_offset = 0;
99 
100  // Size is a variable-size field, could be 1-4 bytes (possibly more?)
101  // read enough data in and figure out the exact size later
102  $MPCheaderData = fread($this->getid3->fp, $keyNameSize + $maxHandledPacketLength);
103  $packet_offset += $keyNameSize;
104  $thisPacket['key'] = substr($MPCheaderData, 0, $keyNameSize);
105  $thisPacket['key_name'] = $this->MPCsv8PacketName($thisPacket['key']);
106  if ($thisPacket['key'] == $thisPacket['key_name']) {
107  $info['error'][] = 'Found unexpected key value "'.$thisPacket['key'].'" at offset '.$thisPacket['offset'];
108 
109  return false;
110  }
111  $packetLength = 0;
112  $thisPacket['packet_size'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $keyNameSize), $packetLength); // includes keyname and packet_size field
113  if ($thisPacket['packet_size'] === false) {
114  $info['error'][] = 'Did not find expected packet length within '.$maxHandledPacketLength.' bytes at offset '.($thisPacket['offset'] + $keyNameSize);
115 
116  return false;
117  }
118  $packet_offset += $packetLength;
119  $offset += $thisPacket['packet_size'];
120 
121  switch ($thisPacket['key']) {
122  case 'SH': // Stream Header
123  $moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
124  if ($moreBytesToRead > 0) {
125  $MPCheaderData .= fread($this->getid3->fp, $moreBytesToRead);
126  }
127  $thisPacket['crc'] = Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 4));
128  $packet_offset += 4;
129  $thisPacket['stream_version'] = Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
130  $packet_offset += 1;
131 
132  $packetLength = 0;
133  $thisPacket['sample_count'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
134  $packet_offset += $packetLength;
135 
136  $packetLength = 0;
137  $thisPacket['beginning_silence'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
138  $packet_offset += $packetLength;
139 
140  $otherUsefulData = Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
141  $packet_offset += 2;
142  $thisPacket['sample_frequency_raw'] = (($otherUsefulData & 0xE000) >> 13);
143  $thisPacket['max_bands_used'] = (($otherUsefulData & 0x1F00) >> 8);
144  $thisPacket['channels'] = (($otherUsefulData & 0x00F0) >> 4) + 1;
145  $thisPacket['ms_used'] = (bool) (($otherUsefulData & 0x0008) >> 3);
146  $thisPacket['audio_block_frames'] = (($otherUsefulData & 0x0007) >> 0);
147  $thisPacket['sample_frequency'] = $this->MPCfrequencyLookup($thisPacket['sample_frequency_raw']);
148 
149  $thisfile_mpc_header['mid_side_stereo'] = $thisPacket['ms_used'];
150  $thisfile_mpc_header['sample_rate'] = $thisPacket['sample_frequency'];
151  $thisfile_mpc_header['samples'] = $thisPacket['sample_count'];
152  $thisfile_mpc_header['stream_version_major'] = $thisPacket['stream_version'];
153 
154  $info['audio']['channels'] = $thisPacket['channels'];
155  $info['audio']['sample_rate'] = $thisPacket['sample_frequency'];
156  $info['playtime_seconds'] = $thisPacket['sample_count'] / $thisPacket['sample_frequency'];
157  $info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
158  break;
159 
160  case 'RG': // Replay Gain
161  $moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
162  if ($moreBytesToRead > 0) {
163  $MPCheaderData .= fread($this->getid3->fp, $moreBytesToRead);
164  }
165  $thisPacket['replaygain_version'] = Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
166  $packet_offset += 1;
167  $thisPacket['replaygain_title_gain'] = Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
168  $packet_offset += 2;
169  $thisPacket['replaygain_title_peak'] = Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
170  $packet_offset += 2;
171  $thisPacket['replaygain_album_gain'] = Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
172  $packet_offset += 2;
173  $thisPacket['replaygain_album_peak'] = Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
174  $packet_offset += 2;
175 
176  if ($thisPacket['replaygain_title_gain']) { $info['replay_gain']['title']['gain'] = $thisPacket['replaygain_title_gain']; }
177  if ($thisPacket['replaygain_title_peak']) { $info['replay_gain']['title']['peak'] = $thisPacket['replaygain_title_peak']; }
178  if ($thisPacket['replaygain_album_gain']) { $info['replay_gain']['album']['gain'] = $thisPacket['replaygain_album_gain']; }
179  if ($thisPacket['replaygain_album_peak']) { $info['replay_gain']['album']['peak'] = $thisPacket['replaygain_album_peak']; }
180  break;
181 
182  case 'EI': // Encoder Info
183  $moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
184  if ($moreBytesToRead > 0) {
185  $MPCheaderData .= fread($this->getid3->fp, $moreBytesToRead);
186  }
187  $profile_pns = Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
188  $packet_offset += 1;
189  $quality_int = (($profile_pns & 0xF0) >> 4);
190  $quality_dec = (($profile_pns & 0x0E) >> 3);
191  $thisPacket['quality'] = (float) $quality_int + ($quality_dec / 8);
192  $thisPacket['pns_tool'] = (bool) (($profile_pns & 0x01) >> 0);
193  $thisPacket['version_major'] = Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
194  $packet_offset += 1;
195  $thisPacket['version_minor'] = Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
196  $packet_offset += 1;
197  $thisPacket['version_build'] = Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
198  $packet_offset += 1;
199  $thisPacket['version'] = $thisPacket['version_major'].'.'.$thisPacket['version_minor'].'.'.$thisPacket['version_build'];
200 
201  $info['audio']['encoder'] = 'MPC v'.$thisPacket['version'].' ('.(($thisPacket['version_minor'] % 2) ? 'unstable' : 'stable').')';
202  $thisfile_mpc_header['encoder_version'] = $info['audio']['encoder'];
203  //$thisfile_mpc_header['quality'] = (float) ($thisPacket['quality'] / 1.5875); // values can range from 0.000 to 15.875, mapped to qualities of 0.0 to 10.0
204  $thisfile_mpc_header['quality'] = (float) ($thisPacket['quality'] - 5); // values can range from 0.000 to 15.875, of which 0..4 are "reserved/experimental", and 5..15 are mapped to qualities of 0.0 to 10.0
205  break;
206 
207  case 'SO': // Seek Table Offset
208  $packetLength = 0;
209  $thisPacket['seek_table_offset'] = $thisPacket['offset'] + $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
210  $packet_offset += $packetLength;
211  break;
212 
213  case 'ST': // Seek Table
214  case 'SE': // Stream End
215  case 'AP': // Audio Data
216  // nothing useful here, just skip this packet
217  $thisPacket = array();
218  break;
219 
220  default:
221  $info['error'][] = 'Found unhandled key type "'.$thisPacket['key'].'" at offset '.$thisPacket['offset'];
222 
223  return false;
224  break;
225  }
226  if (!empty($thisPacket)) {
227  $info['mpc']['packets'][] = $thisPacket;
228  }
229  fseek($this->getid3->fp, $offset);
230  }
231  $thisfile_mpc_header['size'] = $offset;
232 
233  return true;
234  }
SV8variableLengthInteger($data, &$packetLength, $maxHandledPacketLength=9)
Definition: Mpc.php:539
MPCfrequencyLookup($frequencyid)
array $MPCfrequencyLookup
Definition: Mpc.php:473
fseek($bytes, $whence=SEEK_SET)
$info
Definition: example_052.php:80
Create styles array
The data for the language used.
static BigEndian2Int($byteword, $synchsafe=false, $signed=false)
Definition: Helper.php:374
MPCsv8PacketName($packetKey)
array $MPCsv8PacketName
Definition: Mpc.php:571
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ SV8variableLengthInteger()

GetId3\Module\Audio\Mpc::SV8variableLengthInteger (   $data,
$packetLength,
  $maxHandledPacketLength = 9 
)
Parameters
type$data
int$packetLength
type$maxHandledPacketLength
Returns
boolean

Definition at line 539 of file Mpc.php.

References $data.

Referenced by GetId3\Module\Audio\Mpc\ParseMPCsv8().

540  {
541  $packet_size = 0;
542  for ($packetLength = 1; $packetLength <= $maxHandledPacketLength; $packetLength++) {
543  // variable-length size field:
544  // bits, big-endian
545  // 0xxx xxxx - value 0 to 2^7-1
546  // 1xxx xxxx 0xxx xxxx - value 0 to 2^14-1
547  // 1xxx xxxx 1xxx xxxx 0xxx xxxx - value 0 to 2^21-1
548  // 1xxx xxxx 1xxx xxxx 1xxx xxxx 0xxx xxxx - value 0 to 2^28-1
549  // ...
550  $thisbyte = ord(substr($data, ($packetLength - 1), 1));
551  // look through bytes until find a byte with MSB==0
552  $packet_size = ($packet_size << 7);
553  $packet_size = ($packet_size | ($thisbyte & 0x7F));
554  if (($thisbyte & 0x80) === 0) {
555  break;
556  }
557  if ($packetLength >= $maxHandledPacketLength) {
558  return false;
559  }
560  }
561 
562  return $packet_size;
563  }
+ Here is the caller graph for this function:

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