Skip to content

Commit 3b4a4eb

Browse files
committed
Automatic convertion of wav to adpcm for samples
Update vromtool.py and soundtool.py to generate automatically convert input .wav samples into ADPCM-A or ADPCM-B, based on the playback speed This removes to need to convert sound asserts with adpcmtool prior to use them in vromtool or soundtool.
1 parent d687bea commit 3b4a4eb

File tree

2 files changed

+41
-4
lines changed

2 files changed

+41
-4
lines changed

tools/soundtool.py

+17-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import yaml
2424
import glob
2525
import os
26+
import wave
2627

2728
VERBOSE = False
2829

@@ -55,6 +56,17 @@ def merge_descs(a,b):
5556
bdata = b[btype]
5657
return {atype: {**adata, **bdata}}
5758

59+
def detect_adpcm_type(f):
60+
try:
61+
w = wave.open(f, 'rb')
62+
assert w.getnchannels() == 1, "Only mono WAV file is supported"
63+
assert w.getsampwidth() == 2, "Only 16bits per sample is supported"
64+
assert w.getcomptype() == 'NONE', "Only uncompressed WAV file is supported"
65+
wavrate = w.getframerate()
66+
return "adpcm_a" if wavrate == 18500 else "adpcm_b"
67+
except Exception as e:
68+
return ""
69+
5870
existing_data = {}
5971
for i in desc:
6072
itemtype = list(i.keys())[0]
@@ -68,7 +80,7 @@ def merge_descs(a,b):
6880
for f in glob.iglob(e, recursive=True):
6981
if not os.path.isfile(f):
7082
continue
71-
if os.path.splitext(f)[1] not in ('.fur', '.adpcma', '.adpcmb'):
83+
if os.path.splitext(f)[1] not in ('.fur', '.adpcma', '.adpcmb', '.wav'):
7284
continue
7385
if f.endswith('fur'):
7486
# Furnace module
@@ -83,6 +95,10 @@ def merge_descs(a,b):
8395
sfxtype = 'adpcm_a'
8496
elif '.adpcmb' in f.lower():
8597
sfxtype = 'adpcm_b'
98+
elif '.wav' in f.lower():
99+
sfxtype = detect_adpcm_type(f)
100+
if not sfxtype:
101+
error("could not autodetect a suitable ADPCM format for '%s'"%f)
86102
uri='file://'+f
87103
name=mkid(os.path.splitext(os.path.basename(f))[0])
88104
sfx = {'name': name, 'uri': uri}

tools/vromtool.py

+24-3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
import sys
2424
from dataclasses import dataclass
2525
from furtool import samples_from_module
26+
from struct import pack, unpack, unpack_from
27+
import wave
28+
from adpcmtool import ym2610_adpcma, ym2610_adpcmb
2629

2730
import yaml
2831

@@ -126,6 +129,22 @@ def generate_asm_defines(smp, f):
126129
print(" .equ %s_STOP_MSB, 0x%02x" % (s.name.upper(), s.stop_msb), file=f)
127130
print("", file=f)
128131

132+
def convert_to_adpcm(sample, path):
133+
codec = {"adpcm_a": ym2610_adpcma,
134+
"adpcm_b": ym2610_adpcmb}[sample.__class__.__name__]()
135+
try:
136+
w = wave.open(path, 'rb')
137+
assert w.getnchannels() == 1, "Only mono WAV file is supported"
138+
assert w.getsampwidth() == 2, "Only 16bits per sample is supported"
139+
assert w.getcomptype() == 'NONE', "Only uncompressed WAV file is supported"
140+
nframes = w.getnframes()
141+
data = w.readframes(nframes)
142+
except Exception as e:
143+
error("Could not convert sample '%s' to ADPCM: %s"%(path, e))
144+
pcm16s = unpack('<%dh' % (len(data)>>1), data)
145+
adpcms=codec.encode(pcm16s)
146+
adpcms_packed = [(adpcms[i] << 4 | adpcms[i+1]) for i in range(0, len(adpcms), 2)]
147+
return bytes(adpcms_packed)
129148

130149
def load_sample_map_file(filenames):
131150
# Allow multiple documents in the yaml file
@@ -163,9 +182,11 @@ def load_sample_map_file(filenames):
163182
# load sample's data
164183
if sample.uri.startswith("file://"):
165184
samplepath = sample.uri[7:]
166-
with open(samplepath, "rb") as f:
167-
dbg(" %s: loaded from '%s'" % (sample.name, samplepath))
168-
sample.data = f.read()
185+
if samplepath.endswith(".wav"):
186+
sample.data = convert_to_adpcm(sample, samplepath)
187+
else:
188+
with open(samplepath, "rb") as f:
189+
sample.data = data
169190
elif sample.uri.startswith("data:;base64,"):
170191
dbg(" %s: encoded in '%s'" % (sample.name, mapfile))
171192
sample.data = base64.b64decode(sample.uri[13:])

0 commit comments

Comments
 (0)