song global volume and mixer volume

This commit is contained in:
minjaesong
2026-05-02 19:27:36 +09:00
parent d2b1e792b9
commit 5e6ac17146
8 changed files with 39 additions and 9 deletions

6
2taud.sh Executable file
View File

@@ -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

View File

@@ -2371,8 +2371,8 @@ taud.uploadTaudFile(fullPathObj.full, 0, PLAYHEAD)
audio.setMasterVolume(PLAYHEAD, 255) audio.setMasterVolume(PLAYHEAD, 255)
audio.setMasterPan(PLAYHEAD, 128) audio.setMasterPan(PLAYHEAD, 128)
const initialTrackerMixerflags = audio.getTrackerMixerFlags(PLAYHEAD) const initialTrackerMixerflags = audio.getTrackerMixerFlags(PLAYHEAD)
//const initialGlobalVolume = const initialGlobalVolume = audio.getSongGlobalVolume(PLAYHEAD)
//const initialMixingVolume = const initialMixingVolume = audio.getSongMixingVolume(PLAYHEAD)
function isExternalPanel(p) { function isExternalPanel(p) {
return p === VIEW_SAMPLES || p === VIEW_INSTRMNT || p === VIEW_FILE return p === VIEW_SAMPLES || p === VIEW_INSTRMNT || p === VIEW_FILE

View File

@@ -109,8 +109,8 @@ function uploadTaudFile(inFile, songIndex, playhead) {
let bpmStored = sys.peek(filePtr + entryOff + 7) & 0xFF let bpmStored = sys.peek(filePtr + entryOff + 7) & 0xFF
let tickRate = sys.peek(filePtr + entryOff + 8) & 0xFF let tickRate = sys.peek(filePtr + entryOff + 8) & 0xFF
let mixerflags = sys.peek(filePtr + entryOff + 15) & 0xFF let mixerflags = sys.peek(filePtr + entryOff + 15) & 0xFF
let songGlobalVolume = sys.peek(filePtr + entryOff + 16) & 0xFF // TODO use it let songGlobalVolume = sys.peek(filePtr + entryOff + 16) & 0xFF
let songMixingVolume = sys.peek(filePtr + entryOff + 17) & 0xFF // TODO use it let songMixingVolume = sys.peek(filePtr + entryOff + 17) & 0xFF
let patBinCompSize = _peekU32LE(filePtr, entryOff + 18) let patBinCompSize = _peekU32LE(filePtr, entryOff + 18)
let cueSheetCompSize = _peekU32LE(filePtr, entryOff + 22) let cueSheetCompSize = _peekU32LE(filePtr, entryOff + 22)
@@ -148,6 +148,8 @@ function uploadTaudFile(inFile, songIndex, playhead) {
audio.setBPM(playhead, bpm) audio.setBPM(playhead, bpm)
audio.setTickRate(playhead, tickRate > 0 ? tickRate : 6) audio.setTickRate(playhead, tickRate > 0 ? tickRate : 6)
audio.setTrackerMixerFlags(playhead, mixerflags) audio.setTrackerMixerFlags(playhead, mixerflags)
audio.setSongGlobalVolume(playhead, songGlobalVolume)
audio.setSongMixingVolume(playhead, songMixingVolume)
fileHandle.close() fileHandle.close()
@@ -196,10 +198,14 @@ function captureTrackerDataToFile(outFile) {
let numPats = numPatsActual // Uint16, 1-65535 let numPats = numPatsActual // Uint16, 1-65535
let patsToSave = numPatsActual 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 bpm = audio.getBPM(0) || 125
let tickRate = audio.getTickRate(0) || 6 let tickRate = audio.getTickRate(0) || 6
let bpmStored = (bpm - 24) & 0xFF 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 ---------------------------------------------- // -- 4. Compress pattern bin ----------------------------------------------
let patBinSize = patsToSave * PATTERN_SIZE let patBinSize = patsToSave * PATTERN_SIZE
@@ -262,8 +268,8 @@ function captureTrackerDataToFile(outFile) {
0x00,0xA0, // basenote (0xA000 -- C9) 0x00,0xA0, // basenote (0xA000 -- C9)
0x00,0xAC,0x02,0x46, // basefreq (8363 Hz) 0x00,0xAC,0x02,0x46, // basefreq (8363 Hz)
sys.peek(baseAddr - 7), // mixer flags sys.peek(baseAddr - 7), // mixer flags
0x80, // global volume (default) songGlobalVolume & 0xFF, // global volume
0x80, // mixing volume (default) songMixingVolume & 0xFF, // mixing volume
// pattern bin compressed size (4) // pattern bin compressed size (4)
(patCompSize ) & 0xFF, (patCompSize ) & 0xFF,
(patCompSize >>> 8) & 0xFF, (patCompSize >>> 8) & 0xFF,

View File

@@ -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: bit 1 (f) = Amiga pitch-slide mode (IT linear_slides flag inverted)
flags_byte = 0x00 if h.linear_slides else 0x02 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_table = encode_song_entry(
song_offset=song_offset, song_offset=song_offset,
num_voices=C, num_voices=C,
@@ -1753,6 +1756,8 @@ def assemble_taud(h: ITHeader, samples: list, instruments: list,
flags_byte=flags_byte, flags_byte=flags_byte,
pat_bin_comp_size=len(pat_comp), pat_bin_comp_size=len(pat_comp),
cue_sheet_comp_size=len(cue_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 assert len(song_table) == TAUD_SONG_ENTRY

View File

@@ -770,6 +770,8 @@ def assemble_taud(mod: dict) -> bytes:
flags_byte=flags_byte, flags_byte=flags_byte,
pat_bin_comp_size=len(pat_comp), pat_bin_comp_size=len(pat_comp),
cue_sheet_comp_size=len(cue_comp), cue_sheet_comp_size=len(cue_comp),
global_vol=0xFF,
mixing_vol=0xFF,
) )
assert len(song_table) == TAUD_SONG_ENTRY assert len(song_table) == TAUD_SONG_ENTRY

View File

@@ -843,6 +843,8 @@ def assemble_taud(h: S3MHeader, instruments: list, patterns: list) -> bytes:
flags_byte=flags_byte, flags_byte=flags_byte,
pat_bin_comp_size=len(pat_comp), pat_bin_comp_size=len(pat_comp),
cue_sheet_comp_size=len(cue_comp), cue_sheet_comp_size=len(cue_comp),
global_vol=0xFF,
mixing_vol=0xFF,
) )
assert len(song_table) == TAUD_SONG_ENTRY assert len(song_table) == TAUD_SONG_ENTRY

View File

@@ -139,6 +139,12 @@ class AudioJSR223Delegate(private val vm: VM) {
return getFirstSnd()?.playheads?.get(playhead)?.initialGlobalFlags 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) { fun putPcmDataByPtr(playhead: Int, ptr: Int, length: Int, destOffset: Int) {
getFirstSnd()?.let { getFirstSnd()?.let {
val vkMult = if (ptr >= 0) 1 else -1 val vkMult = if (ptr >= 0) 1 else -1

View File

@@ -2279,6 +2279,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
var mixL = 0.0 var mixL = 0.0
var mixR = 0.0 var mixR = 0.0
val gvol = playhead.globalVolume / 255.0 val gvol = playhead.globalVolume / 255.0
val mvol = playhead.mixingVolume / 255.0
for (voice in ts.voices) { for (voice in ts.voices) {
if (!voice.active || voice.muted) continue if (!voice.active || voice.muted) continue
val voiceInst = instruments[voice.instrumentId] 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. // Volume envelope is bypassed (treated as unity) when S $77 has disabled it.
val effEnvVol = if (voice.volEnvOn) voice.envVolume else 1.0 val effEnvVol = if (voice.volEnvOn) voice.envVolume else 1.0
val vol = effEnvVol * voice.fadeoutVolume * voice.rowVolume / 63.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 pan = if (voice.hasPanEnv && voice.panEnvOn) {
val envPanRaw = (voice.envPan * 255.0).roundToInt().coerceIn(0, 255) val envPanRaw = (voice.envPan * 255.0).roundToInt().coerceIn(0, 255)
(voice.channelPan + envPanRaw - 128 + voice.randomPanBias).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 swingScale = 1.0 + bg.randomVolBias / 255.0
val effEnvVol = if (bg.volEnvOn) bg.envVolume else 1.0 val effEnvVol = if (bg.volEnvOn) bg.envVolume else 1.0
val vol = effEnvVol * bg.fadeoutVolume * bg.rowVolume / 63.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 pan = if (bg.hasPanEnv && bg.panEnvOn) {
val envPanRaw = (bg.envPan * 255.0).roundToInt().coerceIn(0, 255) val envPanRaw = (bg.envPan * 255.0).roundToInt().coerceIn(0, 255)
(bg.channelPan + envPanRaw - 128 + bg.randomPanBias).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 patBank1: Int = 0,
var patBank2: Int = 0, var patBank2: Int = 0,
var globalVolume: Int = 0x80, // 8-bit, default $80 (spec §5). Mutated by V $xx00. 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<ByteArray> = Queue<ByteArray>(), var pcmQueue: Queue<ByteArray> = Queue<ByteArray>(),
var pcmQueueSizeIndex: Int = 0, var pcmQueueSizeIndex: Int = 0,
@@ -2794,6 +2796,7 @@ class AudioAdapter(val vm: VM) : PeriBase(VM.PERITYPE_SOUND) {
bpm = 125 bpm = 125
tickRate = 6 tickRate = 6
globalVolume = 0x80 globalVolume = 0x80
mixingVolume = 0x80
trackerState?.let { ts -> trackerState?.let { ts ->
ts.cuePos = 0; ts.rowIndex = 0; ts.tickInRow = 0 ts.cuePos = 0; ts.rowIndex = 0; ts.tickInRow = 0
ts.samplesIntoTick = 0.0; ts.firstRow = true ts.samplesIntoTick = 0.0; ts.firstRow = true