Created
February 26, 2024 22:25
-
-
Save dojoe/b73dabbe5746ec879dadf5694e707e24 to your computer and use it in GitHub Desktop.
How Traktor gets its Stripe/Transient filenames from the Audio ID
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# How to find the name of a Traktor Stripe or Transients file | |
# if you have the AUDIO_ID from collection.nml. | |
# | |
# Unlicense, yo. (https://unlicense.org/) | |
import base64, sys | |
# md5Step() converted from @Zunawe's implementation on github - thanks! | |
A = 0x67452301 | |
B = 0xefcdab89 | |
C = 0x98badcfe | |
D = 0x10325476 | |
S = [ | |
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, | |
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, | |
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, | |
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, | |
] | |
K = [ | |
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, | |
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, | |
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, | |
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, | |
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, | |
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, | |
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, | |
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, | |
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, | |
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, | |
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, | |
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, | |
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, | |
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, | |
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, | |
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391, | |
] | |
# | |
# Bit-manipulation functions defined by the MD5 algorithm | |
# | |
F = lambda X, Y, Z: ((X & Y) | (~X & Z)) | |
G = lambda X, Y, Z: ((X & Z) | (Y & ~Z)) | |
H = lambda X, Y, Z: (X ^ Y ^ Z) | |
I = lambda X, Y, Z: (Y ^ (X | ~Z)) | |
# | |
# Rotates a 32-bit word left by n bits | |
# | |
def rotateLeft(x, n): | |
x &= 0xFFFFFFFF | |
return ((x << n) | (x >> (32 - n))) & 0xFFFFFFFF | |
def md5Step(ctx, data): | |
input = [int.from_bytes(data[i*4:i*4+4], "little") for i in range(16)] | |
AA = ctx[0] | |
BB = ctx[1] | |
CC = ctx[2] | |
DD = ctx[3] | |
for i in range(64): | |
if i < 16: | |
E = F(BB, CC, DD) | |
j = i | |
elif i < 32: | |
E = G(BB, CC, DD) | |
j = ((i * 5) + 1) % 16 | |
elif i < 48: | |
E = H(BB, CC, DD) | |
j = ((i * 3) + 5) % 16 | |
else: | |
E = I(BB, CC, DD) | |
j = (i * 7) % 16 | |
temp = DD | |
DD = CC | |
CC = BB | |
BB = BB + rotateLeft(AA + E + K[i] + input[j], S[i]) | |
AA = temp | |
ctx[0] = (ctx[0] + AA) & 0xFFFFFFFF | |
ctx[1] = (ctx[1] + BB) & 0xFFFFFFFF | |
ctx[2] = (ctx[2] + CC) & 0xFFFFFFFF | |
ctx[3] = (ctx[3] + DD) & 0xFFFFFFFF | |
# MD5 variant used by Traktor | |
def traktorhash(audio_id_bytes): | |
ctx = [A, B, C, D] | |
for i in range(4): | |
md5Step(ctx, audio_id_bytes[i*64 : i*64+64]) | |
# this is what a true md5 would do (after exactly 256 bytes of input): | |
# md5Step(ctx, bytes.fromhex("80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000")) | |
# and this is what Traktor does: | |
md5Step(ctx, bytes(64)) | |
#return b"".join(x.to_bytes(4, "little") for x in ctx) # for debugging | |
return ctx | |
# Determine base32ish stripe filename and directory number from hash | |
def stripe_fname(hash): | |
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345" | |
fname = [] | |
for i in range(4): | |
for j in range(7): | |
fname.append(alphabet[(hash[i] >> 5*j) & 0x1F]) | |
return hash[0] & 127, "".join(fname) | |
# Putting it all together | |
def audioid2stripe(audio_id_from_nml): | |
audio_id_bytes = base64.b64decode(audio_id_from_nml) | |
hash = traktorhash(audio_id_bytes) | |
dirnum, fname = stripe_fname(hash) | |
return "%03d/%s" % (dirnum, fname) | |
# Example use | |
print(audioid2stripe(sys.argv[1])) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment