mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-06 13:38:30 +09:00
taut: faster cue tab
This commit is contained in:
@@ -555,9 +555,10 @@ let timelineRowStyle = 0
|
|||||||
let COLSIZE_TIMELINE_FULL = TIMELINE_COLSIZES[0]
|
let COLSIZE_TIMELINE_FULL = TIMELINE_COLSIZES[0]
|
||||||
let VOCSIZE_TIMELINE_FULL = Math.floor((SCRW - 3) / COLSIZE_TIMELINE_FULL)
|
let VOCSIZE_TIMELINE_FULL = Math.floor((SCRW - 3) / COLSIZE_TIMELINE_FULL)
|
||||||
|
|
||||||
const ORDERS_CMD_X = 5
|
const ORDERS_CMD_X = 5
|
||||||
const ORDERS_VOICE_X = 9
|
const ORDERS_VOICE_X = 12 // 1-indexed col where voice columns begin
|
||||||
const VOCSIZE_ORDERS = Math.floor((SCRW - 10) / 4)
|
const ORDERS_VOICE_COL_W = 4
|
||||||
|
const VOCSIZE_ORDERS = Math.floor((SCRW - (ORDERS_VOICE_X - 1)) / ORDERS_VOICE_COL_W)
|
||||||
|
|
||||||
const VIEW_TIMELINE = 0
|
const VIEW_TIMELINE = 0
|
||||||
const VIEW_CUES = 1
|
const VIEW_CUES = 1
|
||||||
@@ -1407,46 +1408,105 @@ function drawOrdersHeader() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function drawOrdersRowAt(ci) {
|
||||||
|
const vr = ci - ordersScroll
|
||||||
|
if (vr < 0 || vr >= PTNVIEW_HEIGHT) return
|
||||||
|
const y = PTNVIEW_OFFSET_Y + vr
|
||||||
|
const maxCue = song.lastActiveCue < 0 ? 0 : song.lastActiveCue
|
||||||
|
const isSel = (ci === ordersCursor)
|
||||||
|
const isCur = playbackMode !== PLAYMODE_NONE && ci === cueIdx
|
||||||
|
const back = isSel ? (playbackMode !== PLAYMODE_NONE ? colPlayback : colHighlight)
|
||||||
|
: (isCur ? colPlayback : colBackPtn)
|
||||||
|
|
||||||
|
con.move(y, 1)
|
||||||
|
if (ci > maxCue) {
|
||||||
|
con.color_pair(colBackPtn, colBackPtn)
|
||||||
|
print(' '.repeat(SCRW - 1))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const cue = song.cues[ci]
|
||||||
|
con.color_pair(ci % 4 === 0 ? colRowNumEmph1 : colRowNum, back)
|
||||||
|
print(ci.hex03())
|
||||||
|
con.color_pair(colBackPtn, back)
|
||||||
|
print(' ')
|
||||||
|
// CMD column — crosshair highlight at (ordersCursor, col 0)
|
||||||
|
const cmdBack = (isSel && ordersColCursor === 0) ? colPlayback : back
|
||||||
|
con.color_pair(cue.instr ? colStatus : colSep, cmdBack)
|
||||||
|
print(cue.instr ? cueInstToStr(cue.instr) : '------')
|
||||||
|
con.color_pair(colBackPtn, back)
|
||||||
|
print(' ')
|
||||||
|
// Voice columns
|
||||||
|
for (let c = 0; c < VOCSIZE_ORDERS; c++) {
|
||||||
|
const v = ordersVoiceOff + c
|
||||||
|
const ptn = v < song.numVoices ? cue.ptns[v] : CUE_EMPTY
|
||||||
|
const vBack = (isSel && ordersColCursor === v + 1) ? colPlayback : back
|
||||||
|
con.color_pair(ptn === CUE_EMPTY ? colSep : colStatus, vBack)
|
||||||
|
print(ptn === CUE_EMPTY ? '---' : ptn.hex03())
|
||||||
|
con.color_pair(colBackPtn, back)
|
||||||
|
print(' ')
|
||||||
|
}
|
||||||
|
const endX = ORDERS_VOICE_X + VOCSIZE_ORDERS * ORDERS_VOICE_COL_W
|
||||||
|
if (endX <= SCRW) { con.color_pair(colBackPtn, back); print(' '.repeat(SCRW - endX)) }
|
||||||
|
}
|
||||||
|
|
||||||
function drawOrdersContents(wo) {
|
function drawOrdersContents(wo) {
|
||||||
drawOrdersHeader()
|
drawOrdersHeader()
|
||||||
|
for (let vr = 0; vr < PTNVIEW_HEIGHT; vr++) drawOrdersRowAt(ordersScroll + vr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redraw all rows of one voice column slot (0..VOCSIZE_ORDERS-1).
|
||||||
|
function drawOrdersVoiceColumnAt(slot) {
|
||||||
|
const v = ordersVoiceOff + slot
|
||||||
|
const x = ORDERS_VOICE_X + slot * ORDERS_VOICE_COL_W
|
||||||
const maxCue = song.lastActiveCue < 0 ? 0 : song.lastActiveCue
|
const maxCue = song.lastActiveCue < 0 ? 0 : song.lastActiveCue
|
||||||
|
|
||||||
for (let vr = 0; vr < PTNVIEW_HEIGHT; vr++) {
|
for (let vr = 0; vr < PTNVIEW_HEIGHT; vr++) {
|
||||||
const ci = ordersScroll + vr
|
const ci = ordersScroll + vr
|
||||||
const y = PTNVIEW_OFFSET_Y + vr
|
const y = PTNVIEW_OFFSET_Y + vr
|
||||||
|
|
||||||
|
if (ci > maxCue) {
|
||||||
|
con.move(y, x)
|
||||||
|
con.color_pair(colBackPtn, colBackPtn)
|
||||||
|
print(' ')
|
||||||
|
continue
|
||||||
|
}
|
||||||
const isSel = (ci === ordersCursor)
|
const isSel = (ci === ordersCursor)
|
||||||
const isCur = playbackMode !== PLAYMODE_NONE && ci === cueIdx
|
const isCur = playbackMode !== PLAYMODE_NONE && ci === cueIdx
|
||||||
const back = isSel ? (playbackMode !== PLAYMODE_NONE ? colPlayback : colHighlight)
|
const back = isSel ? (playbackMode !== PLAYMODE_NONE ? colPlayback : colHighlight)
|
||||||
: (isCur ? colPlayback : colBackPtn)
|
: (isCur ? colPlayback : colBackPtn)
|
||||||
|
const cue = song.cues[ci]
|
||||||
|
const ptn = v < song.numVoices ? cue.ptns[v] : CUE_EMPTY
|
||||||
|
const vBack = (isSel && ordersColCursor === v + 1) ? colPlayback : back
|
||||||
|
|
||||||
con.move(y, 1)
|
con.move(y, x)
|
||||||
if (ci > maxCue) {
|
con.color_pair(ptn === CUE_EMPTY ? colSep : colStatus, vBack)
|
||||||
con.color_pair(colBackPtn, colBackPtn)
|
print(ptn === CUE_EMPTY ? '---' : ptn.hex03())
|
||||||
print(' '.repeat(SCRW - 1))
|
con.color_pair(colBackPtn, back)
|
||||||
} else {
|
print(' ')
|
||||||
const cue = song.cues[ci]
|
}
|
||||||
con.color_pair(ci % 4 === 0 ? colRowNumEmph1 : colRowNum, back)
|
}
|
||||||
print(ci.hex03())
|
|
||||||
con.color_pair(colBackPtn, back)
|
// Memory-shift the voice-column area horizontally by `dVoice` voice columns.
|
||||||
print(' ')
|
// Positive = scroll left (new column exposed on right); negative = scroll right.
|
||||||
// CMD column — crosshair highlight at (ordersCursor, col 0)
|
// Touches body rows only; the header and Cmd column are untouched.
|
||||||
const cmdBack = (isSel && ordersColCursor === 0) ? colPlayback : back
|
function shiftOrdersAreaHorizontal(dVoice) {
|
||||||
con.color_pair(cue.instr ? colStatus : colSep, cmdBack)
|
if (dVoice === 0) return
|
||||||
print(cue.instr ? cueInstToStr(cue.instr) : '------')
|
const absD = (dVoice < 0) ? -dVoice : dVoice
|
||||||
con.color_pair(colBackPtn, back)
|
if (absD >= VOCSIZE_ORDERS) return // nothing to salvage
|
||||||
print(' ')
|
|
||||||
// Voice columns
|
const stripWidth = (VOCSIZE_ORDERS - absD) * ORDERS_VOICE_COL_W
|
||||||
for (let c = 0; c < VOCSIZE_ORDERS; c++) {
|
const srcX = ORDERS_VOICE_X + (dVoice > 0 ? absD * ORDERS_VOICE_COL_W : 0)
|
||||||
const v = ordersVoiceOff + c
|
const dstX = ORDERS_VOICE_X + (dVoice > 0 ? 0 : absD * ORDERS_VOICE_COL_W)
|
||||||
const ptn = v < song.numVoices ? cue.ptns[v] : CUE_EMPTY
|
const srcOff = srcX - 1
|
||||||
const vBack = (isSel && ordersColCursor === v + 1) ? colPlayback : back
|
const dstOff = dstX - 1
|
||||||
con.color_pair(ptn === CUE_EMPTY ? colSep : colStatus, vBack)
|
|
||||||
print(ptn === CUE_EMPTY ? '---' : ptn.hex03())
|
for (let p = 0; p < 3; p++) {
|
||||||
con.color_pair(colBackPtn, back)
|
const chanOff = TEXT_PLANES[p]
|
||||||
print(' ')
|
for (let vr = 0; vr < PTNVIEW_HEIGHT; vr++) {
|
||||||
}
|
const rowBase = GPU_MEM - chanOff - (PTNVIEW_OFFSET_Y + vr - 1) * SCRW
|
||||||
const endX = ORDERS_VOICE_X + VOCSIZE_ORDERS * 4
|
sys.memcpy(rowBase - srcOff, SCRATCH_PTR, stripWidth)
|
||||||
if (endX <= SCRW) { con.color_pair(colBackPtn, back); print(' '.repeat(SCRW - endX)) }
|
sys.memcpy(SCRATCH_PTR, rowBase - dstOff, stripWidth)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1592,26 +1652,53 @@ function ordersInput(wo, event) {
|
|||||||
stopPlayback(); drawAlwaysOnElems(); return
|
stopPlayback(); drawAlwaysOnElems(); return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keysym === '<UP>') {
|
if (keysym === '<UP>' || keysym === '<DOWN>' || keysym === '<PAGE_UP>' || keysym === '<PAGE_DOWN>') {
|
||||||
ordersCursor = Math.max(0, ordersCursor - moveDelta)
|
const oldCursor = ordersCursor
|
||||||
if (ordersCursor < ordersScroll) ordersScroll = ordersCursor
|
const oldScroll = ordersScroll
|
||||||
drawOrdersContents(wo)
|
|
||||||
} else if (keysym === '<DOWN>') {
|
if (keysym === '<UP>') {
|
||||||
ordersCursor = Math.min(maxCue, ordersCursor + moveDelta)
|
ordersCursor = Math.max(0, ordersCursor - moveDelta)
|
||||||
if (ordersCursor >= ordersScroll + PTNVIEW_HEIGHT) ordersScroll = Math.max(0, ordersCursor - PTNVIEW_HEIGHT + 1)
|
if (ordersCursor < ordersScroll) ordersScroll = ordersCursor
|
||||||
drawOrdersContents(wo)
|
} else if (keysym === '<DOWN>') {
|
||||||
} else if (keysym === '<PAGE_UP>') {
|
ordersCursor = Math.min(maxCue, ordersCursor + moveDelta)
|
||||||
ordersCursor = Math.max(0, ordersCursor - PTNVIEW_HEIGHT)
|
if (ordersCursor >= ordersScroll + PTNVIEW_HEIGHT) ordersScroll = Math.max(0, ordersCursor - PTNVIEW_HEIGHT + 1)
|
||||||
ordersScroll = Math.max(0, ordersScroll - PTNVIEW_HEIGHT)
|
} else if (keysym === '<PAGE_UP>') {
|
||||||
drawOrdersContents(wo)
|
ordersCursor = Math.max(0, ordersCursor - PTNVIEW_HEIGHT)
|
||||||
} else if (keysym === '<PAGE_DOWN>') {
|
ordersScroll = Math.max(0, ordersScroll - PTNVIEW_HEIGHT)
|
||||||
ordersCursor = Math.min(maxCue, ordersCursor + PTNVIEW_HEIGHT)
|
} else if (keysym === '<PAGE_DOWN>') {
|
||||||
if (ordersCursor >= ordersScroll + PTNVIEW_HEIGHT) ordersScroll = Math.max(0, ordersCursor - PTNVIEW_HEIGHT + 1)
|
ordersCursor = Math.min(maxCue, ordersCursor + PTNVIEW_HEIGHT)
|
||||||
drawOrdersContents(wo)
|
if (ordersCursor >= ordersScroll + PTNVIEW_HEIGHT) ordersScroll = Math.max(0, ordersCursor - PTNVIEW_HEIGHT + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ordersCursor === oldCursor && ordersScroll === oldScroll) return
|
||||||
|
const dScroll = ordersScroll - oldScroll
|
||||||
|
if (dScroll === 0) {
|
||||||
|
drawOrdersRowAt(oldCursor)
|
||||||
|
drawOrdersRowAt(ordersCursor)
|
||||||
|
} else if (Math.abs(dScroll) >= PTNVIEW_HEIGHT) {
|
||||||
|
drawOrdersContents(wo)
|
||||||
|
} else {
|
||||||
|
shiftPatternArea(-dScroll)
|
||||||
|
if (dScroll > 0) for (let i = 0; i < dScroll; i++) drawOrdersRowAt(ordersScroll + PTNVIEW_HEIGHT - 1 - i)
|
||||||
|
else for (let i = 0; i < -dScroll; i++) drawOrdersRowAt(ordersScroll + i)
|
||||||
|
if (oldCursor >= ordersScroll && oldCursor < ordersScroll + PTNVIEW_HEIGHT) drawOrdersRowAt(oldCursor)
|
||||||
|
drawOrdersRowAt(ordersCursor)
|
||||||
|
}
|
||||||
} else if (keysym === '<LEFT>' || keysym === '<RIGHT>') {
|
} else if (keysym === '<LEFT>' || keysym === '<RIGHT>') {
|
||||||
|
const oldVoiceOff = ordersVoiceOff
|
||||||
|
const oldColCursor = ordersColCursor
|
||||||
ordersColCursor += (keysym === '<LEFT>') ? -1 : 1
|
ordersColCursor += (keysym === '<LEFT>') ? -1 : 1
|
||||||
clampOrdersHoriz()
|
clampOrdersHoriz()
|
||||||
drawOrdersContents(wo)
|
if (ordersColCursor === oldColCursor) return // hit edge
|
||||||
|
|
||||||
|
const dVoice = ordersVoiceOff - oldVoiceOff
|
||||||
|
if (dVoice !== 0) {
|
||||||
|
shiftOrdersAreaHorizontal(dVoice)
|
||||||
|
if (dVoice > 0) for (let i = 0; i < dVoice; i++) drawOrdersVoiceColumnAt(VOCSIZE_ORDERS - 1 - i)
|
||||||
|
else for (let i = 0; i < -dVoice; i++) drawOrdersVoiceColumnAt(i)
|
||||||
|
}
|
||||||
|
drawOrdersHeader()
|
||||||
|
drawOrdersRowAt(ordersCursor)
|
||||||
} else if (keyJustHit && keysym === '\n') {
|
} else if (keyJustHit && keysym === '\n') {
|
||||||
cueIdx = ordersCursor
|
cueIdx = ordersCursor
|
||||||
clampCue()
|
clampCue()
|
||||||
|
|||||||
@@ -2340,7 +2340,12 @@ TODO:
|
|||||||
linear-freq flag in the song-table flags byte. Spec details in
|
linear-freq flag in the song-table flags byte. Spec details in
|
||||||
TAUD_NOTE_EFFECTS.md §1, §E, §F, §G.
|
TAUD_NOTE_EFFECTS.md §1, §E, §F, §G.
|
||||||
[ ] milkytracker-style volume ramping (on sample-end only)
|
[ ] milkytracker-style volume ramping (on sample-end only)
|
||||||
[ ] make Cues tab horizontally scrollable
|
[x] make Cues tab move faster
|
||||||
|
Resolution: Cues panel now uses memory-shift (`shiftOrdersAreaHorizontal`)
|
||||||
|
for LEFT/RIGHT and `shiftPatternArea` for UP/DOWN, plus per-row
|
||||||
|
(`drawOrdersRowAt`) and per-column (`drawOrdersVoiceColumnAt`) helpers,
|
||||||
|
replacing the full-panel redraw on every keystroke.
|
||||||
|
[ ] volume and panning policy to match note effect policy: when note is "retriggerred" (note command with instrument specified), the volume/pan must take default value; if not (note command with instrument 0) the volume/pan must stay at the old value. Make both audio engine and taut.js simulator changes.
|
||||||
|
|
||||||
|
|
||||||
Play Data: play data are series of tracker-like instructions, visualised as:
|
Play Data: play data are series of tracker-like instructions, visualised as:
|
||||||
|
|||||||
Reference in New Issue
Block a user