mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-11 15:24:05 +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/impulse-tracker` — The original source code for ImpulseTracker
|
||||||
- `reference_materials/MilkyTracker` — FastTracker 2 compatible tracker
|
- `reference_materials/MilkyTracker` — FastTracker 2 compatible tracker
|
||||||
- `reference_materials/schismtracker` — Open-source re-implementation of ImpulseTracker
|
- `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
|
When fetching new references, copy the relevant upstream files verbatim into
|
||||||
a topic folder, write a `README.md` summarising the relevant maths /
|
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.
|
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 |
|
| PT effect | Taud effect | Notes |
|
||||||
|---------|-----------|-------|
|
|---------|---------|-------|
|
||||||
| `0 $xy` | `J $xxyy` | Arpeggio; nibble-repeat each byte. See the 12-TET → Taud table above for conversion losses |
|
| `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 |
|
| `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 |
|
| `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 $Cx` | `S $Cx00` | Note cut |
|
||||||
| `E $Dx` | `S $Dx00` | Note delay |
|
| `E $Dx` | `S $Dx00` | Note delay |
|
||||||
| `E $Ex` | `S $Ex00` | Pattern 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) | `A $xx00` | Set speed |
|
||||||
| `F $xx` (xx ≥ $20) | `T $(xx−$18)00` | Set tempo |
|
| `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
|
updated; legacy `.taud` files (byte 196 == 0) fall back to the
|
||||||
previous "row volume default = 63" behaviour.
|
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:
|
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.
|
// 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.
|
// 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
|
var funkMask: ByteArray? = null
|
||||||
fun toggleFunkBit(loopOffset: Int) {
|
fun toggleFunkBit(loopOffset: Int) {
|
||||||
val len = (sampleLoopEnd - sampleLoopStart).coerceAtLeast(1)
|
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)
|
val idx = loopOffset.coerceIn(0, len - 1)
|
||||||
mask[idx / 8] = (mask[idx / 8].toInt() xor (1 shl (idx and 7))).toByte()
|
mask[idx / 8] = (mask[idx / 8].toInt() xor (1 shl (idx and 7))).toByte()
|
||||||
}
|
}
|
||||||
fun funkBit(loopOffset: Int): Boolean {
|
fun funkBit(loopOffset: Int): Boolean {
|
||||||
val mask = funkMask ?: return false
|
val mask = funkMask ?: return false
|
||||||
val len = (sampleLoopEnd - sampleLoopStart).coerceAtLeast(1)
|
val len = (sampleLoopEnd - sampleLoopStart).coerceAtLeast(1)
|
||||||
|
if (mask.size != (len + 7) / 8) { funkMask = null; return false }
|
||||||
val idx = loopOffset.coerceIn(0, len - 1)
|
val idx = loopOffset.coerceIn(0, len - 1)
|
||||||
return (mask[idx / 8].toInt() ushr (idx and 7)) and 1 != 0
|
return (mask[idx / 8].toInt() ushr (idx and 7)) and 1 != 0
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user