Misc SNES sound questions

Discussion of hardware and software development for Super NES and Super Famicom. See the SNESdev wiki for more information.

Moderator: Moderators

Forum rules
  • For making cartridges of your Super NES games, see Reproduction.
ndiddy
Posts: 33
Joined: Tue Oct 24, 2017 11:07 pm

Misc SNES sound questions

Post by ndiddy »

I've been working on a SNES "game" for a little while, and thought it would be cool to get audio working. However, while my SPC copy routine does appear to do "something", it copies garbage to the SPC instead of actual data. If anyone could help me debug, I'd REALLY appreciate it.

Here's the routine.
Last edited by ndiddy on Wed Mar 07, 2018 9:41 am, edited 1 time in total.
User avatar
HihiDanni
Posts: 186
Joined: Tue Apr 05, 2016 5:25 pm

Re: Need a little help with SPC data transfers

Post by HihiDanni »

A few things I noticed, perhaps not entirely related to the issue you're having but may save you some trouble later:

1. I noticed you're using the .a8, .a16, etc. directives manually after each rep/sep. If you're using ca65 you can use the .smart directive so you don't have to do this. It will help you avoid mistakes (and also make your code look cleaner). Here's what I put at the top of all my ca65 source files:

Code: Select all

.p816   ; 65816 processor
.a16
.i16
.smart
This defaults the accumulator and index registers to 16-bit. Any use of rep/sep will inform ca65 of the new sizes for you.

2. The use of sei is technically not necessary, but it might help for now. However, its use suggests that your NMI routine does not fully protect the interrupted code from side-effects. You should look into this to avoid any future headaches. From my own engine's code:

Code: Select all

VBlank:
    phb
    phd
    rep #$30
    pha
    phx
    phy

    ; Your code here

    rep #$30
    
    ply
    plx
    pla
    pld
    plb
    
    rti
Edit: I'm also not sure why you have two separate loops to perform the transfer. You should only need the one, and you also don't need to transfer in 256-byte increments. You're also incrementing the byte count by 2 (and AND-ing by 7f? Not sure why.). Not sure if it makes a difference but you should just inc by 1 (and not AND, since the 8-bit register size will wrap it by itself). My suggestion is to just use a 16-bit X to keep track of the number of bytes transferred, and inx once after each byte. After sending a byte of data to $2141, send the least significant byte of X to $2140. Compare the 16-bit value of X to the upload size to determine when to finish uploading.

Edit 2: This is the reference I used when writing my upload routine: https://wiki.superfamicom.org/transferr ... e-snes-apu

It seems a lot cleaner than basically everything else I've found online, and it's worked for me so far, but if it does have any issues I would appreciate if someone would correct me.
Last edited by HihiDanni on Sun Mar 04, 2018 3:53 pm, edited 1 time in total.
SNES NTSC 2/1/3 1CHIP | serial number UN318588627
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: Need a little help with SPC data transfers

Post by koitsu »

When HihiDanni says $4121, they mean $2141. And when they say $4120, they mean $2140. Dyslexia? :-)

That linked SPC transfer routine is the shortest and most optimal routine I've ever seen. However, it assumes the SPC data all resides within the active/current bank (which is reasonable, but... well... I'd rather not get into a discussion about that. I think everyone here is smart enough to understand :-) ).

Not to get off-topic or derail the convo -- but purely for comparison, here's Nintendo's official routine per their documentation. Things today that have to be explained in this picture (back in the early 90s a lot of us were more familiar with this):

* Comments ; SNES Emulator refer to Nintendo's official hardware development system and not "an emulator" like you'd know it today. It was called the "Super NES Emulator-SE", and was a hardware device that basically contained the hardware from a SNES console but with a lot of other hardware and interfaces (ex. a hardware ICE). You can find pictures of it online if you want
* Comments ; sony news refer to, IIRC, a 65816 assembler from Sony. It's been a long time...
* Comments ; m16 are just the programmers' way of reminding themselves that the accumulator is 16-bit at that point
* Comments ; > are just the programmers' way of "keeping track" of stack operations, though IIRC there's a typo on the pla line; I believe > was used for pushing, and < was for pulling. Nested stack operations would involve more than one less-than/greater-than. Again: these are just comments/a programmer's style
* The programmer enjoyed using immediate values in binary for sep/rep opcodes; this is just the programmer's style and doesn't mean anything magical
* Instruction 1da is actually lda (note: number-one vs. lowercase-ELL) was caused by use of OCR and never corrected prior to publication
* Instruction inc a isn't special, it's just incrementing the accumulator by 1 (i.e. inc by itself); just another assembler thing
* ! in front of labels/addresses forces 16-bit addressing (ex. lda.w in some other assemblers)
* h on the end of immediate indicates hexadecimal (ex. $aabb in other assemblers)
* Brackets ([]) around labels indicate 24-bit long indirect addressing, not 16-bit indirect addressing (the syntax varies per assembler sometimes). This means the label address in direct page (at location $0000) takes up 3 bytes, not 2

(2018/08/29 Edit: attachments removed.)
Last edited by koitsu on Wed Aug 29, 2018 6:43 pm, edited 1 time in total.
ndiddy
Posts: 33
Joined: Tue Oct 24, 2017 11:07 pm

Re: Need a little help with SPC data transfers

Post by ndiddy »

Got it working: turns out I wasn't setting the data stack pointer to the right value so it was copying stuff from that label's memory location in bank 0 rather than bank 1, where the actual data was.

Fixed code: https://pastebin.com/raw/jZGs9skL
HihiDanni wrote: Edit: I'm also not sure why you have two separate loops to perform the transfer. You should only need the one, and you also don't need to transfer in 256-byte increments. You're also incrementing the byte count by 2 (and AND-ing by 7f? Not sure why.). Not sure if it makes a difference but you should just inc by 1 (and not AND, since the 8-bit register size will wrap it by itself). My suggestion is to just use a 16-bit X to keep track of the number of bytes transferred, and inx once after each byte. After sending a byte of data to $4121, send the least significant byte of X to $4120. Compare the 16-bit value of X to the upload size to determine when to finish uploading.
I was pretty much following the flowchart in the SNES dev manual verbatim when I wrote the routine, which says "[SPC] PORT0 = previous PORT0 data +2 to 127". I was kinda confused by what it meant, but I decided to AND by 7f to make sure the data was within the 0-127 range. Transferring in 256 byte increments is nice because it makes the data offset you need to copy just the bank number in the high byte of A and the byte number the low byte of A.
creaothceann
Posts: 611
Joined: Mon Jan 23, 2006 7:47 am
Location: Germany
Contact:

Re: Need a little help with SPC data transfers

Post by creaothceann »

HihiDanni wrote:You're also incrementing the byte count by 2 (and AND-ing by 7f? Not sure why.). Not sure if it makes a difference but you should just inc by 1 (and not AND, since the 8-bit register size will wrap it by itself). My suggestion is to just use a 16-bit X to keep track of the number of bytes transferred, and inx once after each byte. After sending a byte of data to $2141, send the least significant byte of X to $2140. Compare the 16-bit value of X to the upload size to determine when to finish uploading.
Or count down and compare with zero.
My current setup:
Super Famicom ("2/1/3" SNS-CPU-GPM-02) → SCART → OSSC → StarTech USB3HDCAP → AmaRecTV 3.10
ndiddy
Posts: 33
Joined: Tue Oct 24, 2017 11:07 pm

Re: Need a little help with SPC data transfers

Post by ndiddy »

If I wanted to just have the DSP play a sample on boot, what more do I have to do than this? For some reason it plays nothing (but if I set the DSP directory address to a random point in memory it makes a loud beeping noise). The brr file was made with BRRtools, if that makes a difference.
Revenant
Posts: 462
Joined: Sat Apr 25, 2015 1:47 pm
Location: FL

Re: Need a little help with SPC data transfers

Post by Revenant »

Register $5d has to point to the sample directory, not just a sample. (Note the information about the directory format)
ndiddy
Posts: 33
Joined: Tue Oct 24, 2017 11:07 pm

Re: Need a little help with SPC data transfers

Post by ndiddy »

Revenant wrote:Register $5d has to point to the sample directory, not just a sample. (Note the information about the directory format)
Thanks, it works now.
ndiddy
Posts: 33
Joined: Tue Oct 24, 2017 11:07 pm

Re: Misc SNES sound questions

Post by ndiddy »

Hate to ask ANOTHER question, but is there a way to get rid of the clicking noise when looping a sample? The dev manual says you have to get rid of sudden volume changes in the audio to fix it, bit that obviously won't work if you want to keep a piano note held or something, as the loop point has to be while the note's already playing.
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Misc SNES sound questions

Post by tepples »

First, are you making sure the loop length is a multiple of 16 samples? This means if the sample is of middle C, which is 261 5/8 Hz, the original sample rate would have to be a multiple of (261 5/8)*16 = 4186 Hz.

Second, it may be an artifact of the filters not being in the same state at the start and end of the loop. Adding another 16 samples from the start of the loop to the end of the sample and then moving the loop point 16 samples later may help bring the filters into a more predictable state.

That pretty much exhausts the causes of clicking that I can guess without seeing anything. So third, which sample and which converter are you using? Someone may be able to inspect the input and output of the conversion to see what went wrong.
93143
Posts: 1717
Joined: Fri Jul 04, 2014 9:31 pm

Re: Misc SNES sound questions

Post by 93143 »

ndiddy wrote:Hate to ask ANOTHER question, but is there a way to get rid of the clicking noise when looping a sample? The dev manual says you have to get rid of sudden volume changes in the audio to fix it, bit that obviously won't work if you want to keep a piano note held or something, as the loop point has to be while the note's already playing.
Apologies if the following is too basic, but it's hard to determine the appropriate level of response from your question...

You have to match up the end of the loop with the beginning of the loop, so that the waveform keeps doing the same thing rather than making a sudden jump (a click) or suddenly heading off in a different direction (a ping or blip). That's what that means.

Also, if you're using a tonal sample (e.g. a piano rather than a snare drum), the loop length needs to be a multiple of the fundamental wavelength of the sample. Ideally this would result in the previous requirements being satisfied automatically, but in a real sample the waveform doesn't in general repeat perfectly (and the sample rate is generally not an exact multiple of the note frequency), so you still have to consider both factors.

Also, on the SNES, your loop has to be a multiple of 16 samples (one BRR block), on top of the previous considerations. This may require you to resample the data to an appropriate sample rate so that you can meet all the requirements at the same time. I'd suggest finding a good loop in the uncompressed audio first, and then resampling the audio so that the loop you found is a multiple of 16 samples long. After doing this, it will probably be necessary to trim or pad the beginning of the audio so that the start of the loop is a multiple of 16 samples from the start of the audio.

This is what a good loop looks like (though you can't rely only on looks; you have to listen to your loop and adjust it sample by sample to minimize artifacting):
goodloop.png
goodloop.png (2.18 KiB) Viewed 7201 times
The above is a single-cycle loop, which works reasonably well for a wide variety of tonal sounds, particularly if the playback device is capable of adding modulation to the sound to liven it up (ADSR, LFOs, etc.). It is not necessary to put the loop points at zero crossings, but it may be helpful.

Not every sound works well with a tiny loop range like what I'm showing here. Noisy sounds often lose their character when looped very tightly, and detuned or heavily modulated sounds suffer from obvious "freezing" when the loop hits. In such cases, a longer loop may be warranted if you've got the memory for it.

This is a bad loop:
badloop.png
badloop.png (2.18 KiB) Viewed 7201 times
A loop like this will produce a loud buzzing noise at a frequency different from that of the recorded sound, because there's a loud click every time it loops, and the loop length is not a multiple of the sound's wavelength.

Note that it is absolutely possible to get clicks and other garbage even if your loop length is perfect, because the waveform is probably not going to be totally uniform. If you absolutely can't find a good loop, it may be helpful to upsample to give yourself finer control, but most samples shouldn't be that pathological. Do not remove audio past the end of the loop until you're sure you've got a good loop, because getting a good loop requires you to move both loop points.

This is another bad loop:
badloop2.png
badloop2.png (2.19 KiB) Viewed 7201 times
The buzzing wouldn't be as bad with this one, because the levels are similar between the start and end of the loop. But not only does the waveform have a different slope at both points, but the loop length is nowhere near any multiple of the wavelength of the recorded sound. This one will sound very wrong.
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Misc SNES sound questions

Post by tepples »

In particular, the bad loop in your illustration that "will sound very wrong" has a loop of one and a half periods of the fundamental. This means it will sound a perfect fifth down once it hits the loop point, such as a C dropping to F.
User avatar
Bregalad
Posts: 8056
Joined: Fri Nov 12, 2004 2:49 pm
Location: Divonne-les-bains, France

Re: Misc SNES sound questions

Post by Bregalad »

Also, on the SNES, your loop has to be a multiple of 16 samples (one BRR block), on top of the previous considerations. This may require you to resample the data to an appropriate sample rate so that you can meet all the requirements at the same time. I'd suggest finding a good loop in the uncompressed audio first, and then resampling the audio so that the loop you found is a multiple of 16 samples long. After doing this, it will probably be necessary to trim or pad the beginning of the audio so that the start of the loop is a multiple of 16 samples from the start of the audio.
Note that the BRR conversion tool I wrote allows to do this automatically, so you don't even have to think about whether the loop you found to work is a multiple of 16.
ndiddy
Posts: 33
Joined: Tue Oct 24, 2017 11:07 pm

Re: Misc SNES sound questions

Post by ndiddy »

So my issue is that the sample loop offset is defined by a byte offset in the BRR sample, but obviously you don't know how far into the sample the loop is after you converted it (or what sample number it is if you're doing it through the brrtools command line). I attached the sample and am using BRRTools to convert it. Another problem is that I have no idea what the note in the sample is, I just stole it from a soundfont because I thought it sounded good.
Attachments
roland.zip
(16.45 KiB) Downloaded 213 times
KungFuFurby
Posts: 275
Joined: Wed Jul 09, 2008 8:46 pm

Re: Misc SNES sound questions

Post by KungFuFurby »

That's a chord consisting of C#, F, G# and a C in the next octave up.
Post Reply