First, a reply:
theonyxphoenix: That is actually really helpful since you're the third person to say that, which means that that's more than likely a "bug" we can cross off our list. Which means the only bug currently, other than missing fades, is no music on load of a saved game.
Now that my head is a bit less fuzzy, heres some technical details on the hurdles I'm working to overcome. If you don't want to deal with overly technical stuff, skip to the numbered "workarounds" at the bottom for an idea of the choices we have that *aren't* dangerous.
The NMI, or "Non Maskable Interrupt", is generic computing term for an Interrupt (basically a request to the CPU, literally 'interrupting' its current behavior) that must be handled if it's enabled. On the SNES, the NMI, if enabled, happens every VBlank, so once per field or roughly 60 times per second.
For the most part up until now I've been hooking the routines from the game itself, that is, taking requests the game makes to its own code and rerouting it to mine. For the fading, and for some later things I'm going to have to re: timing (the Opera scene and Dancing Mad during the final battle, among other things), I am now hooking into the game's NMI routine, which means I can run code every frame. I'm using stack manipulation to make sure that I don't harm the game's own NMI handling in the process.
Now, the fading itself is pretty simple. Reduce or increase the volume of the MSU every frame until we've gotten to our desired volume (or close to, since I'm reducing by 5s). The problem comes when fading from one track to another. Currently, we're hooking the game's "new track" routine and immediately switching to an MSU track and playing it, falling back to the game's own SPC routines if a track is missing or unplayable. This allows us to handle looping sound effects like wind and train sounds that are implemented as "songs" in the game but which we don't want to have to do on the MSU. To do the fading, however, we need to, when we're told to switch to a new track, store the requested track and a variable telling my NMI routine that we're fading out and into a new track in RAM, since this needs to be handled over time.
However, this means that the NMI routine, the one that runs every second, needs to handle the actual switching of tracks. Which is a problem for two reasons. One, you need to loop after loading a track and wait for it to be loaded and ready to play before you can play it. This is done because Byuu wanted to make it possible for the MSU-1 to be implemented in cheap, slow flash memory or even as a CD. This loading loop takes less than a second in higan, but we can't assume it will take any specific amount of time. And the NMI only lasts a tiny fraction of a second itself. If I'm still in NMI code when the NMI ends, massive graphical corruption can result, so anything I do during it needs to be only a few instructions long.
We can work around this problem by using the "60 times a second" nature of the NMI. We can set the track to load, set some variable to tell my NMI routine we're waiting for a track to load, and check if it's loaded every frame. On Higan, this might actually be marginally slower than just looping to wait for the track since I'm not sure it even takes 1/60th of a second to load a track, at least on my machine, but it should be fast enough. Then, we would play the track once the MSU states that it's loaded, or, deal with it if it's a bad track.
Which leads to my second problem. Currently, if we detect a "bad track" (one that's missing, in the case of the wind or train scenes, intentionally), we handle it very cleanly: Since we're already in the game's native music loading routine when we're switching tracks, we just return to the routine we were called from without muting the SPC.
However, if we're doing the switching and playing during the NMI, we don't have a routine to return to, so there's no way to fall back to the original SPC routines... Except...
There is a workaround that might work to do this, but it's *extremely* messy and I don't particularly like it:
If we've got a bad track, we can load the correct stuff in the correct RAM spots and registers and "jump" to the original SPC routine, an instruction *after* the point where I hook it into my routine, effectively going into the game's original code to call for a new track in order to deal with our missing track. The problems?
For one, we'd be doing this during an NMI. I'm not sure if the game's SPC code is short enough to be called during an NMI, it's generally called during scene transitions during which the screen is blank and the NMI is disabled (which, we can't very well disable the NMI during the NMI). This could lead to graphical corruption or other bugs.
For another, we'd be at least 2 frames (or 1/30th of a second/0.03 seconds) behind the original call to load the song as far as loading the SPC version, which could throw timing off considerably.
Other workarounds?
Well, there's a few that come to mind at the moment.
1. We can abandon the idea of fading completely, keeping music transitions their current sudden (but working and with simple, easy to debug code) selves.
2. We can do an extremely quick looped fade during our original routine (on the order of less than a frame). This *should* reduce or eliminate popping but won't really sound any less sudden to human ears.
3. We can do a version of the patch that has good fading but that has no fallback routines: Looping sound effects or missing music tracks won't play at all. This is already the case if you're running in BSNES v075 because the "bad track" routine in the MSU-1 was not implemented yet at that point. (which is why I suggest Higan or the SD2SNES when this hack gets released)