ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
Monkey.php
Go to the documentation of this file.
1<?php
2
3namespace GetId3\Module\Audio;
4
7
10// available at http://getid3.sourceforge.net //
11// or http://www.getid3.org //
13// See readme.txt for more details //
15// //
16// module.audio.monkey.php //
17// module for analyzing Monkey's Audio files //
18// dependencies: NONE //
19// ///
21
29class Monkey extends BaseHandler
30{
31
36 public function analyze()
37 {
38 $info = &$this->getid3->info;
39
40 // based loosely on code from TMonkey by Jurgen Faul <jfaulØgmx*de>
41 // http://jfaul.de/atl or http://j-faul.virtualave.net/atl/atl.html
42
43 $info['fileformat'] = 'mac';
44 $info['audio']['dataformat'] = 'mac';
45 $info['audio']['bitrate_mode'] = 'vbr';
46 $info['audio']['lossless'] = true;
47
48 $info['monkeys_audio']['raw'] = array();
49 $thisfile_monkeysaudio = &$info['monkeys_audio'];
50 $thisfile_monkeysaudio_raw = &$thisfile_monkeysaudio['raw'];
51
52 fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
53 $MACheaderData = fread($this->getid3->fp, 74);
54
55 $thisfile_monkeysaudio_raw['magic'] = substr($MACheaderData, 0, 4);
56 $magic = 'MAC ';
57 if ($thisfile_monkeysaudio_raw['magic'] != $magic) {
58 $info['error'][] = 'Expecting "'.Helper::PrintHexBytes($magic).'" at offset '.$info['avdataoffset'].', found "'.Helper::PrintHexBytes($thisfile_monkeysaudio_raw['magic']).'"';
59 unset($info['fileformat']);
60
61 return false;
62 }
63 $thisfile_monkeysaudio_raw['nVersion'] = Helper::LittleEndian2Int(substr($MACheaderData, 4, 2)); // appears to be uint32 in 3.98+
64
65 if ($thisfile_monkeysaudio_raw['nVersion'] < 3980) {
66 $thisfile_monkeysaudio_raw['nCompressionLevel'] = Helper::LittleEndian2Int(substr($MACheaderData, 6, 2));
67 $thisfile_monkeysaudio_raw['nFormatFlags'] = Helper::LittleEndian2Int(substr($MACheaderData, 8, 2));
68 $thisfile_monkeysaudio_raw['nChannels'] = Helper::LittleEndian2Int(substr($MACheaderData, 10, 2));
69 $thisfile_monkeysaudio_raw['nSampleRate'] = Helper::LittleEndian2Int(substr($MACheaderData, 12, 4));
70 $thisfile_monkeysaudio_raw['nHeaderDataBytes'] = Helper::LittleEndian2Int(substr($MACheaderData, 16, 4));
71 $thisfile_monkeysaudio_raw['nWAVTerminatingBytes'] = Helper::LittleEndian2Int(substr($MACheaderData, 20, 4));
72 $thisfile_monkeysaudio_raw['nTotalFrames'] = Helper::LittleEndian2Int(substr($MACheaderData, 24, 4));
73 $thisfile_monkeysaudio_raw['nFinalFrameSamples'] = Helper::LittleEndian2Int(substr($MACheaderData, 28, 4));
74 $thisfile_monkeysaudio_raw['nPeakLevel'] = Helper::LittleEndian2Int(substr($MACheaderData, 32, 4));
75 $thisfile_monkeysaudio_raw['nSeekElements'] = Helper::LittleEndian2Int(substr($MACheaderData, 38, 2));
76 $offset = 8;
77 } else {
78 $offset = 8;
79 // APE_DESCRIPTOR
80 $thisfile_monkeysaudio_raw['nDescriptorBytes'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4));
81 $offset += 4;
82 $thisfile_monkeysaudio_raw['nHeaderBytes'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4));
83 $offset += 4;
84 $thisfile_monkeysaudio_raw['nSeekTableBytes'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4));
85 $offset += 4;
86 $thisfile_monkeysaudio_raw['nHeaderDataBytes'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4));
87 $offset += 4;
88 $thisfile_monkeysaudio_raw['nAPEFrameDataBytes'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4));
89 $offset += 4;
90 $thisfile_monkeysaudio_raw['nAPEFrameDataBytesHigh'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4));
91 $offset += 4;
92 $thisfile_monkeysaudio_raw['nTerminatingDataBytes'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4));
93 $offset += 4;
94 $thisfile_monkeysaudio_raw['cFileMD5'] = substr($MACheaderData, $offset, 16);
95 $offset += 16;
96
97 // APE_HEADER
98 $thisfile_monkeysaudio_raw['nCompressionLevel'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 2));
99 $offset += 2;
100 $thisfile_monkeysaudio_raw['nFormatFlags'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 2));
101 $offset += 2;
102 $thisfile_monkeysaudio_raw['nBlocksPerFrame'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4));
103 $offset += 4;
104 $thisfile_monkeysaudio_raw['nFinalFrameBlocks'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4));
105 $offset += 4;
106 $thisfile_monkeysaudio_raw['nTotalFrames'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4));
107 $offset += 4;
108 $thisfile_monkeysaudio_raw['nBitsPerSample'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 2));
109 $offset += 2;
110 $thisfile_monkeysaudio_raw['nChannels'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 2));
111 $offset += 2;
112 $thisfile_monkeysaudio_raw['nSampleRate'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4));
113 $offset += 4;
114 }
115
116 $thisfile_monkeysaudio['flags']['8-bit'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0001);
117 $thisfile_monkeysaudio['flags']['crc-32'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0002);
118 $thisfile_monkeysaudio['flags']['peak_level'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0004);
119 $thisfile_monkeysaudio['flags']['24-bit'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0008);
120 $thisfile_monkeysaudio['flags']['seek_elements'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0010);
121 $thisfile_monkeysaudio['flags']['no_wav_header'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0020);
122 $thisfile_monkeysaudio['version'] = $thisfile_monkeysaudio_raw['nVersion'] / 1000;
123 $thisfile_monkeysaudio['compression'] = $this->MonkeyCompressionLevelNameLookup($thisfile_monkeysaudio_raw['nCompressionLevel']);
124 if ($thisfile_monkeysaudio_raw['nVersion'] < 3980) {
125 $thisfile_monkeysaudio['samples_per_frame'] = $this->MonkeySamplesPerFrame($thisfile_monkeysaudio_raw['nVersion'], $thisfile_monkeysaudio_raw['nCompressionLevel']);
126 }
127 $thisfile_monkeysaudio['bits_per_sample'] = ($thisfile_monkeysaudio['flags']['24-bit'] ? 24 : ($thisfile_monkeysaudio['flags']['8-bit'] ? 8 : 16));
128 $thisfile_monkeysaudio['channels'] = $thisfile_monkeysaudio_raw['nChannels'];
129 $info['audio']['channels'] = $thisfile_monkeysaudio['channels'];
130 $thisfile_monkeysaudio['sample_rate'] = $thisfile_monkeysaudio_raw['nSampleRate'];
131 if ($thisfile_monkeysaudio['sample_rate'] == 0) {
132 $info['error'][] = 'Corrupt MAC file: frequency == zero';
133
134 return false;
135 }
136 $info['audio']['sample_rate'] = $thisfile_monkeysaudio['sample_rate'];
137 if ($thisfile_monkeysaudio['flags']['peak_level']) {
138 $thisfile_monkeysaudio['peak_level'] = $thisfile_monkeysaudio_raw['nPeakLevel'];
139 $thisfile_monkeysaudio['peak_ratio'] = $thisfile_monkeysaudio['peak_level'] / pow(2, $thisfile_monkeysaudio['bits_per_sample'] - 1);
140 }
141 if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) {
142 $thisfile_monkeysaudio['samples'] = (($thisfile_monkeysaudio_raw['nTotalFrames'] - 1) * $thisfile_monkeysaudio_raw['nBlocksPerFrame']) + $thisfile_monkeysaudio_raw['nFinalFrameBlocks'];
143 } else {
144 $thisfile_monkeysaudio['samples'] = (($thisfile_monkeysaudio_raw['nTotalFrames'] - 1) * $thisfile_monkeysaudio['samples_per_frame']) + $thisfile_monkeysaudio_raw['nFinalFrameSamples'];
145 }
146 $thisfile_monkeysaudio['playtime'] = $thisfile_monkeysaudio['samples'] / $thisfile_monkeysaudio['sample_rate'];
147 if ($thisfile_monkeysaudio['playtime'] == 0) {
148 $info['error'][] = 'Corrupt MAC file: playtime == zero';
149
150 return false;
151 }
152 $info['playtime_seconds'] = $thisfile_monkeysaudio['playtime'];
153 $thisfile_monkeysaudio['compressed_size'] = $info['avdataend'] - $info['avdataoffset'];
154 $thisfile_monkeysaudio['uncompressed_size'] = $thisfile_monkeysaudio['samples'] * $thisfile_monkeysaudio['channels'] * ($thisfile_monkeysaudio['bits_per_sample'] / 8);
155 if ($thisfile_monkeysaudio['uncompressed_size'] == 0) {
156 $info['error'][] = 'Corrupt MAC file: uncompressed_size == zero';
157
158 return false;
159 }
160 $thisfile_monkeysaudio['compression_ratio'] = $thisfile_monkeysaudio['compressed_size'] / ($thisfile_monkeysaudio['uncompressed_size'] + $thisfile_monkeysaudio_raw['nHeaderDataBytes']);
161 $thisfile_monkeysaudio['bitrate'] = (($thisfile_monkeysaudio['samples'] * $thisfile_monkeysaudio['channels'] * $thisfile_monkeysaudio['bits_per_sample']) / $thisfile_monkeysaudio['playtime']) * $thisfile_monkeysaudio['compression_ratio'];
162 $info['audio']['bitrate'] = $thisfile_monkeysaudio['bitrate'];
163
164 // add size of MAC header to avdataoffset
165 if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) {
166 $info['avdataoffset'] += $thisfile_monkeysaudio_raw['nDescriptorBytes'];
167 $info['avdataoffset'] += $thisfile_monkeysaudio_raw['nHeaderBytes'];
168 $info['avdataoffset'] += $thisfile_monkeysaudio_raw['nSeekTableBytes'];
169 $info['avdataoffset'] += $thisfile_monkeysaudio_raw['nHeaderDataBytes'];
170
171 $info['avdataend'] -= $thisfile_monkeysaudio_raw['nTerminatingDataBytes'];
172 } else {
173 $info['avdataoffset'] += $offset;
174 }
175
176 if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) {
177 if ($thisfile_monkeysaudio_raw['cFileMD5'] === str_repeat("\x00", 16)) {
178 //$info['warning'][] = 'cFileMD5 is null';
179 } else {
180 $info['md5_data_source'] = '';
181 $md5 = $thisfile_monkeysaudio_raw['cFileMD5'];
182 for ($i = 0; $i < strlen($md5); $i++) {
183 $info['md5_data_source'] .= str_pad(dechex(ord($md5{$i})), 2, '00', STR_PAD_LEFT);
184 }
185 if (!preg_match('/^[0-9a-f]{32}$/', $info['md5_data_source'])) {
186 unset($info['md5_data_source']);
187 }
188 }
189 }
190
191 $info['audio']['bits_per_sample'] = $thisfile_monkeysaudio['bits_per_sample'];
192 $info['audio']['encoder'] = 'MAC v'.number_format($thisfile_monkeysaudio['version'], 2);
193 $info['audio']['encoder_options'] = ucfirst($thisfile_monkeysaudio['compression']).' compression';
194
195 return true;
196 }
197
204 public function MonkeyCompressionLevelNameLookup($compressionlevel)
205 {
206 static $MonkeyCompressionLevelNameLookup = array(
207 0 => 'unknown',
208 1000 => 'fast',
209 2000 => 'normal',
210 3000 => 'high',
211 4000 => 'extra-high',
212 5000 => 'insane'
213 );
214
215 return (isset($MonkeyCompressionLevelNameLookup[$compressionlevel]) ? $MonkeyCompressionLevelNameLookup[$compressionlevel] : 'invalid');
216 }
217
224 public function MonkeySamplesPerFrame($versionid, $compressionlevel)
225 {
226 if ($versionid >= 3950) {
227 return 73728 * 4;
228 } elseif ($versionid >= 3900) {
229 return 73728;
230 } elseif (($versionid >= 3800) && ($compressionlevel == 4000)) {
231 return 73728;
232 } else {
233 return 9216;
234 }
235 }
236
237}
An exception for terminatinating execution or to throw for unit testing.
GetId3() by James Heinrich info@getid3.org //.
Definition: BaseHandler.php:26
fseek($bytes, $whence=SEEK_SET)
GetId3() by James Heinrich info@getid3.org //.
Definition: Helper.php:27
static LittleEndian2Int($byteword, $signed=false)
Definition: Helper.php:413
static PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8')
Definition: Helper.php:36
GetId3() by James Heinrich info@getid3.org //.
Definition: Monkey.php:30
MonkeySamplesPerFrame($versionid, $compressionlevel)
Definition: Monkey.php:224
MonkeyCompressionLevelNameLookup($compressionlevel)
@staticvar array $MonkeyCompressionLevelNameLookup
Definition: Monkey.php:204
$info
Definition: example_052.php:80