Compare commits

...

2 Commits

Author SHA1 Message Date
minjaesong
a1b62f3155 taud-related changes (docs, converter supports Y eff) 2026-04-24 18:35:24 +09:00
minjaesong
4802e10dfc better tracker font, IT eff for Taud 2026-04-24 12:18:34 +09:00
10 changed files with 45 additions and 20 deletions

2
.gitignore vendored
View File

@@ -68,3 +68,5 @@ assets/disk0/*.mov
assets/diskMediabin/* assets/diskMediabin/*
video_encoder/* video_encoder/*
assets/disk0/tvdos/bin/tautfont.png

View File

@@ -487,7 +487,7 @@ A tempo slide's memory slot is separate from the set-tempo path and is private t
**Plain.** Sets the global mix bus volume (0..$FF). $00 is silence; $FF is full. The default is $80. **Plain.** Sets the global mix bus volume (0..$FF). $00 is silence; $FF is full. The default is $80.
**Compatibility.** ST3's global volume is 0..$40; convert with `taud_v = st3_v × 4`, clamped at $FF. On export, `st3_v = taud_v >> 2`, clamped at $40. **Compatibility.** ST3's global volume is 0..$40; convert with `taud_v = st3_v × 4`, clamped at $FF. On export, `st3_v = taud_v >> 2`, clamped at $40. IT's global volume is 0..$80; convert with `taud_v = it_v × 2`, clamped at $FF.
**Implementation.** Write the high byte to `global_volume` on the row the command appears. The low byte is reserved. ST3's `kST3NoMutedChannels` rule applies: V on a muted channel is ignored by ST3; for strict-compatible playback Taud follows suit, but new Taud compositions should avoid muting channels that carry global effects. **Implementation.** Write the high byte to `global_volume` on the row the command appears. The low byte is reserved. ST3's `kST3NoMutedChannels` rule applies: V on a muted channel is ignored by ST3; for strict-compatible playback Taud follows suit, but new Taud compositions should avoid muting channels that carry global effects.
@@ -517,6 +517,16 @@ Peak at maximum settings: $7F × $FF >> 9 = $3F — the full panning range. Retr
--- ---
## X $xx00 — Set Panning
**Plain.** **Unimplemented**. On IT, sets the panning position of the current channel, $00 being full-left and $FF being full-right.
**Compatibility.** Convert directly into panning effect `0.$xx`, rounded down to nearest 6-bit value.
**Implementation.** Not applicable.
---
# The S subcommand family # The S subcommand family
S is a multiplexing opcode; the **high nibble of the high byte** selects the sub-effect, and the remainder is the sub-argument. S is a multiplexing opcode; the **high nibble of the high byte** selects the sub-effect, and the remainder is the sub-argument.

View File

@@ -70,7 +70,7 @@ middot:MIDDOT
} }
const fxNames = { const fxNames = {
A:"Set tick speed", A:"Tick speed",
B:"Jump to order", B:"Jump to order",
C:"Break pattern", C:"Break pattern",
D:"Volume slide", D:"Volume slide",
@@ -89,8 +89,8 @@ R:"Tremolo",
T:"Tempo", T:"Tempo",
V:"Global volume", V:"Global volume",
S:"Special", S:"Special",
S1:"Glissando ctrl", S1:"Gliss. ctrl",
S2:"Sample finetune", S2:"Sample tune",
S3:"Vibrato LFO", S3:"Vibrato LFO",
S4:"Tremolo LFO", S4:"Tremolo LFO",
S8:"Channel pan", S8:"Channel pan",
@@ -629,18 +629,20 @@ function drawVoiceDetail() {
const effop = ptnDat[5] const effop = ptnDat[5]
const effarg = ptnDat[6] | (ptnDat[7] << 8) const effarg = ptnDat[6] | (ptnDat[7] << 8)
// TODO draw cumulative internal status in the very time play cursor is on
con.move(6,1) con.move(6,1)
print(`Pitch $${note.hex04()}\tInst $${inst.hex02()}\tVolEff ${voleffop}.$${voleffarg.hex02()}\t`+ print(`Pitch $${note.hex04()}\tInst $${inst.hex02()}\tVolEff ${voleffop}.$${voleffarg.hex02()}\t`+
`PanEff ${paneffop}.$${paneffarg.hex02()}`) `PanEff ${paneffop}.$${paneffarg.hex02()}`)
con.move(7,1) con.move(7,1)
let fx = effop.toString(36).toUpperCase() let fx = effop.toString(36).toUpperCase()
if (fx == '0') { if (fx == '0') {
print(`Fx`+' '.repeat(32)) print(`\u00F8`+' '.repeat(32))
} }
else { else {
if (fx == 'S') fx += (effarg >>> 12).hex1() if (fx == 'S') fx += (effarg >>> 12).hex1()
let fxName = fxNames[fx] let fxName = fxNames[fx]
print(`Fx ${fxName} $${effarg.hex04()} `) print(`\u00F8 ${fxName}\t$${effarg.hex04()} `)
} }
} }

Binary file not shown.

View File

@@ -219,8 +219,8 @@ function captureTrackerDataToFile(outFile) {
numPats & 0xFF, (numPats >>> 8) & 0xFF, // numPatterns Uint16 LE numPats & 0xFF, (numPats >>> 8) & 0xFF, // numPatterns Uint16 LE
bpmStored, // BPM with 24 bias bpmStored, // BPM with 24 bias
tickRate, // initial tick-rate tickRate, // initial tick-rate
0x00,0x4C, // basenote (0x4C00 -- A3) 0x00,0x90, // basenote (0x9000 -- C8)
0x00,0x00,0xDC,0x43, // basefreq (440 Hz) 0x00,0xAC,0x02,0x46, // basefreq (8363 Hz)
0, // padding 0, // padding
] ]

View File

@@ -17,8 +17,9 @@ Effect support:
table" and "ScreamTracker 3 conversion notes". ST3 shared-memory recalls table" and "ScreamTracker 3 conversion notes". ST3 shared-memory recalls
(D/E/F/I/J/K/L/Q/R/S with $00 arg) are eagerly resolved per channel. (D/E/F/I/J/K/L/Q/R/S with $00 arg) are eagerly resolved per channel.
Cxx is BCD-decoded. K/L are split into H $0000 / G $0000 + volume-column Cxx is BCD-decoded. K/L are split into H $0000 / G $0000 + volume-column
slide. M/N/X/P fold into volume / pan columns. W (global vol slide) and slide. M/N/X/P fold into volume / pan columns. W (global vol slide) is
Y (panbrello) are dropped with a -v warning. dropped with a -v warning. X converts to pan column. Y (panbrello) converts
to Taud Y. S5 selects the panbrello LFO waveform.
""" """
import argparse import argparse
@@ -116,6 +117,7 @@ TOP_S = 0x1C # sub-effects
TOP_T = 0x1D # tempo set/slide TOP_T = 0x1D # tempo set/slide
TOP_U = 0x1E # fine vibrato TOP_U = 0x1E # fine vibrato
TOP_V = 0x1F # global volume TOP_V = 0x1F # global volume
TOP_Y = 0x22 # panbrello
# Volume / pan column selectors (2-bit field, packed into top of vol/pan byte). # Volume / pan column selectors (2-bit field, packed into top of vol/pan byte).
SEL_SET = 0 # 6-bit value: set vol / pan SEL_SET = 0 # 6-bit value: set vol / pan
@@ -442,10 +444,13 @@ def encode_effect(cmd: int, arg: int, ch: int = 0, row: int = 0) -> tuple:
val = arg & 0xF val = arg & 0xF
if sub in (0x1, 0x2, 0x3, 0x4, 0xB, 0xC, 0xD, 0xE, 0xF): if sub in (0x1, 0x2, 0x3, 0x4, 0xB, 0xC, 0xD, 0xE, 0xF):
return (TOP_S, (sub << 12) | (val << 8), None, None) return (TOP_S, (sub << 12) | (val << 8), None, None)
if sub == 0x5:
# Panbrello LFO waveform — maps directly to Taud S$5x00.
return (TOP_S, 0x5000 | (val << 8), None, None)
if sub == 0x8: if sub == 0x8:
# Coarse pan: nibble-repeat into Taud's S $80xx full-8-bit pan. # Coarse pan: nibble-repeat into Taud's S $80xx full-8-bit pan.
return (TOP_S, 0x8000 | (val * 0x11), None, None) return (TOP_S, 0x8000 | (val * 0x11), None, None)
# S0/S5/S6/S7/S9/SA: filter, NNA, sound-control, stereo — drop silently. # S0/S6/S7/S9/SA: filter, NNA, sound-control, stereo — drop silently.
return (TOP_NONE, 0, None, None) return (TOP_NONE, 0, None, None)
if cmd == EFF_T: if cmd == EFF_T:
@@ -465,8 +470,9 @@ def encode_effect(cmd: int, arg: int, ch: int = 0, row: int = 0) -> tuple:
return (TOP_NONE, 0, None, (SEL_SET, min(arg >> 2, 0x3F))) return (TOP_NONE, 0, None, (SEL_SET, min(arg >> 2, 0x3F)))
if cmd == EFF_Y: if cmd == EFF_Y:
vprint(f" dropped Y{arg:02X} (panbrello) at ch{ch} row{row}") hi = (arg >> 4) & 0xF
return (TOP_NONE, 0, None, None) lo = arg & 0xF
return (TOP_Y, ((hi * 0x11) << 8) | (lo * 0x11), None, None)
if cmd == EFF_Z: if cmd == EFF_Z:
return (TOP_NONE, 0, None, None) return (TOP_NONE, 0, None, None)
@@ -904,7 +910,7 @@ def assemble_taud(h: S3MHeader, instruments: list, patterns: list) -> bytes:
num_taud_pats_hi, num_taud_pats_hi,
bpm_stored, bpm_stored,
speed, speed,
) + b'\x00\x4C' + b'\x00\x00\xDC\x43' + b'\x00' ) + b'\x00\x90' + b'\x00\xAC\xD02\x46' + b'\x00'
assert len(song_table) == TAUD_SONG_ENTRY assert len(song_table) == TAUD_SONG_ENTRY
# Cue sheet (using remapped pattern indices) # Cue sheet (using remapped pattern indices)

View File

@@ -2211,16 +2211,21 @@ Endianness: Little
Uint16 Number of patterns (0 is invalid. pattern bin length = numPats * 8 bytes) Uint16 Number of patterns (0 is invalid. pattern bin length = numPats * 8 bytes)
Uint8 Initial BPM (bias of -24. 0x00=24, 0xFF=279) Uint8 Initial BPM (bias of -24. 0x00=24, 0xFF=279)
Uint8 Initial Tickrate (0 is invalid) Uint8 Initial Tickrate (0 is invalid)
Uint16 Current Tuning base note (1..65533). A3 (the default value) is 0x4C00. If zero, assume the default value Uint16 Current Tuning base note (1..65533). A3 (western default) is 0x4C00. C8 (tracker default) is 0x9000. If zero, assume the tracker default value
Float32 Frequency at the base note. Default (A440) is 440.0. If zero, assume the default value Float32 Frequency at the base note. Tracker default is 8363.0. If zero, assume the tracker default
Byte[1] Reserved for future versions Byte[1] Reserved for future versions
Taud device can queue up to 2 "playdata" in its buffer, which can be interpreted as a song. Taud device can queue up to 2 "playdata" in its buffer, which can be interpreted as a song.
* Known standard tunings * Known standard tunings
A440. ISO standard. Tracker default A440. ISO standard
A435. Former French standard (year 1859)
A452. Old Philharmonic pitch (19th century Britain)
C256. Power of two C256. Power of two
C311. East Asian tuning (ROK National Gugak Center standard) C262. Modern Chinese a-ak tuning convention
C311. Korean hyang-ak tuning standard (ROK National Gugak Center)
For your reference, tracker default tuning at A3 is 439.526 Hz (8363*2^(3/4) / 32)
## Pattern Bin and Cue Sheet ## Pattern Bin and Cue Sheet
Raw Pattern Bin/Cue Sheet images Raw Pattern Bin/Cue Sheet images

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB