Skip to content

Instantly share code, notes, and snippets.

@man-of-eel
Created January 26, 2024 16:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save man-of-eel/c15a50d696707dcf6f38f731be68efc6 to your computer and use it in GitHub Desktop.
Save man-of-eel/c15a50d696707dcf6f38f731be68efc6 to your computer and use it in GitHub Desktop.
Tool to convert ADX music files to Ogg files with loop points with FFmpeg
#!/usr/bin/env python3
import argparse, sys, subprocess, pathlib
ffmpeg = "ffmpeg" # Change to ffmpeg path if you need to
parser = argparse.ArgumentParser(description='Convert ADX music to Ogg/Opus files with loop points')
parser.add_argument('input')
parser.add_argument('output', nargs='?')
parser.add_argument('-b', '--bitrate', dest='bitrate', default="64k", help="Set the bitrate (e.g. 64k)")
parser.add_argument('-c', '--codec', dest='codec', default="libopus", help="Set the codec to use. Recommended: libopus, libvorbis, flac")
#parser.add_argument('--mono', dest='mono', action='store_true')
args = parser.parse_args()
if args.output == None:
output = pathlib.Path(args.input)
output = output.with_suffix('.ogg')
else:
output = args.output
f = open(args.input, 'rb')
header = f.read(2)
if (header != b'\x80\x00'):
sys.exit("Not an ADX file")
f.seek(0x12,0)
fversion = f.read(1)
if (fversion == b'\x03'):
version = 3
elif (fversion == b'\x04') :
version = 4
else:
sys.exit("Only versions 3 and 4 are supported")
f.seek(0x08,0)
samplerate = int.from_bytes(f.read(4), byteorder='big')
if (version == 3):
f.seek(0x1C,0)
loopstart = int.from_bytes(f.read(4), byteorder='big')
f.seek(0x24,0)
loopend = int.from_bytes(f.read(4), byteorder='big')
elif (version == 4):
f.seek(0x28,0)
loopstart = int.from_bytes(f.read(4), byteorder='big')
f.seek(0x30,0)
loopend = int.from_bytes(f.read(4), byteorder='big')
# Thanks to
# https://en.wikipedia.org/w/index.php?title=ADX_(file_format)&oldid=1187485353
codec = args.codec
# Opus only supports 48000hz, and loop points are based on
# samples so they get messed up if the sample rate changes
if (codec == "libopus" and samplerate != 48000):
loopstart = int(loopstart*(48000/samplerate))
loopend = int(loopend*(48000/samplerate))
command = [ffmpeg, "-i", args.input]
#if args.mono:
# command.extend(["-ac","1"])
command.extend(["-c:a", codec, "-b:a", args.bitrate, "-metadata", "LOOP_START="+str(loopstart), "-metadata", "LOOP_END="+str(loopend), output])
subprocess.run(command)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment