From 6ce8d2cc1e282c1535a1565a9372ca4574b6b86e Mon Sep 17 00:00:00 2001 From: minjaesong Date: Fri, 8 May 2026 00:12:45 +0900 Subject: [PATCH] fix: MP2 not decoding --- terranmon.txt | 1 + tsvm_core/src/net/torvald/tsvm/VM.kt | 16 ++++++++++---- .../torvald/tsvm/peripheral/AudioAdapter.kt | 22 +++++++++---------- .../src/net/torvald/tsvm/peripheral/MP2Env.kt | 3 ++- .../src/net/torvald/tsvm/AppLoader.java | 4 ++-- 5 files changed, 28 insertions(+), 18 deletions(-) diff --git a/terranmon.txt b/terranmon.txt index 69d9a65..be3dfb5 100644 --- a/terranmon.txt +++ b/terranmon.txt @@ -2340,6 +2340,7 @@ TODO: linear-freq flag in the song-table flags byte. Spec details in TAUD_NOTE_EFFECTS.md §1, §E, §F, §G. [ ] milkytracker-style volume ramping (on sample-end only) + [ ] make Cues tab horizontally scrollable Play Data: play data are series of tracker-like instructions, visualised as: diff --git a/tsvm_core/src/net/torvald/tsvm/VM.kt b/tsvm_core/src/net/torvald/tsvm/VM.kt index 401ef04..287799f 100644 --- a/tsvm_core/src/net/torvald/tsvm/VM.kt +++ b/tsvm_core/src/net/torvald/tsvm/VM.kt @@ -12,6 +12,7 @@ import java.io.OutputStream import java.nio.charset.Charset import java.util.* import java.util.concurrent.atomic.AtomicBoolean +import kotlin.math.absoluteValue import kotlin.math.ceil @@ -549,7 +550,7 @@ class VM( // println("peek $addr -> ${offset}@${memspace?.javaClass?.canonicalName}") return if (memspace == null) - throw NullPointerException()//null + throw OpenBusException(addr)//null else if (memspace is UnsafePtr) { if (addr >= memspace.size) throw ErrorIllegalAccess(this, addr) @@ -564,7 +565,7 @@ class VM( val (memspace, offset) = translateAddr(addr) return if (memspace == null) - throw NullPointerException()//null + throw OpenBusException(addr)//null else if (memspace is UnsafePtr) { if (addr >= memspace.size) throw ErrorIllegalAccess(this, addr) @@ -583,7 +584,7 @@ class VM( val (memspace, offset) = translateAddr(addr) return if (memspace == null) - throw NullPointerException()//null + throw OpenBusException(addr)//null else if (memspace is UnsafePtr) { if (addr >= memspace.size) throw ErrorIllegalAccess(this, addr) @@ -608,7 +609,7 @@ class VM( val (memspace, offset) = translateAddr(addr) return if (memspace == null) - throw NullPointerException()//null + throw OpenBusException(addr)//null else if (memspace is UnsafePtr) { if (addr >= memspace.size) throw ErrorIllegalAccess(this, addr) @@ -853,3 +854,10 @@ class PeripheralEntry2( ) internal fun Int.kB() = this * 1024L + +fun Long.memAddrToReadable() = "'${this}' (bank " + this.absoluteValue.minus(if (this < 0) 1 else 0).div(1048576) + +" offset " + this.absoluteValue.minus(if (this < 0) 1 else 0).mod(1048576) + ")" + +class OpenBusException(addr: Long) : NullPointerException( + "Address ${addr.memAddrToReadable()} is open bus" +) \ No newline at end of file diff --git a/tsvm_core/src/net/torvald/tsvm/peripheral/AudioAdapter.kt b/tsvm_core/src/net/torvald/tsvm/peripheral/AudioAdapter.kt index 8c08940..0f2d3b1 100644 --- a/tsvm_core/src/net/torvald/tsvm/peripheral/AudioAdapter.kt +++ b/tsvm_core/src/net/torvald/tsvm/peripheral/AudioAdapter.kt @@ -425,7 +425,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) { private var mp2Context = mp2Env.initialise() private fun decodeMp2() { - val periMmioBase = vm.findPeriSlotNum(this)!! * -786432 - 1L + val periMmioBase = vm.findPeriSlotNum(this)!! * -131072 - 1L mp2Env.decodeFrameU8(mp2Context, periMmioBase - 2368, true, periMmioBase - 64) } @@ -2112,7 +2112,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) { 1 -> amigaSlideOnce(voice.noteVal, -mag) // Amiga: subtract from pitch ⇒ adds period 2 -> linearFreqSlideOnce(voice.noteVal, -mag) // Hz/tick: pitch down ⇒ -Hz else -> voice.noteVal - mag // linear 4096-TET - }.coerceIn(0, 0xFFFE) + }.coerceIn(1, 0xFFFD) voice.basePitch = voice.noteVal voice.amigaPeriod = -1.0 // reseed on next per-tick slide voice.linearFreq = -1.0 @@ -2131,7 +2131,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) { 1 -> amigaSlideOnce(voice.noteVal, mag) 2 -> linearFreqSlideOnce(voice.noteVal, mag) else -> voice.noteVal + mag - }.coerceIn(0, 0xFFFE) + }.coerceIn(1, 0xFFFD) voice.basePitch = voice.noteVal voice.amigaPeriod = -1.0 voice.linearFreq = -1.0 @@ -2252,7 +2252,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) { when (sub) { 0x1 -> voice.glissandoOn = (x != 0) 0x2 -> { - voice.noteVal = (voice.noteVal + FINETUNE_OFFSET[x]).coerceIn(0, 0xFFFE) + voice.noteVal = (voice.noteVal + FINETUNE_OFFSET[x]).coerceIn(1, 0xFFFD) voice.basePitch = voice.noteVal voice.amigaPeriod = -1.0 voice.linearFreq = -1.0 @@ -2345,7 +2345,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) { 1 -> amigaSlideTick(voice, voice.slideArg) 2 -> linearFreqSlideTick(voice, voice.slideArg) else -> voice.noteVal + voice.slideArg - }.coerceIn(0, 0xFFFE) + }.coerceIn(1, 0xFFFD) voice.basePitch = voice.noteVal } @@ -2367,7 +2367,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) { voice.noteVal = target voice.tonePortaTarget = -1 } else { - voice.noteVal = freqHzToNoteVal(voice.linearFreq).coerceIn(0, 0xFFFE) + voice.noteVal = freqHzToNoteVal(voice.linearFreq).coerceIn(1, 0xFFFD) } voice.basePitch = voice.noteVal voice.amigaPeriod = -1.0 @@ -2420,14 +2420,14 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) { if (voice.vibratoActive) { val sine = lfoSample(voice.vibratoLfoPos, voice.vibratoWave) val pitchDelta = (sine * voice.mem.huDepth) shr voice.vibratoFineShift - pitchToMixer = (voice.noteVal + pitchDelta).coerceIn(0, 0xFFFE) + pitchToMixer = (voice.noteVal + pitchDelta).coerceIn(1, 0xFFFD) voice.vibratoLfoPos = (voice.vibratoLfoPos + voice.mem.huSpeed * 4) and 0xFF } // Glissando (S$1x) — snap pitchToMixer to nearest semitone but leave noteVal smooth. if (voice.glissandoOn) { val semis = ((pitchToMixer * 12 + 2048) / 4096) - pitchToMixer = (semis * 4096 / 12).coerceIn(0, 0xFFFE) + pitchToMixer = (semis * 4096 / 12).coerceIn(1, 0xFFFD) } // Tremolo (R) — modulates output volume around base. @@ -2450,7 +2450,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) { if (voice.arpActive) { val voiceIdx = ts.tickInRow % 3 val arpDelta = when (voiceIdx) { 1 -> voice.arpOff1 shl 8; 2 -> voice.arpOff2 shl 8; else -> 0 } - pitchToMixer = (voice.basePitch + arpDelta).coerceIn(0, 0xFFFE) + pitchToMixer = (voice.basePitch + arpDelta).coerceIn(1, 0xFFFD) voice.lastArpVoice = voiceIdx } @@ -2487,7 +2487,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) { ((voice.envPfValue - 0.5) * 2.0 * 16.0 * 4096.0 / 12.0).toInt() else 0 - val finalPitch = (pitchToMixer + autoVibDelta + pitchEnvDelta).coerceIn(0, 0xFFFE) + val finalPitch = (pitchToMixer + autoVibDelta + pitchEnvDelta).coerceIn(1, 0xFFFD) voice.playbackRate = computePlaybackRate(inst, finalPitch) // Filter envelope (filter mode): scale baseCut by envValue (0..1, 0.5 = unity). @@ -2581,7 +2581,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) { val pitchEnvDelta = if (bg.hasPfEnv && bg.pfEnvOn && !bg.envPfIsFilter) ((bg.envPfValue - 0.5) * 2.0 * 16.0 * 4096.0 / 12.0).toInt() else 0 - val finalPitch = (bg.noteVal + autoVibDelta + pitchEnvDelta).coerceIn(0, 0xFFFE) + val finalPitch = (bg.noteVal + autoVibDelta + pitchEnvDelta).coerceIn(1, 0xFFFD) bg.playbackRate = computePlaybackRate(inst, finalPitch) // Filter-mode pf envelope: same scaling rule as foreground. if (bg.hasPfEnv && bg.pfEnvOn && bg.envPfIsFilter) { diff --git a/tsvm_core/src/net/torvald/tsvm/peripheral/MP2Env.kt b/tsvm_core/src/net/torvald/tsvm/peripheral/MP2Env.kt index 8e3db98..22aa621 100644 --- a/tsvm_core/src/net/torvald/tsvm/peripheral/MP2Env.kt +++ b/tsvm_core/src/net/torvald/tsvm/peripheral/MP2Env.kt @@ -43,6 +43,7 @@ package net.torvald.tsvm.peripheral import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.toUint import net.torvald.tsvm.VM +import net.torvald.tsvm.memAddrToReadable import java.util.* import kotlin.collections.ArrayList import kotlin.math.ceil @@ -398,7 +399,7 @@ class MP2Env(val vm: VM) { }; // check for valid header: syncword OK, MPEG-Audio Layer 2 if ((syspeek(mp2_frame!!) != 0xFF) || ((syspeek(mp2_frame!! + 1*incr) and 0xFE) != 0xFC)){ - throw Error("Invalid MP2 header at $mp2_frame: ${syspeek(mp2_frame!!).toString(16)} ${syspeek(mp2_frame!! + 1*incr).toString(16)}") + throw Error("Invalid MP2 header at ${(mp2_frame as Long).memAddrToReadable()}: ${syspeek(mp2_frame!!).toString(16)} ${syspeek(mp2_frame!! + 1*incr).toString(16)}") }; // set up the bitstream reader diff --git a/tsvm_executable/src/net/torvald/tsvm/AppLoader.java b/tsvm_executable/src/net/torvald/tsvm/AppLoader.java index 41c24e5..afe942f 100644 --- a/tsvm_executable/src/net/torvald/tsvm/AppLoader.java +++ b/tsvm_executable/src/net/torvald/tsvm/AppLoader.java @@ -54,8 +54,8 @@ public class AppLoader { ArrayList defaultPeripherals = new ArrayList(); - defaultPeripherals.add(new Pair(3, new PeripheralEntry2("net.torvald.tsvm.peripheral.AudioAdapter", vm))); - defaultPeripherals.add(new Pair(4, new PeripheralEntry2("net.torvald.tsvm.peripheral.HostFileHSDPA", vm, "assets/diskMediabin/lnterz_013.mv2", "assets/diskMediabin/ba60d.mov", "", "", 999999999L))); + defaultPeripherals.add(new Pair(2, new PeripheralEntry2("net.torvald.tsvm.peripheral.AudioAdapter", vm))); + defaultPeripherals.add(new Pair(3, new PeripheralEntry2("net.torvald.tsvm.peripheral.HostFileHSDPA", vm, "assets/diskMediabin/lnterz_013.mv2", "assets/diskMediabin/ba60d.mov", "", "", 999999999L))); EmulInstance reference = new EmulInstance(vm, "net.torvald.tsvm.peripheral.ReferenceGraphicsAdapter", diskPath, 560, 448, defaultPeripherals);