mirror of
https://github.com/vsariola/sointu.git
synced 2025-05-28 03:10:24 -04:00
54 lines
2.9 KiB
Python
54 lines
2.9 KiB
Python
# Usage: python parse_gmdls.py <path-to-gmdls>
|
|
# Parses the GMDLs sample and loop locations and dumps them ready to be included in the BEGIN_SAMPLE_OFFSETS block
|
|
|
|
import sys
|
|
|
|
def read_chunk(file,indent=0):
|
|
name = f.read(4)
|
|
length_bytes = f.read(4)
|
|
length = int.from_bytes(length_bytes,byteorder='little')
|
|
data = None
|
|
start = f.tell()
|
|
if name == b"RIFF" or name == b"LIST":
|
|
name = f.read(4)
|
|
data = list()
|
|
while f.tell() < start+length:
|
|
data.append(read_chunk(file,indent + 4))
|
|
if name == b"wave":
|
|
datablock = next((x[1] for x in data if x[0] == b"data"))
|
|
wsmp = next((x[1] for x in data if x[0] == b"wsmp"), None)
|
|
if "loopstart" not in wsmp:
|
|
loopstart,looplength = datablock["length"]-1,1 # For samples without loop, keep on repeating the last sample
|
|
else:
|
|
loopstart,looplength = wsmp["loopstart"],wsmp["looplength"]
|
|
INFO = next((x[1] for x in data if x[0] == b"INFO"), None)
|
|
INAM = next((x[1] for x in INFO if x[0] == b"INAM"), None)
|
|
name = ""
|
|
if INAM is not None:
|
|
name = INAM["name"]
|
|
t = 60 - wsmp["unitynote"] # in MIDI, the middle C = 263 Hz is 60. In Sointu/4klang, it's 72.
|
|
print("SAMPLE_OFFSET START(%d),LOOPSTART(%d),LOOPLENGTH(%d) ; name %s, unitynote %d (transpose to %d), data length %d" % (datablock["start"],loopstart,looplength,name,wsmp["unitynote"],t,datablock["length"]))
|
|
# Something is oddly off: LOOPSTART + LOOPLENGTH != DATA LENGTH /2, but rather LOOPSTART + LOOPLENGTH != DATA LENGTH /2 - 1
|
|
# Logically, LOOPSTART = 0 would mean the sample loops from the beginning. But then why would they store one extra sample?
|
|
# Or, maybe start+length is the index of the last sample included in the loop. For now, I'm assuming that start+length-1
|
|
# is the last sample and there's one unused sample.
|
|
elif name == b"wsmp":
|
|
f.read(4)
|
|
data = dict()
|
|
data["unitynote"] = int.from_bytes(f.read(2),byteorder='little')
|
|
f.read(10)
|
|
numloops = int.from_bytes(f.read(4),byteorder='little')
|
|
if numloops > 0:
|
|
f.read(8)
|
|
data["loopstart"] = int.from_bytes(f.read(4),byteorder='little')
|
|
data["looplength"] = int.from_bytes(f.read(4),byteorder='little')
|
|
elif name == b"data":
|
|
data = {"start": f.tell()/2, "length": length/2}
|
|
elif name == b"INAM":
|
|
data = {"name": f.read(length-1).decode("ascii")}
|
|
f.read(length - f.tell() + start + (length & 1))
|
|
return (name,data)
|
|
|
|
if __name__ == "__main__":
|
|
with open(sys.argv[1], "rb") as f:
|
|
read_chunk(f) |