diff --git a/bin/mtag/audio-bpm.py b/bin/mtag/audio-bpm.py index b87cd73d..e9f7023e 100755 --- a/bin/mtag/audio-bpm.py +++ b/bin/mtag/audio-bpm.py @@ -59,8 +59,6 @@ def main(): try: det(tf) - except: - pass finally: os.unlink(tf) diff --git a/bin/mtag/audio-key-slicing.py b/bin/mtag/audio-key-slicing.py new file mode 100755 index 00000000..b2952d27 --- /dev/null +++ b/bin/mtag/audio-key-slicing.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python + +import re +import os +import sys +import tempfile +import subprocess as sp + +import keyfinder + +from copyparty.util import fsenc + +""" +dep: github/mixxxdj/libkeyfinder +dep: pypi/keyfinder +dep: ffmpeg + +note: this is a janky edition of the regular audio-key.py, + slicing the files at 20sec intervals and keeping 5sec from each, + surprisingly accurate but still garbage (446 ok, 69 bad, 13% miss) + + it is fast tho +""" + + +def get_duration(): + # TODO provide ffprobe tags to mtp as json + + # fmt: off + dur = sp.check_output([ + "ffprobe", + "-hide_banner", + "-v", "fatal", + "-show_streams", + "-show_format", + fsenc(sys.argv[1]) + ]) + # fmt: on + + dur = dur.decode("ascii", "replace").split("\n") + dur = [x.split("=")[1] for x in dur if x.startswith("duration=")] + dur = [float(x) for x in dur if re.match(r"^[0-9\.,]+$", x)] + return list(sorted(dur))[-1] if dur else None + + +def get_segs(dur): + # keep first 5s of each 20s, + # keep entire last segment + ofs = 0 + segs = [] + while True: + seg = [ofs, 5] + segs.append(seg) + if dur - ofs < 20: + seg[-1] = int(dur - seg[0]) + break + + ofs += 20 + + return segs + + +def slice(tf): + dur = get_duration() + dur = min(dur, 600) # max 10min + segs = get_segs(dur) + + # fmt: off + cmd = [ + "ffmpeg", + "-nostdin", + "-hide_banner", + "-v", "fatal", + "-y" + ] + + for seg in segs: + cmd.extend([ + "-ss", str(seg[0]), + "-i", fsenc(sys.argv[1]) + ]) + + filt = "" + for n, seg in enumerate(segs): + filt += "[{}:a:0]atrim=duration={}[a{}]; ".format(n, seg[1], n) + + prev = "a0" + for n in range(1, len(segs)): + nxt = "b{}".format(n) + filt += "[{}][a{}]acrossfade=d=0.5[{}]; ".format(prev, n, nxt) + prev = nxt + + cmd.extend([ + "-filter_complex", filt[:-2], + "-map", "[{}]".format(nxt), + "-sample_fmt", "s16", + tf + ]) + # fmt: on + + # print(cmd) + sp.check_call(cmd) + + +def det(tf): + slice(tf) + print(keyfinder.key(tf).camelot()) + + +def main(): + with tempfile.NamedTemporaryFile(suffix=".flac", delete=False) as f: + f.write(b"h") + tf = f.name + + try: + det(tf) + finally: + os.unlink(tf) + pass + + +if __name__ == "__main__": + main() diff --git a/bin/mtag/audio-key.py b/bin/mtag/audio-key.py index a1fa0b70..fce66006 100755 --- a/bin/mtag/audio-key.py +++ b/bin/mtag/audio-key.py @@ -1,18 +1,52 @@ #!/usr/bin/env python +import os import sys +import tempfile +import subprocess as sp import keyfinder +from copyparty.util import fsenc + """ dep: github/mixxxdj/libkeyfinder dep: pypi/keyfinder dep: ffmpeg - -note: cannot fsenc """ -try: - print(keyfinder.key(sys.argv[1]).camelot()) -except: - pass +# tried trimming the first/last 5th, bad idea, +# misdetects 9a law field (Sphere Caliber) as 10b, +# obvious when mixing 9a ghostly parapara ship + + +def det(tf): + # fmt: off + sp.check_call([ + "ffmpeg", + "-nostdin", + "-hide_banner", + "-v", "fatal", + "-y", "-i", fsenc(sys.argv[1]), + "-t", "300", + "-sample_fmt", "s16", + tf + ]) + # fmt: on + + print(keyfinder.key(tf).camelot()) + + +def main(): + with tempfile.NamedTemporaryFile(suffix=".flac", delete=False) as f: + f.write(b"h") + tf = f.name + + try: + det(tf) + finally: + os.unlink(tf) + + +if __name__ == "__main__": + main() diff --git a/docs/notes.sh b/docs/notes.sh index fb0b4889..8d8cc333 100644 --- a/docs/notes.sh +++ b/docs/notes.sh @@ -106,6 +106,12 @@ find -iname up2k.db | while IFS= read -r x; do sqlite3 "$x" 'select substr(w,1,1 # unschedule mtp scan for all files somewhere under "enc/" sqlite3 -readonly up2k.db 'select substr(up.w,1,16) from up inner join mt on mt.w = substr(up.w,1,16) where rd like "enc/%" and +mt.k = "t:mtp"' > keys; awk '{printf "delete from mt where w = \"%s\" and +k = \"t:mtp\";\n", $0}' k1; sqlite3 -readonly up2k.db 'select w, v from mt where k = "key" order by w' > k2; ok=0; ng=0; while IFS='|' read w k2; do k1="$(grep -E "^$w" k1 | sed -r 's/.*\|//')"; [ "$k1" = "$k2" ] && ok=$((ok+1)) || { ng=$((ng+1)); printf '%3s %3s %s\n' "$k1" "$k2" "$(sqlite3 -readonly up2k.db.key-full "select * from up where substr(w,1,16) = '$w'" | sed -r 's/\|/ | /g')"; }; done < <(cat k2); echo "match $ok diff $ng" + +# actually this is much better +sqlite3 -readonly up2k.db.key-full 'select w, v from mt where k = "key" order by w' > k1; sqlite3 -readonly up2k.db 'select mt.w, mt.v, up.rd, up.fn from mt inner join up on mt.w = substr(up.w,1,16) where mt.k = "key" order by up.rd, up.fn' > k2; ok=0; ng=0; while IFS='|' read w k2 path; do k1="$(grep -E "^$w" k1 | sed -r 's/.*\|//')"; [ "$k1" = "$k2" ] && ok=$((ok+1)) || { ng=$((ng+1)); printf '%3s %3s %s\n' "$k1" "$k2" "$path"; }; done < <(cat k2); echo "match $ok diff $ng" + ## ## media