From 76e297a4125c8efa6936dc7ff0dece356c82c562 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Sat, 20 Jun 2026 04:37:21 +0900 Subject: [PATCH] some bugfixes --- 2taud.sh | 4 ++++ mon2taud.py | 25 ++++++++++++++++++------- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/2taud.sh b/2taud.sh index 0e69903..d4395bc 100755 --- a/2taud.sh +++ b/2taud.sh @@ -10,3 +10,7 @@ for f in *.xm; python3 xm2taud.py $f assets/disk0/home/music/(basename $f .xm).t for f in *.XM; python3 xm2taud.py $f assets/disk0/home/music/(basename $f .XM).taud; end for f in *.mon; python3 mon2taud.py $f assets/disk0/home/music/(basename $f .mon).taud; end for f in *.MON; python3 mon2taud.py $f assets/disk0/home/music/(basename $f .MON).taud; end + +for f in *.mid; python3 midi2taud.py $f GeneralUser-GS.sf2 assets/disk0/home/music/(basename $f .mid).taud --force-synth-loop; end +for f in *.MID; python3 midi2taud.py $f GeneralUser-GS.sf2 assets/disk0/home/music/(basename $f .MID).taud --force-synth-loop; end +for f in *.midi; python3 midi2taud.py $f GeneralUser-GS.sf2 assets/disk0/home/music/(basename $f .midi).taud --force-synth-loop; end diff --git a/mon2taud.py b/mon2taud.py index 689e110..349f41e 100644 --- a/mon2taud.py +++ b/mon2taud.py @@ -86,17 +86,28 @@ def parse_mon(data: bytes): if data[:9] != MON_MAGIC_PREFIX: sys.exit(f"error: bad magic; expected '\\x08MONOTONE', got {data[:9]!r}") - song_len = data[0x5C] + # NOTE: data[0x5C] is totalPatterns (the count of stored pattern blocks), + # NOT the order-list length — see TMTSongFileHeader in MT_SONG.PAS. It must + # not be used to bound the order list. + total_patterns = data[0x5C] num_voices = data[0x5D] if num_voices < 1 or num_voices > 12: sys.exit(f"error: invalid voice count {num_voices} (expected 1..12)") order_raw = data[0x5F:0x15F] - # Effective order list: take first song_len entries and drop 0xFF skip-slots - # (matches mtreader.lua and MT_PLAY.PAS' "ignore 0xFF" semantics). - order_list = [b for b in order_raw[:song_len] if b != 0xFF] + # The order list is contiguous from index 0 and terminated by the first + # 0xFF ("FF = end of song", MT_SONG.PAS line 156 / MT_PLAY.PAS lines 677-683: + # the player advances the order pointer and stops the moment the next order + # byte is 0xFF). The old code sliced order_raw[:totalPatterns], which dropped + # the tail whenever the song was longer than its pattern count — e.g. a + # repeated outro pattern — the "last order ignored" bug. + order_list = [] + for b in order_raw: + if b == 0xFF: + break + order_list.append(b) if not order_list: - sys.exit("error: order list is empty after filtering 0xFF skip slots") + sys.exit("error: order list is empty (first order is the 0xFF EOS marker)") n_patterns = max(order_list) + 1 pattern_size = MON_PATTERN_ROWS * num_voices * MON_CELL_BYTES @@ -123,7 +134,7 @@ def parse_mon(data: bytes): patterns.append(grid) return { - 'song_len': song_len, + 'total_patterns': total_patterns, 'num_voices': num_voices, 'order_list': order_list, 'n_patterns': n_patterns, @@ -541,7 +552,7 @@ def main(): vprint(f"parsing '{args.input}' ({len(data)} bytes)…") mon = parse_mon(data) - vprint(f" songLen={mon['song_len']}, voices={mon['num_voices']}, " + vprint(f" totalPatterns={mon['total_patterns']}, voices={mon['num_voices']}, " f"patterns={mon['n_patterns']}, orders={len(mon['order_list'])}") taud = assemble_taud(mon, with_project_data=not args.no_project_data)