diff --git a/2taud.sh b/2taud.sh new file mode 100755 index 0000000..5a94774 --- /dev/null +++ b/2taud.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env fish + +for f in *.mod; python3 mod2taud.py $f assets/disk0/(basename $f .mod).taud; end +for f in *.s3m; python3 s3m2taud.py $f assets/disk0/(basename $f .s3m).taud; end +for f in *.it; python3 it2taud.py $f assets/disk0/(basename $f .it).taud; end +for f in *.xm; python3 xm2taud.py $f assets/disk0/(basename $f .xm).taud; end diff --git a/assets/disk0/tvdos/bin/taut.js b/assets/disk0/tvdos/bin/taut.js index a706917..0e22918 100644 --- a/assets/disk0/tvdos/bin/taut.js +++ b/assets/disk0/tvdos/bin/taut.js @@ -2371,8 +2371,8 @@ taud.uploadTaudFile(fullPathObj.full, 0, PLAYHEAD) audio.setMasterVolume(PLAYHEAD, 255) audio.setMasterPan(PLAYHEAD, 128) const initialTrackerMixerflags = audio.getTrackerMixerFlags(PLAYHEAD) -//const initialGlobalVolume = -//const initialMixingVolume = +const initialGlobalVolume = audio.getSongGlobalVolume(PLAYHEAD) +const initialMixingVolume = audio.getSongMixingVolume(PLAYHEAD) function isExternalPanel(p) { return p === VIEW_SAMPLES || p === VIEW_INSTRMNT || p === VIEW_FILE diff --git a/assets/disk0/tvdos/include/taud.mjs b/assets/disk0/tvdos/include/taud.mjs index 5481a12..57e94db 100644 --- a/assets/disk0/tvdos/include/taud.mjs +++ b/assets/disk0/tvdos/include/taud.mjs @@ -109,8 +109,8 @@ function uploadTaudFile(inFile, songIndex, playhead) { let bpmStored = sys.peek(filePtr + entryOff + 7) & 0xFF let tickRate = sys.peek(filePtr + entryOff + 8) & 0xFF let mixerflags = sys.peek(filePtr + entryOff + 15) & 0xFF - let songGlobalVolume = sys.peek(filePtr + entryOff + 16) & 0xFF // TODO use it - let songMixingVolume = sys.peek(filePtr + entryOff + 17) & 0xFF // TODO use it + let songGlobalVolume = sys.peek(filePtr + entryOff + 16) & 0xFF + let songMixingVolume = sys.peek(filePtr + entryOff + 17) & 0xFF let patBinCompSize = _peekU32LE(filePtr, entryOff + 18) let cueSheetCompSize = _peekU32LE(filePtr, entryOff + 22) @@ -148,6 +148,8 @@ function uploadTaudFile(inFile, songIndex, playhead) { audio.setBPM(playhead, bpm) audio.setTickRate(playhead, tickRate > 0 ? tickRate : 6) audio.setTrackerMixerFlags(playhead, mixerflags) + audio.setSongGlobalVolume(playhead, songGlobalVolume) + audio.setSongMixingVolume(playhead, songMixingVolume) fileHandle.close() @@ -196,10 +198,14 @@ function captureTrackerDataToFile(outFile) { let numPats = numPatsActual // Uint16, 1-65535 let patsToSave = numPatsActual - // -- 3. BPM / tick-rate from playhead 0 ----------------------------------- + // -- 3. BPM / tick-rate / volumes from playhead 0 ------------------------- let bpm = audio.getBPM(0) || 125 let tickRate = audio.getTickRate(0) || 6 let bpmStored = (bpm - 24) & 0xFF + let songGlobalVolume = audio.getSongGlobalVolume(0) + let songMixingVolume = audio.getSongMixingVolume(0) + if (songGlobalVolume === undefined || songGlobalVolume === null) songGlobalVolume = 0x80 + if (songMixingVolume === undefined || songMixingVolume === null) songMixingVolume = 0x80 // -- 4. Compress pattern bin ---------------------------------------------- let patBinSize = patsToSave * PATTERN_SIZE @@ -262,8 +268,8 @@ function captureTrackerDataToFile(outFile) { 0x00,0xA0, // basenote (0xA000 -- C9) 0x00,0xAC,0x02,0x46, // basefreq (8363 Hz) sys.peek(baseAddr - 7), // mixer flags - 0x80, // global volume (default) - 0x80, // mixing volume (default) + songGlobalVolume & 0xFF, // global volume + songMixingVolume & 0xFF, // mixing volume // pattern bin compressed size (4) (patCompSize ) & 0xFF, (patCompSize >>> 8) & 0xFF, diff --git a/it2taud.py b/it2taud.py index 2f75a12..96195a7 100644 --- a/it2taud.py +++ b/it2taud.py @@ -1742,6 +1742,9 @@ def assemble_taud(h: ITHeader, samples: list, instruments: list, # flags byte: bit 1 (f) = Amiga pitch-slide mode (IT linear_slides flag inverted) flags_byte = 0x00 if h.linear_slides else 0x02 + # IT global/mix volumes are 0..128; rescale to Taud's 0..255 (clamped). + global_vol_taud = min(0xFF, round(h.global_vol * 255 / 128)) + mixing_vol_taud = min(0xFF, round(h.mix_vol * 255 / 128)) song_table = encode_song_entry( song_offset=song_offset, num_voices=C, @@ -1753,6 +1756,8 @@ def assemble_taud(h: ITHeader, samples: list, instruments: list, flags_byte=flags_byte, pat_bin_comp_size=len(pat_comp), cue_sheet_comp_size=len(cue_comp), + global_vol=global_vol_taud, + mixing_vol=mixing_vol_taud, ) assert len(song_table) == TAUD_SONG_ENTRY diff --git a/mod2taud.py b/mod2taud.py index c73eea3..e9f4174 100644 --- a/mod2taud.py +++ b/mod2taud.py @@ -770,6 +770,8 @@ def assemble_taud(mod: dict) -> bytes: flags_byte=flags_byte, pat_bin_comp_size=len(pat_comp), cue_sheet_comp_size=len(cue_comp), + global_vol=0xFF, + mixing_vol=0xFF, ) assert len(song_table) == TAUD_SONG_ENTRY diff --git a/s3m2taud.py b/s3m2taud.py index 57a44c3..6c4da0f 100644 --- a/s3m2taud.py +++ b/s3m2taud.py @@ -843,6 +843,8 @@ def assemble_taud(h: S3MHeader, instruments: list, patterns: list) -> bytes: flags_byte=flags_byte, pat_bin_comp_size=len(pat_comp), cue_sheet_comp_size=len(cue_comp), + global_vol=0xFF, + mixing_vol=0xFF, ) assert len(song_table) == TAUD_SONG_ENTRY diff --git a/tsvm_core/src/net/torvald/tsvm/AudioJSR223Delegate.kt b/tsvm_core/src/net/torvald/tsvm/AudioJSR223Delegate.kt index 2e3cc68..dfabbea 100644 --- a/tsvm_core/src/net/torvald/tsvm/AudioJSR223Delegate.kt +++ b/tsvm_core/src/net/torvald/tsvm/AudioJSR223Delegate.kt @@ -139,6 +139,12 @@ class AudioJSR223Delegate(private val vm: VM) { return getFirstSnd()?.playheads?.get(playhead)?.initialGlobalFlags } + fun setSongGlobalVolume(playhead: Int, volume: Int) { getPlayhead(playhead)?.globalVolume = volume and 255 } + fun getSongGlobalVolume(playhead: Int) = getPlayhead(playhead)?.globalVolume + + fun setSongMixingVolume(playhead: Int, volume: Int) { getPlayhead(playhead)?.mixingVolume = volume and 255 } + fun getSongMixingVolume(playhead: Int) = getPlayhead(playhead)?.mixingVolume + fun putPcmDataByPtr(playhead: Int, ptr: Int, length: Int, destOffset: Int) { getFirstSnd()?.let { val vkMult = if (ptr >= 0) 1 else -1 diff --git a/tsvm_core/src/net/torvald/tsvm/peripheral/AudioAdapter.kt b/tsvm_core/src/net/torvald/tsvm/peripheral/AudioAdapter.kt index 5c57d6d..9658d85 100644 --- a/tsvm_core/src/net/torvald/tsvm/peripheral/AudioAdapter.kt +++ b/tsvm_core/src/net/torvald/tsvm/peripheral/AudioAdapter.kt @@ -2279,6 +2279,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) { var mixL = 0.0 var mixR = 0.0 val gvol = playhead.globalVolume / 255.0 + val mvol = playhead.mixingVolume / 255.0 for (voice in ts.voices) { if (!voice.active || voice.muted) continue val voiceInst = instruments[voice.instrumentId] @@ -2289,7 +2290,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) { // Volume envelope is bypassed (treated as unity) when S $77 has disabled it. val effEnvVol = if (voice.volEnvOn) voice.envVolume else 1.0 val vol = effEnvVol * voice.fadeoutVolume * voice.rowVolume / 63.0 * - swingScale * gvol * instGv * playhead.masterVolume / 255.0 + swingScale * gvol * mvol * instGv * playhead.masterVolume / 255.0 val pan = if (voice.hasPanEnv && voice.panEnvOn) { val envPanRaw = (voice.envPan * 255.0).roundToInt().coerceIn(0, 255) (voice.channelPan + envPanRaw - 128 + voice.randomPanBias).coerceIn(0, 255) @@ -2319,7 +2320,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) { val swingScale = 1.0 + bg.randomVolBias / 255.0 val effEnvVol = if (bg.volEnvOn) bg.envVolume else 1.0 val vol = effEnvVol * bg.fadeoutVolume * bg.rowVolume / 63.0 * - swingScale * gvol * instGv * playhead.masterVolume / 255.0 + swingScale * gvol * mvol * instGv * playhead.masterVolume / 255.0 val pan = if (bg.hasPanEnv && bg.panEnvOn) { val envPanRaw = (bg.envPan * 255.0).roundToInt().coerceIn(0, 255) (bg.channelPan + envPanRaw - 128 + bg.randomPanBias).coerceIn(0, 255) @@ -2703,6 +2704,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) { var patBank1: Int = 0, var patBank2: Int = 0, var globalVolume: Int = 0x80, // 8-bit, default $80 (spec §5). Mutated by V $xx00. + var mixingVolume: Int = 0x80, // 8-bit, default $80 (spec §5). Final-mix scaler, set once per song. var pcmQueue: Queue = Queue(), var pcmQueueSizeIndex: Int = 0, @@ -2794,6 +2796,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) { bpm = 125 tickRate = 6 globalVolume = 0x80 + mixingVolume = 0x80 trackerState?.let { ts -> ts.cuePos = 0; ts.rowIndex = 0; ts.tickInRow = 0 ts.samplesIntoTick = 0.0; ts.firstRow = true