mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-08 06:14:04 +09:00
Panbrello is 'Y' not 'W', oops
This commit is contained in:
@@ -493,27 +493,27 @@ A tempo slide's memory slot is separate from the set-tempo path and is private t
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## W $xxyy — Panbrello (panning vibrato) with speed $xx and depth $yy
|
## 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 $4x.
|
**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 `Wxy` uses nibbles; convert by nibble-repeat. IT's volume 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. W has its own memory slot.
|
**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:
|
**Implementation.** Identical machinery to H with a larger shift to fit the narrower volume range:
|
||||||
|
|
||||||
```
|
```
|
||||||
on row parse (W):
|
on row parse (Y):
|
||||||
if (arg >> 8) != 0: memory_W.speed = arg >> 8
|
if (arg >> 8) != 0: memory_Y.speed = arg >> 8
|
||||||
if (arg & $FF) != 0: memory_W.depth = arg & $FF
|
if (arg & $FF) != 0: memory_Y.depth = arg & $FF
|
||||||
|
|
||||||
on every tick (including tick 0):
|
on every tick (including tick 0):
|
||||||
sine = ModSinusTable[(lfo_pos >> 2) & $3F]
|
sine = ModSinusTable[(lfo_pos >> 2) & $3F]
|
||||||
vol_delta = (sine × memory_W.depth) >> 9
|
vol_delta = (sine × memory_Y.depth) >> 9
|
||||||
applied_vol = clamp(base_vol + vol_delta, 0, $3F)
|
applied_vol = clamp(base_vol + vol_delta, 0, $3F)
|
||||||
lfo_pos = (lfo_pos + memory_W.speed × 4) & $FF
|
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 $4x waveform nibble bit 2: cleared means retrigger on new note, set means preserve LFO position.
|
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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -595,11 +595,11 @@ ProTracker `E5x` maps to Taud `S $2x00` with the same index meaning.
|
|||||||
|
|
||||||
## S $5x00 — Panbrello LFO waveform
|
## S $5x00 — Panbrello LFO waveform
|
||||||
|
|
||||||
**Plain.** Selects the shape of the panbrello (W) oscillator; value encoding is identical to S $3x.
|
**Plain.** Selects the shape of the panbrello (Y) oscillator; value encoding is identical to S $3x.
|
||||||
|
|
||||||
**Compatibility.** IT `S5x` maps directly.
|
**Compatibility.** IT `S5x` maps directly.
|
||||||
|
|
||||||
**Implementation.** As for S $3x, but applied to W's separate state (`panbrello_waveform`, `panbrello_retrigger`, and panbrello `lfo_pos`).
|
**Implementation.** As for S $3x, but applied to Y's separate state (`panbrello_waveform`, `panbrello_retrigger`, and panbrello `lfo_pos`).
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -1089,7 +1088,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
|||||||
// 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,
|
||||||
// W=0x20 panbrello).
|
// 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.
|
||||||
@@ -1154,6 +1153,9 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
|||||||
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_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 =
|
||||||
@@ -1475,11 +1477,11 @@ 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_W -> {
|
EffectOp.OP_Y -> {
|
||||||
val sp = (rawArg ushr 8) and 0xFF
|
val sp = (rawArg ushr 8) and 0xFF
|
||||||
val dp = rawArg and 0xFF
|
val dp = rawArg and 0xFF
|
||||||
if (sp != 0) voice.mem.wSpeed = sp
|
if (sp != 0) voice.mem.ySpeed = sp
|
||||||
if (dp != 0) voice.mem.wDepth = dp
|
if (dp != 0) voice.mem.yDepth = dp
|
||||||
voice.panbrelloActive = true
|
voice.panbrelloActive = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1621,12 +1623,12 @@ 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 (W) — modulates panning around base.
|
// Panbrello (Y) — modulates panning around base.
|
||||||
if (voice.panbrelloActive) {
|
if (voice.panbrelloActive) {
|
||||||
val sine = lfoSample(voice.panbrelloLfoPos, voice.panbrelloWave)
|
val sine = lfoSample(voice.panbrelloLfoPos, voice.panbrelloWave)
|
||||||
val panDelta = (sine * voice.mem.wDepth) shr 9
|
val panDelta = (sine * voice.mem.yDepth) shr 9
|
||||||
voice.rowPan = ((voice.channelPan ushr 2) + panDelta).coerceIn(0, 0x3F)
|
voice.rowPan = ((voice.channelPan ushr 2) + panDelta).coerceIn(0, 0x3F)
|
||||||
voice.panbrelloLfoPos = (voice.panbrelloLfoPos + voice.mem.wSpeed * 4) and 0xFF
|
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).
|
||||||
@@ -1866,9 +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
|
||||||
// W (panbrello) — private speed and depth.
|
// Y (panbrello) — private speed and depth.
|
||||||
var wSpeed: Int = 0
|
var ySpeed: Int = 0
|
||||||
var wDepth: 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
|
||||||
@@ -1930,7 +1932,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
|||||||
var tremoloWave = 0
|
var tremoloWave = 0
|
||||||
var tremoloRetrig = true
|
var tremoloRetrig = true
|
||||||
|
|
||||||
// Panbrello (W) — uses memW.
|
// Panbrello (Y) — uses memY.
|
||||||
var panbrelloActive = false
|
var panbrelloActive = false
|
||||||
var panbrelloLfoPos = 0
|
var panbrelloLfoPos = 0
|
||||||
var panbrelloWave = 0
|
var panbrelloWave = 0
|
||||||
|
|||||||
Reference in New Issue
Block a user