1 ################################################################################
3 # Copyright (C) 2002-2005 Travis Shirk <travis@pobox.com>
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 ################################################################################
20 from binfuncs
import *;
23 #######################################################################
24 class Mp3Exception(Exception):
25 '''Error reading mp3'''
29 SAMPLE_FREQ_TABLE
= ((44100, 22050, 11025),
30 (48000, 24000, 12000),
34 # V1/L1 V1/L2 V1/L3 V2/L1 V2/L2&L3
35 BIT_RATE_TABLE
= ((0, 0, 0, 0, 0),
39 (128, 64, 56, 64, 32),
40 (160, 80, 64, 80, 40),
41 (192, 96, 80, 96, 44),
42 (224, 112, 96, 112, 56),
43 (256, 128, 112, 128, 64),
44 (288, 160, 128, 144, 80),
45 (320, 192, 160, 160, 96),
46 (352, 224, 192, 176, 112),
47 (384, 256, 224, 192, 128),
48 (416, 320, 256, 224, 144),
49 (448, 384, 320, 256, 160),
50 (None, None, None, None, None));
53 TIME_PER_FRAME_TABLE
= (None, 384, 1152, 1152);
56 EMPHASIS_NONE
= "None";
57 EMPHASIS_5015
= "50/15 ms";
58 EMPHASIS_CCIT
= "CCIT J.17";
61 MODE_STEREO
= "Stereo";
62 MODE_JOINT_STEREO
= "Joint stereo";
63 MODE_DUAL_CHANNEL_STEREO
= "Dual channel stereo";
70 VBR_SCALE_FLAG
= 0x0008
72 #######################################################################
73 def computeTimePerFrame(frameHeader
):
74 tpf
= TIME_PER_FRAME_TABLE
[frameHeader
.layer
];
75 tpf
= float(tpf
) / float(frameHeader
.sampleFreq
);
78 #######################################################################
92 # This value is left as is: 0 <= modeExtension <= 3. Consult the
93 # mp3 spec here http://www.dv.co.yu/mpgscript/mpeghdr.htm if you wish to
97 # Pass in a 4 byte integer to determine if it matches a valid mp3 frame
99 def isValid(self
, header
):
100 # Test for the mp3 frame sync: 11 set bits.
101 if (header
& 0xffe00000L
) != 0xffe00000L
:
103 if not ((header
>> 17) & 3):
105 if ((header
>> 12) & 0xf) == 0xf:
107 if not ((header
>> 12) & 0xf):
109 if ((header
>> 10) & 0x3) == 0x3:
111 if (((header
>> 19) & 1) == 1) and (((header
>> 17) & 3) == 3) and \
112 (((header
>> 16) & 1) == 1):
114 if (header
& 0xffff0000L
) == 0xfffe0000L
:
119 # This may throw an Mp3Exception if the header is malformed.
120 def decode(self
, header
):
121 # MPEG audio version from bits 19 and 20.
122 if not header
& (1 << 20) and header
& (1 << 19):
123 raise Mp3Exception("Illegal MPEG audio version");
124 elif not header
& (1 << 20) and not header
& (1 << 19):
127 if not header
& (1 << 19):
133 # MPEG audio layer from bits 18 and 17.
134 if not header
& (1 << 18) and not header
& (1 << 17):
135 raise Mp3Exception("Illegal MPEG layer value");
136 elif not header
& (1 << 18) and header
& (1 << 17):
138 elif header
& (1 << 18) and not header
& (1 << 17):
143 # Decode some simple values.
144 self
.errorProtection
= not (header
>> 16) & 0x1;
145 self
.padding
= (header
>> 9) & 0x1;
146 self
.privateBit
= (header
>> 8) & 0x1;
147 self
.copyright
= (header
>> 3) & 0x1;
148 self
.original
= (header
>> 2) & 0x1;
150 # Obtain sampling frequency.
151 sampleBits
= (header
>> 10) & 0x3;
152 if self
.version
== 2.5:
155 freqCol
= int(self
.version
- 1);
156 self
.sampleFreq
= SAMPLE_FREQ_TABLE
[sampleBits
][freqCol
];
157 if not self
.sampleFreq
:
158 raise Mp3Exception("Illegal MPEG sampling frequency");
162 bitRateIndex
= (header
>> 12) & 0xf;
163 if int(self
.version
) == 1 and self
.layer
== 1:
165 elif int(self
.version
) == 1 and self
.layer
== 2:
167 elif int(self
.version
) == 1 and self
.layer
== 3:
169 elif int(self
.version
) == 2 and self
.layer
== 1:
171 elif int(self
.version
) == 2 and (self
.layer
== 2 or \
175 raise Mp3Exception("Mp3 version %f and layer %d is an invalid "\
176 "combination" % (self
.version
, self
.layer
));
177 self
.bitRate
= BIT_RATE_TABLE
[bitRateIndex
][bitRateCol
];
178 if self
.bitRate
== None:
179 raise Mp3Exception("Invalid bit rate");
180 # We know know the bit rate specified in this frame, but if the file
181 # is VBR we need to obtain the average from the Xing header.
182 # This is done by the caller since right now all we have is the frame
185 # Emphasis; whatever that means??
188 self
.emphasis
= EMPHASIS_NONE
;
190 self
.emphasis
= EMPHASIS_5015
;
192 self
.emphasis
= EMPHASIS_CCIT
;
194 raise Mp3Exception("Illegal mp3 emphasis value: %d" % emph
);
197 modeBits
= (header
>> 6) & 0x3;
199 self
.mode
= MODE_STEREO
;
201 self
.mode
= MODE_JOINT_STEREO
;
203 self
.mode
= MODE_DUAL_CHANNEL_STEREO
;
205 self
.mode
= MODE_MONO
;
206 self
.modeExtension
= (header
>> 4) & 0x3;
208 # Layer II has restrictions wrt to mode and bit rate. This code
213 if (br
== 32 or br
== 48 or br
== 56 or br
== 80) and \
215 raise Mp3Exception("Invalid mode/bitrate combination for layer "\
217 if (br
== 224 or br
== 256 or br
== 320 or br
== 384) and \
219 raise Mp3Exception("Invalid mode/bitrate combination for layer "\
222 br
= self
.bitRate
* 1000;
223 sf
= self
.sampleFreq
;
226 # Layer 1 uses 32 bit slots for padding.
227 p
= self
.padding
* 4;
228 self
.frameLength
= int((((12 * br
) / sf
) + p
) * 4);
230 # Layer 2 and 3 uses 8 bit slots for padding.
231 p
= self
.padding
* 1;
232 self
.frameLength
= int(((144 * br
) / sf
) + p
);
235 TRACE_MSG("MPEG audio version: " + str(self
.version
));
236 TRACE_MSG("MPEG audio layer: " + ("I" * self
.layer
));
237 TRACE_MSG("MPEG sampling frequency: " + str(self
.sampleFreq
));
238 TRACE_MSG("MPEG bit rate: " + str(self
.bitRate
));
239 TRACE_MSG("MPEG channel mode: " + self
.mode
);
240 TRACE_MSG("MPEG channel mode extension: " + str(self
.modeExtension
));
241 TRACE_MSG("MPEG CRC error protection: " + str(self
.errorProtection
));
242 TRACE_MSG("MPEG original: " + str(self
.original
));
243 TRACE_MSG("MPEG copyright: " + str(self
.copyright
));
244 TRACE_MSG("MPEG private bit: " + str(self
.privateBit
));
245 TRACE_MSG("MPEG padding: " + str(self
.padding
));
246 TRACE_MSG("MPEG emphasis: " + str(self
.emphasis
));
247 TRACE_MSG("MPEG frame length: " + str(self
.frameLength
));
249 #######################################################################
256 # Pass in the first mp3 frame from the file as a byte string.
257 # If an Xing header is present in the file it'll be in the first mp3
258 # frame. This method returns true if the Xing header is found in the
259 # frame, and false otherwise.
260 def decode(self
, frame
):
262 id = (ord(frame
[1]) >> 3) & 0x1;
264 mode
= (ord(frame
[3]) >> 6) & 0x3;
266 # Find the start of the Xing header.
277 if frame
[pos
] != 'X' or frame
[pos
+ 1] != 'i' or \
278 frame
[pos
+ 2] != 'n' or frame
[pos
+ 3] != 'g':
280 TRACE_MSG("Xing header detected");
284 headFlags
= bin2dec(bytes2bin(frame
[pos
:pos
+ 4]));
286 TRACE_MSG("Xing header flags: 0x%x" % headFlags
);
288 # Read frames header flag and value if present
289 if headFlags
& FRAMES_FLAG
:
290 self
.numFrames
= bin2dec(bytes2bin(frame
[pos
:pos
+ 4]));
292 TRACE_MSG("Xing numFrames: %d" % self
.numFrames
);
294 # Read bytes header flag and value if present
295 if headFlags
& BYTES_FLAG
:
296 self
.numBytes
= bin2dec(bytes2bin(frame
[pos
:pos
+ 4]));
298 TRACE_MSG("Xing numBytes: %d" % self
.numBytes
);
300 # Read TOC header flag and value if present
301 if headFlags
& TOC_FLAG
:
303 self
.toc
= frame
[pos
:pos
+ 100];
305 TRACE_MSG("Xing TOC (100 bytes): PRESENT");
307 TRACE_MSG("Xing TOC (100 bytes): NOT PRESENT");
309 # Read vbr scale header flag and value if present
310 if headFlags
& VBR_SCALE_FLAG
:
311 self
.vbrScale
= bin2dec(bytes2bin(frame
[pos
:pos
+ 4]));
313 TRACE_MSG("Xing vbrScale: %d" % self
.vbrScale
);