taud: more fixes

This commit is contained in:
minjaesong
2026-05-03 16:50:24 +09:00
parent c7e7ee650d
commit 789c78f1e7
6 changed files with 21 additions and 15 deletions

View File

@@ -856,8 +856,8 @@ function drawControlHint() {
['sep'], ['sep'],
['Sp','Edit'], ['Sp','Edit'],
['sep'], ['sep'],
['n','Solo'],
['m','Mute'], ['m','Mute'],
['s','Solo'],
['sep'], ['sep'],
['Tab','Panel'] ['Tab','Panel']
// ['sep'], // ['sep'],
@@ -1397,7 +1397,7 @@ function timelineInput(wo, event) {
drawVoiceHeaders(); drawSeparators(separatorStyle); drawAlwaysOnElems(); drawVoiceDetail() drawVoiceHeaders(); drawSeparators(separatorStyle); drawAlwaysOnElems(); drawVoiceDetail()
} }
else if (keyJustHit && !shiftDown && event.includes(keys.M)) { toggleMute(cursorVox) } else if (keyJustHit && !shiftDown && event.includes(keys.M)) { toggleMute(cursorVox) }
else if (keyJustHit && !shiftDown && event.includes(keys.S)) { toggleSolo(cursorVox) } else if (keyJustHit && !shiftDown && event.includes(keys.N)) { toggleSolo(cursorVox) }
return return
} }
@@ -1434,7 +1434,7 @@ function timelineInput(wo, event) {
} }
if (keyJustHit && !shiftDown && event.includes(keys.M)) { toggleMute(cursorVox); return } if (keyJustHit && !shiftDown && event.includes(keys.M)) { toggleMute(cursorVox); return }
if (keyJustHit && !shiftDown && event.includes(keys.S)) { toggleSolo(cursorVox); return } if (keyJustHit && !shiftDown && event.includes(keys.N)) { toggleSolo(cursorVox); return }
if (keysym === "<UP>") { cursorRow -= moveDelta; rowMove = true } if (keysym === "<UP>") { cursorRow -= moveDelta; rowMove = true }
else if (keysym === "<DOWN>") { cursorRow += moveDelta; rowMove = true } else if (keysym === "<DOWN>") { cursorRow += moveDelta; rowMove = true }

View File

@@ -1254,8 +1254,9 @@ def build_sample_inst_bin_it(samples_or_proxy: list,
if vol_env: if vol_env:
_write_env(inst_bin, base + 21, vol_env) _write_env(inst_bin, base + 21, vol_env)
else: else:
# Single-point: hold at sample default volume. # Single-point envelope held at full-scale; the per-sample level is
inst_bin[base + 21] = min(getattr(s, 'vol', 63), 63) # carried by IGV (byte 171), so the envelope must be a unit multiplier.
inst_bin[base + 21] = 63
inst_bin[base + 22] = 0 inst_bin[base + 22] = 0
# Force engine to use this single point. # Force engine to use this single point.
struct.pack_into('<H', inst_bin, base + 15, USE_ENV_BIT) struct.pack_into('<H', inst_bin, base + 15, USE_ENV_BIT)

View File

@@ -506,7 +506,9 @@ def build_sample_inst_bin(samples: list) -> tuple:
le = min(s.loop_end, 65535) le = min(s.loop_end, 65535)
loop_mode = 1 if (s.flags & 1) else 0 loop_mode = 1 if (s.flags & 1) else 0
flags_byte = loop_mode & 0x3 flags_byte = loop_mode & 0x3
env_vol = min(s.volume, 63) # Envelope first point is full-scale; per-sample level is carried by
# IGV (byte 171) so the envelope must contribute a unit multiplier.
env_vol = 63
vol_env_flags = 0x0020 # use-envelope bit vol_env_flags = 0x0020 # use-envelope bit
base = taud_idx * 192 base = taud_idx * 192
@@ -763,7 +765,7 @@ def assemble_taud(mod: dict) -> bytes:
pat_bin_comp_size=len(pat_comp), pat_bin_comp_size=len(pat_comp),
cue_sheet_comp_size=len(cue_comp), cue_sheet_comp_size=len(cue_comp),
global_vol=0xFF, global_vol=0xFF,
mixing_vol=0x7F, mixing_vol=180,
) )
assert len(song_table) == TAUD_SONG_ENTRY assert len(song_table) == TAUD_SONG_ENTRY

View File

@@ -495,8 +495,9 @@ def build_sample_inst_bin(instruments: list) -> tuple:
loop_mode = 1 if (inst.flags & 1) else 0 loop_mode = 1 if (inst.flags & 1) else 0
flags_byte = loop_mode & 0x3 # 0b 0000 00pp flags_byte = loop_mode & 0x3 # 0b 0000 00pp
# Volume envelope: hold at instrument volume (clamped to 0x3F). # Volume envelope first point is full-scale; per-sample level is carried
env_vol = min(inst.volume, 63) # by IGV (byte 171) so the envelope contributes a unit multiplier.
env_vol = 63
# Vol env-flags: enable use-envelope bit (b=1) so engine reads the single point. # Vol env-flags: enable use-envelope bit (b=1) so engine reads the single point.
vol_env_flags = 0x0020 # b=bit 5 vol_env_flags = 0x0020 # b=bit 5
@@ -820,7 +821,7 @@ def assemble_taud(h: S3MHeader, instruments: list, patterns: list) -> bytes:
pat_bin_comp_size=len(pat_comp), pat_bin_comp_size=len(pat_comp),
cue_sheet_comp_size=len(cue_comp), cue_sheet_comp_size=len(cue_comp),
global_vol=0xFF, global_vol=0xFF,
mixing_vol=0x7F, mixing_vol=180,
) )
assert len(song_table) == TAUD_SONG_ENTRY assert len(song_table) == TAUD_SONG_ENTRY

View File

@@ -2111,7 +2111,7 @@ TODO:
[x] Fix 4THSYM.it filters [x] Fix 4THSYM.it filters
[x] 4THSYM.it: pitchbend is wrong, some notes keep playing (loudly!) even if new notes are emitted [x] 4THSYM.it: pitchbend is wrong, some notes keep playing (loudly!) even if new notes are emitted
[x] `*2taud.py`: some notes are emitted with wrong volume-set command. Tested with GSLINGER.mod: on order 0x15 channel 1, mod2taud.py emits volume 8 -- also many of the effects are dropped. Suggested solution: currently all converters write default volume to the voleff when original modules (.mod/.s3m/.it) specify nothing; we should also write nothing and let the engine resolve the value just like other trackers do (also we now have "Instrument Global Volume" on instrument definition unlike the other time). This bug may affecting other formats, not just mod2taud.py, as well [x] `*2taud.py`: some notes are emitted with wrong volume-set command. Tested with GSLINGER.mod: on order 0x15 channel 1, mod2taud.py emits volume 8 -- also many of the effects are dropped. Suggested solution: currently all converters write default volume to the voleff when original modules (.mod/.s3m/.it) specify nothing; we should also write nothing and let the engine resolve the value just like other trackers do (also we now have "Instrument Global Volume" on instrument definition unlike the other time). This bug may affecting other formats, not just mod2taud.py, as well
[ ] nearly_there_.mod: `C#5 SD300 / ... / C-5 SD200 / A#4 / G#4 (at tickspeed 4)`: every `C-5 SD200` (there are four occurances) gets skipped [x] nearly_there_.mod: `C#5 SD300 / ... / C-5 SD200 / A#4 / G#4 (at tickspeed 4)`: every `C-5 SD200` (there are four occurances) gets skipped
[ ] scale Oxxxx when samples get resampled [ ] scale Oxxxx when samples get resampled
[ ] implement bitcrusher and overdrive (eff sym '8' and '9') [ ] implement bitcrusher and overdrive (eff sym '8' and '9')

View File

@@ -1469,8 +1469,8 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
if (i0 in ls until inst.sampleLoopEnd && inst.funkBit(i0 - ls)) b0 = b0 xor 0x80 if (i0 in ls until inst.sampleLoopEnd && inst.funkBit(i0 - ls)) b0 = b0 xor 0x80
if (i1 in ls until inst.sampleLoopEnd && inst.funkBit(i1 - ls)) b1 = b1 xor 0x80 if (i1 in ls until inst.sampleLoopEnd && inst.funkBit(i1 - ls)) b1 = b1 xor 0x80
} }
val s0 = (b0 - 128) / 128.0 val s0 = (b0 - 127.5) / 127.5
val s1 = (b1 - 128) / 128.0 val s1 = (b1 - 127.5) / 127.5
val sample = s0 + (s1 - s0) * frac val sample = s0 + (s1 - s0) * frac
if (voice.forward) { if (voice.forward) {
@@ -2371,6 +2371,8 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
} }
} }
private fun Double.sqrt() = Math.sqrt(this)
internal fun generateTrackerAudio(playhead: Playhead): ByteArray? { internal fun generateTrackerAudio(playhead: Playhead): ByteArray? {
val ts = playhead.trackerState ?: return null val ts = playhead.trackerState ?: return null
@@ -2408,7 +2410,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
val swingScale = 1.0 + voice.randomVolBias / 255.0 val swingScale = 1.0 + voice.randomVolBias / 255.0
// Volume envelope is bypassed (treated as unity) when S $77 has disabled it. // Volume envelope is bypassed (treated as unity) when S $77 has disabled it.
val effEnvVol = if (voice.volEnvOn) voice.envVolume else 1.0 val effEnvVol = if (voice.volEnvOn) voice.envVolume else 1.0
val vol = effEnvVol * voice.fadeoutVolume * voice.rowVolume / 63.0 * val vol = effEnvVol.sqrt() * voice.fadeoutVolume * (voice.rowVolume / 63.0).sqrt() *
swingScale * gvol * mvol * instGv * playhead.masterVolume / 255.0 swingScale * gvol * mvol * instGv * playhead.masterVolume / 255.0
val pan = if (voice.hasPanEnv && voice.panEnvOn) { val pan = if (voice.hasPanEnv && voice.panEnvOn) {
val envPanRaw = (voice.envPan * 255.0).roundToInt().coerceIn(0, 255) val envPanRaw = (voice.envPan * 255.0).roundToInt().coerceIn(0, 255)
@@ -2438,7 +2440,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
val instGv = bgInst.instGlobalVolume / 255.0 val instGv = bgInst.instGlobalVolume / 255.0
val swingScale = 1.0 + bg.randomVolBias / 255.0 val swingScale = 1.0 + bg.randomVolBias / 255.0
val effEnvVol = if (bg.volEnvOn) bg.envVolume else 1.0 val effEnvVol = if (bg.volEnvOn) bg.envVolume else 1.0
val vol = effEnvVol * bg.fadeoutVolume * bg.rowVolume / 63.0 * val vol = effEnvVol.sqrt() * bg.fadeoutVolume * (bg.rowVolume / 63.0).sqrt() *
swingScale * gvol * mvol * instGv * playhead.masterVolume / 255.0 swingScale * gvol * mvol * instGv * playhead.masterVolume / 255.0
val pan = if (bg.hasPanEnv && bg.panEnvOn) { val pan = if (bg.hasPanEnv && bg.panEnvOn) {
val envPanRaw = (bg.envPan * 255.0).roundToInt().coerceIn(0, 255) val envPanRaw = (bg.envPan * 255.0).roundToInt().coerceIn(0, 255)