mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-06 05:28:31 +09:00
fix: MP2 not decoding
This commit is contained in:
@@ -2340,6 +2340,7 @@ TODO:
|
|||||||
linear-freq flag in the song-table flags byte. Spec details in
|
linear-freq flag in the song-table flags byte. Spec details in
|
||||||
TAUD_NOTE_EFFECTS.md §1, §E, §F, §G.
|
TAUD_NOTE_EFFECTS.md §1, §E, §F, §G.
|
||||||
[ ] milkytracker-style volume ramping (on sample-end only)
|
[ ] 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:
|
Play Data: play data are series of tracker-like instructions, visualised as:
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import java.io.OutputStream
|
|||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
import kotlin.math.ceil
|
import kotlin.math.ceil
|
||||||
|
|
||||||
|
|
||||||
@@ -549,7 +550,7 @@ class VM(
|
|||||||
// println("peek $addr -> ${offset}@${memspace?.javaClass?.canonicalName}")
|
// println("peek $addr -> ${offset}@${memspace?.javaClass?.canonicalName}")
|
||||||
|
|
||||||
return if (memspace == null)
|
return if (memspace == null)
|
||||||
throw NullPointerException()//null
|
throw OpenBusException(addr)//null
|
||||||
else if (memspace is UnsafePtr) {
|
else if (memspace is UnsafePtr) {
|
||||||
if (addr >= memspace.size)
|
if (addr >= memspace.size)
|
||||||
throw ErrorIllegalAccess(this, addr)
|
throw ErrorIllegalAccess(this, addr)
|
||||||
@@ -564,7 +565,7 @@ class VM(
|
|||||||
val (memspace, offset) = translateAddr(addr)
|
val (memspace, offset) = translateAddr(addr)
|
||||||
|
|
||||||
return if (memspace == null)
|
return if (memspace == null)
|
||||||
throw NullPointerException()//null
|
throw OpenBusException(addr)//null
|
||||||
else if (memspace is UnsafePtr) {
|
else if (memspace is UnsafePtr) {
|
||||||
if (addr >= memspace.size)
|
if (addr >= memspace.size)
|
||||||
throw ErrorIllegalAccess(this, addr)
|
throw ErrorIllegalAccess(this, addr)
|
||||||
@@ -583,7 +584,7 @@ class VM(
|
|||||||
val (memspace, offset) = translateAddr(addr)
|
val (memspace, offset) = translateAddr(addr)
|
||||||
|
|
||||||
return if (memspace == null)
|
return if (memspace == null)
|
||||||
throw NullPointerException()//null
|
throw OpenBusException(addr)//null
|
||||||
else if (memspace is UnsafePtr) {
|
else if (memspace is UnsafePtr) {
|
||||||
if (addr >= memspace.size)
|
if (addr >= memspace.size)
|
||||||
throw ErrorIllegalAccess(this, addr)
|
throw ErrorIllegalAccess(this, addr)
|
||||||
@@ -608,7 +609,7 @@ class VM(
|
|||||||
val (memspace, offset) = translateAddr(addr)
|
val (memspace, offset) = translateAddr(addr)
|
||||||
|
|
||||||
return if (memspace == null)
|
return if (memspace == null)
|
||||||
throw NullPointerException()//null
|
throw OpenBusException(addr)//null
|
||||||
else if (memspace is UnsafePtr) {
|
else if (memspace is UnsafePtr) {
|
||||||
if (addr >= memspace.size)
|
if (addr >= memspace.size)
|
||||||
throw ErrorIllegalAccess(this, addr)
|
throw ErrorIllegalAccess(this, addr)
|
||||||
@@ -853,3 +854,10 @@ class PeripheralEntry2(
|
|||||||
)
|
)
|
||||||
|
|
||||||
internal fun Int.kB() = this * 1024L
|
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"
|
||||||
|
)
|
||||||
@@ -425,7 +425,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
|||||||
private var mp2Context = mp2Env.initialise()
|
private var mp2Context = mp2Env.initialise()
|
||||||
|
|
||||||
private fun decodeMp2() {
|
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)
|
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
|
1 -> amigaSlideOnce(voice.noteVal, -mag) // Amiga: subtract from pitch ⇒ adds period
|
||||||
2 -> linearFreqSlideOnce(voice.noteVal, -mag) // Hz/tick: pitch down ⇒ -Hz
|
2 -> linearFreqSlideOnce(voice.noteVal, -mag) // Hz/tick: pitch down ⇒ -Hz
|
||||||
else -> voice.noteVal - mag // linear 4096-TET
|
else -> voice.noteVal - mag // linear 4096-TET
|
||||||
}.coerceIn(0, 0xFFFE)
|
}.coerceIn(1, 0xFFFD)
|
||||||
voice.basePitch = voice.noteVal
|
voice.basePitch = voice.noteVal
|
||||||
voice.amigaPeriod = -1.0 // reseed on next per-tick slide
|
voice.amigaPeriod = -1.0 // reseed on next per-tick slide
|
||||||
voice.linearFreq = -1.0
|
voice.linearFreq = -1.0
|
||||||
@@ -2131,7 +2131,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
|||||||
1 -> amigaSlideOnce(voice.noteVal, mag)
|
1 -> amigaSlideOnce(voice.noteVal, mag)
|
||||||
2 -> linearFreqSlideOnce(voice.noteVal, mag)
|
2 -> linearFreqSlideOnce(voice.noteVal, mag)
|
||||||
else -> voice.noteVal + mag
|
else -> voice.noteVal + mag
|
||||||
}.coerceIn(0, 0xFFFE)
|
}.coerceIn(1, 0xFFFD)
|
||||||
voice.basePitch = voice.noteVal
|
voice.basePitch = voice.noteVal
|
||||||
voice.amigaPeriod = -1.0
|
voice.amigaPeriod = -1.0
|
||||||
voice.linearFreq = -1.0
|
voice.linearFreq = -1.0
|
||||||
@@ -2252,7 +2252,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
|||||||
when (sub) {
|
when (sub) {
|
||||||
0x1 -> voice.glissandoOn = (x != 0)
|
0x1 -> voice.glissandoOn = (x != 0)
|
||||||
0x2 -> {
|
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.basePitch = voice.noteVal
|
||||||
voice.amigaPeriod = -1.0
|
voice.amigaPeriod = -1.0
|
||||||
voice.linearFreq = -1.0
|
voice.linearFreq = -1.0
|
||||||
@@ -2345,7 +2345,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
|||||||
1 -> amigaSlideTick(voice, voice.slideArg)
|
1 -> amigaSlideTick(voice, voice.slideArg)
|
||||||
2 -> linearFreqSlideTick(voice, voice.slideArg)
|
2 -> linearFreqSlideTick(voice, voice.slideArg)
|
||||||
else -> voice.noteVal + voice.slideArg
|
else -> voice.noteVal + voice.slideArg
|
||||||
}.coerceIn(0, 0xFFFE)
|
}.coerceIn(1, 0xFFFD)
|
||||||
voice.basePitch = voice.noteVal
|
voice.basePitch = voice.noteVal
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2367,7 +2367,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
|||||||
voice.noteVal = target
|
voice.noteVal = target
|
||||||
voice.tonePortaTarget = -1
|
voice.tonePortaTarget = -1
|
||||||
} else {
|
} else {
|
||||||
voice.noteVal = freqHzToNoteVal(voice.linearFreq).coerceIn(0, 0xFFFE)
|
voice.noteVal = freqHzToNoteVal(voice.linearFreq).coerceIn(1, 0xFFFD)
|
||||||
}
|
}
|
||||||
voice.basePitch = voice.noteVal
|
voice.basePitch = voice.noteVal
|
||||||
voice.amigaPeriod = -1.0
|
voice.amigaPeriod = -1.0
|
||||||
@@ -2420,14 +2420,14 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
|||||||
if (voice.vibratoActive) {
|
if (voice.vibratoActive) {
|
||||||
val sine = lfoSample(voice.vibratoLfoPos, voice.vibratoWave)
|
val sine = lfoSample(voice.vibratoLfoPos, voice.vibratoWave)
|
||||||
val pitchDelta = (sine * voice.mem.huDepth) shr voice.vibratoFineShift
|
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
|
voice.vibratoLfoPos = (voice.vibratoLfoPos + voice.mem.huSpeed * 4) and 0xFF
|
||||||
}
|
}
|
||||||
|
|
||||||
// Glissando (S$1x) — snap pitchToMixer to nearest semitone but leave noteVal smooth.
|
// Glissando (S$1x) — snap pitchToMixer to nearest semitone but leave noteVal smooth.
|
||||||
if (voice.glissandoOn) {
|
if (voice.glissandoOn) {
|
||||||
val semis = ((pitchToMixer * 12 + 2048) / 4096)
|
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.
|
// Tremolo (R) — modulates output volume around base.
|
||||||
@@ -2450,7 +2450,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
|
|||||||
if (voice.arpActive) {
|
if (voice.arpActive) {
|
||||||
val voiceIdx = ts.tickInRow % 3
|
val voiceIdx = ts.tickInRow % 3
|
||||||
val arpDelta = when (voiceIdx) { 1 -> voice.arpOff1 shl 8; 2 -> voice.arpOff2 shl 8; else -> 0 }
|
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
|
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()
|
((voice.envPfValue - 0.5) * 2.0 * 16.0 * 4096.0 / 12.0).toInt()
|
||||||
else 0
|
else 0
|
||||||
|
|
||||||
val finalPitch = (pitchToMixer + autoVibDelta + pitchEnvDelta).coerceIn(0, 0xFFFE)
|
val finalPitch = (pitchToMixer + autoVibDelta + pitchEnvDelta).coerceIn(1, 0xFFFD)
|
||||||
voice.playbackRate = computePlaybackRate(inst, finalPitch)
|
voice.playbackRate = computePlaybackRate(inst, finalPitch)
|
||||||
|
|
||||||
// Filter envelope (filter mode): scale baseCut by envValue (0..1, 0.5 = unity).
|
// 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)
|
val pitchEnvDelta = if (bg.hasPfEnv && bg.pfEnvOn && !bg.envPfIsFilter)
|
||||||
((bg.envPfValue - 0.5) * 2.0 * 16.0 * 4096.0 / 12.0).toInt()
|
((bg.envPfValue - 0.5) * 2.0 * 16.0 * 4096.0 / 12.0).toInt()
|
||||||
else 0
|
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)
|
bg.playbackRate = computePlaybackRate(inst, finalPitch)
|
||||||
// Filter-mode pf envelope: same scaling rule as foreground.
|
// Filter-mode pf envelope: same scaling rule as foreground.
|
||||||
if (bg.hasPfEnv && bg.pfEnvOn && bg.envPfIsFilter) {
|
if (bg.hasPfEnv && bg.pfEnvOn && bg.envPfIsFilter) {
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ package net.torvald.tsvm.peripheral
|
|||||||
|
|
||||||
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.toUint
|
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.toUint
|
||||||
import net.torvald.tsvm.VM
|
import net.torvald.tsvm.VM
|
||||||
|
import net.torvald.tsvm.memAddrToReadable
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
import kotlin.math.ceil
|
import kotlin.math.ceil
|
||||||
@@ -398,7 +399,7 @@ class MP2Env(val vm: VM) {
|
|||||||
};
|
};
|
||||||
// check for valid header: syncword OK, MPEG-Audio Layer 2
|
// check for valid header: syncword OK, MPEG-Audio Layer 2
|
||||||
if ((syspeek(mp2_frame!!) != 0xFF) || ((syspeek(mp2_frame!! + 1*incr) and 0xFE) != 0xFC)){
|
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
|
// set up the bitstream reader
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ public class AppLoader {
|
|||||||
|
|
||||||
|
|
||||||
ArrayList defaultPeripherals = new ArrayList();
|
ArrayList defaultPeripherals = new ArrayList();
|
||||||
defaultPeripherals.add(new Pair(3, new PeripheralEntry2("net.torvald.tsvm.peripheral.AudioAdapter", vm)));
|
defaultPeripherals.add(new Pair(2, 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(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);
|
EmulInstance reference = new EmulInstance(vm, "net.torvald.tsvm.peripheral.ReferenceGraphicsAdapter", diskPath, 560, 448, defaultPeripherals);
|
||||||
|
|||||||
Reference in New Issue
Block a user