mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-06 05:28:31 +09:00
Compare commits
5 Commits
8d28bde119
...
6d70960e5c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d70960e5c | ||
|
|
3d99568359 | ||
|
|
1fe966ca09 | ||
|
|
037b2c1a16 | ||
|
|
ea09065802 |
@@ -1,6 +1,6 @@
|
|||||||
# Taud Tracker Effect Command Reference
|
# Taud Tracker Effect Command Reference
|
||||||
|
|
||||||
Taud is a tracker-style music format derived from ScreamTracker 3's pattern command set, extended to 16-bit effect arguments and a 4096-tone equal-temperament pitch grid. This document defines every effect command a Taud engine must implement. Each command entry has three parts: a plain explanation for composers, compatibility notes for converting patterns from ScreamTracker 3 (ST3) or ProTracker (PT), and implementation details for engine writers.
|
Taud is a tracker-style music format derived from ScreamTracker 3's pattern command set, extended to 16-bit effect arguments and a 4096-tone equal-temperament pitch grid. This document defines every effect command a Taud engine must implement. Each command entry has three parts: a plain explanation for composers, compatibility notes for converting patterns from ScreamTracker 3 (ST3), ImpulseTracker (IT) or ProTracker (PT), and implementation details for engine writers.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -493,6 +493,30 @@ A tempo slide's memory slot is separate from the set-tempo path and is private t
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Y $xxyy — Panbrello (panning vibrato) with speed $xx and depth $yy
|
||||||
|
|
||||||
|
**Plain.** Modulates panning with an LFO, symmetrically with H's pitch modulation. `$xx` is LFO speed, `$yy` depth; the waveform is selected by S $5x.
|
||||||
|
|
||||||
|
**Compatibility.** IT `Yxy` uses nibbles; convert by nibble-repeat. IT's panning cap is $40; Taud's is $3F — very deep vibrato that would have briefly clipped at $40 in IT may clip slightly earlier in Taud. Y has its own memory slot.
|
||||||
|
|
||||||
|
**Implementation.** Identical machinery to H with a larger shift to fit the narrower volume range:
|
||||||
|
|
||||||
|
```
|
||||||
|
on row parse (Y):
|
||||||
|
if (arg >> 8) != 0: memory_Y.speed = arg >> 8
|
||||||
|
if (arg & $FF) != 0: memory_Y.depth = arg & $FF
|
||||||
|
|
||||||
|
on every tick (including tick 0):
|
||||||
|
sine = ModSinusTable[(lfo_pos >> 2) & $3F]
|
||||||
|
vol_delta = (sine × memory_Y.depth) >> 9
|
||||||
|
applied_vol = clamp(base_vol + vol_delta, 0, $3F)
|
||||||
|
lfo_pos = (lfo_pos + memory_Y.speed × 4) & $FF
|
||||||
|
```
|
||||||
|
|
||||||
|
Peak at maximum settings: $7F × $FF >> 9 = $3F — the full panning range. Retrigger behaviour tracks the S $5x waveform nibble bit 2: cleared means retrigger on new note, set means preserve LFO position.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# The S subcommand family
|
# The S subcommand family
|
||||||
|
|
||||||
S is a multiplexing opcode; the **high nibble of the high byte** selects the sub-effect, and the remainder is the sub-argument.
|
S is a multiplexing opcode; the **high nibble of the high byte** selects the sub-effect, and the remainder is the sub-argument.
|
||||||
@@ -567,6 +591,17 @@ ProTracker `E5x` maps to Taud `S $2x00` with the same index meaning.
|
|||||||
|
|
||||||
**Implementation.** As for S $3x, but applied to R's separate state (`tremolo_waveform`, `tremolo_retrigger`, and tremolo `lfo_pos`).
|
**Implementation.** As for S $3x, but applied to R's separate state (`tremolo_waveform`, `tremolo_retrigger`, and tremolo `lfo_pos`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## S $5x00 — Panbrello LFO waveform
|
||||||
|
|
||||||
|
**Plain.** Selects the shape of the panbrello (Y) oscillator; value encoding is identical to S $3x.
|
||||||
|
|
||||||
|
**Compatibility.** IT `S5x` maps directly.
|
||||||
|
|
||||||
|
**Implementation.** As for S $3x, but applied to Y's separate state (`panbrello_waveform`, `panbrello_retrigger`, and panbrello `lfo_pos`).
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## S $80xx — Set channel pan position
|
## S $80xx — Set channel pan position
|
||||||
|
|||||||
@@ -8,9 +8,10 @@ const win = require("wintex")
|
|||||||
const font = require("font")
|
const font = require("font")
|
||||||
const taud = require("taud")
|
const taud = require("taud")
|
||||||
|
|
||||||
|
font.setLowRom("A:/tvdos/bin/tautfont_low.chr")
|
||||||
font.setHighRom("A:/tvdos/bin/tautfont_high.chr")
|
font.setHighRom("A:/tvdos/bin/tautfont_high.chr")
|
||||||
|
|
||||||
const BUILD_DATE = "260423"
|
const BUILD_DATE = "260424"
|
||||||
const TRACKER_SIGNATURE = "TsvmTaut"+BUILD_DATE // 14-byte string
|
const TRACKER_SIGNATURE = "TsvmTaut"+BUILD_DATE // 14-byte string
|
||||||
|
|
||||||
const MIDDOT = "\u00FA"
|
const MIDDOT = "\u00FA"
|
||||||
@@ -71,7 +72,7 @@ middot:MIDDOT
|
|||||||
const fxNames = {
|
const fxNames = {
|
||||||
A:"Set tick speed",
|
A:"Set tick speed",
|
||||||
B:"Jump to order",
|
B:"Jump to order",
|
||||||
C:"Break pattern to",
|
C:"Break pattern",
|
||||||
D:"Volume slide",
|
D:"Volume slide",
|
||||||
E:"Pitch down",
|
E:"Pitch down",
|
||||||
F:"Pitch up",
|
F:"Pitch up",
|
||||||
@@ -80,13 +81,13 @@ H:"Vibrato",
|
|||||||
U:"Fine vibrato",
|
U:"Fine vibrato",
|
||||||
I:"Tremor",
|
I:"Tremor",
|
||||||
J:"Arpeggio",
|
J:"Arpeggio",
|
||||||
K:"Vibrato + vol slide",
|
K:"Vibra+v.slide",
|
||||||
L:"Portamento + vol slide",
|
L:"Porta+v.slide",
|
||||||
O:"Sample offset",
|
O:"Sample offset",
|
||||||
Q:"Retrigger",
|
Q:"Retrigger",
|
||||||
R:"Tremolo",
|
R:"Tremolo",
|
||||||
T:"Tempo",
|
T:"Tempo",
|
||||||
V:"Gloval volume",
|
V:"Global volume",
|
||||||
S:"Special",
|
S:"Special",
|
||||||
S1:"Glissando ctrl",
|
S1:"Glissando ctrl",
|
||||||
S2:"Sample finetune",
|
S2:"Sample finetune",
|
||||||
@@ -154,8 +155,8 @@ 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}`]},
|
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],
|
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}`]},
|
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:"Shierlu", table:[0x0,0x184,0x2B8,0x43C,0x570,0x6F4,0x828,0x95C,0xAE0,0xC14,0xD98,0xECC],
|
10123:{index:10123,name:"Shi'er lu", table:[0x0,0x184,0x2B8,0x43C,0x570,0x6F4,0x828,0x95C,0xAE0,0xC14,0xD98,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}`]},
|
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`]},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -165,9 +166,9 @@ sym:[`C${sym.accnull}`,`C${sym.sharp}`,`D${sym.accnull}`,`D${sym.sharp}`,`E${sym
|
|||||||
const volEffSym = [sym.volset, sym.volup, sym.voldn, sym.volfineup, sym.volfinedn]
|
const volEffSym = [sym.volset, sym.volup, sym.voldn, sym.volfineup, sym.volfinedn]
|
||||||
const panEffSym = [sym.panset, sym.panle, sym.panri, sym.panfinele, sym.panfineri]
|
const panEffSym = [sym.panset, sym.panle, sym.panri, sym.panfinele, sym.panfineri]
|
||||||
|
|
||||||
const colNote = 239
|
const colNote = 254
|
||||||
const colInst = 114
|
const colInst = 114
|
||||||
const colVol = 117
|
const colVol = 155
|
||||||
const colPan = 221
|
const colPan = 221
|
||||||
const colEffOp = 213
|
const colEffOp = 213
|
||||||
const colEffArg = 231
|
const colEffArg = 231
|
||||||
@@ -548,8 +549,8 @@ function drawPatternView() {
|
|||||||
|
|
||||||
function drawControlHint() {
|
function drawControlHint() {
|
||||||
let hintElem = [
|
let hintElem = [
|
||||||
[`\u008427u\u008425u\u008424u\u008426u`,'Ptn'],
|
[`\u008428u\u008429u`,'Ptn'],
|
||||||
[`Pg\u008424u\u008425u`,'Cue'],
|
[`Pg\u008418u`,'Cue'],
|
||||||
['sep'],
|
['sep'],
|
||||||
['F5','Song'],
|
['F5','Song'],
|
||||||
['F6','Cue'],
|
['F6','Cue'],
|
||||||
@@ -965,6 +966,7 @@ while (!exitFlag) {
|
|||||||
drawVoiceHeaders()
|
drawVoiceHeaders()
|
||||||
drawSeparators(separatorStyle)
|
drawSeparators(separatorStyle)
|
||||||
drawStatusBar()
|
drawStatusBar()
|
||||||
|
drawVoiceDetail()
|
||||||
}
|
}
|
||||||
else if (keysym === "m" || keysym === "M") { toggleMute(cursorVox) }
|
else if (keysym === "m" || keysym === "M") { toggleMute(cursorVox) }
|
||||||
else if (keysym === "s" || keysym === "S") { toggleSolo(cursorVox) }
|
else if (keysym === "s" || keysym === "S") { toggleSolo(cursorVox) }
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
BIN
assets/disk0/tvdos/bin/tautfont_low.chr
Normal file
BIN
assets/disk0/tvdos/bin/tautfont_low.chr
Normal file
Binary file not shown.
@@ -2178,7 +2178,7 @@ Tracker Note Effects has been moved to `TAUD_NOTE_EFFECTS.md`
|
|||||||
**Taud serialisation format**
|
**Taud serialisation format**
|
||||||
Created by CuriousTorvald on 2026-04-19
|
Created by CuriousTorvald on 2026-04-19
|
||||||
|
|
||||||
This is a file format for Taud tracker data.
|
This is a file format for Taud tracker data. Taud can be extended with project data in backward-and-forward-compatible manner.
|
||||||
|
|
||||||
Endianness: Little
|
Endianness: Little
|
||||||
|
|
||||||
@@ -2194,17 +2194,18 @@ Endianness: Little
|
|||||||
[PATTERN BIN for SONG 2]
|
[PATTERN BIN for SONG 2]
|
||||||
[CUE SHEET for SONG 2]
|
[CUE SHEET for SONG 2]
|
||||||
...
|
...
|
||||||
|
[PROJECT DATA] (optional)
|
||||||
|
|
||||||
## Header
|
## Header
|
||||||
Byte[8] Magic
|
Byte[8] Magic
|
||||||
Uint8 Format version (always 1)
|
Uint8 Format version (always 1)
|
||||||
Uint8 Number of songs in SONG TABLE
|
Uint8 Number of songs in SONG TABLE
|
||||||
Uint32 Compressed size of SAMPLE+INST section (used to calculate offset to SONG TABLE)
|
Uint32 Compressed size of SAMPLE+INST section (used to calculate offset to SONG TABLE)
|
||||||
Uint32 Reserved for Taud Project Format. Fill with zero
|
Uint32 Offset to Project Data. Zero if Project Data is nonexistent
|
||||||
Byte[14]Tracker/Converter signature
|
Byte[14]Tracker/Converter signature
|
||||||
|
|
||||||
## SONG TABLE
|
## Song Table
|
||||||
Rows of 16 bytes:
|
* Rows of 16 bytes:
|
||||||
Uint32 Song offset
|
Uint32 Song offset
|
||||||
Uint8 Number of voices
|
Uint8 Number of voices
|
||||||
Uint16 Number of patterns (0 is invalid. pattern bin length = numPats * 8 bytes)
|
Uint16 Number of patterns (0 is invalid. pattern bin length = numPats * 8 bytes)
|
||||||
@@ -2214,38 +2215,20 @@ Rows of 16 bytes:
|
|||||||
Float32 Frequency at the base note. Default (A440) is 440.0. If zero, assume the default value
|
Float32 Frequency at the base note. Default (A440) is 440.0. If zero, assume the default value
|
||||||
Byte[1] Reserved for future versions
|
Byte[1] Reserved for future versions
|
||||||
|
|
||||||
Taud device can queue up to 2 "playdata" in its buffer, which can be interpreted as a song.
|
Taud device can queue up to 2 "playdata" in its buffer, which can be interpreted as a song.
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
* Known standard tunings
|
||||||
|
A440. ISO standard. Tracker default
|
||||||
|
C256. Power of two
|
||||||
|
C311. East Asian tuning (ROK National Gugak Center standard)
|
||||||
|
|
||||||
**Taud Project Format** is an extension to Taud format
|
## Pattern Bin and Cue Sheet
|
||||||
Created by CuriousTorvald on 2026-04-22
|
Raw Pattern Bin/Cue Sheet images
|
||||||
|
|
||||||
Endianness: Little
|
|
||||||
|
|
||||||
# File Structure
|
|
||||||
\x1F T S V M a u d
|
|
||||||
[HEADER] (modified)
|
|
||||||
[SAMPLE+INSTRUMENT BIN IMAGE (GZip or Zstd compressed. Read 4-byte magic to determine)]
|
|
||||||
[SONG TABLE]
|
|
||||||
[PATTERN BIN for SONG 0]
|
|
||||||
[CUE SHEET for SONG 0]
|
|
||||||
[PATTERN BIN for SONG 1]
|
|
||||||
[CUE SHEET for SONG 1]
|
|
||||||
[PATTERN BIN for SONG 2]
|
|
||||||
[CUE SHEET for SONG 2]
|
|
||||||
...
|
|
||||||
[PROJECT DATA] (new!)
|
|
||||||
|
|
||||||
## Header
|
|
||||||
Byte[8] Magic
|
|
||||||
Uint8 Format version (always 129; high-bit set and number 0x01)
|
|
||||||
Uint8 Number of songs in SONG TABLE
|
|
||||||
Uint32 Compressed size of SAMPLE+INST section (used to calculate offset to SONG TABLE)
|
|
||||||
Uint32 Offset to Project Data (low twobyte)
|
|
||||||
Byte[14]Tracker/Converter signature
|
|
||||||
|
|
||||||
## Project Data
|
## Project Data
|
||||||
|
|
||||||
|
Project Data is just a concatenation of blocks identified by their FourCC.
|
||||||
|
|
||||||
Byte[8] Magic (\x1E T a u d P r J)
|
Byte[8] Magic (\x1E T a u d P r J)
|
||||||
Byte[8] Reserved
|
Byte[8] Reserved
|
||||||
* Repetition of
|
* Repetition of
|
||||||
@@ -2278,6 +2261,15 @@ prefixes:
|
|||||||
Uint8 Song index
|
Uint8 Song index
|
||||||
Uint32 Size of this table following this field
|
Uint32 Size of this table following this field
|
||||||
Uint16 Notation used for this song (takes notation index)
|
Uint16 Notation used for this song (takes notation index)
|
||||||
|
0: raw numbers
|
||||||
|
10*n: TET-number times 10 (12-TET = 120)
|
||||||
|
* Following systems have alternative notation conventions:
|
||||||
|
531: 53-TET Pythagorean Notation
|
||||||
|
* Following list defines ethnic notations in 12-tone scale
|
||||||
|
10121: Pythagorean Diminished Fifth
|
||||||
|
10122: Pythagorean Augmented Fourth
|
||||||
|
10123: Shi'er lü (East Asian traditional tuning)
|
||||||
|
|
||||||
Byte[*] Song name, null terminated. Encoding: UTF-8
|
Byte[*] Song name, null terminated. Encoding: UTF-8
|
||||||
Byte[*] Song composer, null terminated. Encoding: UTF-8
|
Byte[*] Song composer, null terminated. Encoding: UTF-8
|
||||||
Byte[*] Song copyright string, null terminated. Encoding: UTF-8
|
Byte[*] Song copyright string, null terminated. Encoding: UTF-8
|
||||||
@@ -2299,7 +2291,14 @@ prefixes:
|
|||||||
|
|
||||||
Note: custom notations will use internal index 65535 down to 65520 (index 0 = 65535, index 15 = 65520)
|
Note: custom notations will use internal index 65535 down to 65520 (index 0 = 65535, index 15 = 65520)
|
||||||
|
|
||||||
|
Note Tuning:
|
||||||
|
1. "Base Note at C3" will be derived using "Current Tuning Base Note" and "Frequency at the Base Note" from the song table. If the values are A3,440Hz, it will be converted to C3,261.6255653Hz
|
||||||
|
2. Frequency at C4 will be (Base Note at C3) × (Interval Size)
|
||||||
|
3. 4096 notes will be equidistance-distributed between (Frequency at C3) and (Frequency at C4), with logarithmic pitch progression; this builds the frequency-offset table
|
||||||
|
4. Frequency-Offset Table from the previous step will be applied against the "Base Note at C3" to construct the notes within the notation. Value at index zero of the Frequency Table must be 0
|
||||||
|
5. The progress will continue outside the "root interval" (C3..C4) to build a complete note-to-frequency table
|
||||||
|
|
||||||
|
Note: if your sample is pre-tuned for your system, keep the project setting as A4,440Hz. If you are not working with the conventional octave system, you still need to specify the Interval Size
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import net.torvald.UnsafePtr
|
|||||||
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.toUint
|
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.toUint
|
||||||
import net.torvald.tsvm.ThreeFiveMiniUfloat
|
import net.torvald.tsvm.ThreeFiveMiniUfloat
|
||||||
import net.torvald.tsvm.VM
|
import net.torvald.tsvm.VM
|
||||||
import net.torvald.tsvm.getHashStr
|
|
||||||
import net.torvald.tsvm.toInt
|
import net.torvald.tsvm.toInt
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
@@ -1088,7 +1087,8 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
|||||||
// H=0x11 vibrato, I=0x12 tremor, J=0x13 arpeggio,
|
// H=0x11 vibrato, I=0x12 tremor, J=0x13 arpeggio,
|
||||||
// K=0x14 K, L=0x15 L, O=0x18 sample offset,
|
// K=0x14 K, L=0x15 L, O=0x18 sample offset,
|
||||||
// Q=0x1A retrig, R=0x1B tremolo, S=0x1C subcommands,
|
// Q=0x1A retrig, R=0x1B tremolo, S=0x1C subcommands,
|
||||||
// T=0x1D tempo, U=0x1E fine vibrato, V=0x1F global vol).
|
// T=0x1D tempo, U=0x1E fine vibrato, V=0x1F global vol,
|
||||||
|
// Y=0x22 panbrello).
|
||||||
// K (0x14) and L (0x15) are intentionally no-op in the engine — the
|
// K (0x14) and L (0x15) are intentionally no-op in the engine — the
|
||||||
// converter is required to split them into a recall-only H/G plus a
|
// converter is required to split them into a recall-only H/G plus a
|
||||||
// volume-column slide cell.
|
// volume-column slide cell.
|
||||||
@@ -1152,6 +1152,10 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
|||||||
const val OP_T = 0x1D
|
const val OP_T = 0x1D
|
||||||
const val OP_U = 0x1E
|
const val OP_U = 0x1E
|
||||||
const val OP_V = 0x1F
|
const val OP_V = 0x1F
|
||||||
|
const val OP_W = 0x20
|
||||||
|
const val OP_X = 0x21
|
||||||
|
const val OP_Y = 0x22
|
||||||
|
const val OP_Z = 0x23
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun computePlaybackRate(inst: TaudInst, noteVal: Int): Double =
|
private fun computePlaybackRate(inst: TaudInst, noteVal: Int): Double =
|
||||||
@@ -1241,9 +1245,10 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
|||||||
}
|
}
|
||||||
voice.rowVolume = voice.channelVolume
|
voice.rowVolume = voice.channelVolume
|
||||||
voice.noteWasCut = false
|
voice.noteWasCut = false
|
||||||
// Vibrato/tremolo retrigger: reset LFO position when waveform requests it.
|
// Vibrato/tremolo/panbrello retrigger: reset LFO position when waveform requests it.
|
||||||
if (voice.vibratoRetrig) voice.vibratoLfoPos = 0
|
if (voice.vibratoRetrig) voice.vibratoLfoPos = 0
|
||||||
if (voice.tremoloRetrig) voice.tremoloLfoPos = 0
|
if (voice.tremoloRetrig) voice.tremoloLfoPos = 0
|
||||||
|
if (voice.panbrelloRetrig) voice.panbrelloLfoPos = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun applyVolColumn(voice: Voice, value: Int, sel: Int) {
|
private fun applyVolColumn(voice: Voice, value: Int, sel: Int) {
|
||||||
@@ -1301,6 +1306,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
|||||||
voice.tremorOn = 0
|
voice.tremorOn = 0
|
||||||
voice.vibratoActive = false
|
voice.vibratoActive = false
|
||||||
voice.tremoloActive = false
|
voice.tremoloActive = false
|
||||||
|
voice.panbrelloActive = false
|
||||||
voice.retrigActive = false
|
voice.retrigActive = false
|
||||||
voice.tempoSlideDir = 0
|
voice.tempoSlideDir = 0
|
||||||
voice.volColSlideUp = 0; voice.volColSlideDown = 0
|
voice.volColSlideUp = 0; voice.volColSlideDown = 0
|
||||||
@@ -1471,6 +1477,13 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
|||||||
val hi = (rawArg ushr 8) and 0xFF
|
val hi = (rawArg ushr 8) and 0xFF
|
||||||
playhead.globalVolume = hi
|
playhead.globalVolume = hi
|
||||||
}
|
}
|
||||||
|
EffectOp.OP_Y -> {
|
||||||
|
val sp = (rawArg ushr 8) and 0xFF
|
||||||
|
val dp = rawArg and 0xFF
|
||||||
|
if (sp != 0) voice.mem.ySpeed = sp
|
||||||
|
if (dp != 0) voice.mem.yDepth = dp
|
||||||
|
voice.panbrelloActive = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1486,6 +1499,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
|||||||
}
|
}
|
||||||
0x3 -> { voice.vibratoWave = x and 3; voice.vibratoRetrig = (x and 4) == 0 }
|
0x3 -> { voice.vibratoWave = x and 3; voice.vibratoRetrig = (x and 4) == 0 }
|
||||||
0x4 -> { voice.tremoloWave = x and 3; voice.tremoloRetrig = (x and 4) == 0 }
|
0x4 -> { voice.tremoloWave = x and 3; voice.tremoloRetrig = (x and 4) == 0 }
|
||||||
|
0x5 -> { voice.panbrelloWave = x and 3; voice.panbrelloRetrig = (x and 4) == 0 }
|
||||||
0x8 -> {
|
0x8 -> {
|
||||||
// S$80xx — full 8-bit pan; arg low byte is the value.
|
// S$80xx — full 8-bit pan; arg low byte is the value.
|
||||||
voice.channelPan = arg and 0xFF
|
voice.channelPan = arg and 0xFF
|
||||||
@@ -1609,6 +1623,14 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
|||||||
voice.tremoloLfoPos = (voice.tremoloLfoPos + voice.mem.rSpeed * 4) and 0xFF
|
voice.tremoloLfoPos = (voice.tremoloLfoPos + voice.mem.rSpeed * 4) and 0xFF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Panbrello (Y) — modulates panning around base.
|
||||||
|
if (voice.panbrelloActive) {
|
||||||
|
val sine = lfoSample(voice.panbrelloLfoPos, voice.panbrelloWave)
|
||||||
|
val panDelta = (sine * voice.mem.yDepth) shr 9
|
||||||
|
voice.rowPan = ((voice.channelPan ushr 2) + panDelta).coerceIn(0, 0x3F)
|
||||||
|
voice.panbrelloLfoPos = (voice.panbrelloLfoPos + voice.mem.ySpeed * 4) and 0xFF
|
||||||
|
}
|
||||||
|
|
||||||
// Arpeggio (J) — overrides pitchToMixer for this tick (overlay on basePitch).
|
// Arpeggio (J) — overrides pitchToMixer for this tick (overlay on basePitch).
|
||||||
if (voice.arpActive) {
|
if (voice.arpActive) {
|
||||||
val voiceIdx = ts.tickInRow % 3
|
val voiceIdx = ts.tickInRow % 3
|
||||||
@@ -1846,6 +1868,9 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
|||||||
// R (tremolo) — private speed and depth.
|
// R (tremolo) — private speed and depth.
|
||||||
var rSpeed: Int = 0
|
var rSpeed: Int = 0
|
||||||
var rDepth: Int = 0
|
var rDepth: Int = 0
|
||||||
|
// Y (panbrello) — private speed and depth.
|
||||||
|
var ySpeed: Int = 0
|
||||||
|
var yDepth: Int = 0
|
||||||
// Private slots
|
// Private slots
|
||||||
var d: Int = 0
|
var d: Int = 0
|
||||||
var i: Int = 0
|
var i: Int = 0
|
||||||
@@ -1907,6 +1932,12 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
|||||||
var tremoloWave = 0
|
var tremoloWave = 0
|
||||||
var tremoloRetrig = true
|
var tremoloRetrig = true
|
||||||
|
|
||||||
|
// Panbrello (Y) — uses memY.
|
||||||
|
var panbrelloActive = false
|
||||||
|
var panbrelloLfoPos = 0
|
||||||
|
var panbrelloWave = 0
|
||||||
|
var panbrelloRetrig = true
|
||||||
|
|
||||||
// Glissando flag (S$1x).
|
// Glissando flag (S$1x).
|
||||||
var glissandoOn = false
|
var glissandoOn = false
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user