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