1import deprecation
2from math import log10
3
4from . import Mp3Exception
5from ..utils.binfuncs import bytes2bin, bytes2dec, bin2dec
6from ..utils.log import getLogger
7from ..__about__ import __version__
8
9log = getLogger(__name__)
10
11
12def isValidHeader(header):
13    """Determine if ``header`` (an integer, 4 bytes compared) is a valid mp3
14    frame header."""
15    # Test for the mp3 frame sync: 11 set bits.
16    sync = (header >> 16)
17    if sync & 0xffe0 != 0xffe0:
18        # ffe0 is 11 sync bits, 12 are not used in order to support identifying
19        # mpeg v2.5 (bits 20,19)
20        return False
21
22    # All the remaining tests are not entirely required, but do help in
23    # finding false syncs
24
25    version = (header >> 19) & 0x3
26    if version == 1:
27        # This is a "reserved" version
28        log.debug("invalid mpeg version")
29        return False
30
31    layer = (header >> 17) & 0x3
32    if layer == 0:
33        # This is a "reserved" layer
34        log.debug("invalid mpeg layer")
35        return False
36
37    bitrate = (header >> 12) & 0xf
38    if bitrate in (0, 0xf):
39        # free and bad bitrate values
40        log.debug("invalid mpeg bitrate")
41        return False
42
43    sample_rate = (header >> 10) & 0x3
44    if sample_rate == 0x3:
45        # this is a "reserved" sample rate
46        log.debug("invalid mpeg sample rate")
47        return False
48
49    return True
50
51
52def findHeader(fp, start_pos=0):
53    """Locate the first mp3 header in file stream ``fp`` starting a offset
54    ``start_pos`` (defaults to 0). Returned is a 3-tuple containing the offset
55    where the header was found, the header as an integer, and the header as 4
56    bytes. If no header is found header_int will equal 0.
57    """
58
59    def isBOM(buffer, pos):
60        """Check for unicode BOM"""
61        try:
62            if pos - 1 >= 0:
63                if buffer[pos - 1] == 254:
64                    return True
65            return buffer[pos + 1] == 254
66        except IndexError:
67            return False
68
69    def find_sync(_fp, _pos=0):
70        chunk_sz = 8192  # Measured as optimal
71
72        _fp.seek(_pos)
73        data = _fp.read(chunk_sz)
74
75        while data:
76            pos = 0
77            while 0 <= pos < chunk_sz:
78                pos = data.find(b"\xff", pos)
79                if pos == -1:
80                    break
81
82                if not isBOM(data, pos):
83                    h = data[pos:pos + 4]
84                    if len(h) == 4:
85                        return tuple([_pos + pos, h])
86
87                pos += 1
88
89            _pos += len(data)
90            data = _fp.read(chunk_sz)
91
92        return None, None
93
94    sync_pos, header_bytes = find_sync(fp, start_pos)
95    while sync_pos is not None:
96        header = bytes2dec(header_bytes)
97        if isValidHeader(header):
98            return tuple([sync_pos, header, header_bytes])
99        sync_pos, header_bytes = find_sync(fp, start_pos + sync_pos + 2)
100
101    return None, None, None
102
103
104def timePerFrame(mp3_header, vbr):
105    """Computes the number of seconds per mp3 frame. It can be used to
106    compute overall playtime and bitrate. The mp3 layer and sample
107    rate from ``mp3_header`` are used to compute the number of seconds
108    (fractional float point value) per mp3 frame. Be sure to set ``vbr`` True
109    when dealing with VBR, otherwise playtimes may be incorrect."""
110    # https://bitbucket.org/nicfit/eyed3/issue/32/mp3audioinfotime_secs-incorrect-for-mpeg2
111    if mp3_header.version >= 2.0 and vbr:
112        row = _mp3VersionKey(mp3_header.version)
113    else:
114        row = 0
115    return (float(SAMPLES_PER_FRAME_TABLE[row][mp3_header.layer]) /
116            float(mp3_header.sample_freq))
117
118
119@deprecation.deprecated(deprecated_in="0.9a2", removed_in="1.0", current_version=__version__,
120                        details="Use timePerFrame instead")
121def compute_time_per_frame(mp3_header):
122    if mp3_header is not None:
123        return timePerFrame(mp3_header, False)
124
125
126class Mp3Header:
127    """Header container for MP3 frames."""
128    def __init__(self, header_data=None):
129        self.version = None
130        self.layer = None
131        self.error_protection = None
132        self.bit_rate = None
133        self.sample_freq = None
134        self.padding = None
135        self.private_bit = None
136        self.copyright = None
137        self.original = None
138        self.emphasis = None
139        self.mode = None
140        # This value is left as is: 0<=mode_extension<=3.
141        # See http://www.dv.co.yu/mpgscript/mpeghdr.htm for how to interpret
142        self.mode_extension = None
143        self.frame_length = None
144
145        if header_data:
146            self.decode(header_data)
147
148    # This may throw an Mp3Exception if the header is malformed.
149    def decode(self, header):
150        if not isValidHeader(header):
151            raise Mp3Exception("Invalid MPEG header")
152
153        # MPEG audio version from bits 19 and 20.
154        version = (header >> 19) & 0x3
155        self.version = [2.5, None, 2.0, 1.0][version]
156        if self.version is None:
157            raise Mp3Exception("Illegal MPEG version")
158
159        # MPEG layer
160        self.layer = 4 - ((header >> 17) & 0x3)
161        if self.layer == 4:
162            raise Mp3Exception("Illegal MPEG layer")
163
164        # Decode some simple values.
165        self.error_protection = not (header >> 16) & 0x1
166        self.padding = (header >> 9) & 0x1
167        self.private_bit = (header >> 8) & 0x1
168        self.copyright = (header >> 3) & 0x1
169        self.original = (header >> 2) & 0x1
170
171        # Obtain sampling frequency.
172        sample_bits = (header >> 10) & 0x3
173        self.sample_freq = \
174            SAMPLE_FREQ_TABLE[sample_bits][_mp3VersionKey(self.version)]
175        if not self.sample_freq:
176            raise Mp3Exception("Illegal MPEG sampling frequency")
177
178        # Compute bitrate.
179        bit_rate_row = (header >> 12) & 0xf
180        if int(self.version) == 1 and self.layer == 1:
181            bit_rate_col = 0
182        elif int(self.version) == 1 and self.layer == 2:
183            bit_rate_col = 1
184        elif int(self.version) == 1 and self.layer == 3:
185            bit_rate_col = 2
186        elif int(self.version) == 2 and self.layer == 1:
187            bit_rate_col = 3
188        elif int(self.version) == 2 and (self.layer == 2 or
189                                         self.layer == 3):
190            bit_rate_col = 4
191        else:
192            raise Mp3Exception("Mp3 version %f and layer %d is an invalid "
193                               "combination" % (self.version, self.layer))
194        self.bit_rate = BIT_RATE_TABLE[bit_rate_row][bit_rate_col]
195        if self.bit_rate is None:
196            raise Mp3Exception("Invalid bit rate")
197        # We know know the bit rate specified in this frame, but if the file
198        # is VBR we need to obtain the average from the Xing header.
199        # This is done by the caller since right now all we have is the frame
200        # header.
201
202        # Emphasis; whatever that means??
203        emph = header & 0x3
204        if emph == 0:
205            self.emphasis = EMPHASIS_NONE
206        elif emph == 1:
207            self.emphasis = EMPHASIS_5015
208        elif emph == 2:
209            self.emphasis = EMPHASIS_CCIT
210        else:
211            raise Mp3Exception("Illegal mp3 emphasis value: %d" % emph)
212
213        # Channel mode.
214        mode_bits = (header >> 6) & 0x3
215        if mode_bits == 0:
216            self.mode = MODE_STEREO
217        elif mode_bits == 1:
218            self.mode = MODE_JOINT_STEREO
219        elif mode_bits == 2:
220            self.mode = MODE_DUAL_CHANNEL_STEREO
221        else:
222            self.mode = MODE_MONO
223        self.mode_extension = (header >> 4) & 0x3
224
225        # Layer II has restrictions wrt to mode and bit rate.  This code
226        # enforces them.
227        if self.layer == 2:
228            m = self.mode
229            br = self.bit_rate
230            if (br in [32, 48, 56, 80] and (m != MODE_MONO)):
231                raise Mp3Exception("Invalid mode/bitrate combination for layer "
232                                   "II")
233            if (br in [224, 256, 320, 384] and (m == MODE_MONO)):
234                raise Mp3Exception("Invalid mode/bitrate combination for layer "
235                                   "II")
236
237        br = self.bit_rate * 1000
238        sf = self.sample_freq
239        p = self.padding
240        if self.layer == 1:
241            # Layer 1 uses 32 bit slots for padding.
242            p = self.padding * 4
243            self.frame_length = int((((12 * br) / sf) + p) * 4)
244        else:
245            # Layer 2 and 3 uses 8 bit slots for padding.
246            p = self.padding * 1
247            self.frame_length = int(((144 * br) / sf) + p)
248
249        # Dump the state.
250        log.debug("MPEG audio version: " + str(self.version))
251        log.debug("MPEG audio layer: " + ("I" * self.layer))
252        log.debug("MPEG sampling frequency: " + str(self.sample_freq))
253        log.debug("MPEG bit rate: " + str(self.bit_rate))
254        log.debug("MPEG channel mode: " + self.mode)
255        log.debug("MPEG channel mode extension: " + str(self.mode_extension))
256        log.debug("MPEG CRC error protection: " + str(self.error_protection))
257        log.debug("MPEG original: " + str(self.original))
258        log.debug("MPEG copyright: " + str(self.copyright))
259        log.debug("MPEG private bit: " + str(self.private_bit))
260        log.debug("MPEG padding: " + str(self.padding))
261        log.debug("MPEG emphasis: " + str(self.emphasis))
262        log.debug("MPEG frame length: " + str(self.frame_length))
263
264
265class VbriHeader(object):
266    def __init__(self):
267        self.vbr = True
268        self.version = None
269
270    ##
271    # \brief Decode the VBRI info from \a frame.
272    # http://www.codeproject.com/audio/MPEGAudioInfo.asp#VBRIHeader
273    def decode(self, frame):
274
275        # The header is 32 bytes after the end of the first MPEG audio header,
276        # therefore 4 + 32 = 36
277        offset = 36
278        head = frame[offset:offset + 4]
279        if head != 'VBRI':
280            return False
281        log.debug("VBRI header detected @ %x" % (offset))
282        offset += 4
283
284        self.version = bin2dec(bytes2bin(frame[offset:offset + 2]))
285        offset += 2
286
287        self.delay = bin2dec(bytes2bin(frame[offset:offset + 2]))
288        offset += 2
289
290        self.quality = bin2dec(bytes2bin(frame[offset:offset + 2]))
291        offset += 2
292
293        self.num_bytes = bin2dec(bytes2bin(frame[offset:offset + 4]))
294        offset += 4
295
296        self.num_frames = bin2dec(bytes2bin(frame[offset:offset + 4]))
297        offset += 4
298
299        return True
300
301
302class XingHeader:
303    """Header class for the Xing header extensions."""
304
305    def __init__(self):
306        self.numFrames = int()
307        self.numBytes = int()
308        self.toc = [0] * 100
309        self.vbrScale = int()
310
311    # Pass in the first mp3 frame from the file as a byte string.
312    # If an Xing header is present in the file it'll be in the first mp3
313    # frame.  This method returns true if the Xing header is found in the
314    # frame, and false otherwise.
315    def decode(self, frame):
316        # mp3 version
317        version = (frame[1] >> 3) & 0x1
318        # channel mode.
319        mode = (frame[3] >> 6) & 0x3
320
321        # Find the start of the Xing header.
322        if version:
323            # +4 in all of these to skip initial mp3 frame header.
324            if mode != 3:
325                pos = 32 + 4
326            else:
327                pos = 17 + 4
328        else:
329            if mode != 3:
330                pos = 17 + 4
331            else:
332                pos = 9 + 4
333        head = frame[pos:pos + 4]
334        self.vbr = (head == b'Xing') and True or False
335        if head not in [b'Xing', b'Info']:
336            return False
337        log.debug("%s header detected @ %x" % (head, pos))
338        pos += 4
339
340        # Read Xing flags.
341        headFlags = bin2dec(bytes2bin(frame[pos:pos + 4]))
342        pos += 4
343        log.debug("%s header flags: 0x%x" % (head, headFlags))
344
345        # Read frames header flag and value if present
346        if headFlags & FRAMES_FLAG:
347            self.numFrames = bin2dec(bytes2bin(frame[pos:pos + 4]))
348            pos += 4
349            log.debug("%s numFrames: %d" % (head, self.numFrames))
350
351        # Read bytes header flag and value if present
352        if headFlags & BYTES_FLAG:
353            self.numBytes = bin2dec(bytes2bin(frame[pos:pos + 4]))
354            pos += 4
355            log.debug("%s numBytes: %d" % (head, self.numBytes))
356
357        # Read TOC header flag and value if present
358        if headFlags & TOC_FLAG:
359            self.toc = frame[pos:pos + 100]
360            pos += 100
361            log.debug("%s TOC (100 bytes): PRESENT" % head)
362        else:
363            log.debug("%s TOC (100 bytes): NOT PRESENT" % head)
364
365        # Read vbr scale header flag and value if present
366        if headFlags & VBR_SCALE_FLAG and head == b'Xing':
367            self.vbrScale = bin2dec(bytes2bin(frame[pos:pos + 4]))
368            pos += 4
369            log.debug("%s vbrScale: %d" % (head, self.vbrScale))
370
371        return True
372
373
374class LameHeader(dict):
375    r""" Mp3 Info tag (AKA LAME Tag)
376
377    Lame (and some other encoders) write a tag containing various bits of info
378    about the options used at encode time.  If available, the following are
379    parsed and stored in the LameHeader dict:
380
381    encoder_version: short encoder version [str]
382    tag_revision:    revision number of the tag [int]
383    vbr_method:      VBR method used for encoding [str]
384    lowpass_filter:  lowpass filter frequency in Hz [int]
385    replaygain:      if available, radio and audiofile gain (see below) [dict]
386    encoding_flags:  encoding flags used [list]
387    nogap:           location of gaps when --nogap was used [list]
388    ath_type:        ATH type [int]
389    bitrate:         bitrate and type (Constant, Target, Minimum) [tuple]
390    encoder_delay:   samples added at the start of the mp3 [int]
391    encoder_padding: samples added at the end of the mp3 [int]
392    noise_shaping:   noise shaping method [int]
393    stereo_mode:     stereo mode used [str]
394    unwise_settings: whether unwise settings were used [boolean]
395    sample_freq:     source sample frequency [str]
396    mp3_gain:        mp3 gain adjustment (rarely used) [float]
397    preset:          preset used [str]
398    surround_info:   surround information [str]
399    music_length:    length in bytes of original mp3 [int]
400    music_crc:       CRC-16 of the mp3 music data [int]
401    infotag_crc:     CRC-16 of the info tag [int]
402
403    Prior to ~3.90, Lame simply stored the encoder version in the first frame.
404    If the infotag_crc is invalid, then we try to read this version string.  A
405    simple way to tell if the LAME Tag is complete is to check for the
406    infotag_crc key.
407
408    Replay Gain data is only available since Lame version 3.94b.  If set, the
409    replaygain dict has the following structure:
410
411    \code
412       peak_amplitude: peak signal amplitude [float]
413       radio:
414          name:       name of the gain adjustment [str]
415          adjustment: gain adjustment [float]
416          originator: originator of the gain adjustment [str]
417       audiofile: [same as radio]
418    \endcode
419
420    Note that as of 3.95.1, Lame uses 89dB as a reference level instead of the
421    83dB that is specified in the Replay Gain spec.  This is not automatically
422    compensated for.  You can do something like this if you want:
423
424    \code
425       import eyeD3
426       af = eyeD3.mp3.Mp3AudioFile('/path/to/some.mp3')
427       lamever = af.lameTag['encoder_version']
428       name, ver = lamever[:4], lamever[4:]
429       gain = af.lameTag['replaygain']['radio']['adjustment']
430       if name == 'LAME' and eyeD3.mp3.lamevercmp(ver, '3.95') > 0:
431           gain -= 6
432    \endcode
433
434    Radio and Audiofile Replay Gain are often referrered to as Track and Album
435    gain, respectively.  See http://replaygain.hydrogenaudio.org/ for futher
436    details on Replay Gain.
437
438    See http://gabriel.mp3-tech.org/mp3infotag.html for the gory details of the
439    LAME Tag.
440    """
441
442    # from the LAME source:
443    # http://lame.cvs.sourceforge.net/*checkout*/lame/lame/libmp3lame/VbrTag.c
444    _crc16_table = [
445      0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
446      0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
447      0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
448      0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
449      0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
450      0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
451      0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
452      0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
453      0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
454      0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
455      0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
456      0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
457      0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
458      0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
459      0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
460      0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
461      0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
462      0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
463      0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
464      0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
465      0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
466      0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
467      0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
468      0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
469      0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
470      0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
471      0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
472      0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
473      0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
474      0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
475      0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
476      0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040]
477
478    ENCODER_FLAGS = {
479      'NSPSYTUNE': 0x0001,
480      'NSSAFEJOINT': 0x0002,
481      'NOGAP_NEXT': 0x0004,
482      'NOGAP_PREV': 0x0008}
483
484    PRESETS = {
485      0: 'Unknown',
486      # 8 to 320 are reserved for ABR bitrates
487      410: 'V9',
488      420: 'V8',
489      430: 'V7',
490      440: 'V6',
491      450: 'V5',
492      460: 'V4',
493      470: 'V3',
494      480: 'V2',
495      490: 'V1',
496      500: 'V0',
497      1000: 'r3mix',
498      1001: 'standard',
499      1002: 'extreme',
500      1003: 'insane',
501      1004: 'standard/fast',
502      1005: 'extreme/fast',
503      1006: 'medium',
504      1007: 'medium/fast'}
505
506    REPLAYGAIN_NAME = {
507      0: 'Not set',
508      1: 'Radio',
509      2: 'Audiofile'}
510
511    REPLAYGAIN_ORIGINATOR = {
512      0: 'Not set',
513      1: 'Set by artist',
514      2: 'Set by user',
515      3: 'Set automatically',
516      100: 'Set by simple RMS average'}
517
518    SAMPLE_FREQUENCIES = {
519      0: '<= 32 kHz',
520      1: '44.1 kHz',
521      2: '48 kHz',
522      3: '> 48 kHz'}
523
524    STEREO_MODES = {
525      0: 'Mono',
526      1: 'Stereo',
527      2: 'Dual',
528      3: 'Joint',
529      4: 'Force',
530      5: 'Auto',
531      6: 'Intensity',
532      7: 'Undefined'}
533
534    SURROUND_INFO = {
535      0: 'None',
536      1: 'DPL encoding',
537      2: 'DPL2 encoding',
538      3: 'Ambisonic encoding',
539      8: 'Reserved'}
540
541    VBR_METHODS = {
542      0: 'Unknown',
543      1: 'Constant Bitrate',
544      2: 'Average Bitrate',
545      3: 'Variable Bitrate method1 (old/rh)',
546      4: 'Variable Bitrate method2 (mtrh)',
547      5: 'Variable Bitrate method3 (mt)',
548      6: 'Variable Bitrate method4',
549      8: 'Constant Bitrate (2 pass)',
550      9: 'Average Bitrate (2 pass)',
551      15: 'Reserved'}
552
553    def __init__(self, frame):
554        """Read the LAME info tag.
555        frame should be the first frame of an mp3.
556        """
557        super().__init__()
558        self.decode(frame)
559
560    def _crc16(self, data, val=0):
561        """Compute a CRC-16 checksum on a data stream."""
562        for c in [bytes([b]) for b in data]:
563            val = self._crc16_table[ord(c) ^ (val & 0xff)] ^ (val >> 8)
564        return val
565
566    def decode(self, frame):
567        """Decode the LAME info tag."""
568        try:
569            pos = frame.index(b"LAME")
570        except ValueError:
571            return
572
573        log.debug(f"Lame info tag found at position {pos}")
574
575        # check the info tag crc. If it's not valid, no point parsing much more.
576        lamecrc = bin2dec(bytes2bin(frame[190:192]))
577        if self._crc16(frame[:190]) != lamecrc:
578            log.warning("Lame tag CRC check failed")
579        else:
580            log.debug("Lame tag CRC OK")
581
582        try:
583            # Encoder short VersionString, 9 bytes
584            self['encoder_version'] = str(frame[pos:pos + 9].rstrip(), "latin1")
585            log.debug('Lame Encoder Version: %s' % self['encoder_version'])
586            pos += 9
587
588            # Info Tag revision + VBR method, 1 byte
589            self['tag_revision'] = bin2dec(bytes2bin(frame[pos:pos + 1])[:5])
590            vbr_method = bin2dec(bytes2bin(frame[pos:pos + 1])[5:])
591            self['vbr_method'] = self.VBR_METHODS.get(vbr_method, 'Unknown')
592            log.debug('Lame info tag version: %s' % self['tag_revision'])
593            log.debug('Lame VBR method: %s' % self['vbr_method'])
594            pos += 1
595
596            # Lowpass filter value, 1 byte
597            self['lowpass_filter'] = bin2dec(
598                                       bytes2bin(frame[pos:pos + 1])) * 100
599            log.debug('Lame Lowpass filter value: %s Hz' %
600                      self['lowpass_filter'])
601            pos += 1
602
603            # Replay Gain, 8 bytes total
604            replaygain = {}
605
606            # Peak signal amplitude, 4 bytes
607            peak = bin2dec(bytes2bin(frame[pos:pos + 4])) << 5
608            if peak > 0:
609                peak /= float(1 << 28)
610                db = 20 * log10(peak)
611                replaygain['peak_amplitude'] = peak
612                log.debug('Lame Peak signal amplitude: %.8f (%+.1f dB)' %
613                          (peak, db))
614            pos += 4
615
616            # Radio and Audiofile Gain, AKA track and album, 2 bytes each
617            for gaintype in ['radio', 'audiofile']:
618                name = bin2dec(bytes2bin(frame[pos:pos + 2])[:3])
619                orig = bin2dec(bytes2bin(frame[pos:pos + 2])[3:6])
620                sign = bin2dec(bytes2bin(frame[pos:pos + 2])[6:7])
621                adj = bin2dec(bytes2bin(frame[pos:pos + 2])[7:]) / 10.0
622                if sign:
623                    adj *= -1
624
625                # Lame 3.95.1 and above use 89dB as a reference instead of 83dB as defined by the
626                # Replay Gain spec. This will be compensated for with `adj -= 6`
627                lamever = self['encoder_version']
628                if lamever[:4] == 'LAME' and lamevercmp(lamever[4:], "3.95") > 0:
629                    adj -= 6
630
631                if orig:
632                    name = self.REPLAYGAIN_NAME.get(name, 'Unknown')
633                    orig = self.REPLAYGAIN_ORIGINATOR.get(orig, 'Unknown')
634                    replaygain[gaintype] = {'name': name, 'adjustment': adj,
635                                            'originator': orig}
636                    log.debug('Lame %s Replay Gain: %s dB (%s)' %
637                              (name, adj, orig))
638                pos += 2
639            if replaygain:
640                self['replaygain'] = replaygain
641
642            # Encoding flags + ATH Type, 1 byte
643            encflags = bin2dec(bytes2bin(frame[pos:pos + 1])[:4])
644            (self['encoding_flags'],
645             self['nogap']) = self._parse_encflags(encflags)
646            self['ath_type'] = bin2dec(bytes2bin(frame[pos:pos + 1])[4:])
647            log.debug('Lame Encoding flags: %s' %
648                      ' '.join(self['encoding_flags']))
649            if self['nogap']:
650                log.debug('Lame No gap: %s' % ' and '.join(self['nogap']))
651            log.debug('Lame ATH type: %s' % self['ath_type'])
652            pos += 1
653
654            # if ABR {specified bitrate} else {minimal bitrate}, 1 byte
655            btype = 'Constant'
656            if 'Average' in self['vbr_method']:
657                btype = 'Target'
658            elif 'Variable' in self['vbr_method']:
659                btype = 'Minimum'
660            # bitrate may be modified below after preset is read
661            self['bitrate'] = (bin2dec(bytes2bin(frame[pos:pos + 1])), btype)
662            log.debug('Lame Bitrate (%s): %s' % (btype, self['bitrate'][0]))
663            pos += 1
664
665            # Encoder delays, 3 bytes
666            self['encoder_delay'] = bin2dec(bytes2bin(frame[pos:pos + 3])[:12])
667            self['encoder_padding'] = bin2dec(
668                                        bytes2bin(frame[pos:pos + 3])[12:])
669            log.debug('Lame Encoder delay: %s samples' % self['encoder_delay'])
670            log.debug('Lame Encoder padding: %s samples' %
671                      self['encoder_padding'])
672            pos += 3
673
674            # Misc, 1 byte
675            sample_freq = bin2dec(bytes2bin(frame[pos:pos + 1])[:2])
676            unwise_settings = bin2dec(bytes2bin(frame[pos:pos + 1])[2:3])
677            stereo_mode = bin2dec(bytes2bin(frame[pos:pos + 1])[3:6])
678            self['noise_shaping'] = bin2dec(bytes2bin(frame[pos:pos + 1])[6:])
679            self['sample_freq'] = self.SAMPLE_FREQUENCIES.get(sample_freq,
680                                                              'Unknown')
681            self['unwise_settings'] = bool(unwise_settings)
682            self['stereo_mode'] = self.STEREO_MODES.get(stereo_mode, 'Unknown')
683            log.debug('Lame Source Sample Frequency: %s' % self['sample_freq'])
684            log.debug('Lame Unwise settings used: %s' % self['unwise_settings'])
685            log.debug('Lame Stereo mode: %s' % self['stereo_mode'])
686            log.debug('Lame Noise Shaping: %s' % self['noise_shaping'])
687            pos += 1
688
689            # MP3 Gain, 1 byte
690            sign = bytes2bin(frame[pos:pos + 1])[0]
691            gain = bin2dec(bytes2bin(frame[pos:pos + 1])[1:])
692            if sign:
693                gain *= -1
694            self['mp3_gain'] = gain
695            db = gain * 1.5
696            log.debug('Lame MP3 Gain: %s (%+.1f dB)' % (self['mp3_gain'], db))
697            pos += 1
698
699            # Preset and surround info, 2 bytes
700            surround = bin2dec(bytes2bin(frame[pos:pos + 2])[2:5])
701            preset = bin2dec(bytes2bin(frame[pos:pos + 2])[5:])
702            if preset in range(8, 321):
703                if self['bitrate'][0] >= 255:
704                    # the value from preset is better in this case
705                    self['bitrate'] = (preset, btype)
706                    log.debug('Lame Bitrate (%s): %s' %
707                              (btype, self['bitrate'][0]))
708                if 'Average' in self['vbr_method']:
709                    preset = 'ABR %s' % preset
710                else:
711                    preset = 'CBR %s' % preset
712            else:
713                preset = self.PRESETS.get(preset, preset)
714            self['surround_info'] = self.SURROUND_INFO.get(surround, surround)
715            self['preset'] = preset
716            log.debug('Lame Surround Info: %s' % self['surround_info'])
717            log.debug('Lame Preset: %s' % self['preset'])
718            pos += 2
719
720            # MusicLength, 4 bytes
721            self['music_length'] = bin2dec(bytes2bin(frame[pos:pos + 4]))
722            log.debug('Lame Music Length: %s bytes' % self['music_length'])
723            pos += 4
724
725            # MusicCRC, 2 bytes
726            self['music_crc'] = bin2dec(bytes2bin(frame[pos:pos + 2]))
727            log.debug('Lame Music CRC: %04X' % self['music_crc'])
728            pos += 2
729
730            # CRC-16 of Info Tag, 2 bytes
731            self['infotag_crc'] = lamecrc  # we read this earlier
732            log.debug('Lame Info Tag CRC: %04X' % self['infotag_crc'])
733            pos += 2
734        except IndexError:
735            log.warning("Truncated LAME info header, values incomplete.")
736
737    def _parse_encflags(self, flags):
738        """Parse encoder flags.
739
740        Returns a tuple containing lists of encoder flags and nogap data in
741        human readable format.
742        """
743
744        encoder_flags, nogap = [], []
745
746        if not flags:
747            return encoder_flags, nogap
748
749        if flags & self.ENCODER_FLAGS['NSPSYTUNE']:
750            encoder_flags.append('--nspsytune')
751        if flags & self.ENCODER_FLAGS['NSSAFEJOINT']:
752            encoder_flags.append('--nssafejoint')
753
754        NEXT = self.ENCODER_FLAGS['NOGAP_NEXT']
755        PREV = self.ENCODER_FLAGS['NOGAP_PREV']
756        if flags & (NEXT | PREV):
757            encoder_flags.append('--nogap')
758            if flags & PREV:
759                nogap.append('before')
760            if flags & NEXT:
761                nogap.append('after')
762        return encoder_flags, nogap
763
764
765def lamevercmp(x, y):
766    """Compare LAME version strings.
767
768    alpha and beta versions are considered older.
769    Versions with sub minor parts or end with 'r' are considered newer.
770
771    :param x: The first version to compare.
772    :param y: The second version to compare.
773    :returns: Return negative if x<y, zero if x==y, positive if x>y.
774    """
775
776    def cmp(a, b):
777        # This is Python2's built-in `cmp`, which was removed from Python3
778        # And depends on bool - bool yielding the integer -1, 0, 1
779        return (a > b) - (a < b)
780
781    x = x.ljust(5)
782    y = y.ljust(5)
783    if x[:5] == y[:5]:
784        return 0
785    ret = cmp(x[:4], y[:4])
786    if ret:
787        return ret
788    xmaj, xmin = x.split('.')[:2]
789    ymaj, ymin = y.split('.')[:2]
790    minparts = ['.']
791    # lame 3.96.1 added the use of r in the very short version for post releases
792    if (xmaj == '3' and xmin >= '96') or (ymaj == '3' and ymin >= '96'):
793        minparts.append('r')
794    if x[4] in minparts:
795        return 1
796    if y[4] in minparts:
797        return -1
798    if x[4] == ' ':
799        return 1
800    if y[4] == ' ':
801        return -1
802    return cmp(x[4], y[4])
803
804
805#                     MPEG1  MPEG2  MPEG2.5
806SAMPLE_FREQ_TABLE = ((44100, 22050, 11025),
807                     (48000, 24000, 12000),
808                     (32000, 16000, 8000),
809                     (None, None, None))
810
811#              V1/L1  V1/L2 V1/L3 V2/L1 V2/L2&L3
812BIT_RATE_TABLE = ((0,    0,    0,    0,    0),                          # noqa
813                  (32,   32,   32,   32,   8),                          # noqa
814                  (64,   48,   40,   48,   16),                         # noqa
815                  (96,   56,   48,   56,   24),                         # noqa
816                  (128,  64,   56,   64,   32),                         # noqa
817                  (160,  80,   64,   80,   40),                         # noqa
818                  (192,  96,   80,   96,   48),                         # noqa
819                  (224,  112,  96,   112,  56),                         # noqa
820                  (256,  128,  112,  128,  64),                         # noqa
821                  (288,  160,  128,  144,  80),                         # noqa
822                  (320,  192,  160,  160,  96),                         # noqa
823                  (352,  224,  192,  176,  112),                        # noqa
824                  (384,  256,  224,  192,  128),                        # noqa
825                  (416,  320,  256,  224,  144),                        # noqa
826                  (448,  384,  320,  256,  160),                        # noqa
827                  (None, None, None, None, None))
828
829# Rows 1 and 2 (mpeg 2.x) are only used for those versions *and* VBR.
830#                                  L1   L2   L3
831SAMPLES_PER_FRAME_TABLE = ((None, 384, 1152, 1152),  # MPEG 1
832                           (None, 384, 1152, 576),   # MPEG 2
833                           (None, 384, 1152, 576),   # MPEG 2.5
834                          )
835
836# Emphasis constants
837EMPHASIS_NONE = "None"
838EMPHASIS_5015 = "50/15 ms"
839EMPHASIS_CCIT = "CCIT J.17"
840
841# Mode constants
842MODE_STEREO = "Stereo"
843MODE_JOINT_STEREO = "Joint stereo"
844MODE_DUAL_CHANNEL_STEREO = "Dual channel stereo"
845MODE_MONO = "Mono"
846
847# Xing flag bits
848FRAMES_FLAG = 0x0001
849BYTES_FLAG = 0x0002
850TOC_FLAG = 0x0004
851VBR_SCALE_FLAG = 0x0008
852
853
854def _mp3VersionKey(version):
855    """Map mp3 version float to a data structure index.
856    1 -> 0, 2 -> 1, 2.5 -> 2
857    """
858    key = None
859    if version == 2.5:
860        key = 2
861    else:
862        key = int(version - 1)
863    assert(0 <= key <= 2)
864    return key
865