[SOLVED] Any tutorials on how to add music to a SNES game?

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
[SOLVED] Any tutorials on how to add music to a SNES game?
by on (#238576)
UPDATE: Jump to my post here on this thread for the solution.

I'm trying to make my own SNES game. The next thing I wanted to add was music. I downloaded SNES GSS and made a song. I exported the .spc file and attempted to fit it into my game but according to this reply by calima, .spc files are NOT meant to be played!

I tried to do File > Export with SNES GSS, but then I get I/O error 123.... So not sure how to proceed from there.

Is there another way to turn .spc files into the appropriate code?

Also, once I have the sound code, how do I actually include it in the ROM? I literally can't find any source other than this wikibooks article, but that talks about using .spc files!

Suffice to say I'm very lost...
Re: Any tutorials on how to add music to a SNES game?
by on (#238582)
I can't help with GSS specifically, but I can say while there are some practical reasons that you wouldn't use .spc files in a game, there's also no show-stopping reason that you can't do it anyways.

.spc file is just a saved state of what's in the SPC's memory. the SPC chip has a tiny ROM with a loader that is used to get started, but after that you must supply your own code that communicates between the CPU and SPC. So if you're just loading an .spc file, the code in your CPU-side will need to be compatible with the SPC-side of the interface. If it's not, you'll be able to start the music playing, but you'll never be able to talk the SPC chip again (well, not until you reset the system). If you don't ever want to change songs or play sound effects, you can play pretty much any .spc file.

For practical reasons, when you want to change songs and instrument sets, you probably want to store and transfer just those parts of data, and not an entire dump of SPC RAM. These kind of transfers, like all other communication (beyond that IPL boot ROM), is done manually by the user for both CPUs involved.

Instead of hacking all that stuff up though, it's probably better to get GSS working as intended. Just saying though.
Re: Any tutorials on how to add music to a SNES game?
by on (#238591)
There are no tutorials I'm afraid. The only GSS documentation is the README included in the GSS zip. The asm is reasonably commented.
Re: Any tutorials on how to add music to a SNES game?
by on (#238937)
So I managed to get the exported files by using the command line instead of the GUI (as stated in the README).

I have a bunch of files but honest to God I have no idea what assembly functions I'm supposed to call and in what order... the comments say nothing about what to do unfortunately.

All the assembly is written in wla dx which I can port to ca65 I guess (though that's gonna be tedious). But also, sneslib.h has some C functions, and I'm not even sure how to compile and assemble it!

Has anyone worked with SNES GSS in depth before? I found a video online where a guy shows how to use SNES GSS and use an SPC player to get it to play on an emulator, but not how to actually include the music in a SNES game
Re: Any tutorials on how to add music to a SNES game?
by on (#239017)
I don't use the editor itself due to a platform incompatibility, but I know of some source code that does use SNESGSS in their games:

Furry RPG (music only, still a WIP): https://github.com/Ramsis-SNES/furryrpg
Kung Fu Master (music only, still a WIP): https://github.com/AntiheroSoftware/sfc-Kung-Fu-Master
undisbeliever's Simon Says (SFX only, no music): https://github.com/undisbeliever/1gam-c ... simon_says

sneslib.h is used for C compilers (although I'm trying to remember which one... tcc-65816?). The code seems to be adapted for C-based compilers in the first place, as there are some routines only implemented in C, and not in ASM. Reminds me of code compiled with pvsneslib. Hmm...
Re: Any tutorials on how to add music to a SNES game?
by on (#239027)
Yeah, looks like it's intended to be used in C, the C functions call those assembly subroutines. So what you'll need to do if you're changing it to CA65, is port the assembly parts (the parts you need) to CA65, then recreate the C functions that you need in assembly.

I was originally looking at the copy of it someone put on Github. I decided to look at the version on Shiru's site, and in the /src/ folder there is another readme.txt, which is entirely missing from the Github version. Gee, that's totally not going to cause problems for people who try to use it.. WTF.

So here's the relevant excerpt from it:

Code:
Export and use

When all music and sound effects created, the project can be exported
(File>Export) for further use in a SNES program. The editor exports
a number of files:

spc700.bin -  contains compiled SPC700 driver code, samples, sound effects
              data. This means you don't need to use a SPC700 assembler.

music_N.bin - contains music data. Each sub song that is not marked as a
              sound effect will be exported into separate file. This allows
           to save room for more sample data, while music data is only
           loaded when needed.

sounds.asm  - contains incbin's in the WLA DX format, considering LoROM
              configuration.

sounds.h    - contains automatically generated aliases for every sub song
              and sound effect, as well as sub song names.

The latter two files aren't necessary, they meant to be used in a specific
SNES dev environment. The interfacing part of the environment is provided
along with the editor in the /snes/ directory, it consist of two files,
sneslib.asm and sneslib.h. There are 65816 assembly functions to load the
SPC700 driver and communicate with it, as well as C interface to these
functions.

You can either adapt provided code for your purposes, or create a new one,
using the code as a guide. spc700.bin loading and starting address is
$0200. When the driver is loaded, communication is done through APU ports
using the communication routines (see snes/sneslib.asm:542 and beyond).
Driver loading code is in snes/sneslib.h:774. 'Play a sound effect'
function is in sneslib.asm:842, it also uses spc_command_asm routine
(uniform for most commands), which is in sneslib.asm:658.

The spc700.asm file in the /snes/ directory is only provided for reference,
in case you're interested in internals of the driver. It does not need to
be compiled or otherwise used in a SNES program.

Take a note that, unlike the editor or exported SPC file, driver starts in
mono mode, for compatibility reasons, as many old TV sets does not have
stereo, and some part of the sound would be missing. To get stereo output,
the stereo enable command must be sent first.
Re: Any tutorials on how to add music to a SNES game?
by on (#239048)
So I'm looking at the sneslib.asm file to see what all I can use. It has a lot of PPU related functions that I'm gonna ignore. Also, I'm not gonna bother with the .h file because I really don't wanna open a can of worms of getting C code to work lol.

Anyways one handy subroutine is this guy:

Code:
;void spc_load_data(unsigned int adr,unsigned int size,const unsigned char *src)

spc_load_data:
    ...                 ; do a bunch of stuff
    rtl


I had found this guide earlier on how to transfer data to the APU, and this seems to follow that guide more or less.

So with that subroutine I can transfer spc700.bin (the driver) to APU at address $0200. Nice.

There's also another subroutine:

Code:
;void spc_stereo(unsigned int stereo);

spc_stereo:
    ...                 ; do a bunch of stuff
    rtl


I can use that guy to enable stereo. Also nice.

Now here's the part I'm confused about. First, what do I do with my music data, music_1.bin. The README.txt does not mention where in the address space it's supposed to go. I looked at the stop of the driver file, spc700.asm, and saw this:

Code:
//memory layout
// $0000..$00ef direct page, all driver variables are there
// $00f0..$00ff DSP registers
// $0100..$01ff stack page
// $0200..$nnnn driver code
//        $xx00 sample directory
//        $nnnn adsr data list
//        $nnnn sound effects data
//        $nnnn music data
//        $nnnn BRR streamer buffer


I'm not sure how to read that... Is it saying that the music data should go right after the driver data? I.e. $0200 + sizeof(spc700.bin)?

Another thing is I have no idea how to tell the music to start playing. Here are all the subroutines (at least the ones related to the APU) given by sneslib.asm
    1. spc_load_data
    2. spc_command_asm (sends a command. Appears to get the parameters from global variables acting as "mailboxes")
    3. spc_command (also sends a command but gets paramsters from the stack. I guess this is for C compatibility)
    4. spc_stereo
    5. spc_global_volume
    6. spc_channel_volume
    7. music_stop
    8. music_pause
    9. sound_stop_all
    10. sfx_play
    11. spc_stream_update

There does not appear to be a music_play command. Overall I'm very lost as to what's going on...
Re: Any tutorials on how to add music to a SNES game?
by on (#239049)
UPDATE

Ah dammit... I just looked in sneslib.h. I thought that file was just there to give hooks for C code to call the assembly routine.

NOPE

There is vital C code in that header...

For one:

Code:
/*initialize sound, set variables, and upload driver code*/

void spc_init(void)
{
    const unsigned int header_size=2;
    static unsigned int i,size;

    size=spc700_code_1[0]+(spc700_code_1[1]<<8);

    spc_music_load_adr=spc700_code_1[14]+(spc700_code_1[15]<<8);

    spc_stream_enable=FALSE;

    nmi_enable(FALSE);

    if(size<32768-header_size)
    {
        spc_load_data(0x0200,size,spc700_code_1+header_size);
    }
    else
    {
        spc_load_data(0x0200,32768-header_size,spc700_code_1+header_size);
        spc_command(SCMD_LOAD,0);
        spc_load_data(0x0200+32768-header_size,size-(32768-header_size),spc700_code_2);
    }

    spc_command(SCMD_INITIALIZE,0);

    nmi_enable(TRUE);
    nmi_wait();
}


So turns out there is a LOT that you have to do to initialize the APU. Loading the spc700.bin driver file into $0200 is NOT sufficient; I need to call spc_init(). Boy that seems like something the README should say...

Furthermore, there exists exactly the function I want:

Code:
/*play music*/

void music_play(const unsigned char *data)
{
    static unsigned int size;

    size=data[0]+(data[1]<<8);

    spc_command(SCMD_MUSIC_STOP,0);
    spc_command(SCMD_LOAD,0);
    spc_load_data(spc_music_load_adr,size,data+2);
    spc_command(SCMD_MUSIC_PLAY,0);
}


Even though the comment elucidates nothing, I can tell from context that data should be a pointer to music_1.bin. So if I pass that in, I should be golden.


Actual questions

How do include a C header file into an assembly program? Do I just use .include?

How do I get it to compile? I'm using ca65 as my assembler. Do I need like a C compiler or something?

Also, how specifically do you call C functions? I know you put arguments on the stack and do a jsl, but what all needs to go on the stack and in what order?


Update

I forgot to consider the possibility that I could port the C functions to assembly equivalents. They don't look so complex (they stand on the shoulders of the existing subroutines after all).
Re: Any tutorials on how to add music to a SNES game?
by on (#239059)
rchoudhary wrote:
How do include a C header file into an assembly program? Do I just use .include?

How do I get it to compile? I'm using ca65 as my assembler. Do I need like a C compiler or something?

Also, how specifically do you call C functions? I know you put arguments on the stack and do a jsl, but what all needs to go on the stack and in what order?



cc65 (included with ca65) is the C compiler that produces ca65-compatible output. With cc65 I have very little experience with including asm in a C program, and no experience including C in an asm program. You can't .include the H file, the assembler won't recognize C syntax. I think what you're supposed to do, is in the assembly file .import global variables and C function names. You would compile the C file (er, H file in this case I guess), assemble the ASM file, then using LD65, link the object files together. At the linking stage, the .imported label names will be reconciled.

That being said, if you're writing the game in 100% asm anyways, and because there's very little C code in this case, I think it would be easier to just manually rewrite that C code into assembly. I might be overestimating the difficulty of calling C routines from assembly because I've never tried it. Someone else hopefully can answer the 3rd question (I'm curious too). With cc65 though I do know it outputs 6502, not 65C816.

edit: you figured it out before I finished the post :)
Re: Any tutorials on how to add music to a SNES game?
by on (#239060)
I don't know what the tcc-816 ABI is. cc65 ABI is well documented, but this code is for tcc-816.
https://github.com/cc65/wiki/wiki/Param ... onventions

I agree that for OP's project it's best to write the init and music_play funcs in asm.
Re: Any tutorials on how to add music to a SNES game?
by on (#239147)
I'VE GOTTEN SOUND TO WORK!!!
I am posting this so that anyone who stumbles across this thread on Google will not be left hanging

Sorry in advance for this being long, the goal is for it to be comprehensive more than anything.

    NOTE: My specific solution is for ca65, not wla dx; however, I'm sure the crux of it carries over.

So first I made the track in SNES GSS. Then I did File > Export and it produced 4 files:
    1. spc700.bin - the driver file for the SPC700
    2. music_1.bin - the music data
    3. sounds.asm - unused, I deleted this because I didn't need it
    4. sounds.h - unused, I deleted this one too for the same reason

    NOTE: Originally when I would do File > Export it wouldn't work (it said I/O Error 123). I think the issue was I needed to do File > Save and Export first. I had done File > Save earlier but I guess that doesn't cut it. Anyways, if you also find yourself in a spot like that, simply do snesgss.exe filename.gsm -e [optional export path]. That also produces those 4 files I mentioned earlier.

    NOTE: By the way, when you do File > Export, a dialog pops up which lets you choose where to save the binary files. If you do File > Save and Export, it exports the binary files in the same folder you save the .gsm file.

My driver file was quite small since I used the sawtooth and triangle waveforms that came with SNES GSS (located in the instruments folder). As a result, it fit in one ROM bank.

The secret sauce came from this Kung Fu Master port (Github link) written by Antihero Software. Thanks to KungFuFurby for linking me to that! (Post is earlier in this thread)

The files I needed were src/snesgss.asm and src/includes/snesgss.inc. I downloaded them and added them to my project folder. snesgss.asm had all the subroutines I needed to call. snesgss.inc contained the .import directives needed to access these subroutines.

    NOTE: These files were written for the ca65 assembler. Unfortunately, they will not work if you're using wla dx. You could port it over manually, or in that post by KungFuFurby, the other two repos use wla dx assembler (I believe).

    EXTREMELY IMPORTANT NOTE: If you search online you will see some posts on this forum itself that say that the first two bytes of spc700.bin contain the size of the driver file and as a result should NOT be transferred to the SPC700. This is 100% correct however do NOT fix this by opening up a hex editor and deleting the first two bytes! The snessgss.asm file takes care of this for you by having the .incbin directive skip over the first two bytes. Basically, don't touch spc700.bin!!

I had to make one change in snesgss.asm. On line 169, there's a macro expansion:
Code:
_GSSDataGenerator "../ressource/music", 1, "BANK7"


The first parameter (the string) was the location of spc700.bin and music_1.bin relative to snesgss.asm. I changed that string to "music".

    NOTE: You need to keep spc700.bin and music_1.bin in the same directory for the macro expansion to work!

The next parameter was the number of songs. I kept that as 1.

Finally, the last parameter was the bank to put all the data in. I changed it to "BANK4" cuz I only was using banks 1-3 up until that point.

    NOTE: if spc700.bin and music_1.bin don't fit in a bank together (i.e. their combined sizes is > 32 KB) then you'll have to modify the macro to break up the files and place them into 2 separate banks.

Moving on, at the top of my main.asm I added .include "snesgss.inc". Then to my startup routine, I added the following lines of code to my startup routine:
Code:
    jsl         gss_init
    jsl         gss_setStereo

    lda         #$00                    ; Play music_1.bin
    jsl         gss_playTrack


These lines needed to go after SNES initialization (of course) but before VBlank is enabled!!! (dear god is that an important point).

Finally, I had to modify my build.sh bash script (I'm on a Mac) to look like this:
Code:
ca65 -v snesgss.asm
ca65 -v main.asm
ld65 -C lorom128.cfg -o game.smc main.o snesgss.o -v
rm -rf main.o


Basically I had to compile snesgss.asm separately and then link the produced object file.

After all that, I ran my build script and was greeted with glorious audio! :D

Of course I was just trying to play a single song that loops over and over. Changing songs, playing sound effects, and other APU functionality I have not messed with and probably won't for this current project. Hopefully this all is useful to someone some day (it sure would've been useful to me :wink: )


---

P.S. If anyone needs more detail about what I did, go ahead and just reply to this thread. I get email notifications, so I'll see that and reply (probably)

P.P.S. If anyone spots any errors please reply to the thread so newcomers don't do the wrong thing and waste time banging their head against the wall! I can update my post if needed too.


---

UPDATE 1: For anyone wondering, here is a SNES GSS repo: https://github.com/nathancassano/snesgss. The README does a pretty good job describing how to use it. See Update 2!

There's also a video of a guy teaching you how to use it: https://vimeo.com/119986872.

Those two together should be enough to figure out how to create the music.


---

UPDATE 2: As calima points out in this thread, the repo I linked has an old version of SNES GSS (version 1.22 to be exact). This is a very outdated version. Instead you should get version 1.42 from Shiru's website: https://shiru.untergrund.net/software.shtml.

Here's what you miss out if you use that Github repo instead of the one from Shiru's website:

v1.42 20.02.17 - Instrument selection in the main menu
v1.41 25.05.14 - MIDI input device can be changed in the config file
v1.4 24.05.14 - paste over; shifting markers and labels in multichannel Expand/Shrink; MIDI input support
v1.31 03.04.14 - song duration estimate, displayed in the window header
v1.3 02.04.14 - Output monitor[/u] and [u]built-in tuner; minor fixes
v1.23 01.04.14 - SPC700 driver fix to remove noise burst on initialize heard on the real HW and latest snes9x versions


Definitely get the newest version!

As far as I can tell it works almost exactly like the version in the video (which is actually version 1.2, even older than mine!).
Re: Any tutorials on how to add music to a SNES game?
by on (#239151)
Just FYI, that github repo is third-party and ancient. You will get issues on hardware if you used such an old version of snesgss.exe to export your music. Use the latest from Shiru's site.
Re: [SOLVED] Any tutorials on how to add music to a SNES gam
by on (#239163)
Ah thanks for pointing that out, I downloaded it and updated my own binary files and then updated my poost.