title screen issues, audio issues, and replacing gf c

This commit is contained in:
bryanthaboi
2026-08-10 14:00:26 -04:00
parent 943ba5dcbf
commit 12c2677dc2
14 changed files with 830 additions and 181 deletions
+37 -39
View File
@@ -1,12 +1,3 @@
-- Music playback supports compact ROM channel programs synthesized live by
-- ChipAudio, def-local chip programs (ChipAsm), and file definitions. The
-- branch is chosen per song definition, never by a global import flag, so a
-- file-backed song and a chip song coexist in one dataset. Songs with split
-- files chain def.file into def.loopFile in Music.update().
-- Map themes switch on map change; battles override with the battle
-- theme and restore afterwards; riding the bike overrides outdoor map
-- themes with the bike song until dismount.
local Logger = require("src.core.Logger")
local Runtime = require("src.mods.Runtime")
@@ -14,20 +5,10 @@ local Music = {}
local VOLUME = 0.7
-- port additions driven by OptionsMenu / save.options: musicVol scales
-- VOLUME (0-7 level like the GB's NR50 master volume) and musicFilter
-- low-passes the song. Each filter step keeps 40% of the previous
-- step's treble (highgain 0.4^level), so 2X/3X are the 1X filter
-- applied twice/three times over.
local volumeScale = 1
local FILTER_HIGHGAIN = { 0.4, 0.16, 0.064 }
local filterLevel = 0
-- Forward-declared here so applyVolume (below) closes over the real playback
-- state rather than a nil global: the table literal is assigned further down,
-- but a `local state = {}` there would leave every reference above it bound
-- to the global `state`. Before this, registering the `music.volume` mod
-- hook crashed applyVolume on `state.current` (a nil index).
local state
local function applyVolume(src)
@@ -115,11 +96,6 @@ function Music.duckForFanfare(src)
end
end
-- Overworld themes where the bike can be ridden (outdoor maps plus the
-- caves/dungeons where gen-1 allows cycling). Indoor themes such as
-- Pokecenter/Gym/SilphCo never get replaced by the bike theme.
-- data.audio.outdoorSongs supersedes this; the copy stays as the fallback
-- for caches built before the importer wrote the table.
local OUTDOOR = {
Music_PalletTown = true,
Music_Cities1 = true,
@@ -218,6 +194,7 @@ end
-- the single choke point every song choice passes through, so one hook
-- covers map themes, battle themes, jingles and scene music
local function selectSong(song, ctx)
if ctx and ctx.selected then return song end
if not Runtime.wantsHook("music.select") then return song end
return Runtime.call("music.select", function(chosen) return chosen end, song, {
reason = ctx and ctx.reason or "direct",
@@ -234,17 +211,28 @@ end
function Music.play(data, song, loop, ctx)
if not song then return end
if not love.audio then return end -- headless test stub
ctx = ctx or {}
song = selectSong(song, ctx)
-- ctx.tempo is a Music_*AlternateTempo cue (audio/alternate_tempo.asm):
-- the same song restarted with channel 1 re-pointed at a stub whose only
-- difference is its `tempo`, so the same label at a different tempo is a
-- different cue and must not be deduped away (#847)
local tempo = ctx and ctx.tempo or nil
-- a hook may silence the cue outright, or swap in a label the dedupe
-- below has to compare against
if not song or (song == state.current and tempo == state.tempo) then return end
local def = songDef(data, song)
if not def or state.failed[song] then return end
if ctx.fade and state.source then
local queued = {}
for key, value in pairs(ctx) do queued[key] = value end
queued.fade, queued.selected = nil, true
local pending = { data = data, song = song, loop = loop, ctx = queued }
if state.fade then
state.fade.pending = pending
else
Music.fadeOut(ctx.fade, pending)
end
return
end
if tempo then
-- shallow copy: the registry def is shared, only this playback is slowed
local slowed = {}
@@ -317,24 +305,26 @@ function Music.reload()
Music.stop()
end
-- Ramp the current song's volume to silence, then stop it, mirroring the
-- Game Boy's audio fade-out (home/fade_audio.asm FadeOutAudio +
-- home/audio.asm's .fadeOut): rAUDVOL's master volume steps 7 -> 0 in
-- integer levels, one level every `control` frames, and the music stops
-- when it reaches 0. `control` is the wAudioFadeOutControl value the ROM
-- writes (oak_speech.asm sets 10 at the shrink beat -> 7*10 = 70 frames
-- to silence). Ticked once per frame from Music.update().
function Music.fadeOut(control)
if not state.source then Music.stop() return end
function Music.fadeOut(control, pending)
if not state.source then
Music.stop()
if pending then
Music.play(pending.data, pending.song, pending.loop, pending.ctx)
end
return
end
control = math.max(1, control or 10)
state.fade = {
control = control,
counter = control, -- frames until the next volume step
level = 7, -- current master-volume level (rAUDVOL nibble)
from = VOLUME * volumeScale, -- level-7 (full) source volume
pending = pending,
}
end
Music.MAP_FADE = 10
-- the song a map should currently play, honoring the bike/surf overrides
local function effectiveMapSong(data, song)
if not song or not outdoorSongs(data)[song] then return song end
@@ -351,14 +341,17 @@ end
-- overworld map theme; onBike/surfing override outdoor themes with the
-- bike/surf songs and restore the map theme when they end
function Music.playMap(data, mapId, onBike, surfing)
function Music.playMap(data, mapId, onBike, surfing, fade)
local song = data and data.audio and data.audio.mapSongs
and mapId and data.audio.mapSongs[mapId] or nil
state.mapSong = song
state.onBike = not not onBike
state.surfing = not not surfing
local play = effectiveMapSong(data, song)
if play then Music.play(data, play, nil, { reason = "map", mapId = mapId }) end
if play then
Music.play(data, play, nil,
{ reason = "map", mapId = mapId, fade = fade })
end
end
-- toggle the surf override mid-map (starting/ending a surf)
@@ -475,8 +468,13 @@ function Music.update(data)
f.counter = f.control
f.level = f.level - 1
if f.level <= 0 then
-- ..(home/fade_audio.asm ln 36)
state.fade = nil
local pending = f.pending
Music.stop()
if pending then
Music.play(pending.data, pending.song, pending.loop, pending.ctx)
end
return
end
local vol = f.from * f.level / 7