It is currently Sat Jun 23, 2018 10:45 pm

All times are UTC - 7 hours



Forum rules


Related:



Post new topic Reply to topic  [ 19 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Sun Mar 04, 2018 8:29 am 
Offline

Joined: Tue Oct 24, 2017 11:07 pm
Posts: 13
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.

Top
 Profile  
 
PostPosted: Sun Mar 04, 2018 9:26 am 
Offline
User avatar

Joined: Tue Apr 05, 2016 5:25 pm
Posts: 186
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:
.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:
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/transferring-data-from-rom-to-the-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.

_________________
SNES NTSC 2/1/3 1CHIP | serial number UN318588627


Last edited by HihiDanni on Sun Mar 04, 2018 3:53 pm, edited 1 time in total.

Top
 Profile  
 
PostPosted: Sun Mar 04, 2018 2:35 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 3389
Location: Mountain View, CA
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


Attachments:
Untitled.png
Untitled.png [ 87.95 KiB | Viewed 1440 times ]
Top
 Profile  
 
PostPosted: Sun Mar 04, 2018 3:10 pm 
Offline

Joined: Tue Oct 24, 2017 11:07 pm
Posts: 13
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.


Top
 Profile  
 
PostPosted: Mon Mar 05, 2018 8:05 am 
Offline
User avatar

Joined: Mon Jan 23, 2006 7:47 am
Posts: 113
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.


Top
 Profile  
 
PostPosted: Mon Mar 05, 2018 10:57 am 
Offline

Joined: Tue Oct 24, 2017 11:07 pm
Posts: 13
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.


Top
 Profile  
 
PostPosted: Mon Mar 05, 2018 1:43 pm 
Offline

Joined: Sat Apr 25, 2015 1:47 pm
Posts: 389
Location: FL
Register $5d has to point to the sample directory, not just a sample. (Note the information about the directory format)


Top
 Profile  
 
PostPosted: Mon Mar 05, 2018 2:58 pm 
Offline

Joined: Tue Oct 24, 2017 11:07 pm
Posts: 13
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.


Top
 Profile  
 
PostPosted: Wed Mar 07, 2018 9:46 am 
Offline

Joined: Tue Oct 24, 2017 11:07 pm
Posts: 13
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.


Top
 Profile  
 
PostPosted: Wed Mar 07, 2018 10:15 am 
Online

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 20167
Location: NE Indiana, USA (NTSC)
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.


Top
 Profile  
 
PostPosted: Wed Mar 07, 2018 3:52 pm 
Offline

Joined: Fri Jul 04, 2014 9:31 pm
Posts: 926
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):

Attachment:
goodloop.png
goodloop.png [ 2.18 KiB | Viewed 1097 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:

Attachment:
badloop.png
badloop.png [ 2.18 KiB | Viewed 1097 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:

Attachment:
badloop2.png
badloop2.png [ 2.19 KiB | Viewed 1097 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.


Top
 Profile  
 
PostPosted: Wed Mar 07, 2018 4:46 pm 
Online

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 20167
Location: NE Indiana, USA (NTSC)
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.


Top
 Profile  
 
PostPosted: Thu Mar 08, 2018 3:48 am 
Offline
User avatar

Joined: Fri Nov 12, 2004 2:49 pm
Posts: 7438
Location: Chexbres, VD, Switzerland
Quote:
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.


Top
 Profile  
 
PostPosted: Thu Mar 08, 2018 11:11 am 
Offline

Joined: Tue Oct 24, 2017 11:07 pm
Posts: 13
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 19 times
Top
 Profile  
 
PostPosted: Thu Mar 08, 2018 1:07 pm 
Offline

Joined: Wed Jul 09, 2008 8:46 pm
Posts: 253
That's a chord consisting of C#, F, G# and a C in the next octave up.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 19 posts ]  Go to page 1, 2  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: Google [Bot] and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group