@@ -207,12 +207,14 @@ class adpcm_a_sample:
207
207
class adpcm_b_sample :
208
208
name : str = ""
209
209
data : bytearray = field (default = b"" , repr = False )
210
+ frequency : int = 0
210
211
211
212
212
213
@dataclass
213
214
class pcm_sample :
214
215
name : str = ""
215
216
data : bytearray = field (default = b"" , repr = False )
217
+ frequency : int = 0
216
218
loop : bool = False
217
219
218
220
@@ -245,6 +247,7 @@ class adpcm_a_instrument:
245
247
class adpcm_b_instrument :
246
248
name : str = ""
247
249
sample : adpcm_b_sample = None
250
+ tuned : int = 0
248
251
loop : bool = False
249
252
250
253
@@ -276,54 +279,70 @@ def read_fm_instrument(bs):
276
279
return ifm
277
280
278
281
279
- @dataclass
280
- class ssg_prop :
281
- name : str = ""
282
- offset : int = 0
283
-
284
-
285
- def read_ssg_macro (length , bs ):
286
- # TODO -1 are unsupported in nullsound
287
- code_map = {0 : ssg_prop ("volume" , 3 ), # volume
288
- 3 : ssg_prop ("waveform" , 4 ), # noise_tune
289
- 6 : ssg_prop ("env" , 0 ), # envelope shape
290
- 7 : ssg_prop ("env_vol_num" , 1 ), # volume envelope numerator
291
- 8 : ssg_prop ("env_vol_den" , 2 ) # volume envelope denominator
292
- }
293
-
294
- blocks = {}
295
- autoenv = False
296
- init = bs .pos
282
+ def read_macro_data (length , bs ):
283
+ macros = {}
297
284
max_pos = bs .pos + length
298
285
header_len = bs .u2 ()
299
- # pass: read all macro blocks
300
286
while bs .pos < max_pos :
301
287
header_start = bs .pos
288
+ # macro code (vol, arp, pitch...)
302
289
code = bs .u1 ()
303
290
if code == 255 :
304
291
break
305
292
length = bs .u1 ()
306
293
# TODO unsupported. no loop
307
294
loop = bs .u1 ()
308
- # TODO unsupported. last macro stays
295
+ # TODO unsupported. last macro value stays
309
296
release = bs .u1 ()
310
297
# TODO meaning?
311
298
mode = bs .u1 ()
312
299
msize , mtype = ubits (bs .u1 (), [7 , 6 ], [2 , 1 ])
313
300
assert msize == 0 , "macro value should be of type '8-bit unsigned'"
314
- assert mtype == 0 , "macro should be of type 'sequence'"
301
+ assert mtype == 0 , "macro should be of type 'sequence'. ADSR or LFO unsupported "
315
302
# TODO unsupported. no delay
316
303
delay = bs .u1 ()
317
304
# TODO unsupported. same speed as the module tick
318
305
speed = bs .u1 ()
319
306
header_end = bs .pos
320
307
assert header_end - header_start == header_len
321
308
data = [bs .u1 () for i in range (length )]
309
+ macros [code ]= data
310
+ assert bs .pos == max_pos
311
+ return macros
312
+
313
+
314
+ def configure_b_macros (ins , macros ):
315
+ # temporary workaround for instrument manually tuned with arpeggio macro
316
+ if 1 in macros and len (macros [1 ])== 1 :
317
+ ins .tuned = macros [1 ][0 ]
318
+ else :
319
+ error ("unsupported use of macros in ADPCM-B instrument %s" % ins .name )
320
+
321
+
322
+ @dataclass
323
+ class ssg_prop :
324
+ name : str = ""
325
+ offset : int = 0
326
+
327
+
328
+ def read_ssg_macro (length , bs ):
329
+ # TODO -1 are unsupported in nullsound
330
+ code_map = {0 : ssg_prop ("volume" , 3 ), # volume
331
+ 3 : ssg_prop ("waveform" , 4 ), # noise_tune
332
+ 6 : ssg_prop ("env" , 0 ), # envelope shape
333
+ 7 : ssg_prop ("env_vol_num" , 1 ), # volume envelope numerator
334
+ 8 : ssg_prop ("env_vol_den" , 2 ) # volume envelope denominator
335
+ }
336
+
337
+ autoenv = False
338
+ blocks = {}
339
+ macros = read_macro_data (length , bs )
340
+ for code in macros :
322
341
if code not in code_map :
323
342
warning ("macro element not supported yet: %02x" % code )
324
343
else :
325
- blocks [code_map [code ].offset ]= data
326
- assert bs . pos == max_pos
344
+ blocks [code_map [code ].offset ] = macros [ code ]
345
+
327
346
# pass: create a "empty" waveform property if it's not there
328
347
# we need it to tell nullsound to not update the envelope SSG register
329
348
if 0 not in blocks :
@@ -416,7 +435,11 @@ def asm_ident(x):
416
435
sample = bs .u2 ()
417
436
bs .u2 () # unused flags and waveform
418
437
elif feat == b"MA" and itype == 6 :
438
+ # SSG macro is essentially the full SSG instrument
419
439
mac = read_ssg_macro (length , bs )
440
+ elif feat == b"MA" and itype == 38 :
441
+ # other macro types are currently not supported
442
+ mac = read_macro_data (length , bs )
420
443
elif feat == b"NE" :
421
444
# NES DPCM tag is present when the instrument
422
445
# uses a PCM sample instead of ADPCM. Skip it
@@ -447,6 +470,8 @@ def asm_ident(x):
447
470
return mac
448
471
else :
449
472
ins .name = asm_ident ("instr_%02x_%s" % (nth , name ))
473
+ if itype == 38 and mac :
474
+ configure_b_macros (ins , mac )
450
475
return ins
451
476
452
477
@@ -482,7 +507,6 @@ def read_sample(bs, sample_idx):
482
507
data_padding = 0 # adpcmtool codecs automatically adds padding
483
508
else :
484
509
error ("sample '%s' is of unsupported type: %d" % (str (name ), stype ))
485
- # assert c4_freq == {5: 18500, 6: 44100}[stype]
486
510
bs .u1 () # unused loop direction
487
511
bs .u2 () # unused flags
488
512
loop_start , loop_end = bs .s4 (), bs .s4 ()
@@ -494,6 +518,7 @@ def read_sample(bs, sample_idx):
494
518
6 : adpcm_b_sample ,
495
519
16 : pcm_sample }[stype ](insname , data )
496
520
ins .loop = loop_start != - 1 and loop_end != - 1
521
+ ins .frequency = c4_freq
497
522
return ins
498
523
499
524
0 commit comments