Skip to content

Commit caebcc3

Browse files
committed
nsstool: improved channel filtering
Ability to generate NSS for only a subset of available channels, without disturbing the playback if filtered channels contained FX that control the flow (e.g. 0Bxx, 0Dxx, FFxx...)
1 parent 8dbb943 commit caebcc3

File tree

1 file changed

+30
-38
lines changed

1 file changed

+30
-38
lines changed

tools/nsstool.py

Lines changed: 30 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,7 @@ class row_actions:
382382
location: object = None
383383
jmp_to_order: int = -1
384384
flow_fx: list = field(default_factory=list)
385+
ctx: object = None
385386
pre_fx: list = field(default_factory=list)
386387
ins: object = None
387388
vol: object = None
@@ -496,10 +497,10 @@ def convert_row(row, channel):
496497
out = row_actions()
497498

498499
if is_empty(row):
499-
return out.jmp_to_order, []
500+
return out
500501

501502
out.location = nss_loc(location_order, location_channel, location_row)
502-
out.flow_fx = factory.ctx()
503+
out.ctx = factory.ctx()
503504

504505
# note
505506
location_pos = (0,3)
@@ -565,7 +566,7 @@ def convert_row(row, channel):
565566
out.fx.append(pan_op)
566567
# groove
567568
elif fx == 0x09:
568-
out.fx.append(groove(fxval))
569+
out.flow_fx.append(groove(fxval))
569570
# jump to order
570571
elif fx == 0x0b:
571572
out.jmp_to_order = fxval
@@ -574,7 +575,7 @@ def convert_row(row, channel):
574575
out.jmp_to_order = 256
575576
# speed
576577
elif fx == 0x0f:
577-
out.fx.append(speed(fxval))
578+
out.flow_fx.append(speed(fxval))
578579
# FM OP1 level
579580
elif fx == 0x12:
580581
out.fx.append(op1_lvl(fxval))
@@ -658,17 +659,12 @@ def convert_row(row, channel):
658659
else:
659660
row_warn("unsupported FX")
660661

661-
all_ops = [o if isinstance(o, list) else [o] if o else [] for o in
662-
(out.location, out.flow_fx, out.pre_fx, out.ins, out.vol, out.fx, out.note, out.post_fx)]
663-
flattened_ops = sum(all_ops, [])
664-
665-
return out.jmp_to_order, flattened_ops
666-
662+
return out
667663

668664

669665

670666
cached_nss = {}
671-
def raw_nss(m, p, bs, channels, compact):
667+
def raw_nss(m, p, bs, channels, compact, capture):
672668
global location_order, location_row
673669

674670
# a cache of already parsed rows data
@@ -682,6 +678,12 @@ def row_to_nss(func, pat, pos):
682678
cached_nss[idx] = func(location_data, pat.channel)
683679
return cached_nss[idx]
684680

681+
def row_actions_to_nss(a):
682+
all_ops = [o if isinstance(o, list) else [o] if o else [] for o in
683+
(a.location, a.flow_fx, a.ctx, a.pre_fx, a.ins, a.vol, a.fx, a.note, a.post_fx)]
684+
flattened_ops = sum(all_ops, [])
685+
return flattened_ops
686+
685687
# unoptimized nss opcodes generated from the Furnace song
686688
nss = []
687689

@@ -749,36 +751,25 @@ def row_to_nss(func, pat, pos):
749751
opcodes = []
750752
location_order, location_row = order, index
751753

752-
# FM channels
753-
for channel in f_channels:
754-
j, f_opcodes = row_to_nss(convert_row, order_patterns[channel], index)
755-
if channel in selected_f:
756-
opcodes.extend(f_opcodes)
757-
jmp_to_order = max(jmp_to_order, j)
758-
# SSG channels
759-
for channel in s_channels:
760-
j, s_opcodes = row_to_nss(convert_row, order_patterns[channel], index)
761-
if channel in selected_s:
762-
opcodes.extend(s_opcodes)
763-
jmp_to_order = max(jmp_to_order, j)
764-
# ADPCM-A channels
765-
for channel in a_channels:
766-
j, a_opcodes = row_to_nss(convert_row, order_patterns[channel], index)
767-
if channel in selected_a:
768-
opcodes.extend(a_opcodes)
769-
jmp_to_order = max(jmp_to_order, j)
770-
# ADPCM-B channel
771-
for channel in b_channel:
772-
j, b_opcodes = row_to_nss(convert_row, order_patterns[channel], index)
773-
if channel in selected_b:
774-
opcodes.extend(b_opcodes)
775-
jmp_to_order = max(jmp_to_order, j)
754+
# Get all the NSS opcodes produced by all the tracks for the current row
755+
all_actions = [row_to_nss(convert_row, order_patterns[c], index) for c in range(14)]
756+
757+
# If needed, capture missing flow_fx from filtered channels and
758+
# make sure those fx are not lost by reinjecting them into
759+
# a channel that is part of the NSS output
760+
if capture:
761+
flows = [a.flow_fx for i, a in enumerate(all_actions) if a.flow_fx and i not in channels]
762+
all_actions[channels[0]].flow_fx.extend(sum(flows,[]))
776763

777764
# all channels are processed for this pos.
778-
# add all generated opcodes plus a time sync
779-
pattern_opcodes.extend(opcodes + [wait_n(1)])
765+
# add all generated opcodes for all channels in a flat,
766+
# plus a time sync to denote the end of row
767+
# pattern_opcodes.extend(opcodes + [wait_n(1)])
768+
channels_opcodes = [row_actions_to_nss(a) for i, a in enumerate(all_actions) if i in channels]
769+
pattern_opcodes.extend(sum(channels_opcodes, []) + [wait_n(1)])
780770

781771
# stop processing further rows if a JMP fx was used
772+
jmp_to_order = next((a.jmp_to_order for a in all_actions if a.jmp_to_order != -1), -1)
782773
if jmp_to_order != -1:
783774
break
784775

@@ -1557,9 +1548,10 @@ def remove_empty_streams(channels, streams):
15571548
tempo_injected=False
15581549
def generate_nss_stream(m, p, bs, ins, channels, stream_idx):
15591550
compact = stream_idx >= 0
1551+
capture_flow_fx = stream_idx == 0
15601552

15611553
dbg("Convert Furnace patterns to unoptimized NSS opcodes")
1562-
nss = raw_nss(m, p, bs, channels, compact)
1554+
nss = raw_nss(m, p, bs, channels, compact, capture_flow_fx)
15631555

15641556
# insert a tempo opcode on the first track that is used in the Furnace module
15651557
global tempo_injected

0 commit comments

Comments
 (0)