mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-06 05:28:31 +09:00
funk repeat OOB fix
This commit is contained in:
@@ -37,6 +37,7 @@ Current topics:
|
||||
- `reference_materials/impulse-tracker` — The original source code for ImpulseTracker
|
||||
- `reference_materials/MilkyTracker` — FastTracker 2 compatible tracker
|
||||
- `reference_materials/schismtracker` — Open-source re-implementation of ImpulseTracker
|
||||
- `reference_materials/pt2-clone` — Open-source re-implementation of ProTracker 2
|
||||
|
||||
When fetching new references, copy the relevant upstream files verbatim into
|
||||
a topic folder, write a `README.md` summarising the relevant maths /
|
||||
|
||||
@@ -1190,7 +1190,7 @@ There is no separate "use fadeout" flag — both extremes share the same field,
|
||||
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.
|
||||
|
||||
| PT effect | Taud effect | Notes |
|
||||
|---------|-----------|-------|
|
||||
|---------|---------|-------|
|
||||
| `0 $xy` | `J $xxyy` | Arpeggio; nibble-repeat each byte. See the 12-TET → Taud table above for conversion losses |
|
||||
| `1 $xx` | `F $00xx` (Amiga mode, `f` set) | Portamento up; raw PT period units, applied in period space |
|
||||
| `2 $xx` | `E $00xx` (Amiga mode, `f` set) | Portamento down; raw PT period units, applied in period space |
|
||||
@@ -1220,7 +1220,7 @@ This table maps each PT effect to its Taud equivalent. Arguments follow PT's two
|
||||
| `E $Cx` | `S $Cx00` | Note cut |
|
||||
| `E $Dx` | `S $Dx00` | Note delay |
|
||||
| `E $Ex` | `S $Ex00` | Pattern delay |
|
||||
| `E $Fx` | `S $Fx00` | Funk repeat |
|
||||
| `E $Fx` | `S $Fyyy` | Funk repeat, where `yyy = funk_table[x]` |
|
||||
| `F $xx` (xx < $20) | `A $xx00` | Set speed |
|
||||
| `F $xx` (xx ≥ $20) | `T $(xx−$18)00` | Set tempo |
|
||||
|
||||
|
||||
@@ -2395,6 +2395,19 @@ TODO:
|
||||
updated; legacy `.taud` files (byte 196 == 0) fall back to the
|
||||
previous "row volume default = 63" behaviour.
|
||||
|
||||
TODO - list of demo songs that MUST ship with Microtone:
|
||||
* 4THSYM (rename to Fourth Symmetriad) — excellent piece for demonstrating NNAs and filter envelopes
|
||||
(C) Skaven 1998
|
||||
* Slumberjack — for demonstrating XM-compatible instrument definitions
|
||||
(C) raina 2005
|
||||
* Space Debris — MOD with tons of effects
|
||||
(C) Captain/Image 1991
|
||||
* Changing Waves — for Funk Repeat emulation
|
||||
(C) 4mat/orb 2023
|
||||
* Aboriginal Derivatives — for demonstrating Monotone compatibility.
|
||||
(C) Jakim 2010
|
||||
* SWINGIN1 (rename to Swinging Waste) — for demonstrating Monotone compatibility.
|
||||
(C) Phoenix/Hornet 2015
|
||||
|
||||
Play Data: play data are series of tracker-like instructions, visualised as:
|
||||
|
||||
|
||||
@@ -3613,16 +3613,25 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
||||
|
||||
// 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.
|
||||
// Mask is sized for the loop length at allocation time; if the loop bounds change
|
||||
// (e.g. a new song reuses this instrument slot with different sample data) the old
|
||||
// mask is stale and must be discarded — otherwise indexing past its end crashes the
|
||||
// render thread with ArrayIndexOutOfBoundsException.
|
||||
var funkMask: ByteArray? = null
|
||||
fun toggleFunkBit(loopOffset: Int) {
|
||||
val len = (sampleLoopEnd - sampleLoopStart).coerceAtLeast(1)
|
||||
val mask = funkMask ?: ByteArray((len + 7) / 8).also { funkMask = it }
|
||||
val expectedSize = (len + 7) / 8
|
||||
var mask = funkMask
|
||||
if (mask == null || mask.size != expectedSize) {
|
||||
mask = ByteArray(expectedSize).also { funkMask = it }
|
||||
}
|
||||
val idx = loopOffset.coerceIn(0, len - 1)
|
||||
mask[idx / 8] = (mask[idx / 8].toInt() xor (1 shl (idx and 7))).toByte()
|
||||
}
|
||||
fun funkBit(loopOffset: Int): Boolean {
|
||||
val mask = funkMask ?: return false
|
||||
val len = (sampleLoopEnd - sampleLoopStart).coerceAtLeast(1)
|
||||
if (mask.size != (len + 7) / 8) { funkMask = null; return false }
|
||||
val idx = loopOffset.coerceIn(0, len - 1)
|
||||
return (mask[idx / 8].toInt() ushr (idx and 7)) and 1 != 0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user