Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
38 lines
1.4 KiB
Python
38 lines
1.4 KiB
Python
"""MPEG Layer III duration without external tools."""
|
|
|
|
_BR1 = [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320]
|
|
_BR2 = [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160]
|
|
_SR = {3: [44100, 48000, 32000], 2: [22050, 24000, 16000], 0: [11025, 12000, 8000]}
|
|
|
|
|
|
def mp3_duration(path):
|
|
"""Duration of an MPEG Layer III file by walking frame headers (works for VBR)."""
|
|
with open(path, "rb") as fh:
|
|
data = fh.read()
|
|
i = 0
|
|
if data[:3] == b"ID3" and len(data) >= 10:
|
|
i = 10 + ((data[6] & 0x7f) << 21 | (data[7] & 0x7f) << 14 | (data[8] & 0x7f) << 7 | (data[9] & 0x7f))
|
|
samples = 0.0
|
|
first = True
|
|
n = len(data)
|
|
while i + 4 <= n:
|
|
h = int.from_bytes(data[i:i + 4], "big")
|
|
ver, layer, bri, sri = (h >> 19) & 3, (h >> 17) & 3, (h >> 12) & 15, (h >> 10) & 3
|
|
if (h >> 21) & 0x7ff != 0x7ff or ver == 1 or layer != 1 or bri in (0, 15) or sri == 3:
|
|
i += 1
|
|
continue
|
|
sr = _SR[ver][sri]
|
|
pad = (h >> 9) & 1
|
|
if ver == 3:
|
|
flen, spf = 144000 * _BR1[bri] // sr + pad, 1152
|
|
else:
|
|
flen, spf = 72000 * _BR2[bri] // sr + pad, 576
|
|
if first:
|
|
first = False
|
|
if b"Xing" in data[i:i + 64] or b"Info" in data[i:i + 64]:
|
|
i += flen
|
|
continue
|
|
samples += spf / sr
|
|
i += flen
|
|
return samples
|