mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-06 05:28:31 +09:00
taud: 12 envelope nodes; taut proj tab
This commit is contained in:
@@ -196,7 +196,7 @@ sym:[`C${sym.accnull}`,`C${sym.sharp}`,`D${sym.accnull}`,`D${sym.sharp}`,`E${sym
|
||||
sym:[`C${sym.accnull}`,`C${sym.sharp}`,`D${sym.accnull}`,`D${sym.sharp}`,`E${sym.accnull}`,`F${sym.accnull}`,`F${sym.sharp}`,`G${sym.accnull}`,`G${sym.sharp}`,`A${sym.accnull}`,`A${sym.sharp}`,`B${sym.accnull}`]},
|
||||
10122:{index:10122,name:"Pythagorean Augmented Fourth", table:[0x0,0x134,0x2B8,0x3EC,0x570,0x6A4,0x828,0x95C,0xA90,0xC14,0xD48,0xECC],
|
||||
sym:[`C${sym.accnull}`,`C${sym.sharp}`,`D${sym.accnull}`,`D${sym.sharp}`,`E${sym.accnull}`,`F${sym.accnull}`,`F${sym.sharp}`,`G${sym.accnull}`,`G${sym.sharp}`,`A${sym.accnull}`,`A${sym.sharp}`,`B${sym.accnull}`]},
|
||||
10123:{index:10123,name:"Shi'er lu", table:[0x0,0x184,0x2B8,0x43C,0x570,0x6F4,0x828,0x95C,0xAE0,0xC14,0xD98,0xECC],
|
||||
10123:{index:10123,name:"\u00FC\u00FD\u00FE", table:[0x0,0x184,0x2B8,0x43C,0x570,0x6F4,0x828,0x95C,0xAE0,0xC14,0xD98,0xECC],
|
||||
sym:[` \u00E0\u00E1`,` \u00E2\u00E3`,` \u00E4\u00E5`,` \u00E6\u00E7`,` \u00E8\u00E9`,` \u00EA\u00EB`,` \u00EC\u00ED`,` \u00EE\u00EF`,` \u00F0\u00F1`,` \u00F2\u00F3`,` \u00F4\u00F5`,` \u00F6\u00F7`]},
|
||||
|
||||
|
||||
@@ -218,6 +218,7 @@ const colBackPtn = 255
|
||||
let PITCH_PRESET_IDX = 240 // TODO read from the Project Data section of the .taud
|
||||
let beatDivPrimary = 4 // TODO read from the Project Data section of the .taud
|
||||
let beatDivSecondary = 16
|
||||
let hasUnsavedChanges = false
|
||||
|
||||
// pitchSymLut[pitchInOct] = [symString, octaveOffset]
|
||||
// octaveOffset is 1 when pitchInOct is closer to the next octave's root (wraps up) than to any table entry.
|
||||
@@ -850,18 +851,22 @@ function drawControlHint() {
|
||||
[`\u008428u\u008429u`,'Nav'],
|
||||
[`Pg\u008418u`,'Cue'],
|
||||
['sep'],
|
||||
['WER','ViewMode'],
|
||||
['WER','View'],
|
||||
['sep'],
|
||||
['Sp','Edit'],
|
||||
['sep'],
|
||||
['m','Mute'],
|
||||
['s','Solo'],
|
||||
['sep'],
|
||||
['Tab','Panel'],
|
||||
['Tab','Panel']
|
||||
// ['sep'],
|
||||
// ['q','Quit'],
|
||||
]
|
||||
let hintElemOrders = [
|
||||
[`\u008428u\u008429u`,'Nav'],
|
||||
[`Ent`,'Go to cue'],
|
||||
['sep'],
|
||||
['Sp','Edit'],
|
||||
['sep'],
|
||||
['Tab','Panel'],
|
||||
// ['sep'],
|
||||
@@ -871,14 +876,84 @@ function drawControlHint() {
|
||||
let hintElemPatterns = [
|
||||
[`\u008428u\u008429u`,'Nav'],
|
||||
[`Pg\u008418u`,'Ptn'],
|
||||
['sep'],
|
||||
['Sp','Edit'],
|
||||
['sep'],
|
||||
['Tab','Panel'],
|
||||
// ['sep'],
|
||||
// ['q','Quit'],
|
||||
]
|
||||
|
||||
let hintElemEditNoteValue = [ // only enabled in viewmode 'E' or in pattern editor
|
||||
[`\u008428u\u008429u`,'Nav'],
|
||||
[`Pg\u008418u`,'Cue'],
|
||||
['sep'],
|
||||
[`A${sym.doubledot}G`,'Note'],
|
||||
[`0${sym.doubledot}9`,'Oct'],
|
||||
['[]',`Tone\u008418u`],
|
||||
['sep'],
|
||||
['#',sym.sharp],
|
||||
['@','Acc'],
|
||||
['sep'],
|
||||
['=','KOff'],
|
||||
['^','KCut'],
|
||||
// ['sep'],
|
||||
// ['Sp','ExitEdit'],
|
||||
]
|
||||
let hintElemEditInstValue = [
|
||||
[`\u008428u\u008429u`,'Nav'],
|
||||
[`Pg\u008418u`,'Cue'],
|
||||
['sep'],
|
||||
[`0${sym.doubledot}9 A${sym.doubledot}F`,'Instrument'],
|
||||
['sep'],
|
||||
['Sp','ExitEdit'],
|
||||
]
|
||||
let hintElemEditVolEff = [
|
||||
[`\u008428u\u008429u`,'Nav'],
|
||||
[`Pg\u008418u`,'Cue'],
|
||||
['sep'],
|
||||
['h','Set'],
|
||||
['j','SlideDn'],
|
||||
['k','SlideUp'],
|
||||
['u','FineDn'],
|
||||
['i','FineUp'],
|
||||
[`0${sym.doubledot}9 A${sym.doubledot}F`,'Val'],
|
||||
// ['sep'],
|
||||
// ['Sp','ExitEdit'],
|
||||
]
|
||||
let hintElemEditPanEff = [
|
||||
[`\u008428u\u008429u`,'Nav'],
|
||||
[`Pg\u008418u`,'Cue'],
|
||||
['sep'],
|
||||
['h','Set'],
|
||||
['j','SlideL'],
|
||||
['k','SlideR'],
|
||||
['u','FineL'],
|
||||
['i','FineR'],
|
||||
[`0${sym.doubledot}9 A${sym.doubledot}F`,'Val'],
|
||||
// ['sep'],
|
||||
// ['Sp','ExitEdit'],
|
||||
]
|
||||
let hintElemEditFxSym = [
|
||||
[`\u008428u\u008429u`,'Nav'],
|
||||
[`Pg\u008418u`,'Cue'],
|
||||
['sep'],
|
||||
[`0${sym.doubledot}9 A${sym.doubledot}F`,`FxSym`],
|
||||
['sep'],
|
||||
['Sp','ExitEdit'],
|
||||
]
|
||||
let hintElemEditFxVal = [
|
||||
[`\u008428u\u008429u`,'Nav'],
|
||||
[`Pg\u008418u`,'Cue'],
|
||||
['sep'],
|
||||
[`0${sym.doubledot}9 A${sym.doubledot}F`,`FxVal`],
|
||||
['sep'],
|
||||
['Sp','ExitEdit'],
|
||||
]
|
||||
|
||||
const hintElemExternal = [['Tab','Panel']]
|
||||
let hintElems = [hintElemTimeline, hintElemOrders, hintElemPatterns, hintElemExternal, hintElemExternal, hintElemExternal, hintElemExternal]
|
||||
let hintElemPat = [hintElemEditNoteValue, hintElemEditInstValue, hintElemEditVolEff, hintElemEditPanEff, hintElemEditFxSym, hintElemEditFxVal]
|
||||
|
||||
// erase current line
|
||||
con.move(SCRH, 1)
|
||||
@@ -1837,9 +1912,36 @@ function makeExternalPanelDraw(progName) {
|
||||
function drawProjectContents(wo) {
|
||||
fillLine(PTNVIEW_OFFSET_Y - 1, colVoiceHdr, 255)
|
||||
for (let y = PTNVIEW_OFFSET_Y; y < SCRH; y++) fillLine(y, colBackPtn, 255)
|
||||
con.move(PTNVIEW_OFFSET_Y + 2, 3)
|
||||
con.color_pair(colStatus, 255)
|
||||
print('[Project settings — not yet implemented]')
|
||||
|
||||
let mixerflag = initialTrackerMixerflags
|
||||
let flagstrbuf = ''
|
||||
let flagstr = [
|
||||
['Linear pan','Equal-energy pan'],
|
||||
['Linear tone','Amiga tone'],
|
||||
]
|
||||
for (let i = 0; i < flagstr.length; i++) {
|
||||
let s = flagstr[i][(mixerflag >>> i) & 1 != 0]
|
||||
if (i > 0) flagstrbuf += ', ';
|
||||
flagstrbuf += s
|
||||
}
|
||||
|
||||
|
||||
let projMeta = {
|
||||
Filename: fullPathObj.string.split('\\').last(),
|
||||
Patterns: `${song.numPats}/4095 ($${song.numPats.hex03()})`,
|
||||
Cues: `${song.lastActiveCue}/1024 ($${song.lastActiveCue.hex03()})`,
|
||||
Notation: pitchTablePresets[PITCH_PRESET_IDX].name,
|
||||
Flags: `${flagstrbuf} ($${mixerflag.hex02()})`,
|
||||
}
|
||||
|
||||
Object.entries(projMeta).forEach(([key, value], index) => {
|
||||
con.move(PTNVIEW_OFFSET_Y + index, 2)
|
||||
con.color_pair(colStatus, 255); print(key)
|
||||
con.move(PTNVIEW_OFFSET_Y + index, 12)
|
||||
con.color_pair(colVoiceHdr, colBLACK); print(value)
|
||||
})
|
||||
|
||||
con.color_pair(colStatus, 255) // reset colour
|
||||
}
|
||||
function externalPanelInput(wo, event) {}
|
||||
|
||||
@@ -2148,7 +2250,7 @@ function drawGotoPopup(popup, buf) {
|
||||
const promptStr = prompts[currentPanel] || 'Number:'
|
||||
|
||||
con.move(popup.y + 2, popup.x + 2)
|
||||
con.color_pair(colStatus, colPopupBack)
|
||||
con.color_pair(colWHITE, colPopupBack)
|
||||
print(promptStr + ' ')
|
||||
con.color_pair(230, 240)
|
||||
print('[' + buf.padEnd(3, '_') + ']')
|
||||
@@ -2171,8 +2273,8 @@ function applyGoto(num) {
|
||||
}
|
||||
|
||||
function openConfirmQuit() {
|
||||
const pw = 25
|
||||
const ph = 5
|
||||
const pw = 25 + hasUnsavedChanges * 4
|
||||
const ph = 5 + hasUnsavedChanges
|
||||
const px = ((SCRW - pw) / 2 | 0) + 1
|
||||
const py = ((SCRH - ph) / 2 | 0)
|
||||
|
||||
@@ -2184,11 +2286,17 @@ function openConfirmQuit() {
|
||||
popup.drawFrame()
|
||||
|
||||
con.move(py + 2, px + 2)
|
||||
con.color_pair(colStatus, colPopupBack)
|
||||
con.color_pair(colWHITE, colPopupBack)
|
||||
print('Exit Microtone? ')
|
||||
con.color_pair(230, 240)
|
||||
print('[Y/N]')
|
||||
|
||||
if (hasUnsavedChanges) {
|
||||
con.move(py + 3, px + 2)
|
||||
con.color_pair(colWHITE, colPopupBack)
|
||||
print('You have unsaved changes.')
|
||||
}
|
||||
|
||||
con.color_pair(colStatus, 255) // reset colour
|
||||
|
||||
let result = false
|
||||
@@ -2261,6 +2369,7 @@ resetAudioDevice()
|
||||
taud.uploadTaudFile(fullPathObj.full, 0, PLAYHEAD)
|
||||
audio.setMasterVolume(PLAYHEAD, 255)
|
||||
audio.setMasterPan(PLAYHEAD, 128)
|
||||
const initialTrackerMixerflags = audio.getTrackerMixerFlags(PLAYHEAD)
|
||||
|
||||
function isExternalPanel(p) {
|
||||
return p === VIEW_SAMPLES || p === VIEW_INSTRMNT || p === VIEW_FILE
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -43,9 +43,9 @@ function _pokeU32LE(ptr, off, v) {
|
||||
*
|
||||
* @param inFile Full path with drive letter, e.g. "A:/music/song.taud"
|
||||
* @param songIndex 0-based index of the song in the SONG TABLE
|
||||
* @param targetPlaydataSlot Playhead number (0-3) to configure
|
||||
* @param playhead Playhead number (0-3) to configure
|
||||
*/
|
||||
function uploadTaudFile(inFile, songIndex, targetPlaydataSlot) {
|
||||
function uploadTaudFile(inFile, songIndex, playhead) {
|
||||
const drive = inFile[0].toUpperCase()
|
||||
const diskPath = inFile.substring(2)
|
||||
|
||||
@@ -107,6 +107,7 @@ function uploadTaudFile(inFile, songIndex, targetPlaydataSlot) {
|
||||
let numPatsHi = sys.peek(filePtr + entryOff + 6) & 0xFF
|
||||
let bpmStored = sys.peek(filePtr + entryOff + 7) & 0xFF
|
||||
let tickRate = sys.peek(filePtr + entryOff + 8) & 0xFF
|
||||
let mixerflags = sys.peek(filePtr + entryOff + 15) & 0xFF
|
||||
|
||||
let bpm = bpmStored + 24
|
||||
let patsToLoad = numPatsLo | (numPatsHi << 8)
|
||||
@@ -130,9 +131,10 @@ function uploadTaudFile(inFile, songIndex, targetPlaydataSlot) {
|
||||
}
|
||||
|
||||
// -- 8. Configure playhead ------------------------------------------------
|
||||
audio.setTrackerMode(targetPlaydataSlot)
|
||||
audio.setBPM(targetPlaydataSlot, bpm)
|
||||
audio.setTickRate(targetPlaydataSlot, tickRate > 0 ? tickRate : 6)
|
||||
audio.setTrackerMode(playhead)
|
||||
audio.setBPM(playhead, bpm)
|
||||
audio.setTickRate(playhead, tickRate > 0 ? tickRate : 6)
|
||||
audio.setTrackerMixerFlags(playhead, mixerflags)
|
||||
|
||||
|
||||
fileHandle.close()
|
||||
|
||||
@@ -2022,13 +2022,12 @@ Instrument bin: Registry for 256 instruments, formatted as:
|
||||
Uint8 Instrument Global Volume (0..255)
|
||||
* ImpulseTracker has range of 0..128; multiply by (255/128) then round to int
|
||||
* FastTracker2 has range of 0..64; multiply by (255/64) then round to int
|
||||
Bit16x8 Volume envelopes
|
||||
Bit16x12 Volume envelopes
|
||||
Byte 1: Volume (00..3F)
|
||||
Byte 2: Time until the next point, in seconds (3.5 Unsigned Minifloat). 0 = hold at this point indefinitely.
|
||||
Bit16x8 Panning envelopes
|
||||
Bit16x12 Panning envelopes
|
||||
Byte 1: Pan (00..FF)
|
||||
Byte 2: Time until the next point, in seconds (3.5 Unsigned Minifloat). 0 = hold at this point indefinitely.
|
||||
Bit16x8 Reserved
|
||||
|
||||
Play Data: play data are series of tracker-like instructions, visualised as:
|
||||
|
||||
|
||||
@@ -131,6 +131,14 @@ class AudioJSR223Delegate(private val vm: VM) {
|
||||
}
|
||||
}
|
||||
|
||||
fun setTrackerMixerFlags(playhead: Int, flags: Int) {
|
||||
getFirstSnd()?.playheads?.get(playhead)?.initialGlobalFlags = flags
|
||||
}
|
||||
|
||||
fun getTrackerMixerFlags(playhead: Int): Int? {
|
||||
return getFirstSnd()?.playheads?.get(playhead)?.initialGlobalFlags
|
||||
}
|
||||
|
||||
fun putPcmDataByPtr(playhead: Int, ptr: Int, length: Int, destOffset: Int) {
|
||||
getFirstSnd()?.let {
|
||||
val vkMult = if (ptr >= 0) 1 else -1
|
||||
|
||||
@@ -1202,8 +1202,8 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
||||
voice.envTimeSec = 0.0
|
||||
voice.envIndex = vSusStart
|
||||
voice.envVolume = (inst.volEnvelopes[voice.envIndex].value / 63.0).coerceIn(0.0, 1.0)
|
||||
} else if (voice.envIndex >= 7) {
|
||||
voice.envVolume = (inst.volEnvelopes[7].value / 63.0).coerceIn(0.0, 1.0)
|
||||
} else if (voice.envIndex >= 11) {
|
||||
voice.envVolume = (inst.volEnvelopes[11].value / 63.0).coerceIn(0.0, 1.0)
|
||||
} else {
|
||||
val vOffset = inst.volEnvelopes[voice.envIndex].offset.toDouble()
|
||||
if (vOffset == 0.0) {
|
||||
@@ -1213,12 +1213,12 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
||||
if (voice.envTimeSec >= vOffset) {
|
||||
voice.envTimeSec -= vOffset
|
||||
val nextIdx = if (vSusOn && voice.envIndex == vSusEnd) vSusStart
|
||||
else (voice.envIndex + 1).coerceAtMost(7)
|
||||
else (voice.envIndex + 1).coerceAtMost(11)
|
||||
voice.envIndex = nextIdx
|
||||
voice.envVolume = (inst.volEnvelopes[voice.envIndex].value / 63.0).coerceIn(0.0, 1.0)
|
||||
} else {
|
||||
val cur = (inst.volEnvelopes[voice.envIndex].value / 63.0).coerceIn(0.0, 1.0)
|
||||
val nxt = (inst.volEnvelopes[(voice.envIndex + 1).coerceAtMost(7)].value / 63.0).coerceIn(0.0, 1.0)
|
||||
val nxt = (inst.volEnvelopes[(voice.envIndex + 1).coerceAtMost(11)].value / 63.0).coerceIn(0.0, 1.0)
|
||||
voice.envVolume = cur + (nxt - cur) * (voice.envTimeSec / vOffset)
|
||||
}
|
||||
}
|
||||
@@ -1242,8 +1242,8 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
||||
voice.envPanTimeSec = 0.0
|
||||
voice.envPanIndex = pSusStart
|
||||
voice.envPan = inst.panEnvelopes[voice.envPanIndex].value / 255.0
|
||||
} else if (voice.envPanIndex >= 7) {
|
||||
voice.envPan = inst.panEnvelopes[7].value / 255.0
|
||||
} else if (voice.envPanIndex >= 11) {
|
||||
voice.envPan = inst.panEnvelopes[11].value / 255.0
|
||||
} else {
|
||||
val pOffset = inst.panEnvelopes[voice.envPanIndex].offset.toDouble()
|
||||
if (pOffset == 0.0) {
|
||||
@@ -1253,12 +1253,12 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
||||
if (voice.envPanTimeSec >= pOffset) {
|
||||
voice.envPanTimeSec -= pOffset
|
||||
val nextIdx = if (pSusOn && voice.envPanIndex == pSusEnd) pSusStart
|
||||
else (voice.envPanIndex + 1).coerceAtMost(7)
|
||||
else (voice.envPanIndex + 1).coerceAtMost(11)
|
||||
voice.envPanIndex = nextIdx
|
||||
voice.envPan = inst.panEnvelopes[voice.envPanIndex].value / 255.0
|
||||
} else {
|
||||
val cur = inst.panEnvelopes[voice.envPanIndex].value / 255.0
|
||||
val nxt = inst.panEnvelopes[(voice.envPanIndex + 1).coerceAtMost(7)].value / 255.0
|
||||
val nxt = inst.panEnvelopes[(voice.envPanIndex + 1).coerceAtMost(11)].value / 255.0
|
||||
voice.envPan = cur + (nxt - cur) * (voice.envPanTimeSec / pOffset)
|
||||
}
|
||||
}
|
||||
@@ -2336,12 +2336,12 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
||||
var volEnvSustain: Int, // byte 13: ut eee sss (u=enable, t=sustain (1=breaks on key-off, 0=loops forever))
|
||||
var panEnvSustain: Int, // byte 14: ut eee sss (u=enable, t=sustain (1=breaks on key-off, 0=loops forever))
|
||||
var instGlobalVolume: Int, // byte 15: instrument global volume (0..255, 255 = unity)
|
||||
var volEnvelopes: Array<TaudInstEnvPoint>, // 8 points, value 0x00-0x3F
|
||||
var panEnvelopes: Array<TaudInstEnvPoint> // 8 points, value 0x00-0xFF (0x80 = centre)
|
||||
var volEnvelopes: Array<TaudInstEnvPoint>, // 12 points, value 0x00-0x3F
|
||||
var panEnvelopes: Array<TaudInstEnvPoint> // 12 points, value 0x00-0xFF (0x80 = centre)
|
||||
) {
|
||||
constructor(index: Int) : this(index, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF,
|
||||
Array(8) { TaudInstEnvPoint(0x3F, ThreeFiveMiniUfloat(0)) },
|
||||
Array(8) { TaudInstEnvPoint(0x80, ThreeFiveMiniUfloat(0)) })
|
||||
Array(12) { TaudInstEnvPoint(0x3F, ThreeFiveMiniUfloat(0)) },
|
||||
Array(12) { TaudInstEnvPoint(0x80, ThreeFiveMiniUfloat(0)) })
|
||||
|
||||
// Funk repeat (S$Fx00) bit-mask — non-destructive XOR overlay across the loop region.
|
||||
// Lazily allocated; a 1-bit flips the byte, a 0-bit leaves it intact.
|
||||
@@ -2382,11 +2382,10 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
||||
13 -> volEnvSustain.toByte()
|
||||
14 -> panEnvSustain.toByte()
|
||||
15 -> instGlobalVolume.toByte()
|
||||
in 16..30 step 2 -> volEnvelopes[(offset - 16) / 2].value.toByte()
|
||||
in 17..31 step 2 -> volEnvelopes[(offset - 17) / 2].offset.index.toByte()
|
||||
in 32..46 step 2 -> panEnvelopes[(offset - 32) / 2].value.toByte()
|
||||
in 33..47 step 2 -> panEnvelopes[(offset - 33) / 2].offset.index.toByte()
|
||||
in 48..63 -> 0
|
||||
in 16..38 step 2 -> volEnvelopes[(offset - 16) / 2].value.toByte()
|
||||
in 17..39 step 2 -> volEnvelopes[(offset - 17) / 2].offset.index.toByte()
|
||||
in 40..62 step 2 -> panEnvelopes[(offset - 40) / 2].value.toByte()
|
||||
in 41..63 step 2 -> panEnvelopes[(offset - 41) / 2].offset.index.toByte()
|
||||
else -> throw InternalError("Bad offset $offset")
|
||||
}
|
||||
|
||||
@@ -2418,11 +2417,10 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
||||
14 -> { panEnvSustain = byte }
|
||||
15 -> { instGlobalVolume = byte and 0xFF }
|
||||
|
||||
in 16..30 step 2 -> volEnvelopes[(offset - 16) / 2].value = byte
|
||||
in 17..31 step 2 -> volEnvelopes[(offset - 17) / 2].offset = ThreeFiveMiniUfloat(byte)
|
||||
in 32..46 step 2 -> panEnvelopes[(offset - 32) / 2].value = byte
|
||||
in 33..47 step 2 -> panEnvelopes[(offset - 33) / 2].offset = ThreeFiveMiniUfloat(byte)
|
||||
in 48..63 -> {}
|
||||
in 16..38 step 2 -> volEnvelopes[(offset - 16) / 2].value = byte
|
||||
in 17..39 step 2 -> volEnvelopes[(offset - 17) / 2].offset = ThreeFiveMiniUfloat(byte)
|
||||
in 40..62 step 2 -> panEnvelopes[(offset - 40) / 2].value = byte
|
||||
in 41..63 step 2 -> panEnvelopes[(offset - 41) / 2].offset = ThreeFiveMiniUfloat(byte)
|
||||
else -> throw InternalError("Bad offset $offset")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user