volume policy when unspecified: retrigger (note+inst cmd) -> default value, no retrigger (note cmd only) -> prev value

This commit is contained in:
minjaesong
2026-05-08 01:11:19 +09:00
parent a767eebc2e
commit 34b3b83d65
3 changed files with 39 additions and 22 deletions

View File

@@ -1826,8 +1826,10 @@ function simulateRowState(ptnDat, uptoRow) {
const isGRow = (effop === OP_G)
const isNoteDelay = (effop === OP_S) && (((effarg >>> 12) & 0xF) === 0xD)
// Track whether this row reloads the channel's default volume. Engine:
// triggerNote() resets channelVolume to 0x3F on fresh triggers, and an
// instrument byte on a tone-porta row also reloads default vol (matches
// triggerNote() resets channelVolume to 0x3F only when the row carries an
// instrument byte; a note-only retrigger (inst === 0) inherits the
// channel's existing volume. Tone-porta rows follow the same rule —
// an instrument byte on a porta row reloads default vol (matches
// schism csf_instrument_change inst_column branch).
let reloadDefaultVol = false
if (note !== 0xFFFF && note !== 0xFFFE) {
@@ -1842,17 +1844,21 @@ function simulateRowState(ptnDat, uptoRow) {
lastNote = note
pitchOff = 0
portaTarget = -1
reloadDefaultVol = true
if (inst !== 0) reloadDefaultVol = true
} else {
lastNote = note
pitchOff = 0
portaTarget = -1
reloadDefaultVol = true
if (inst !== 0) reloadDefaultVol = true
}
}
if (inst !== 0) lastInst = inst
// Default vol reset must happen before the volume column so a SET selector
// can still override on the same row (engine order: triggerNote → applyVolColumn).
// Pan: simulator does not track per-instrument default pan, so it never resets
// panAbs on trigger — this naturally matches the "stay at old value when inst === 0"
// half of the policy. The engine-side default-pan reload (gated on inst !== 0)
// is invisible here.
if (reloadDefaultVol) volAbs = 0x3F
// Pre-scan effect column for S$80xx (8-bit pan SET wins over volcol/pancol SET).