|
|
|
|
@@ -10,6 +10,9 @@ const taud = require("taud")
|
|
|
|
|
|
|
|
|
|
font.setHighRom("A:/tvdos/bin/tautfont_high.chr")
|
|
|
|
|
|
|
|
|
|
const BUILD_DATE = "260423"
|
|
|
|
|
const TRACKER_SIGNATURE = "TsvmTaut"+BUILD_DATE // 14-byte string
|
|
|
|
|
|
|
|
|
|
const MIDDOT = "\u00FA"
|
|
|
|
|
const BIGDOT = "\u00F9"
|
|
|
|
|
const BULLET = "\u00847u"
|
|
|
|
|
@@ -47,13 +50,13 @@ keyoff:"\u00A0\u00CD\u00CD\u00A1",
|
|
|
|
|
notecut:"\u00A4\u00A4\u00A4\u00A4",
|
|
|
|
|
|
|
|
|
|
/* special effects */
|
|
|
|
|
volset:MIDDOT,
|
|
|
|
|
volset:'',//MIDDOT,
|
|
|
|
|
volup:"\u008430u",
|
|
|
|
|
voldn:"\u008431u",
|
|
|
|
|
volfineup:"+",
|
|
|
|
|
volfinedn:"-",
|
|
|
|
|
|
|
|
|
|
panset:MIDDOT,
|
|
|
|
|
panset:'',//MIDDOT,
|
|
|
|
|
panle:"\u008417u",
|
|
|
|
|
panri:"\u008416u",
|
|
|
|
|
panfinele:"\u008427u",
|
|
|
|
|
@@ -65,6 +68,55 @@ ticked:"\u009F",
|
|
|
|
|
middot:MIDDOT
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const fxNames = {
|
|
|
|
|
A:"Set tick speed",
|
|
|
|
|
B:"Jump to order",
|
|
|
|
|
C:"Break pattern to",
|
|
|
|
|
D:"Volume slide",
|
|
|
|
|
E:"Pitch down",
|
|
|
|
|
F:"Pitch up",
|
|
|
|
|
G:"Portamento",
|
|
|
|
|
H:"Vibrato",
|
|
|
|
|
U:"Fine vibrato",
|
|
|
|
|
I:"Tremor",
|
|
|
|
|
J:"Arpeggio",
|
|
|
|
|
K:"Vibrato + vol slide",
|
|
|
|
|
L:"Portamento + vol slide",
|
|
|
|
|
O:"Sample offset",
|
|
|
|
|
Q:"Retrigger",
|
|
|
|
|
R:"Tremolo",
|
|
|
|
|
T:"Tempo",
|
|
|
|
|
V:"Gloval volume",
|
|
|
|
|
S:"Special",
|
|
|
|
|
S1:"Glissando ctrl",
|
|
|
|
|
S2:"Sample finetune",
|
|
|
|
|
S3:"Vibrato LFO",
|
|
|
|
|
S4:"Tremolo LFO",
|
|
|
|
|
S8:"Channel pan",
|
|
|
|
|
SB:"Pattern loop",
|
|
|
|
|
SC:"Note cut",
|
|
|
|
|
SD:"Note delay",
|
|
|
|
|
SE:"Pattern delay",
|
|
|
|
|
SF:"Funk it"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const panFxNames = {
|
|
|
|
|
0:"Set",
|
|
|
|
|
1:"Pan slide L",
|
|
|
|
|
2:"Pan slide R",
|
|
|
|
|
3:"Fine pan slide",
|
|
|
|
|
30:"Fine pan slide L",
|
|
|
|
|
31:"Fine pan slide R"
|
|
|
|
|
}
|
|
|
|
|
const volFxNames = {
|
|
|
|
|
0:"Set",
|
|
|
|
|
1:"Vol slide UP",
|
|
|
|
|
2:"Vol slide DN",
|
|
|
|
|
3:"Fine vol slide",
|
|
|
|
|
30:"Fine vol slide DN",
|
|
|
|
|
31:"Fine vol slide UP"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const pitchTablePresets = {
|
|
|
|
|
// index: pitch table number to be recorded on .taudproj file
|
|
|
|
|
0:{index:0,name:"null", table:[], sym:[]}, // when null is specified, hex numbers will be displayed instead
|
|
|
|
|
@@ -121,6 +173,8 @@ const colEffOp = 213
|
|
|
|
|
const colEffArg = 231
|
|
|
|
|
const colBackPtn = 255
|
|
|
|
|
|
|
|
|
|
const PITCH_PRESET_IDX = 240 // TODO read from the Project Data section of the .taud
|
|
|
|
|
|
|
|
|
|
Number.prototype.hex02 = function() {
|
|
|
|
|
return this.toString(16).toUpperCase().padStart(2,'0')
|
|
|
|
|
}
|
|
|
|
|
@@ -133,6 +187,9 @@ Number.prototype.hex04 = function() {
|
|
|
|
|
Number.prototype.hexD2 = function() {
|
|
|
|
|
return this.toString(16).toUpperCase().padStart(2, sym.middot)
|
|
|
|
|
}
|
|
|
|
|
Number.prototype.hex1 = function() {
|
|
|
|
|
return this.toString(16).toUpperCase()
|
|
|
|
|
}
|
|
|
|
|
Number.prototype.dec02 = function() {
|
|
|
|
|
return this.toString(10).toUpperCase().padStart(2,'0')
|
|
|
|
|
}
|
|
|
|
|
@@ -141,6 +198,24 @@ Number.prototype.decD2 = function() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function noteToStr(note) {
|
|
|
|
|
if (note === 0xFFFF) return sym.middot.repeat(4)
|
|
|
|
|
if (note === 0xFFFE) return sym.notecut
|
|
|
|
|
if (note === 0x0000) return sym.keyoff
|
|
|
|
|
const table = pitchTablePresets[PITCH_PRESET_IDX].table
|
|
|
|
|
const syms = pitchTablePresets[PITCH_PRESET_IDX].sym
|
|
|
|
|
if (table.length === 0) return note.hex04()
|
|
|
|
|
const pitchInOct = note & 0xFFF
|
|
|
|
|
const octave = (note >> 12) - 1
|
|
|
|
|
let best = 0, bestDist = 0x1000
|
|
|
|
|
for (let i = 0; i < table.length; i++) {
|
|
|
|
|
const d = Math.abs(pitchInOct - table[i])
|
|
|
|
|
if (d < bestDist) { bestDist = d; best = i }
|
|
|
|
|
}
|
|
|
|
|
if ((0x1000 - pitchInOct) < bestDist) return syms[0] + (octave + 1)
|
|
|
|
|
return syms[best] + octave
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Builds the coloured string fragments for a single row of pattern data.
|
|
|
|
|
*/
|
|
|
|
|
@@ -156,53 +231,56 @@ function buildRowCell(ptnDat, row) {
|
|
|
|
|
const effop = ptnDat[off+5]
|
|
|
|
|
const effarg = ptnDat[off+6] | (ptnDat[off+7] << 8)
|
|
|
|
|
|
|
|
|
|
let sNote = note.hex04()
|
|
|
|
|
if (note == 0xFFFF) sNote = sym.middot.repeat(4)
|
|
|
|
|
else if (note == 0xFFFE) sNote = sym.notecut
|
|
|
|
|
else if (note == 0x0000) sNote = sym.keyoff
|
|
|
|
|
const sNote = noteToStr(note)
|
|
|
|
|
|
|
|
|
|
let sInst = inst.hexD2()
|
|
|
|
|
if (inst == 0) sInst = sym.middot.repeat(2)
|
|
|
|
|
|
|
|
|
|
let sVolEff = volEffSym[voleff >>> 6]
|
|
|
|
|
let sVolArg = voleffarg.decD2()
|
|
|
|
|
let sVolArg = voleffarg.hexD2()
|
|
|
|
|
if (voleff === 0) {
|
|
|
|
|
sVolEff = sym.middot
|
|
|
|
|
sVolEff = ''
|
|
|
|
|
sVolArg = sym.middot.repeat(2)
|
|
|
|
|
}
|
|
|
|
|
else if (voleff >>> 6 == 1 || voleff >>> 6 == 2) {
|
|
|
|
|
sVolArg = (voleffarg & 15).hex1()
|
|
|
|
|
}
|
|
|
|
|
else if (voleff >>> 6 == 3) {
|
|
|
|
|
if (voleffarg == 0) {
|
|
|
|
|
sVolEff = sym.middot
|
|
|
|
|
sVolArg = sym.middot.repeat(2)
|
|
|
|
|
sVolArg = sym.middot.repeat(1)
|
|
|
|
|
}
|
|
|
|
|
else if (voleffarg >= 32) {
|
|
|
|
|
sVolEff = volEffSym[3]
|
|
|
|
|
sVolArg = (voleffarg & 31).dec02()
|
|
|
|
|
sVolArg = (voleffarg & 15).hex1()
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
sVolEff = volEffSym[4]
|
|
|
|
|
sVolArg = (voleffarg & 31).dec02()
|
|
|
|
|
sVolArg = (voleffarg & 15).hex1()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let sPanEff = panEffSym[paneff >>> 6]
|
|
|
|
|
let sPanArg = paneffarg.decD2()
|
|
|
|
|
let sPanArg = paneffarg.hexD2()
|
|
|
|
|
if (paneff === 0) {
|
|
|
|
|
sPanEff = sym.middot
|
|
|
|
|
sPanEff = ''
|
|
|
|
|
sPanArg = sym.middot.repeat(2)
|
|
|
|
|
}
|
|
|
|
|
else if (paneff >>> 6 == 1 || paneff >>> 6 == 2) {
|
|
|
|
|
sPanArg = (paneffarg & 15).hex1()
|
|
|
|
|
}
|
|
|
|
|
else if (paneff >>> 6 == 3) {
|
|
|
|
|
if (paneffarg == 0) {
|
|
|
|
|
sPanEff = sym.middot
|
|
|
|
|
sPanArg = sym.middot.repeat(2)
|
|
|
|
|
sPanArg = sym.middot.repeat(1)
|
|
|
|
|
}
|
|
|
|
|
else if (paneffarg >= 32) {
|
|
|
|
|
sPanEff = panEffSym[4]
|
|
|
|
|
sPanArg = (paneffarg & 31).dec02()
|
|
|
|
|
sPanArg = (paneffarg & 15).hex1()
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
sPanEff = panEffSym[3]
|
|
|
|
|
sPanArg = (paneffarg & 31).dec02()
|
|
|
|
|
sPanArg = (paneffarg & 15).hex1()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -219,9 +297,9 @@ function buildRowCell(ptnDat, row) {
|
|
|
|
|
const EMPTY_CELL = {
|
|
|
|
|
sNote: sym.middot.repeat(4),
|
|
|
|
|
sInst: sym.middot.repeat(3),
|
|
|
|
|
sVolEff: sym.middot,
|
|
|
|
|
sVolEff: '',
|
|
|
|
|
sVolArg: sym.middot.repeat(2),
|
|
|
|
|
sPanEff: sym.middot,
|
|
|
|
|
sPanEff: '',
|
|
|
|
|
sPanArg: sym.middot.repeat(2),
|
|
|
|
|
sEffOp: sym.middot,
|
|
|
|
|
sEffArg: sym.middot.repeat(4)
|
|
|
|
|
@@ -339,11 +417,11 @@ function loadTaud(filePath, songIndex) {
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
const [SCRH, SCRW] = con.getmaxyx()
|
|
|
|
|
const PTNVIEW_OFFSET_X = 5
|
|
|
|
|
const PTNVIEW_OFFSET_X = 3
|
|
|
|
|
const PTNVIEW_OFFSET_Y = 9
|
|
|
|
|
const PTNVIEW_HEIGHT = SCRH - PTNVIEW_OFFSET_Y
|
|
|
|
|
const COLSIZE = 18
|
|
|
|
|
const VOCSIZE = 4
|
|
|
|
|
const COLSIZE = 15
|
|
|
|
|
const VOCSIZE = 5
|
|
|
|
|
|
|
|
|
|
const VIEW_TIMELINE = 0
|
|
|
|
|
const VIEW_ORDERS = 1
|
|
|
|
|
@@ -358,6 +436,8 @@ const colStatus = 253
|
|
|
|
|
const colVoiceHdr = 230
|
|
|
|
|
const colSep = 252
|
|
|
|
|
|
|
|
|
|
let separatorStyle = 0
|
|
|
|
|
|
|
|
|
|
function fillLine(y, c, back) {
|
|
|
|
|
con.color_pair(c, back)
|
|
|
|
|
for (let x = 1; x <= SCRW; x++) {
|
|
|
|
|
@@ -369,18 +449,37 @@ function drawStatusBar() {
|
|
|
|
|
fillLine(1, colStatus, 255)
|
|
|
|
|
const maxCue = song.lastActiveCue < 0 ? 0 : song.lastActiveCue
|
|
|
|
|
const vHi = Math.min(voiceOff + VOCSIZE, song.numVoices)
|
|
|
|
|
const txt = ` ${song.filePath} Cue ${cueIdx.hex03()}/${maxCue.hex03()} Row ${cursorRow.dec02()} V${(voiceOff+1).dec02()}-${vHi.dec02()}/${song.numVoices.dec02()} BPM ${song.bpm} Spd ${song.tickRate} `
|
|
|
|
|
const txt = `${song.filePath} Cue ${cueIdx.hex03()}/${maxCue.hex03()} Row ${cursorRow.dec02()} V${(voiceOff+1).dec02()}-${vHi.dec02()}/${song.numVoices.dec02()} BPM ${song.bpm} Spd ${song.tickRate} `
|
|
|
|
|
con.move(1, 1)
|
|
|
|
|
con.color_pair(colStatus, 255)
|
|
|
|
|
print(txt)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function drawSeparators(posY, col_size) {
|
|
|
|
|
/**
|
|
|
|
|
* @param style 0: condensed timeline, 1: vertical bars between voices
|
|
|
|
|
*/
|
|
|
|
|
function drawSeparators(style) {
|
|
|
|
|
if (style == 1) {
|
|
|
|
|
con.color_pair(colSep, 255)
|
|
|
|
|
for (let c = 0; c < VOCSIZE - 1; c++) {
|
|
|
|
|
con.move(posY, PTNVIEW_OFFSET_X + col_size * (c+1) - 1)
|
|
|
|
|
for (let y = PTNVIEW_OFFSET_Y - 1; y < PTNVIEW_HEIGHT; y++) {
|
|
|
|
|
con.move(y, PTNVIEW_OFFSET_X + COLSIZE * (c+1) - 1)
|
|
|
|
|
con.prnch(0xB3)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// paint the first column of pattern view with colour
|
|
|
|
|
for (let x = PTNVIEW_OFFSET_X; x < SCRW - 3; x += COLSIZE) {
|
|
|
|
|
for (let y = 0; y < PTNVIEW_HEIGHT+1; y++) {
|
|
|
|
|
let memOffset = (y+PTNVIEW_OFFSET_Y-2) * SCRW + (x-1)
|
|
|
|
|
let bgColOffset = GPU_MEM - TEXT_BACK_OFF - memOffset
|
|
|
|
|
sys.poke(bgColOffset, colHighlight)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
con.color_pair(colSep, 255)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function drawVoiceHeaders() {
|
|
|
|
|
@@ -405,7 +504,7 @@ function drawVoiceHeaders() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
drawSeparators(PTNVIEW_OFFSET_Y - 1, COLSIZE)
|
|
|
|
|
drawSeparators(separatorStyle)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function drawPatternRowAt(viewRow) {
|
|
|
|
|
@@ -440,7 +539,7 @@ function drawPatternRowAt(viewRow) {
|
|
|
|
|
drawCellAt(y, x, cell, back)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
drawSeparators(y, COLSIZE)
|
|
|
|
|
drawSeparators(separatorStyle)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function drawPatternView() {
|
|
|
|
|
@@ -449,9 +548,8 @@ function drawPatternView() {
|
|
|
|
|
|
|
|
|
|
function drawControlHint() {
|
|
|
|
|
let hintElem = [
|
|
|
|
|
[`\u008424u\u008425u`,'Row'],
|
|
|
|
|
[`\u008427u\u008426u`,'Vox'],
|
|
|
|
|
[`Pg\u008424u\u008425u`,'Ptn'],
|
|
|
|
|
[`\u008427u\u008425u\u008424u\u008426u`,'Ptn'],
|
|
|
|
|
[`Pg\u008424u\u008425u`,'Cue'],
|
|
|
|
|
['sep'],
|
|
|
|
|
['F5','Song'],
|
|
|
|
|
['F6','Cue'],
|
|
|
|
|
@@ -459,7 +557,9 @@ function drawControlHint() {
|
|
|
|
|
['F8/Sp','Stop'],
|
|
|
|
|
['sep'],
|
|
|
|
|
['m','Mute'],
|
|
|
|
|
['s','Solo']
|
|
|
|
|
['s','Solo'],
|
|
|
|
|
['sep'],
|
|
|
|
|
['q','Quit'],
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
// erase current line
|
|
|
|
|
@@ -529,10 +629,18 @@ function drawVoiceDetail() {
|
|
|
|
|
const effarg = ptnDat[6] | (ptnDat[7] << 8)
|
|
|
|
|
|
|
|
|
|
con.move(6,1)
|
|
|
|
|
print(`Pattern $${ptnIdx.hex02()}\tRow ${cursorRow.dec02()}\tVoice ${cursorVox}`)
|
|
|
|
|
print(`Pitch $${note.hex04()}\tInst $${inst.hex02()}\tVolEff ${voleffop}.$${voleffarg.hex02()}\t`+
|
|
|
|
|
`PanEff ${paneffop}.$${paneffarg.hex02()}`)
|
|
|
|
|
con.move(7,1)
|
|
|
|
|
print(`Pitch $${note.hex04()}\tInst $${inst.hex02()}\tVolEff ${voleffop}.${voleffarg.dec02()}\t`+
|
|
|
|
|
`PanEff ${paneffop}.${paneffarg.dec02()}\tFx ${effop.toString(36).toUpperCase()}.${effarg.hex04()}`)
|
|
|
|
|
let fx = effop.toString(36).toUpperCase()
|
|
|
|
|
if (fx == '0') {
|
|
|
|
|
print(`Fx`+' '.repeat(32))
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (fx == 'S') fx += (effarg >>> 12).hex1()
|
|
|
|
|
let fxName = fxNames[fx]
|
|
|
|
|
print(`Fx ${fxName} $${effarg.hex04()} `)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function drawAll() {
|
|
|
|
|
@@ -541,6 +649,7 @@ function drawAll() {
|
|
|
|
|
drawVoiceHeaders()
|
|
|
|
|
drawPatternView()
|
|
|
|
|
drawVoiceDetail()
|
|
|
|
|
drawSeparators(separatorStyle)
|
|
|
|
|
drawControlHint()
|
|
|
|
|
con.move(1, 1)
|
|
|
|
|
}
|
|
|
|
|
@@ -564,12 +673,8 @@ const TEXT_PLANES = [TEXT_CHAR_OFF, TEXT_BACK_OFF, TEXT_FORE_OFF]
|
|
|
|
|
// One scratch strip, reused across shifts
|
|
|
|
|
const SCRATCH_PTR = sys.malloc(SCRW * PTNVIEW_HEIGHT)
|
|
|
|
|
|
|
|
|
|
// Horizontal salvage: 3 carried voice columns minus the missing trailing separator.
|
|
|
|
|
// For shift-left: source x=23..75 (old cols 1,2,3); dest x=5..57 (new cols 0,1,2).
|
|
|
|
|
// For shift-right: source x=5..57 (old cols 0,1,2); dest x=23..75 (new cols 1,2,3).
|
|
|
|
|
// The separator at the boundary of the exposed column is already in place after
|
|
|
|
|
// the shift (it was never overwritten), so no extra separator fix-up is needed.
|
|
|
|
|
const SALVAGE_HORIZ_LEN = (VOCSIZE - 1) * COLSIZE - 1 // 53 chars
|
|
|
|
|
// Horizontal salvage
|
|
|
|
|
const SALVAGE_HORIZ_LEN = (VOCSIZE - 1) * COLSIZE
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Shift the pattern-view rows by `dy` lines (positive = down, negative = up)
|
|
|
|
|
@@ -639,7 +744,6 @@ function drawVoiceColumnAt(slot) {
|
|
|
|
|
cell = buildRowCell(song.patterns[ptnIdx], actualRow)
|
|
|
|
|
}
|
|
|
|
|
drawCellAt(y, x, cell, back)
|
|
|
|
|
drawSeparators(y, COLSIZE)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -789,6 +893,7 @@ function updatePlayback() {
|
|
|
|
|
drawPatternRowAt(cursorRow - scrollRow)
|
|
|
|
|
}
|
|
|
|
|
drawStatusBar()
|
|
|
|
|
drawSeparators(separatorStyle)
|
|
|
|
|
drawVoiceDetail()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -809,7 +914,9 @@ function clampVoice() {
|
|
|
|
|
if (cursorVox < 0) cursorVox = 0
|
|
|
|
|
if (cursorVox >= song.numVoices) cursorVox = song.numVoices - 1
|
|
|
|
|
if (cursorVox < voiceOff) voiceOff = cursorVox
|
|
|
|
|
if (cursorVox >= voiceOff + VOCSIZE) voiceOff = cursorVox - VOCSIZE + 1
|
|
|
|
|
// keep cursor centred until view reaches an edge (mirrors clampCursor logic)
|
|
|
|
|
if (cursorVox < voiceOff + (VOCSIZE>>>1) && voiceOff > 0) voiceOff = cursorVox - (VOCSIZE>>>1)
|
|
|
|
|
if (cursorVox >= voiceOff + ((VOCSIZE+1)>>>1)) voiceOff = cursorVox - ((VOCSIZE+1)>>>1) + 1
|
|
|
|
|
const maxOff = Math.max(0, song.numVoices - VOCSIZE)
|
|
|
|
|
if (voiceOff < 0) voiceOff = 0
|
|
|
|
|
if (voiceOff > maxOff) voiceOff = maxOff
|
|
|
|
|
@@ -836,6 +943,8 @@ while (!exitFlag) {
|
|
|
|
|
input.withEvent(event => {
|
|
|
|
|
if (event[0] !== "key_down") return
|
|
|
|
|
const keysym = event[1]
|
|
|
|
|
const shiftDown = (event.indexOf(59) > 0 || event.indexOf(60) > 0)
|
|
|
|
|
const moveDelta = shiftDown ? 4 : 1
|
|
|
|
|
|
|
|
|
|
if (keysym === "<ESC>" || keysym === "q" || keysym === "Q") {
|
|
|
|
|
exitFlag = true
|
|
|
|
|
@@ -846,7 +955,7 @@ while (!exitFlag) {
|
|
|
|
|
if (keysym === "<F8>" || keysym === " ") { stopPlayback(); drawAll() }
|
|
|
|
|
else if (keysym === "<LEFT>" || keysym === "<RIGHT>") {
|
|
|
|
|
const oldVoiceOff = voiceOff
|
|
|
|
|
cursorVox += (keysym === "<LEFT>") ? -1 : 1
|
|
|
|
|
cursorVox += (keysym === "<LEFT>") ? -moveDelta : moveDelta
|
|
|
|
|
clampVoice()
|
|
|
|
|
const dVoice = voiceOff - oldVoiceOff
|
|
|
|
|
if (dVoice !== 0) {
|
|
|
|
|
@@ -854,6 +963,7 @@ while (!exitFlag) {
|
|
|
|
|
drawVoiceColumnAt(dVoice > 0 ? VOCSIZE - 1 : 0)
|
|
|
|
|
}
|
|
|
|
|
drawVoiceHeaders()
|
|
|
|
|
drawSeparators(separatorStyle)
|
|
|
|
|
drawStatusBar()
|
|
|
|
|
}
|
|
|
|
|
else if (keysym === "m" || keysym === "M") { toggleMute(cursorVox) }
|
|
|
|
|
@@ -873,7 +983,7 @@ while (!exitFlag) {
|
|
|
|
|
|
|
|
|
|
if (keysym === "<LEFT>" || keysym === "<RIGHT>") {
|
|
|
|
|
const oldVoiceOff = voiceOff
|
|
|
|
|
cursorVox += (keysym === "<LEFT>") ? -1 : 1
|
|
|
|
|
cursorVox += (keysym === "<LEFT>") ? -moveDelta : moveDelta
|
|
|
|
|
clampVoice()
|
|
|
|
|
const dVoice = voiceOff - oldVoiceOff
|
|
|
|
|
if (dVoice !== 0) {
|
|
|
|
|
@@ -881,6 +991,7 @@ while (!exitFlag) {
|
|
|
|
|
drawVoiceColumnAt(dVoice > 0 ? VOCSIZE - 1 : 0)
|
|
|
|
|
}
|
|
|
|
|
drawVoiceHeaders()
|
|
|
|
|
drawSeparators(separatorStyle)
|
|
|
|
|
drawStatusBar()
|
|
|
|
|
drawVoiceDetail()
|
|
|
|
|
return
|
|
|
|
|
@@ -889,12 +1000,12 @@ while (!exitFlag) {
|
|
|
|
|
if (keysym === "m" || keysym === "M") { toggleMute(cursorVox); return }
|
|
|
|
|
if (keysym === "s" || keysym === "S") { toggleSolo(cursorVox); return }
|
|
|
|
|
|
|
|
|
|
if (keysym === "<UP>") { cursorRow -= 1; rowMove = true }
|
|
|
|
|
else if (keysym === "<DOWN>") { cursorRow += 1; rowMove = true }
|
|
|
|
|
if (keysym === "<UP>") { cursorRow -= moveDelta; rowMove = true }
|
|
|
|
|
else if (keysym === "<DOWN>") { cursorRow += moveDelta; rowMove = true }
|
|
|
|
|
else if (keysym === "<HOME>") { cursorRow = 0; rowMove = true }
|
|
|
|
|
else if (keysym === "<END>") { cursorRow = ROWS_PER_PAT-1; rowMove = true }
|
|
|
|
|
else if (keysym === "<PAGE_UP>") { cueIdx -= 1; fullRedraw = true }
|
|
|
|
|
else if (keysym === "<PAGE_DOWN>") { cueIdx += 1; fullRedraw = true }
|
|
|
|
|
else if (keysym === "<PAGE_UP>") { cueIdx -= moveDelta; fullRedraw = true }
|
|
|
|
|
else if (keysym === "<PAGE_DOWN>") { cueIdx += moveDelta; fullRedraw = true }
|
|
|
|
|
else return
|
|
|
|
|
|
|
|
|
|
clampCursor(); clampVoice(); clampCue()
|
|
|
|
|
@@ -933,6 +1044,7 @@ while (!exitFlag) {
|
|
|
|
|
drawPatternRowAt(cursorRow - scrollRow)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
drawSeparators(separatorStyle)
|
|
|
|
|
drawStatusBar()
|
|
|
|
|
drawVoiceDetail()
|
|
|
|
|
})
|
|
|
|
|
|