From 7f0ff3e6539e3f7d85e4b542283726004d8cb3aa Mon Sep 17 00:00:00 2001 From: minjaesong Date: Fri, 17 Apr 2026 16:42:17 +0900 Subject: [PATCH] Taud tracker: explicit nop and key-off behaviour --- terranmon.txt | 8 +++++- .../torvald/tsvm/peripheral/AudioAdapter.kt | 27 +++++++++++-------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/terranmon.txt b/terranmon.txt index 4cd0b5e..3967d80 100644 --- a/terranmon.txt +++ b/terranmon.txt @@ -2012,10 +2012,16 @@ Instrument bin: Registry for 256 instruments, formatted as: Play Data: play data are series of tracker-like instructions, visualised as: rr||NOTE|Ins|E.Vol|E.Pan|EE.ff| -63||FFFF|255|3+ 64|3+ 64|16 FF| (8 bytes per line, 512 bytes per pattern, 256 patterns on 128 kB block) +63||FFFF|255|3 63|3 63|FF FFFF| (8 bytes per line, 512 bytes per pattern, 256 patterns on 128 kB block) notes are tuned as 4096 Tone-Equal Temperament. Tuning is set per-sample using their Sampling rate value. +Special values: + +note 0xFFFF: no-op +note 0xFFFE: note cut +note 0x0000: key-off + Sound Adapter MMIO diff --git a/tsvm_core/src/net/torvald/tsvm/peripheral/AudioAdapter.kt b/tsvm_core/src/net/torvald/tsvm/peripheral/AudioAdapter.kt index 66555a7..0b88296 100644 --- a/tsvm_core/src/net/torvald/tsvm/peripheral/AudioAdapter.kt +++ b/tsvm_core/src/net/torvald/tsvm/peripheral/AudioAdapter.kt @@ -1156,17 +1156,22 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) { 0xEC -> voice.cutAtTick = row.effectArg and 0xFF } - if (row.note != 0xFFFF) { - val inst = instruments[row.instrment] - voice.instrumentId = row.instrment - voice.samplePos = inst.samplePlayStart.toDouble() - voice.forward = true - voice.active = true - voice.envIndex = 0 - voice.envTimeSec = 0.0 - voice.envVolume = inst.envelopes[0].volume / 255.0 - voice.noteVal = row.note - voice.playbackRate = computePlaybackRate(inst, row.note) + when (row.note) { + 0xFFFF -> {} // no-op: continue current note unchanged + 0x0000 -> voice.active = false // key-off (TODO: trigger envelope release phase) + 0xFFFE -> voice.active = false // note cut: immediate silence + else -> { + val inst = instruments[row.instrment] + voice.instrumentId = row.instrment + voice.samplePos = inst.samplePlayStart.toDouble() + voice.forward = true + voice.active = true + voice.envIndex = 0 + voice.envTimeSec = 0.0 + voice.envVolume = inst.envelopes[0].volume / 255.0 + voice.noteVal = row.note + voice.playbackRate = computePlaybackRate(inst, row.note) + } } } }