mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-06 05:28:31 +09:00
taud: panning law toggle
This commit is contained in:
@@ -749,6 +749,27 @@ NOTE: **`3.00` — is No-op**
|
||||
|
||||
---
|
||||
|
||||
# Effects That Modifies Global Behaviour
|
||||
|
||||
Effects in this section modifies the behaviour of the mixer. Primary intention of the commands is to provide switches for legacy tracker and modern DAW behaviours.
|
||||
|
||||
## 1 $01xx — Set stereo panning law
|
||||
|
||||
**Plain.** Sets how the mixer should treat the panning. Available modes are:
|
||||
|
||||
- 0: Linear panning mode (tracker-accurate). Centre panning gets 3 dB boost. Default setting.
|
||||
- 1: Equal-power panning mode. L/R amplitude is at 0.707 when centre-panned.
|
||||
|
||||
**Implementation.**
|
||||
- Mode 0:
|
||||
- L_gain = if (pan < 0x80) 1.0 else 1.0 - (pan - 128.0) / 128.0
|
||||
- R_gain = if (pan < 0x80) pan / 128.0 else 1.0
|
||||
- Mode 1:
|
||||
- L_gain = cos(pi*x / 512.0)
|
||||
- R_gain = sin(pi*x / 512.0)
|
||||
|
||||
---
|
||||
|
||||
# ProTracker to Taud conversion table
|
||||
|
||||
This table maps each PT effect to its Taud equivalent. Arguments follow PT's two-nibble form and expand to Taud's 16-bit form as shown.
|
||||
|
||||
@@ -75,7 +75,7 @@ play:"\u008422u\u008423u",
|
||||
|
||||
const fxNames = {
|
||||
'0':"No effect ",
|
||||
'1':"UNIMPLEMENTED",
|
||||
'1':"Mixer config ", // Taud: 1 01xx: set stereo panning law
|
||||
'2':"UNIMPLEMENTED",
|
||||
'3':"UNIMPLEMENTED",
|
||||
'4':"UNIMPLEMENTED",
|
||||
@@ -94,26 +94,26 @@ G:"Portamento ",
|
||||
H:"Vibrato ",
|
||||
I:"Tremor ",
|
||||
J:"Arpeggio ",
|
||||
K:"UNIMPLEMENTED",
|
||||
L:"UNIMPLEMENTED",
|
||||
M:"UNIMPLEMENTED",
|
||||
N:"UNIMPLEMENTED",
|
||||
K:"UNIMPLEMENTED", // Volume slide+Vibrato. Use H0000 and VolEff instead
|
||||
L:"UNIMPLEMENTED", // Volume slide+Portamento. Use G0000 and VolEff instead
|
||||
M:"UNIMPLEMENTED", // IT: Set channel volume. Use VolEff instead
|
||||
N:"UNIMPLEMENTED", // IT: Channel volume slide. Use VolEff instead
|
||||
O:"Sample offset",
|
||||
P:"UNIMPLEMENTED",
|
||||
P:"UNIMPLEMENTED", // IT: panning slide. Use PanEff instead
|
||||
Q:"Retrigger ",
|
||||
R:"Tremolo ",
|
||||
S:"Special ",
|
||||
S0:"UNIMPLEMENTED",
|
||||
S0:"UNIMPLEMENTED", // PT: Set audio filter.
|
||||
S1:"Gliss. ctrl ",
|
||||
S2:"Sample tune ",
|
||||
S3:"Vibrato LFO ",
|
||||
S4:"Tremolo LFO ",
|
||||
S5:"Panbrello LFO",
|
||||
S6:"UNIMPLEMENTED",
|
||||
S7:"UNIMPLEMENTED",
|
||||
S8:"Channel pan ",
|
||||
S9:"UNIMPLEMENTED",
|
||||
SA:"UNIMPLEMENTED",
|
||||
S6:"UNIMPLEMENTED", // IT: Fine pattern delay.
|
||||
S7:"UNIMPLEMENTED", // IT: misc. functions
|
||||
S8:"Channel pan ", // Taud: 8-bit channel panning.
|
||||
S9:"UNIMPLEMENTED", // IT: Sound control.
|
||||
SA:"UNIMPLEMENTED", // SC3: Stereo control. IT: Sample offset high twobyte.
|
||||
SB:"Pattern loop ",
|
||||
SC:"Note cut ",
|
||||
SD:"Note delay ",
|
||||
@@ -122,10 +122,10 @@ SF:"Funk it ",
|
||||
T:"Tempo ",
|
||||
U:"Fine vibrato ",
|
||||
V:"Global volume",
|
||||
W:"UNIMPLEMENTED",
|
||||
X:"UNIMPLEMENTED",
|
||||
W:"UNIMPLEMENTED", // IT: Global volume slide.
|
||||
X:"UNIMPLEMENTED", // IT: 8-bit channel panning. Use PanEff or S80xx instead
|
||||
Y:"Panbrello ",
|
||||
Z:"UNIMPLEMENTED",
|
||||
Z:"UNIMPLEMENTED", // IT: MIDI macro.
|
||||
}
|
||||
const panFxNames = {
|
||||
0:"Set to",
|
||||
|
||||
@@ -12,8 +12,11 @@ import net.torvald.tsvm.ThreeFiveMiniUfloat
|
||||
import net.torvald.tsvm.VM
|
||||
import net.torvald.tsvm.toInt
|
||||
import java.io.ByteArrayInputStream
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.math.sin
|
||||
import kotlin.math.PI
|
||||
|
||||
private class RenderRunnable(val playhead: AudioAdapter.Playhead) : Runnable {
|
||||
private fun printdbg(msg: Any) {
|
||||
@@ -1133,6 +1136,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
||||
// Letters A..Z map to 0x0A..0x23 (digit value 10..35).
|
||||
private object EffectOp {
|
||||
const val OP_NONE = 0x00
|
||||
const val OP_1 = 0x01
|
||||
const val OP_A = 0x0A
|
||||
const val OP_B = 0x0B
|
||||
const val OP_C = 0x0C
|
||||
@@ -1352,6 +1356,10 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
||||
private fun applyEffectRow(ts: TrackerState, playhead: Playhead, voice: Voice, vi: Int, op: Int, rawArg: Int) {
|
||||
when (op) {
|
||||
EffectOp.OP_NONE -> {}
|
||||
EffectOp.OP_1 -> {
|
||||
// 1 $01xx — Set stereo panning law. High byte selects subcommand; only $01 is defined.
|
||||
if ((rawArg ushr 8) == 0x01) ts.panLaw = rawArg and 0xFF
|
||||
}
|
||||
EffectOp.OP_A -> {
|
||||
val tr = (rawArg ushr 8) and 0xFF
|
||||
if (tr != 0) playhead.tickRate = tr
|
||||
@@ -1733,8 +1741,21 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
||||
if (!voice.active || voice.muted) continue
|
||||
val s = fetchTrackerSample(voice, instruments[voice.instrumentId])
|
||||
val vol = voice.envVolume * voice.rowVolume / 63.0 * gvol * playhead.masterVolume / 255.0
|
||||
mixL += s * vol * (63 - voice.rowPan) / 63.0
|
||||
mixR += s * vol * voice.rowPan / 63.0
|
||||
val pan = voice.channelPan
|
||||
val lGain: Double
|
||||
val rGain: Double
|
||||
when (ts.panLaw) {
|
||||
1 -> { // equal-power: constant loudness at centre (0.707 each)
|
||||
lGain = cos(PI * pan / 512.0)
|
||||
rGain = sin(PI * pan / 512.0)
|
||||
}
|
||||
else -> { // linear balance (tracker default): centre gives 0 dB on both channels
|
||||
lGain = if (pan < 0x80) 1.0 else 1.0 - (pan - 128.0) / 128.0
|
||||
rGain = if (pan < 0x80) pan / 128.0 else 1.0
|
||||
}
|
||||
}
|
||||
mixL += s * vol * lGain
|
||||
mixR += s * vol * rGain
|
||||
}
|
||||
|
||||
ts.mixLeft[n] = mixL.toFloat().coerceIn(-1.0f, 1.0f)
|
||||
@@ -1988,6 +2009,9 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
||||
var firstRow = true
|
||||
val voices = Array(20) { Voice() }
|
||||
|
||||
// Global mixer config (effect 1).
|
||||
var panLaw = 0 // 0 = linear balance (default), 1 = equal-power
|
||||
|
||||
// Pending row-end events (set during a row by B/C; consumed at row end).
|
||||
var pendingOrderJump = -1 // -1 = none; otherwise the order index to jump to
|
||||
var pendingRowJump = -1 // -1 = none; otherwise the row index for the next pattern
|
||||
@@ -2109,6 +2133,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
||||
ts.pendingOrderJump = -1; ts.pendingRowJump = -1
|
||||
ts.patternDelayRemaining = 0; ts.patternDelayActive = false
|
||||
ts.sexWinningChannel = -1
|
||||
ts.panLaw = 0
|
||||
ts.voices.forEach {
|
||||
it.active = false
|
||||
it.channelVolume = 0x3F
|
||||
|
||||
Reference in New Issue
Block a user