fix: S Bx00 not working as indtended

This commit is contained in:
minjaesong
2026-05-02 13:51:43 +09:00
parent 2b91251d6e
commit 5dc87a80be
2 changed files with 27 additions and 3 deletions

View File

@@ -2092,8 +2092,8 @@ TODO:
[x] (same context as above) implement S7x command [x] (same context as above) implement S7x command
[x] on playback, panning changes randomly on Taud made by s3m2taud.py and mod2taud.py, but not by it2taud.py (maybe something's off with the instrument exports?) [x] on playback, panning changes randomly on Taud made by s3m2taud.py and mod2taud.py, but not by it2taud.py (maybe something's off with the instrument exports?)
[x] NNA not disabled for S3M and MOD [x] NNA not disabled for S3M and MOD
[x] `S B000` and `S B100` not working as intended -- on first playback it jumps to the next cue same row, on subsequent playbacks the commands are completely ignored
[ ] implement S6x command [ ] implement S6x command
[ ] `S B000` and `S B100` not working as intended -- on first playback it jumps to the next cue same row, on subsequent playbacks the commands are completely ignored
[ ] implement Wxx command (global volume slide) [ ] implement Wxx command (global volume slide)
[ ] implement sample loop sustain [ ] implement sample loop sustain
[ ] cue and pattern compression of the Taud format (taud_common.py, taud.mjs) [ ] cue and pattern compression of the Taud format (taud_common.py, taud.mjs)

View File

@@ -1938,9 +1938,13 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
if (voice.loopCount == 0) { if (voice.loopCount == 0) {
voice.loopCount = x voice.loopCount = x
ts.pendingRowJump = voice.loopStartRow ts.pendingRowJump = voice.loopStartRow
ts.pendingRowJumpLocal = true
} else if (!ts.patternDelayActive) { } else if (!ts.patternDelayActive) {
voice.loopCount-- voice.loopCount--
if (voice.loopCount > 0) ts.pendingRowJump = voice.loopStartRow if (voice.loopCount > 0) {
ts.pendingRowJump = voice.loopStartRow
ts.pendingRowJumpLocal = true
}
} }
} }
} }
@@ -2211,6 +2215,14 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
playhead.position = ts.cuePos playhead.position = ts.cuePos
} }
// Per TAUD_NOTE_EFFECTS.md §S$Bx00: on pattern change reset loop_start_row and loop_count.
private fun resetPatternLoopState(ts: TrackerState) {
for (voice in ts.voices) {
voice.loopStartRow = 0
voice.loopCount = 0
}
}
internal fun generateTrackerAudio(playhead: Playhead): ByteArray? { internal fun generateTrackerAudio(playhead: Playhead): ByteArray? {
val ts = playhead.trackerState ?: return null val ts = playhead.trackerState ?: return null
@@ -2325,25 +2337,34 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
val pendingB = ts.pendingOrderJump val pendingB = ts.pendingOrderJump
val pendingC = ts.pendingRowJump val pendingC = ts.pendingRowJump
val pendingLocal = ts.pendingRowJumpLocal
ts.pendingOrderJump = -1 ts.pendingOrderJump = -1
ts.pendingRowJump = -1 ts.pendingRowJump = -1
ts.pendingRowJumpLocal = false
when { when {
pendingB >= 0 -> { pendingB >= 0 -> {
ts.cuePos = pendingB.coerceAtMost(1023) ts.cuePos = pendingB.coerceAtMost(1023)
ts.rowIndex = if (pendingC >= 0) pendingC else 0 ts.rowIndex = if (pendingC >= 0) pendingC else 0
playhead.position = ts.cuePos playhead.position = ts.cuePos
resetPatternLoopState(ts)
}
pendingC >= 0 && pendingLocal -> {
// S$Bx pattern loop — stay in the current cue, just rewind the row.
ts.rowIndex = pendingC.coerceIn(0, 63)
} }
pendingC >= 0 -> { pendingC >= 0 -> {
// Pattern break — advance order by one (or honour cue's own instruction), then jump to row. // C$xx pattern break — advance order by one (or honour cue's own instruction), then jump to row.
advanceTrackerCue(ts, playhead) advanceTrackerCue(ts, playhead)
ts.rowIndex = pendingC.coerceIn(0, 63) ts.rowIndex = pendingC.coerceIn(0, 63)
resetPatternLoopState(ts)
} }
else -> { else -> {
ts.rowIndex++ ts.rowIndex++
if (ts.rowIndex >= 64) { if (ts.rowIndex >= 64) {
ts.rowIndex = 0 ts.rowIndex = 0
advanceTrackerCue(ts, playhead) advanceTrackerCue(ts, playhead)
resetPatternLoopState(ts)
} }
} }
} }
@@ -2610,6 +2631,8 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
// Pending row-end events (set during a row by B/C; consumed at row end). // Pending row-end events (set during a row by B/C; consumed at row end).
var pendingOrderJump = -1 // -1 = none; otherwise the order index to jump to var pendingOrderJump = -1 // -1 = none; otherwise the order index to jump to
var pendingRowJump = -1 // -1 = none; otherwise the row index for the next pattern var pendingRowJump = -1 // -1 = none; otherwise the row index for the next pattern
// Distinguishes S$Bx pattern-loop (stays in current cue) from C$xx pattern-break (advances cue).
var pendingRowJumpLocal = false
// Pattern-delay state (S$Ex) — number of additional row-repetitions remaining. // Pattern-delay state (S$Ex) — number of additional row-repetitions remaining.
var patternDelayRemaining = 0 var patternDelayRemaining = 0
@@ -2738,6 +2761,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
ts.cuePos = 0; ts.rowIndex = 0; ts.tickInRow = 0 ts.cuePos = 0; ts.rowIndex = 0; ts.tickInRow = 0
ts.samplesIntoTick = 0.0; ts.firstRow = true ts.samplesIntoTick = 0.0; ts.firstRow = true
ts.pendingOrderJump = -1; ts.pendingRowJump = -1 ts.pendingOrderJump = -1; ts.pendingRowJump = -1
ts.pendingRowJumpLocal = false
ts.patternDelayRemaining = 0; ts.patternDelayActive = false ts.patternDelayRemaining = 0; ts.patternDelayActive = false
ts.sexWinningChannel = -1 ts.sexWinningChannel = -1
ts.panLaw = initialGlobalFlags and 1 ts.panLaw = initialGlobalFlags and 1