taut.js: fxNames update

This commit is contained in:
minjaesong
2026-05-04 02:14:19 +09:00
parent 9524bf36e0
commit 517d0ad9a7
5 changed files with 52 additions and 22 deletions

View File

@@ -116,17 +116,17 @@ P:"UNIMPLEMENTED", // IT: panning slide. Use PanEff instead
Q:"Retrigger ", Q:"Retrigger ",
R:"Tremolo ", R:"Tremolo ",
S:"Special ", S:"Special ",
S0:"UNIMPLEMENTED", // PT: Set audio filter. S0:"UNIMPLEMENTED", // PT: Set audio filter
S1:"Gliss. ctrl ", S1:"Gliss. ctrl ",
S2:"Sample tune ", S2:"Sample tune ",
S3:"Vibrato LFO ", S3:"Vibrato LFO ",
S4:"Tremolo LFO ", S4:"Tremolo LFO ",
S5:"Panbrello LFO", S5:"Panbrello LFO",
S6:"UNIMPLEMENTED", // IT: Fine pattern delay. S6:"Fine delay ",
S7:"UNIMPLEMENTED", // IT: misc. functions S7:"Note action ",
S8:"Channel pan ", // Taud: 8-bit channel panning. S8:"Channel pan ", // Taud: 8-bit channel panning
S9:"UNIMPLEMENTED", // IT: Sound control. S9:"UNIMPLEMENTED", // IT: Sound control
SA:"UNIMPLEMENTED", // SC3: Stereo control. IT: Sample offset high twobyte. SA:"UNIMPLEMENTED", // SC3: Stereo control. IT: Sample offset high twobyte (not applicable because Taud has 64k limit)
SB:"Pattern loop ", SB:"Pattern loop ",
SC:"Note cut ", SC:"Note cut ",
SD:"Note delay ", SD:"Note delay ",
@@ -135,10 +135,10 @@ SF:"Funk it ",
T:"Tempo ", T:"Tempo ",
U:"Fine vibrato ", U:"Fine vibrato ",
V:"Global volume", V:"Global volume",
W:"UNIMPLEMENTED", // IT: Global volume slide. W:"G.Vol Slide ",
X:"UNIMPLEMENTED", // IT: 8-bit channel panning. Use PanEff or S80xx instead X:"UNIMPLEMENTED", // IT: 8-bit channel panning. Use S80xx instead
Y:"Panbrello ", Y:"Panbrello ",
Z:"UNIMPLEMENTED", // IT: MIDI macro. Z:"UNIMPLEMENTED", // IT: MIDI macro
} }
const panFxNames = { const panFxNames = {
0:"Set to", 0:"Set to",

View File

@@ -52,7 +52,7 @@ from taud_common import (
EFF_K, EFF_L, EFF_M, EFF_N, EFF_O, EFF_P, EFF_Q, EFF_R, EFF_S, EFF_T, EFF_K, EFF_L, EFF_M, EFF_N, EFF_O, EFF_P, EFF_Q, EFF_R, EFF_S, EFF_T,
EFF_U, EFF_V, EFF_W, EFF_X, EFF_Y, EFF_Z, EFF_U, EFF_V, EFF_W, EFF_X, EFF_Y, EFF_Z,
J_SEMI_TABLE, J_SEMI_TABLE,
d_arg_to_col, resample_linear, encode_cue, deduplicate_patterns, d_arg_to_col, resample_linear, rescale_offset_effects, encode_cue, deduplicate_patterns,
normalise_sample, encode_song_entry, normalise_sample, encode_song_entry,
) )
@@ -1314,7 +1314,7 @@ def build_sample_inst_bin_it(samples_or_proxy: list,
vprint(f" instrument[{taud_idx}] '{s.name}' ptr:{ptr} c5spd:{s.c5_speed}") vprint(f" instrument[{taud_idx}] '{s.name}' ptr:{ptr} c5spd:{s.c5_speed}")
return bytes(sample_bin) + bytes(inst_bin), offsets return bytes(sample_bin) + bytes(inst_bin), offsets, ratio
# ── Pattern builder ─────────────────────────────────────────────────────────── # ── Pattern builder ───────────────────────────────────────────────────────────
@@ -1682,7 +1682,7 @@ def assemble_taud(h: ITHeader, samples: list, instruments: list,
'dct': inst.dct, 'dct': inst.dct,
'dca': inst.dca, 'dca': inst.dca,
} }
sampleinst_raw, _ = build_sample_inst_bin_it(proxy, instr_data_by_slot) sampleinst_raw, _, sample_ratio = build_sample_inst_bin_it(proxy, instr_data_by_slot)
else: else:
# Samples referenced directly; proxy is samples list (0-based, slot 0 unused) # Samples referenced directly; proxy is samples list (0-based, slot 0 unused)
proxy = [None] + list(samples) proxy = [None] + list(samples)
@@ -1691,7 +1691,7 @@ def assemble_taud(h: ITHeader, samples: list, instruments: list,
for i, s in enumerate(samples) for i, s in enumerate(samples)
if s is not None if s is not None
} }
sampleinst_raw, _ = build_sample_inst_bin_it(proxy) sampleinst_raw, _, sample_ratio = build_sample_inst_bin_it(proxy)
assert len(sampleinst_raw) == SAMPLEINST_SIZE assert len(sampleinst_raw) == SAMPLEINST_SIZE
@@ -1723,8 +1723,11 @@ def assemble_taud(h: ITHeader, samples: list, instruments: list,
pat_bin += build_pattern_it(cg, ch, default_pans[vi], inst_vols, pat_bin += build_pattern_it(cg, ch, default_pans[vi], inst_vols,
amiga_mode=not h.linear_slides) amiga_mode=not h.linear_slides)
# Rescale TOP_O sample-offset args if samples were globally downsampled.
pat_bin = rescale_offset_effects(bytes(pat_bin), sample_ratio)
orig_count = len(taud_cue_list) * C orig_count = len(taud_cue_list) * C
pat_bin, pat_remap, num_taud_pats = deduplicate_patterns(bytes(pat_bin), orig_count) pat_bin, pat_remap, num_taud_pats = deduplicate_patterns(pat_bin, orig_count)
vprint(f" patterns: {orig_count}{num_taud_pats} unique " vprint(f" patterns: {orig_count}{num_taud_pats} unique "
f"({orig_count - num_taud_pats} deduplicated)") f"({orig_count - num_taud_pats} deduplicated)")

View File

@@ -39,7 +39,7 @@ from taud_common import (
TOP_J, TOP_K, TOP_L, TOP_O, TOP_Q, TOP_R, TOP_S, TOP_T, TOP_U, TOP_V, TOP_Y, TOP_J, TOP_K, TOP_L, TOP_O, TOP_Q, TOP_R, TOP_S, TOP_T, TOP_U, TOP_V, TOP_Y,
SEL_SET, SEL_UP, SEL_DOWN, SEL_FINE, SEL_SET, SEL_UP, SEL_DOWN, SEL_FINE,
J_SEMI_TABLE, J_SEMI_TABLE,
d_arg_to_col, resample_linear, encode_cue, deduplicate_patterns, d_arg_to_col, resample_linear, rescale_offset_effects, encode_cue, deduplicate_patterns,
encode_song_entry, encode_song_entry,
) )
@@ -546,7 +546,7 @@ def build_sample_inst_bin(samples: list) -> tuple:
vprint(f" instrument[{taud_idx}] '{s.name}' ptr={ptr} c2spd={s.c2spd} " vprint(f" instrument[{taud_idx}] '{s.name}' ptr={ptr} c2spd={s.c2spd} "
f"vol={s.volume} loop=({ls},{le},{'on' if loop_mode else 'off'})") f"vol={s.volume} loop=({ls},{le},{'on' if loop_mode else 'off'})")
return bytes(sample_bin) + bytes(inst_bin), offsets return bytes(sample_bin) + bytes(inst_bin), offsets, ratio
# ── Pattern build ──────────────────────────────────────────────────────────── # ── Pattern build ────────────────────────────────────────────────────────────
@@ -704,7 +704,7 @@ def assemble_taud(mod: dict) -> bytes:
relocate_late_note_delays(patterns, order_list, n_channels, init_speed) relocate_late_note_delays(patterns, order_list, n_channels, init_speed)
vprint(" building sample/instrument bin…") vprint(" building sample/instrument bin…")
sampleinst_raw, _offsets = build_sample_inst_bin(samples) sampleinst_raw, _offsets, sample_ratio = build_sample_inst_bin(samples)
assert len(sampleinst_raw) == SAMPLEINST_SIZE assert len(sampleinst_raw) == SAMPLEINST_SIZE
compressed = gzip.compress(sampleinst_raw, compresslevel=9, mtime=0) compressed = gzip.compress(sampleinst_raw, compresslevel=9, mtime=0)
@@ -742,9 +742,12 @@ def assemble_taud(mod: dict) -> bytes:
pat_bin += build_pattern(grid, ch, default_pan, inst_vols) pat_bin += build_pattern(grid, ch, default_pan, inst_vols)
assert len(pat_bin) == n_patterns * n_channels * PATTERN_BYTES assert len(pat_bin) == n_patterns * n_channels * PATTERN_BYTES
# Rescale TOP_O sample-offset args if samples were globally downsampled.
pat_bin = rescale_offset_effects(bytes(pat_bin), sample_ratio)
vprint(" deduplicating patterns…") vprint(" deduplicating patterns…")
orig_count = n_patterns * n_channels orig_count = n_patterns * n_channels
pat_bin, pat_remap, num_taud_pats = deduplicate_patterns(bytes(pat_bin), orig_count) pat_bin, pat_remap, num_taud_pats = deduplicate_patterns(pat_bin, orig_count)
vprint(f" patterns: {orig_count}{num_taud_pats} unique " vprint(f" patterns: {orig_count}{num_taud_pats} unique "
f"({orig_count - num_taud_pats} deduplicated)") f"({orig_count - num_taud_pats} deduplicated)")

View File

@@ -43,7 +43,7 @@ from taud_common import (
EFF_K, EFF_L, EFF_M, EFF_N, EFF_O, EFF_P, EFF_Q, EFF_R, EFF_S, EFF_T, EFF_K, EFF_L, EFF_M, EFF_N, EFF_O, EFF_P, EFF_Q, EFF_R, EFF_S, EFF_T,
EFF_U, EFF_V, EFF_W, EFF_X, EFF_Y, EFF_Z, EFF_U, EFF_V, EFF_W, EFF_X, EFF_Y, EFF_Z,
J_SEMI_TABLE, J_SEMI_TABLE,
d_arg_to_col, resample_linear, encode_cue, deduplicate_patterns, d_arg_to_col, resample_linear, rescale_offset_effects, encode_cue, deduplicate_patterns,
normalise_sample, encode_song_entry, normalise_sample, encode_song_entry,
) )
@@ -528,7 +528,7 @@ def build_sample_inst_bin(instruments: list) -> tuple:
if inst.c2spd > 65535: if inst.c2spd > 65535:
vprint(f" warning: sampling rate of '{inst.name}' exceeds 65535 (got '{inst.c2spd}')") vprint(f" warning: sampling rate of '{inst.name}' exceeds 65535 (got '{inst.c2spd}')")
return bytes(sample_bin) + bytes(inst_bin), offsets return bytes(sample_bin) + bytes(inst_bin), offsets, ratio
def _default_channel_pan(ch_setting: int) -> int: def _default_channel_pan(ch_setting: int) -> int:
@@ -740,7 +740,7 @@ def assemble_taud(h: S3MHeader, instruments: list, patterns: list) -> bytes:
# Build sample+instrument bin # Build sample+instrument bin
vprint(" building sample/instrument bin…") vprint(" building sample/instrument bin…")
sampleinst_raw, _offsets = build_sample_inst_bin(instruments) sampleinst_raw, _offsets, sample_ratio = build_sample_inst_bin(instruments)
assert len(sampleinst_raw) == SAMPLEINST_SIZE assert len(sampleinst_raw) == SAMPLEINST_SIZE
# Compress # Compress
@@ -787,10 +787,13 @@ def assemble_taud(h: S3MHeader, instruments: list, patterns: list) -> bytes:
inst_vols, amiga_mode=not h.linear_slides) inst_vols, amiga_mode=not h.linear_slides)
assert len(pat_bin) == num_taud_pats * PATTERN_BYTES assert len(pat_bin) == num_taud_pats * PATTERN_BYTES
# Rescale TOP_O sample-offset args if samples were globally downsampled.
pat_bin = rescale_offset_effects(bytes(pat_bin), sample_ratio)
# Deduplicate identical patterns # Deduplicate identical patterns
vprint(" deduplicating patterns…") vprint(" deduplicating patterns…")
orig_count = num_taud_pats orig_count = num_taud_pats
pat_bin, pat_remap, num_taud_pats = deduplicate_patterns(bytes(pat_bin), orig_count) pat_bin, pat_remap, num_taud_pats = deduplicate_patterns(pat_bin, orig_count)
vprint(f" patterns: {orig_count}{num_taud_pats} unique ({orig_count - num_taud_pats} deduplicated)") vprint(f" patterns: {orig_count}{num_taud_pats} unique ({orig_count - num_taud_pats} deduplicated)")
# Cue sheet (using remapped pattern indices) # Cue sheet (using remapped pattern indices)

View File

@@ -131,6 +131,27 @@ def resample_linear(data: bytes, ratio: float) -> bytes:
return bytes(out) return bytes(out)
def rescale_offset_effects(pat_bin: bytes, ratio: float) -> bytes:
"""Scale TOP_O sample-offset args in raw pattern bytes by `ratio`.
Each row is 8 bytes; byte 5 is the effect opcode, bytes 6-7 are the
little-endian 16-bit arg (= byte offset into the sample). When the
sample bin overflows and every sample is downsampled globally, the
offset commands must shrink the same amount or O-jumps land past
the new end of sample.
"""
if ratio == 1.0 or not pat_bin:
return pat_bin
out = bytearray(pat_bin)
for i in range(0, len(out) - 7, 8):
if out[i + 5] == TOP_O:
arg = out[i + 6] | (out[i + 7] << 8)
arg = max(0, min(0xFFFF, int(arg * ratio + 0.5)))
out[i + 6] = arg & 0xFF
out[i + 7] = (arg >> 8) & 0xFF
return bytes(out)
def encode_cue(patterns12: list, instruction: int) -> bytearray: def encode_cue(patterns12: list, instruction: int) -> bytearray:
"""Encode a 32-byte cue entry for up to 20 voices with 12-bit pattern numbers.""" """Encode a 32-byte cue entry for up to 20 voices with 12-bit pattern numbers."""
pats = list(patterns12) + [0xFFF] * NUM_VOICES pats = list(patterns12) + [0xFFF] * NUM_VOICES