Adding Sound Effects with FamiStudio
So your game has music, but it's missing something: the sweet sounds of plumbers jumping, bullets firing, and coins being collected. It's time to add sound effects!
Exporting from FamiStudio
To export sound effects from FamiStudio, simply follow the export instructions in the music tutorial with a few tweaks. In the Export Songs window, choose FamiStudio SFX Code on the left, instead of FamiStudio Music Code. Then, before you export, be sure to choose only the sound effect tracks and not any music.
Name your data file something like "mygame_sfx.s" and save it in the same location as your music data.
Enable SFX in the Sound Engine
To empower our sound engine to play sound effects, we must return to the "famistudio_ca65.s" file. In config section three,
"GLOBAL ENGINE CONFIGURATION", uncomment FAMISTUDIO_CFG_SFX_SUPPORT
and FAMISTUDIO_CFG_SFX_STREAMS
so they're live.
You can change the setting for FAMISTUDIO_CFG_SFX_STREAMS
if you expect to play lots of overlapping sound effects, but two
seems plenty to me.
Enabling this setting gets us two sets of exports from the sound engine file: the SFX subroutines, and four zero-page variables representing the four possible SFX channels.
Integrate the SFX Engine
Switch over to the file where you put your music-control code (for me, "src/audio/audio.asm"). In your imports list, add three more lines.
.import famistudio_sfx_init
.import famistudio_sfx_play
.include "mygame_sfx.s"
Unsurprisingly, the first two subroutines let us initialize the SFX portion of the sound engine and play sound effects, respectively. The
.include
statement brings in all the data and exports of the SFX data file we created from FamiStudio.
The SFX engine needs an additional import, however, that wasn't necessary to play music. Somewhere at the top of your audio control file, add a zero-page import like this.
.segment "ZEROPAGE"
.importzp FAMISTUDIO_SFX_CH0
This brings in the zero-page variable that the sound engine defined to describe sound effect channel 0. We'll use it in a moment, but for now,
just note that if you intend to play overlapping sound effects, you'll also need to import FAMISTUDIO_SFX_CH1
through CH3
as necessary, then code a way to determine which channel to use. That's beyond the scope of this tutorial, mostly since I haven't tried it myself.
Almost done! Let's add a few lines to our audio initialization subroutine, just after the call to famistudio_init
that set up
the music player.
; load sfx data address from "mygame_sfx.s"
LDX #.lobyte(sounds)
LDY #.hibyte(sounds)
; initialize sfx engine
JSR famistudio_sfx_init
Look familiar? This works like the music init call, with a few minor differences. First, the label whose address we're passing is just called "sounds", not something custom like "music_data_mygame". As far as I can tell, it's always called "sounds", but you can easily check your SFX data file for the name of the export. Again, this label is available to us because we included "mygame_sfx.s".
The only other difference is that the SFX engine doesn't need to discern between NTSC and PAL, so we don't have to load anything into A.
Trigger a Sound!
At last, it's time to trigger a sound effect! Add a subroutine to your audio controller that looks something like this.
.export sfx_chest_open
.proc sfx_chest_open
LDA #0
LDX FAMISTUDIO_SFX_CH0
JSR famistudio_sfx_play
RTS
.endproc
Also pretty straightforward. We load the index of the sound effect we want (essentially its "track number") into A and the
SFX channel reference into X, then call famistudio_sfx_play
. And that's it! Now, in my "open a treasure chest"
subroutine (which could also change the treasure chest sprite, increase the player's score, etc.) I just add a call to
sfx_chest_open
.
As with music tracks, you could write a different subroutine for each sound effect, but it's probably better to combine
them into a single SFX-triggering routine. For example, I could remove the first line of sfx_chest_open
and
set A to zero in my "open a treasure chest" code, just before I call sfx_chest_open
. The drawback, obviously,
is that it would be harder to remember which sound effect is being triggered at any point.
Playing Sound
If you completed the music tutorial, you've already done this, but just to recap: our game
actually triggers each frame of sound by calling the sound engine's famistudio_update
routine from the NMI handler.
If you already have music playing, there's nothing you need to do.