Uses the FFmpeg library, needs to be installed and of course python3.
This was tested on Mac, converts wav’s to 48kHz 24Bit SQ drive WAV format.
The tracks need to be mono. You can only link 1+2 3+4 5+6 etc., make sure there are no gaps. Best practice is to number the tracks before conversion, the script runs all .WAV files in the directory and ignores .sh and .py files.
Howto:
- Create an empty file called “convert.py”, copy the script code from the post into the empty file with a text editor.
- Copy the convert.py file to the directory holding the mono wav tracks
- run the script from the console with “python3 convert.py” inside of the directory
- tracks are created: TRK01.WAV, TRK02.WAV etc.
- move the converted tracks to the SQ-Drive under :\AHSQ\USBMTK create a SQ-MTXXX folder with an unused number e.g. SQ-MT012
If you have problems installing python3 or ffmpeg, ask Google AI, it can easily help you out. The script is not flawless it’s AI assisted, use at own risk (no unit tests ran).
Works well here though.
Script:
#!/usr/bin/env python3
import os
import subprocess
import glob
import struct
import re
# Collect all files in the current working directory
files = sorted(glob.glob('*'))
track_index = 1
print("Starting byte-perfect audio conversion for Allen & Heath SQ-Drive...")
for f in files:
if not os.path.isfile(f):
continue
# Skip already converted TRKxx.WAV files and script files to avoid loops
if re.match(r'^TRK\d+\.WAV$', f, re.IGNORECASE) or f.endswith('.py') or f.endswith('.sh'):
continue
# Safely query audio stream indices via ffprobe
cmd = ['ffprobe', '-v', 'error', '-select_streams', 'a', '-show_entries', 'stream=index', '-of', 'csv=p=0', f]
res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if res.returncode != 0 or not res.stdout.strip():
continue
streams = res.stdout.strip().split('\n')
for si in streams:
if not si.strip():
continue
out_name = f"TRK{track_index:02d}.WAV"
raw_name = f"tmp_{track_index}.raw"
print(f"Processing '{f}' (Stream #{si})...")
# 1. Extract raw 24-bit little-endian PCM stream via FFmpeg (completely containerless)
ffmpeg_cmd = [
'ffmpeg', '-y', '-i', f, '-map', f'0:a:{si}',
'-ac', '1', '-ar', '48000', '-c:a', 'pcm_s24le', '-f', 's24le', '-vn', raw_name
]
subprocess.run(ffmpeg_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
if os.path.exists(raw_name):
with open(raw_name, 'rb') as rf:
pcm_data = rf.read()
os.remove(raw_name)
data_size = len(pcm_data)
riff_size = data_size + 504
# 2. Manually stitch a strict 512-byte byte-perfect console header block
# This perfectly emulates the console's exact chunk sizes and structural layout:
# - RIFF WAVE fmt (36 bytes)
# - LIST INFO INAM (30 bytes)
# - junk padding (438 bytes)
# Total text/structural headers = 504 bytes. Then 8 bytes of 'data' tag/size = 512 bytes offset.
header_part1 = struct.pack('<4sI4s4sIHHIIHH4sI4s4sI9sB4sI',
b'RIFF', riff_size, b'WAVE',
b'fmt ', 16, 1, 1, 48000, 144000, 3, 24,
b'LIST', 22, b'INFO', b'INAM', 9, b'MainLR L\x00', 0,
b'junk', 430
)
junk_padding = b'\x00' * 430
header_part2 = struct.pack('<4sI', b'data', data_size)
full_header = header_part1 + junk_padding + header_part2
# 3. Combine header and payload into the final recognized track file
with open(out_name, 'wb') as wf:
wf.write(full_header)
wf.write(pcm_data)
print(f" -> Successfully generated: {out_name}")
track_index += 1
print(f"\nDone! Processed {track_index - 1} audio tracks cleanly.")