-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathis_mqa.py
More file actions
80 lines (64 loc) · 2.33 KB
/
is_mqa.py
File metadata and controls
80 lines (64 loc) · 2.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import struct
import sys
import io
import wave
import flac
from pathlib import Path
from bitstring import Bits
MAGIC = Bits('0xbe0498c88')
def twos_complement(n, bits):
mask = 2 ** (bits - 1)
return -(n & mask) + (n & ~mask)
def iter_i24_as_i32(data):
for l, h in struct.iter_unpack('<BH', data):
yield twos_complement(h << 8 | l, 24) << 8
def iter_i16_as_i32(data):
for x, in struct.iter_unpack('<h', data):
yield x << 16
def peek(f, n):
o = f.tell()
r = f.read(n)
f.seek(o)
return r
def main(path):
with open(str(path), 'rb') as f:
magic = peek(f, 4)
if magic == b'fLaC':
with flac.BitInputStream(f) as bf:
f = io.BytesIO()
flac.decode_file(bf, f, seconds=1)
f.seek(0)
with wave.open(f) as wf:
nchannels, sampwidth, framerate, *_ = wf.getparams()
if nchannels != 2:
raise ValueError('Input must be stereo')
if sampwidth == 3:
iter_data = iter_i24_as_i32
elif sampwidth == 2:
iter_data = iter_i16_as_i32
else:
raise ValueError('Input must be 16- or 24-bit')
sound_data = wf.readframes(framerate)
samples = list(iter_data(sound_data))
streams = (Bits((x ^ y) >> p & 1
for x, y in zip(samples[::2], samples[1::2]))
for p in range(16, 24))
if any(s.find(MAGIC) for s in streams):
print('\x1b[1;31m MQA syncword present. [{}] \x1b[0m'.format(str(path)))
else:
print('\x1b[1;32m Didn\'t find an MQA syncword. [{}] \x1b[0m'.format(path.parts[-1]))
if __name__ == '__main__':
args = sys.argv[1:]
flacpaths = []
for path in args:
path = Path(path)
if Path.is_dir(path):
flacpaths += sorted(Path(path).glob('**/*.flac'))
elif str(path).endswith('.flac') and path.is_file():
flacpaths.append(path)
print('\x1b[1;33m Found {} flac file(s). Decoding now... \x1b[0m'.format(len(flacpaths)))
for fpath in flacpaths:
try:
main(fpath)
except Exception as ex:
print(ex)