DrumsThis week we are going to take a look at drums. I saved them until now because they use the Noise
channel, which operates much differently from the other 3 channels.
With the Square and Triangle channels we manually set the waveform
period to choose what note to play. Our note lookup table was just a
table of periods to plug into the channel ports. The Noise channel on
the other hand produces random noise. We don't use a note table at all.

For
our purposes, we will set Saw Envelope Disable and Length Counter
Disable and forget about them. This will allow us to have full control
of "note" length and volume via Volume Envelopes. We did the same
thing for the Square channels.

Random Generator - $400E$400E let's us control the settings for the random generator. It looks like this:

NOI_RAND ($400E)

76543210| ||||| ++++- Sound Type+-------- Mode

Mode sets the mode. There are two modes, Mode-0 and Mode-1.
Mode-0 sounds dull and breathy, like static sssh. Mode-1 sounds more
sharp, robotic and buzzy. There's really no good way to describe in
words, so the best way to know the difference in sound is to listen to
both modes. (see below)

Each mode has 16 possible sounds, selected by Sound Type.
This means that the Noise channel really only gives us 32 possible
sounds total! More complex sound effects on the Noise channel are
created by playing combinations of these 32 noises one after another.
To hear what each of the 32 sounds sound like, listen to Song 8 in this week's sample program. It plays the 16 Mode-0 sounds followed by the 16 Mode-1 sounds.

Note:
In this tutorial I will assign each of the 32 Noise channel sounds a
number 00-1F. The left number (0 or 1) refers to the Mode. The right number (0-F) refers to the Sound Type. For example, sound "04" means "Mode 0, Sound Type 4". Sound "1E" means "Mode 1, Sound Type E".

Length Counter - $400F$400F is the Noise channel's length counter. We disabled the length counter in $400C, so we can ignore this port completely!
Simple Noise Drums

The simplest way to make a drum sound on the Noise
channel is to play a single Sound Type under a volume envelope that
decays to 0 (silence). Many games use this kind of drum exclusively.
The Guardian Legend for example only uses two drum sounds throughout
the whole game: 04 and 06. Battle Kid makes heavy use this simple drum
style too. Lots of games do.

So how will we represent simple
drums in the sound data? Recall that our sound engine distinguishes
between Notes, Note Lengths and Opcodes using ranges:

Although
we won't use our note table for the Noise channel, we will treat drums
like notes as far as ranges are concerned. So we need our drum data
values to be in the range of $00-$7F. That's easy. We'll assign the
Mode-0 sounds to $00-$0F and Mode-1 sounds to $10-$1F. Some drum data
might look like this then:

This
code checks to see if the channel is the Noise channel. If so it JSRs
to a special Noise subroutine. Upon return, it jumps over the
note_table code and updates the volume envelope and stream pointer like
normal.

So what does our special Noise subroutine, se_do_noise, look like? The job of se_do_noise
will be to take the note value and convert it into something we can
write to $400E (NOI_RAND). Recall that $400E expects the Mode number
in bit7 and the Sound Type in bits 0-3:

NOI_RAND ($400E)

76543210| ||||| ++++- Sound Type+-------- Mode

Mode-0
sounds don't need to be converted at all. We represent them with the
values $00-$0F, which correspond exactly to the values we need to write
to $400E.

Mode-1 sounds need to be tweaked a bit. We
represent Mode-1 sounds with the values $10-$1F, or in binary
%00010000-%00011111. Notice we identify the Mode number using bit4.
Port $400E expects the Mode number in bit7 though, not bit4, so we will
need to set bit7 ourselves:

Now everything is set. Note values of $00-$0F will get written to stream_note_LO directly. Note values of $10-$1F will get converted to $90-$9F first and then get written to stream_note_LO. Note that we do not bother clearing bit4 for Mode-1 sounds. Bit4 has no effect on $400E so we can just leave it as it is.

Drum DecayThe only thing left to add is a volume envelope for the simple drums to use. It should be short and decay to zero (silence):

Double click drums.bat. That will run NESASM3 and should produce the drums.nes file. Run that NES file in FCEUXD SP.

Use the controller to select songs and play them. Controls are as follows:

Up: PlayDown: Stop Right : Next Song/SFXLeft : Previous Song/SFX

Song0 is a silence song. Not selectable.Song1-Song7 are the same as last week, but a few of them (1, 4 and 6) have drums now.Song8
is a new "song" that plays each of the Noise channel's 32 sounds, in
order from 00-1F. First it plays them with a sustained volume envelope
so that you can hear how they sound drawn out. Next they are played
using the 7-frame ve_drum_decay volume envelope we made so you can hear
how they sound as simple drum sounds.

I just wanted to bump this thread with a hearty thank you. This stuff is really fantastic, and while I'm not programming at the moment, I still take the time to read through your tutorials every time one is posted. Keep up the awesome work, and thank you for contributing to the community like this.

And that's ten. Whew. Some good stuff here. I can't wait to bust out some Pantera on this bad boy. Seriously, though, good job. I can't imagine the time investment that you put into these. This is a great asset to the programming community.

-------------------------

This is my shiny thing, and if you try to take it off me, I may have to eat you.

For some reason, I have to do a write to $400F (any value) prior to my write to $400E. Any clue as to why?

I believe I've said it before, but these tutorials are fantastic.

I've also been wondering why this is (even when length counter is disabled and using the constant volume flag for the envelope). Haven't been able to find an explanation for it from any of the available APU docs.

For some reason, I have to do a write to $400F (any value) prior to my write to $400E. Any clue as to why?

I believe I've said it before, but these tutorials are fantastic.

I've also been wondering why this is (even when length counter is disabled and using the constant volume flag for the envelope). Haven't been able to find an explanation for it from any of the available APU docs.

Going to answer myself here. I'm guessing that the $400F write is needed because the length counter might be initially holding a 0 value -- the $400F write will reload a non-zero value into the length counter (and the length counter's halt flag will then keep it at a non-zero value).

You don't see this problem with the pulse channels because the pulse's "length counter load" is in the same register ($4003/$4007) as the high 3 bits of period, so the length counter reload will happen as a side-effect of writing those high 3 bits.

Also, since the length counter value is read from a lookup table which doesn't contain zeros, the written value is irrelevant (if the length counter halt flag is set).

EDIT: Just as a further note, even if length counter is not used, the zero value in it is a problem because it will unconditionally silence the channel.
EDIT #2: Accidentally had written "$400E write", not "$400F write". Fixed.