Generating different instruments on the APU

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Generating different instruments on the APU
by on (#38883)
Greetings everyone,

I'm currently writing the sound engine for my NES game, and was wondering how to implement different instruments.

I'm no musician, but I have a decent (not expert) understanding of the physics of sound. A friend of mine is composing MIDI files on his Mac, which I then convert to a format suitable for APU playback.

So far, I have a 4-track tune playing through the Pulse 1, Pulse 2, Triangle, and Noise channels. (I haven't experimented with the DMC channel yet.)

The note frequencies are correct, but the waveforms themselves sound pretty generic at present. Right now, they're just your basic pulse/triangle waves with no modulation. The song already sounds fantastic, but I'd like to take it over the top with some sweet instruments.

So, how does one synthesize different types of instruments on the APU? Is it just a matter of selecting the right ADSR envelope, or is there more to it? Do you employ frequency and/or duty cycle modulation of any kind? I haven't got the slightest clue.

Also, are there specific values that distinguish a piano from, say, a guitar, trumpet or harp? Any insight would be much appreciated.

by on (#38896)
Go download famitracker and play around with the instrument editor.

by on (#38898)
Another kind of neat tool is this iNES file by SnowBro:

http://nesdev.com/sndtest.zip

If you're not sure what each bit in the registers are for, check out the wiki or NESSOUND.txt

http://nesdevwiki.org/index.php/NES_APU

For some reason I can't find NESSOUND.txt right now : P It's out there somewhere though. Also, I'm not sure how much you've read up on the APU, but I thought I'd throw these at you just in case.

by on (#38903)
Good volume control is a must. ADSR is one way to control it, but a lot of people like to define the volume frame-by-frame. That's easy to implement (read lookup table for instrument, then subtract channel and/or global volume). Manually defined volume (with a defined loop exit for note-off) is my favorite, but maybe a bit fancy. Maybe if you've got a whole ton of different instrument, ADSR would be better. But usually there aren't a lot of instruments per game.

And you'd want the duty-cycle control to be the same way, defined per-frame. It's pretty common for the first frame or 2 of a note to be a different duty-cycle than the rest, for a slight percussive touch.

As far as messing with the frequency, arpeggios are somewhat common so you can play chords in one channel. It's worth considering that high-speed arpeggios tend to bug the living shit out of many listeners.

It works out best to just forget about other instruments, if you're arranging a song then the NES is the instrument.
I don't know if it'd help, but here's a few cover songs I made with Famitracker:
http://2a03.free.fr/?p=pub&dir=Memblers

by on (#38919)
Thanks for your help, gents!

@Dwedit: Wow, FamiTracker is insane...ly cool, that is. It'll take some time to get my bearings with that bad boy.

@Roth: That sndtest tool is awesome! Previously, I had been setting random bits in the sound registers and rebuilding the ROM to get a sense of how each parameter operates. The sndtest tool greatly speeds up that experimentation.

@Memblers: Thanks for clarifying some areas of confusion for me. I have one question about the math, though. (Maybe it's just late and I'm not thinking clearly.) How does subtracting the channel/global volume from the value in the instrument lookup table yield the correct final volume? I'd imagine the per-frame instrument volume would be stored as a fixed-point percentage in the range [0.0, 1.0] (at least conceptually), which is then multiplied by the channel/global volume. I can understand trying to avoid fixed-point multiplication, but I think I'm missing something.

by on (#38921)
Roth wrote:
Another kind of neat tool is this iNES file by SnowBro:
http://nesdev.com/sndtest.zip


Roth, you made my day ;) I didn't know about that small soft. Very useful indeed. I need to pay you a beer someday. Maybe while watching football, who knows? :lol:

by on (#38922)
SecretServiceDude wrote:
I have one question about the math, though. (Maybe it's just late and I'm not thinking clearly.) How does subtracting the channel/global volume from the value in the instrument lookup table yield the correct final volume? I'd imagine the per-frame instrument volume would be stored as a fixed-point percentage in the range [0.0, 1.0] (at least conceptually), which is then multiplied by the channel/global volume. I can understand trying to avoid fixed-point multiplication, but I think I'm missing something.

It would ordinarily be an 8-bit by 8-bit multiplication, which takes 200-300 cycles without a half kilobyte lookup table of (x^2/2) values or 100 cycles with one. That's an extra 3 to 7 scanlines of CPU time across the three channels that have a usable volume control (1, 2, and 4).

by on (#38926)
Banshaku wrote:
Roth, you made my day ;) I didn't know about that small soft. Very useful indeed. I need to pay you a beer someday. Maybe while watching football, who knows? :lol:


haha! Now that's something I can get on board with : ) I'll even watch your Patriots with you in that case haha

Yeah, that's a pretty good program overall. At one point I had considered modifying the source so all channels could be changed and played at once. That could be useful for figuring out really nice booming sound effects, or something that takes up all channels, if you know what I mean. I have other priorities right now, but I think that would be a neat-o idea if anyone else would like to do it some day.

by on (#38946)
Tepples: Quick question. In my own (naive) sound driver I've been writing, I assumed that I could just do a 0.4x0.4 -> 0.4 multiply using a lookup table, since the NES only has 4 bit volumes anyway. (Other than triangle modulation by the DAC) ... Bit rotations are comparitively inexpensive, too, so even if I want something different (say 0.6x0.6 for MOD-like syntax) it shouldn't be that bad...

Or am I missing something?

by on (#38948)
4x4 or even 6x6 would probably work, using a reasonably-sized square table.

(a + b)^2 = a^2 + b^2 + 2*a*b
((a + b)^2 - a^2 - b^2)/2 = a*b

by on (#38952)
Roth wrote:
haha! Now that's something I can get on board with : ) I'll even watch your Patriots with you in that case haha


The only problem is the ride is long from japan for a weekend of football thought :) I still want to go see a live game someday if I have a chance.

Roth wrote:
Yeah, that's a pretty good program overall. At one point I had considered modifying the source so all channels could be changed and played at once.


Yes, that could be interesting. I may try to check how to modify it.

What would be even better is a little software that allow you to make a multi-frame/channel sound fx then dump the data you need so you could use it in your game. Basically, a sound fx sequencer. That would be great.