From 54f262bac524e8db83a1fd1cb7c183187a91d312 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Tue, 23 Jun 2026 00:42:22 +0900 Subject: [PATCH] taut: note jamming on inst views --- assets/disk0/tvdos/bin/taut.js | 16 +++++++++++++ assets/disk0/tvdos/bin/taut_views.mjs | 24 +++++++++++++------ .../net/torvald/tsvm/peripheral/IOSpace.kt | 2 +- 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/assets/disk0/tvdos/bin/taut.js b/assets/disk0/tvdos/bin/taut.js index 128fb0e..915ef85 100644 --- a/assets/disk0/tvdos/bin/taut.js +++ b/assets/disk0/tvdos/bin/taut.js @@ -4010,6 +4010,22 @@ HUB.markUnsaved = () => { hasUnsavedChanges = true } HUB.tickPlayback = () => { if (playbackMode !== PLAYMODE_NONE) updatePlayback() } HUB.stopPlayback = stopPlayback +// Shared piano-jam audition for the Instruments view + Advanced Edit: if `event` is a jam +// key (a..k / w..u, no shift) and playback is stopped, audition `instSlot` at editOctave and +// return true (so the caller swallows the key). Scancode-based like the pattern-view jam. +HUB.tryJamFromEvent = function(event, instSlot) { + if (!event || event[0] !== 'key_down' || event[2] !== 1) return false + if (playbackMode !== PLAYMODE_NONE) return false + if (event.includes(59) || event.includes(60)) return false // shifted = other commands + let sc = event[3]; if (sc == 59) sc = event[4]; if (sc == 60) sc = event[5] + const semi = jamScancodeToSemitone(sc) + if (semi === null) return false + const n = semitoneToNote(semi, editOctave) + if (n !== null && (instSlot | 0) >= 1 && typeof audio.jamNote === 'function') + audio.jamNote(PLAYHEAD, 0, n, instSlot | 0) + return true +} + HUB.views = requireTaut("taut_views").init(HUB) const { drawSamplesContents, samplesInput, drawInstrumentsContents, instrumentsInput, diff --git a/assets/disk0/tvdos/bin/taut_views.mjs b/assets/disk0/tvdos/bin/taut_views.mjs index 64b0d64..4830193 100644 --- a/assets/disk0/tvdos/bin/taut_views.mjs +++ b/assets/disk0/tvdos/bin/taut_views.mjs @@ -1916,7 +1916,7 @@ function instrumentsInput(wo, event) { const n = instrumentsCache ? instrumentsCache.length : 0 if (n === 0) { - if (keysym === 'e' || keysym === 'E') { + if (keysym === '\n' || keysym === 'E') { // Enter / Shift+E opens a new instrument openAdvancedInstEdit(-1) } return @@ -1937,9 +1937,12 @@ function instrumentsInput(wo, event) { if (keysym === '4') { instSubTab = INST_TAB_PAN; drawInstrumentsContents(); return } if (keysym === '5') { instSubTab = INST_TAB_PIT; drawInstrumentsContents(); return } if (keysym === '6') { instSubTab = INST_TAB_FILT; drawInstrumentsContents(); return } - if (keysym === 'e' || keysym === 'E') { - const e = instrumentsCache[instListCursor] - if (e) openAdvancedInstEdit(e.slot) + // Note jamming: audition the selected instrument with the piano keys (a..k / w..u). + const sel = instrumentsCache[instListCursor] + if (HUB.tryJamFromEvent && HUB.tryJamFromEvent(event, sel ? sel.slot : 0)) return + // Open Advanced Edit: Enter or Shift+E (lowercase 'e' is the D# jam key). + if (keysym === '\n' || keysym === 'E') { + if (sel) openAdvancedInstEdit(sel.slot) return } } @@ -2531,7 +2534,7 @@ function editorModalLoop(onKey, onTick) { if (ev[0] !== 'key_down') return const ks = ev[1] if (ks === '' || ks === '' || ks === '') { if (1 === ev[2]) finish(); return } - onKey(ks, finish, (1 === ev[2])) + onKey(ks, finish, (1 === ev[2]), ev) }) if (!done && onTick) onTick() // don't overpaint the new panel after a switch } @@ -2702,6 +2705,7 @@ function ovr(val, isDefault) { return isDefault ? '--' : ('$' + (val & 0xFF).toS // (bottom-right). Mouse-aware (patches + transport). See plan Step 2. function openAdvancedInstEdit(slot) { const SLOT = (slot !== undefined && slot >= 0) ? (slot | 0) : -1 + if (typeof audio.jamStop === 'function') audio.jamStop(PLAYHEAD) // clean slate: drop any list-view jam const Y = PTNVIEW_OFFSET_Y - 1 // start one row above the normal panel top (row 4), 1 row taller const cHdr = colVoiceHdr, cStatus = colStatus, cDim = colSep, cBack = 255 @@ -3108,7 +3112,10 @@ function openAdvancedInstEdit(slot) { const litVol = new Array(zones.length).fill(0) // max CURRENT eff vol per zone (brightness) const litCnt = new Array(zones.length).fill(0) // sounding-voice count per zone (heat) for (let v = 0; v < nv; v++) { - if (!playing || !audio.getVoiceActive(PLAYHEAD, v) || audio.getVoiceInstrument(PLAYHEAD, v) !== SLOT) { voicePeak[v] = null; continue } + // During playback show only THIS instrument's voices; during a (stopped) jam + // audition show the active jam voice(s) — a meta plays its foreground layer under + // a layer instrument, not SLOT, so don't filter on SLOT while stopped. + if (!audio.getVoiceActive(PLAYHEAD, v) || (playing && audio.getVoiceInstrument(PLAYHEAD, v) !== SLOT)) { voicePeak[v] = null; continue } const note = audio.getVoiceNote(PLAYHEAD, v) const eff = audio.getVoiceEffectiveVolume(PLAYHEAD, v) || 0 let pk = voicePeak[v] @@ -3192,7 +3199,9 @@ function openAdvancedInstEdit(slot) { drawDetail(); drawEnvGraph(); drawHint() registerMouse() - editorModalLoop((ks, finish, first) => { + editorModalLoop((ks, finish, first, ev) => { + // Note jamming: audition the instrument being edited with the piano keys (a..k / w..u). + if (SLOT >= 1 && HUB.tryJamFromEvent && HUB.tryJamFromEvent(ev, SLOT)) return if (zones.length === 0) return if (ks === '') { if (selIdx > 0) { selIdx--; redrawSel() } return } if (ks === '') { if (selIdx < zones.length - 1) { selIdx++; redrawSel() } return } @@ -3205,6 +3214,7 @@ function openAdvancedInstEdit(slot) { if (ks === '') { envKind = (envKind + 1) % ENV_TABS.length; drawEnvGraph(); liveSig = '~'; return } }, refreshLiveVoices) + if (typeof audio.jamStop === 'function') audio.jamStop(PLAYHEAD) // silence any lingering jam audition clearEnvGraphics() // don't leave the graph over the restored viewer refreshInstrumentsCache() clampInstrumentsCursor() diff --git a/tsvm_core/src/net/torvald/tsvm/peripheral/IOSpace.kt b/tsvm_core/src/net/torvald/tsvm/peripheral/IOSpace.kt index 5c6ba22..983bdc9 100644 --- a/tsvm_core/src/net/torvald/tsvm/peripheral/IOSpace.kt +++ b/tsvm_core/src/net/torvald/tsvm/peripheral/IOSpace.kt @@ -523,7 +523,7 @@ private class Beeper { // Arpeggio note-effects step at 60 Hz: 48000 / 60 = 800 samples per step. private const val SAMPLES_PER_ARP_TICK = SAMPLE_RATE / ARPRATE private const val CHUNK = SAMPLES_PER_ARP_TICK - private const val AMPLITUDE = 8192 // ~ -12 dBFS; square waves are loud + private const val AMPLITUDE = 16384 // ~ -6 dBFS; square waves are loud } // MMIO 94..99 write-staging registers: