38 $info = &$this->getid3->info;
41 $thisfile_mpc_header = &
$info[
'mpc'][
'header'];
43 $info[
'fileformat'] =
'mpc';
44 $info[
'audio'][
'dataformat'] =
'mpc';
45 $info[
'audio'][
'bitrate_mode'] =
'vbr';
46 $info[
'audio'][
'channels'] = 2;
47 $info[
'audio'][
'lossless'] =
false;
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);
52 if (preg_match(
'#^MPCK#',
$info[
'mpc'][
'header'][
'preamble'])) {
57 } elseif (preg_match(
'#^MP\+#',
$info[
'mpc'][
'header'][
'preamble'])) {
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)) {
69 $info[
'error'][] =
'Expecting "MP+" or "MPCK" at offset '.$info[
'avdataoffset'].
', found "'.
Helper::PrintHexBytes(substr($MPCheaderData, 0, 4)).
'"';
70 unset(
$info[
'fileformat']);
88 $info = &$this->getid3->info;
89 $thisfile_mpc_header = &
$info[
'mpc'][
'header'];
92 $maxHandledPacketLength = 9;
94 $offset =
ftell($this->getid3->fp);
95 while ($offset <
$info[
'avdataend']) {
96 $thisPacket =
array();
97 $thisPacket[
'offset'] = $offset;
102 $MPCheaderData =
fread($this->getid3->fp, $keyNameSize + $maxHandledPacketLength);
103 $packet_offset += $keyNameSize;
104 $thisPacket[
'key'] = substr($MPCheaderData, 0, $keyNameSize);
106 if ($thisPacket[
'key'] == $thisPacket[
'key_name']) {
107 $info[
'error'][] =
'Found unexpected key value "'.$thisPacket[
'key'].
'" at offset '.$thisPacket[
'offset'];
113 if ($thisPacket[
'packet_size'] ===
false) {
114 $info[
'error'][] =
'Did not find expected packet length within '.$maxHandledPacketLength.
' bytes at offset '.($thisPacket[
'offset'] + $keyNameSize);
118 $packet_offset += $packetLength;
119 $offset += $thisPacket[
'packet_size'];
121 switch ($thisPacket[
'key']) {
123 $moreBytesToRead = $thisPacket[
'packet_size'] - $keyNameSize - $maxHandledPacketLength;
124 if ($moreBytesToRead > 0) {
125 $MPCheaderData .=
fread($this->getid3->fp, $moreBytesToRead);
133 $thisPacket[
'sample_count'] = $this->
SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
134 $packet_offset += $packetLength;
137 $thisPacket[
'beginning_silence'] = $this->
SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
138 $packet_offset += $packetLength;
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']);
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'];
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'];
161 $moreBytesToRead = $thisPacket[
'packet_size'] - $keyNameSize - $maxHandledPacketLength;
162 if ($moreBytesToRead > 0) {
163 $MPCheaderData .=
fread($this->getid3->fp, $moreBytesToRead);
165 $thisPacket[
'replaygain_version'] =
Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
167 $thisPacket[
'replaygain_title_gain'] =
Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
169 $thisPacket[
'replaygain_title_peak'] =
Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
171 $thisPacket[
'replaygain_album_gain'] =
Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
173 $thisPacket[
'replaygain_album_peak'] =
Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
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']; }
183 $moreBytesToRead = $thisPacket[
'packet_size'] - $keyNameSize - $maxHandledPacketLength;
184 if ($moreBytesToRead > 0) {
185 $MPCheaderData .=
fread($this->getid3->fp, $moreBytesToRead);
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);
199 $thisPacket[
'version'] = $thisPacket[
'version_major'].
'.'.$thisPacket[
'version_minor'].
'.'.$thisPacket[
'version_build'];
201 $info[
'audio'][
'encoder'] =
'MPC v'.$thisPacket[
'version'].
' ('.(($thisPacket[
'version_minor'] % 2) ?
'unstable' :
'stable').
')';
202 $thisfile_mpc_header[
'encoder_version'] =
$info[
'audio'][
'encoder'];
204 $thisfile_mpc_header[
'quality'] = (float) ($thisPacket[
'quality'] - 5);
209 $thisPacket[
'seek_table_offset'] = $thisPacket[
'offset'] + $this->
SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
210 $packet_offset += $packetLength;
217 $thisPacket =
array();
221 $info[
'error'][] =
'Found unhandled key type "'.$thisPacket[
'key'].
'" at offset '.$thisPacket[
'offset'];
226 if (!empty($thisPacket)) {
227 $info[
'mpc'][
'packets'][] = $thisPacket;
229 fseek($this->getid3->fp, $offset);
231 $thisfile_mpc_header[
'size'] = $offset;
245 $info = &$this->getid3->info;
246 $thisfile_mpc_header = &
$info[
'mpc'][
'header'];
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+');
256 $thisfile_mpc_header[
'stream_version_major'] = ($StreamVersionByte & 0x0F) >> 0;
257 $thisfile_mpc_header[
'stream_version_minor'] = ($StreamVersionByte & 0xF0) >> 4;
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'].
')';
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);
290 $thisfile_mpc_header[
'true_gapless'] = (bool) (($FlagsDWORD2 & 0x80000000) >> 31);
291 $thisfile_mpc_header[
'last_frame_length'] = ($FlagsDWORD2 & 0x7FF00000) >> 20;
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';
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'];
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';
316 $info[
'avdataoffset'] += $thisfile_mpc_header[
'size'];
318 $info[
'audio'][
'bitrate'] = ((
$info[
'avdataend'] -
$info[
'avdataoffset']) * 8) /
$info[
'playtime_seconds'];
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;
325 $thisfile_mpc_header[
'title_gain_db'] = (float) $thisfile_mpc_header[
'raw'][
'title_gain'] / 100;
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;
333 $thisfile_mpc_header[
'album_gain_db'] = (float) $thisfile_mpc_header[
'raw'][
'album_gain'] / 100;;
335 $thisfile_mpc_header[
'encoder_version'] = $this->
MPCencoderVersionLookup($thisfile_mpc_header[
'raw'][
'encoder_version']);
337 $info[
'replay_gain'][
'track'][
'adjustment'] = $thisfile_mpc_header[
'title_gain_db'];
338 $info[
'replay_gain'][
'album'][
'adjustment'] = $thisfile_mpc_header[
'album_gain_db'];
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));
345 if ($thisfile_mpc_header[
'album_peak'] > 0) {
346 $info[
'replay_gain'][
'album'][
'peak'] = $thisfile_mpc_header[
'album_peak'];
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);
365 $info = &$this->getid3->info;
366 $thisfile_mpc_header = &
$info[
'mpc'][
'header'];
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']);
374 $info[
'avdataoffset'] += $thisfile_mpc_header[
'size'];
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;
395 $thisfile_mpc_header[
'max_band'] = ($HeaderDWORD[0] & 0x000007C0) >> 6;
396 $thisfile_mpc_header[
'block_size'] = ($HeaderDWORD[0] & 0x0000003F);
398 switch ($thisfile_mpc_header[
'stream_version_major']) {
400 $thisfile_mpc_header[
'frame_count'] = ($HeaderDWORD[1] >> 16);
405 $thisfile_mpc_header[
'frame_count'] = $HeaderDWORD[1];
409 $info[
'error'] =
'Expecting 4, 5 or 6 in version field, found '.$thisfile_mpc_header[
'stream_version_major'].
' instead';
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'];
420 $thisfile_mpc_header[
'sample_rate'] = 44100;
421 $info[
'audio'][
'sample_rate'] = $thisfile_mpc_header[
'sample_rate'];
422 $thisfile_mpc_header[
'samples'] = $thisfile_mpc_header[
'frame_count'] * 1152 *
$info[
'audio'][
'channels'];
424 if ($thisfile_mpc_header[
'target_bitrate'] == 0) {
425 $info[
'audio'][
'bitrate_mode'] =
'vbr';
427 $info[
'audio'][
'bitrate_mode'] =
'cbr';
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'];
445 static $MPCprofileNameLookup =
array(
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)' 464 return (isset($MPCprofileNameLookup[$profileid]) ? $MPCprofileNameLookup[$profileid] :
'invalid');
475 static $MPCfrequencyLookup =
array(
482 return (isset($MPCfrequencyLookup[$frequencyid]) ? $MPCfrequencyLookup[$frequencyid] :
'invalid');
493 return ((log10($intvalue) / log10(2)) - 15) * 6;
511 if ($encoderversion == 0) {
513 return 'Buschmann v1.7.0-v1.7.9 or Klemm v0.90-v1.05';
516 if (($encoderversion % 10) == 0) {
519 return number_format($encoderversion / 100, 2);
521 } elseif (($encoderversion % 2) == 0) {
524 return number_format($encoderversion / 100, 2).
' beta';
529 return number_format($encoderversion / 100, 2).
' alpha';
542 for ($packetLength = 1; $packetLength <= $maxHandledPacketLength; $packetLength++) {
550 $thisbyte = ord(substr(
$data, ($packetLength - 1), 1));
552 $packet_size = ($packet_size << 7);
553 $packet_size = ($packet_size | ($thisbyte & 0x7F));
554 if (($thisbyte & 0x80) === 0) {
557 if ($packetLength >= $maxHandledPacketLength) {
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',
587 return (isset($MPCsv8PacketName[$packetKey]) ? $MPCsv8PacketName[$packetKey] : $packetKey);
SV8variableLengthInteger($data, &$packetLength, $maxHandledPacketLength=9)
MPCprofileNameLookup($profileid)
array $MPCprofileNameLookup
static PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8')
GetId3() by James Heinrich info@getid3.org //.
MPCfrequencyLookup($frequencyid)
array $MPCfrequencyLookup
MPCpeakDBLookup($intvalue)
fseek($bytes, $whence=SEEK_SET)
MPCencoderVersionLookup($encoderversion)
static CastAsInt($floatnum)
Create styles array
The data for the language used.
static LittleEndian2Int($byteword, $signed=false)
static BigEndian2Int($byteword, $synchsafe=false, $signed=false)
GetId3() by James Heinrich info@getid3.org //.
MPCsv8PacketName($packetKey)
array $MPCsv8PacketName
ParseMPCsv8()
http://trac.musepack.net/trac/wiki/SV8Specification