Replacing audio in NES games

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

Post Reply
Sour
Posts: 891
Joined: Sun Feb 07, 2016 6:16 pm

Replacing audio in NES games

Post by Sour »

Recently I've spent some time improving support for HDNes' HD packs in Mesen (and adding a few features to the format along the way).
Up until now, that support was limited to graphic replacement, but with that mostly being done, I decided to give audio replacement a go, too.
HDNes has a tool to create conditions to instruct the emulator on when to replace music, but I figured making this flexible enough to work on all games would be a lot of work. So I decided to go in another direction, similar to byuu's MSU1.

This is what I came up with:

Code: Select all

switch(addr) {
	//Playback Options
	//Bit 0: Loop BGM
	//Bit 1-7: Unused, reserved - must be 0
	case 0x4FF9: _oggMixer->SetPlaybackOptions(value); break;

	//Playback Control
	//Bit 0: Toggle Pause/Resume (only affects BGM)
	//Bit 1: Stop BGM
	//Bit 2: Stop all SFX
	//Bit 3-7: Unused, reserved - must be 0
	case 0x4FFA: ProcessControlFlags(value); break;

	//BGM Volume: 0 = mute, 255 = max
	//Also has an immediate effect on currently playing BGM
	case 0x4FFB: _oggMixer->SetBgmVolume(value); break;

	//SFX Volume: 0 = mute, 255 = max
	//Also has an immediate effect on all currently playing SFX
	case 0x4FFC: _oggMixer->SetSfxVolume(value); break;
		
	//Album number: 0-255 (Allows for up to 64k BGM and SFX tracks)
	//No immediate effect - only affects subsequent $4FFE/$4FFF writes
	case 0x4FFD: _album = value; break;

	//Play BGM track (0-255 = track number)
	//Stop the current BGM and starts a new track
	case 0x4FFE: _trackError = PlayBgmTrack(value); break;

	//Play sound effect (0-255 = sfx number)
	//Plays a new sound effect (no limit to the number of simultaneous sound effects)
	case 0x4FFF: _trackError = PlaySfx(value); break;
}
Basically, it's implemented as a set of registers at $4FF9-$4FFF and it allows for 64k different bgm tracks, and 64k different sound effects (the "album" value acts as the MSB of a 16-bit track number)
Only 1 bgm (looped or not) can play at a time, along with any number of sound effects. The bgm can be paused, but not sound effects (maybe they should?)
BGM & sound effect volume is independent, but all sound effects share the same volume setting.
Obviously the downside to this is that the game needs to be patched (IPS/BPS patches can be bundled with the audio files in Mesen's case). The upside is that it can be made to work with pretty much any game.

Does anyone see anything weird, missing or outright bad with this?
e.g:
-Mapper-wise, as far as I know, no major mapper uses this address range, I think?
-There is no seek capability, just play/pause/stop - I couldn't really find any scenarios where seeking would be useful, but I'm not a game dev. Would it be useful to have?
-Does the API make sense from the point of view of someone coding a game in 6502 assembly? I did hack up SMB1 to use this and replaced a few BGMs and some sound effects in a short amount of time (video). The fully disassembled & commented version of the code definitely helped make this easy, though.
-Would using these registers on an actual cart make sense?

I have no intention of actually designing a chip that would support this, but at the very least, I'd like to make it so that it would technically be possible to make one.
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Replacing audio in NES games

Post by tepples »

I think Namco 163 uses $4800-$4FFF.

Seeking is needed for Super Mario Bros., which returns to the same entry in the order table after the timer jingle plays.

As for the hardware interface, I'd recommend starting by implementing the MSU1, as that has support on one flash card for a console with a 6502-family CPU. It'd have to be relocated from $20xx so as not to interfere with the PPU though.
Sour
Posts: 891
Joined: Sun Feb 07, 2016 6:16 pm

Re: Replacing audio in NES games

Post by Sour »

Ah, maybe I could add a toggle to select between $4xxx range and $5xxx range then - that should allow pretty much anything to work in one way or other.

With the way I have things setup, the timer jingle already doesn't interrupt the music, but it doesn't speed it up either. So you'd have to change the bgm track at the same time. With the jingle covering the bgm for a split second though, it wouldn't really be that bad if the sped-up bgm track starts over from the start instead of trying to resume where the regular speed version left off, I think.

The MSU1 doesn't actually support everything I am trying to do - as far as I can tell, it only supports playing 1 bgm at a time. So it doesn't seem like there's any way to use it for sound effects and the like?
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Replacing audio in NES games

Post by tepples »

MSU1 games play sound effects through the S-SMP and S-DSP, just as with normal cartridge games. Games using the NES or Famicom version of MSU1 could play sound effects through the PSG, DMC, or expansion audio, and these sound effects could be more detailed because they no longer need to share bytes or cycles with music.

At some point, you need to decide whether you're making a music player or a generic sampler. And this decision informs how you structure the storage for sound effects.
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: Replacing audio in NES games

Post by lidnariq »

Sour wrote:Ah, maybe I could add a toggle to select between $4xxx range and $5xxx range then - that should allow pretty much anything to work in one way or other.
Namco 163 uses the entire range from $4800 up through $FFFF. Other mappers are known to exist that use $4020--$43ff and $4500-$7ff. $44XX is probably clear. CopyNES puts its BIOS in $3XXX.

Overlaying write-only ports on top of the mirrors of the PPU's read-only port (e.g. $2FFA) is probably one of the few guaranteed safe options.
User avatar
rainwarrior
Posts: 8732
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Replacing audio in NES games

Post by rainwarrior »

lidnariq wrote:CopyNES puts its BIOS in $3XXX.
Only briefly before it starts the cartridge, and for write-only registers that shouldn't be a conflict anyway, but...

...the PPU interface on the other hand is mirrored through the $3000s all the time (if not temporarily replaced by the CopyNES) so it's not really an option anyway? (Except the read-only addresses you mentioned.)
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: Replacing audio in NES games

Post by lidnariq »

rainwarrior wrote:
lidnariq wrote:CopyNES puts its BIOS in $3XXX.
Only briefly before it starts the cartridge, and for write-only registers that shouldn't be a conflict anyway
Yeah, you're right, that just confused the question.
Sour
Posts: 891
Joined: Sun Feb 07, 2016 6:16 pm

Re: Replacing audio in NES games

Post by Sour »

Spent a few minutes searching through my code, and assuming it's not wrong, some mappers appear to map $4020-$FFFF entirely.
Using the PPU's read-only registers idea is clever, but having a register every 8 bytes feels like it might make them pretty hard to remember (e.g 3002, 300A, 3012, 301A, ...)

If I exclude unlicensed mappers, though, it looks like $4100-$47FF is safe? Study Box maps to $4200-$43FF, but I'm not even sure that's a licensed product (and nobody would want to use this for that either way...)
I think I might just use $4100-$410X - avoiding overlap with licensed/homebrew mappers is my main objective.
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: Replacing audio in NES games

Post by lidnariq »

I feel like the contemporary US-released unlicensed games are probably worth entertaining also. (i.e. NINA-03/06)

Since you're only talking about seven registers, you could pack them into the $F00s or $F0s nybble instead, e.g. $3xF2 or $3Fx2, so that the layout is easier to remember even though they're not contiguous.
Sour
Posts: 891
Joined: Sun Feb 07, 2016 6:16 pm

Re: Replacing audio in NES games

Post by Sour »

Thanks, putting the registers every 16 or 256 bytes definitely makes it a lot easier to remember them.
In the end, I kept a toggle to switch between 2 configurations and used this:

Code: Select all

Write:
$4100/$3002: Playback Options
$4101/$3012: Playback Control
$4102/$3022: BGM Volume
$4103/$3032: SFX Volume
$4104/$3042: Album Number
$4105/$3052: Play BGM Track
$4106/$3062: Play SFX Track

Read:
$4100: Status bits
$4101: Revision number
I had originally planned on using $4018/4019 as the alternate read registers, but I'm not sure these are actually open bus on a NES? (e.g due to the test/disabled registers from 4018 to 401A?)
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: Replacing audio in NES games

Post by lidnariq »

In practice, they'll only interfere with someone who has a 2A03G CPU and has modified their mainboard. So it's probably safe to put read-only registers there.

Of course, it's also probably safe to put read-only registers overlaying the actual write-only APU registers, too.
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Replacing audio in NES games

Post by tepples »

Or put read/write ports at $4009 and $400D, as those channels don't have a sweep.
Sour
Posts: 891
Joined: Sun Feb 07, 2016 6:16 pm

Re: Replacing audio in NES games

Post by Sour »

I'll keep them as $4018/4019 for now (since that's what I ended up committing to Github last time). I doubt anybody would ever actually make a cartridge supporting this in hardware either way, but it's nice to think that it could potentially be done.

Thanks a lot for the help on this!
Post Reply