PCM Drum machine / sampler

Discuss NSF files, FamiTracker, MML tools, or anything else related to NES music.

Moderator: Moderators

frantik
Posts: 377
Joined: Tue Mar 03, 2009 3:56 pm

PCM Drum machine / sampler

Post by frantik »

Hey NESDev folks..

I had this idea and I was hoping you guys could help me out with some of the details

I would like like to make a "drum machine" using the PCM channel. at first i'll be happy with DPCM but eventually i would like to make writes directly $4011 for higher quality audio.

I want to store each sound in its own memory bank, and then just swap in the bank and load the sound on each beat. To program the drum machine I would make a table which contained a list of the different bank numbers to switch between

the loop would be


Get Sound Number
Change bank to Sound Number
Play sound
Wait until end of beat


The main questions I have are:

1. which mapper should I choose to most easily accommodate this plan? I want at least 8 sounds (so 8 banks), though 16 or more would be better.

2. How do I set up the timing to wait before firing the next sound? Is there an easy way? (like just going into a loop of NOPs or something?) ideally i would like variable timing. Will all my sounds need to be the same length for best results?

3. I saw in the documentation about the DMC channel that you can have it fire an interrupt when the sound is done playing.. how does one go about handling the interrupt? or is this even useful? it seems like this might be the easiest way to handle the timing.. just set the play length with $4013 and wait for the sound to end, the fire the next sound

thanks for any insight you could provide :)
Last edited by frantik on Sat Mar 07, 2009 5:32 am, edited 1 time in total.
Celius
Posts: 2158
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Re: (D)PCM Drum machine - some questions

Post by Celius »

First off, welcome!
frantik wrote: I would like like to make a "drum machine" using the PCM channel. at first i'll be happy with DPCM but eventually i would like to make writes directly $4011 for higher quality audio.
PCM does give good sound, however it is definitely not equal to the built in DCM. The main thing to be concerned with is that you have to stream the sound data yourself; the system doesn't do it for you. And an audio sample needs to be decent quality and size. You will probably find yourself sacrificing one for the other. At 6 KHz, you will have a pretty muffly sound (though it sounds impressive for the NES), but still a pretty big audio file. You have to store the data into $4011 manually 6000 times a frame. That requires incredibly precise timing, which pretty much rules out doing anything else during that time unless you use an interrupt that saves all registers that could be destroyed.
frantik wrote: I want to store each sound in its own memory bank, and then just swap in the bank and load the sound on each beat. To program the drum machine I would make a table which contained a list of the different bank numbers to switch between

the loop would be


Get Sound Number
Change bank to Sound Number
Play sound
Wait until end of beat
So are you planning on making this part of a game, or just a stand-alone thing? Because if you're making this part of a game, 8 banks is a goldmine in terms of space. Seriously, that's half of Final Fantasy (assuming you're referring to 16k banks?). If you're just using a sample in a game with the DMC, this system probably isn't good. Though the idea of a sound number is okay, just make a table of pointers that you can index with "Sound number". You can put more than 1 sound in a bank if it's a short drum hit.

And remember if you're using DCM that you have to set the starting address to be like the last possible address you can make it so it will wrap around to $8000. That's one of the major disadvantages of the DCM channel. If you do decide to use PCM though, this isn't a problem. Or (if there's a mapper that does this) bankswitch $C000-$DFFF out for a sample. That would limit your size to 8k though.
frantik wrote: 1. which mapper should I choose to most easily accommodate this plan? I want at least 8 sounds (so 8 banks), though 16 or more would be better.
Like I said, you could squeeze like 3+ sounds in a bank if you use lower quality for DCM. I'm not sure what exactly this project is, but if it's a stand alone thing, MMC1 or MMC3 would be good. MMC3 has a scanline counter you might be able to use for PCM. Both allow for bankswitching, of course.
frantik wrote: 2. How do I set up the timing to wait before firing the next sound? Is there an easy way? (like just going into a loop of NOPs or something?) ideally i would like variable timing. Will all my sounds need to be the same length for best results?
I'm not sure what you mean by this one. If you're wondering how to wait between DMC sounds, you'll want to wait in terms of frames, and you'll want to not waste time. Each frame, the same code (or some of the same code) is executed (like the NMI). Tell the NMI to decrease a variable. Once that variable is 0, move onto the next sample. It's up to you to tell the number of that variable.
frantik wrote: 3. I saw in the documentation about the DMC channel that you can have it fire an interrupt when the sound is done playing.. how does one go about handling the interrupt? or is this even useful?
You know, I'm actually not quite sure about this. I know a user here (doynax) used it for timing in his polygonal demo, but I've never worked with it. It could prove to be useful though. Oh, and actually, if you're trying to move onto the next sample after one plays, just use that interrupt I guess because it'll tell you when one is done (only for DMC, not PCM of course).
User avatar
Memblers
Site Admin
Posts: 4044
Joined: Mon Sep 20, 2004 6:04 am
Location: Indianapolis
Contact:

Post by Memblers »

An NES drum machine would be cool. I've always though it'd be fun to make one where I could hit a PowerPad with drumsticks.

1. I'd recommend mapper #180, it's a variant of UNROM. It would give you up to 16 banks at $C000+. It's the same board as UNROM, but 1 logic chip is replaced. My 'Chipography' player on NES uses this mapper, there's lots of DPCM.

2. You should count frames, use the NMI interrupt (and you'll need to anyways if you want to put anything on the screen). Length won't matter, but samples are probably are best kept short, because any kind of fadeout/silence sounds like hell in DPCM.

3. I had a lot of fun with it, I used it to make a speech synthesizer by stringing together phoneme samples. Write $C0 to so $4017 to enable it, run a CLI instruction to enable IRQs, then it works.
frantik
Posts: 377
Joined: Tue Mar 03, 2009 3:56 pm

Post by frantik »

thanks for the replies folks :)
So are you planning on making this part of a game, or just a stand-alone thing?
I want to make a totally stand alone drum machine.. i'm not concerned about anything but playing sounds (thats why PCM would be ok) In the future I want to be able to display stuff to the screen and even allow editing of the drum patterns but first I just want to do the proof of concept.. I can play 1 dcm sound, so at least I've got that going for me :D


the idea behind 1 bank for 1 sound is to simplify the coding and also make it easy to upgrade to PCM which will need as much space as possible.

From what I recall about mappers, the program code needs to be in $C000 - $FFFF right? but the DCM code expects the address pointer to also be within that location? It's fine if i have to put the program code in every memory bank, though not ideal.

For DCM 8k banks a fine since the sound can only be 0xFF1 bytes long right? I guess i could put dcm 2 sounds in 1 8k bank if i had to
3. I had a lot of fun with it, I used it to make a speech synthesizer by stringing together phoneme samples. Write $C0 to so $4017 to enable it, run a CLI instruction to enable IRQs, then it works.
cool, I think I will look into this first. Is there a document you would recommend on using IRQs? Or a code snippet you could drop? I don't get how you tell the program to catch the interrupt (I'm sure this is a basic concept)

Also can you recommend a document on counting frames an using the NMI for timing? This sounds like it will be more tedious but might be more flexible in the long run. It's also probably what I will have to learn to do the precise timing for PCM playback, right?

I've hacked games and added my own routines but I'm not super familiar with the lower level stuff

*goes off to research*
Last edited by frantik on Tue Mar 03, 2009 8:23 pm, edited 1 time in total.
User avatar
Memblers
Site Admin
Posts: 4044
Joined: Mon Sep 20, 2004 6:04 am
Location: Indianapolis
Contact:

Post by Memblers »

frantik wrote:thanks for the replies folks :)

cool, I think I will look into this first. Is there a document you would recommend on using IRQs? Or a code snippet you could drop? I don't get how you tell the program to catch the interrupt (I'm sure this is a basic concept)

Also can you recommend a document on counting frames an using the NMI for timing? This sounds like it will be more tedious but might be more flexible in the long run. It's also probably what I will have to learn to do the precise timing for PCM playback, right?
There's a lot of posts about doing timing with NMI, usually buried in random threads. Here's one http://nesdev.com/bbs/viewtopic.php?t=1511

The frame time is 1/60th of a second. If you use PCM through $4011, you'll find it easiest to shut the screen off and disable NMIs entirely (then you really will be using a loop with timed NOPs in it). It's worth noting that having the screen on causes a bit of interference with the audio (maybe annoying if you're recording or something).

As for interrupts, it mostly automatic, you set the vectors and it will run the code pointed to by the vectors (NMI/RESET/IRQ).
http://www.6502.org/tutorials/interrupts.html
Celius
Posts: 2158
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

If you use 8k banks, you could probably use a mapper (perhaps MMC3) that switches $C000-$DFFF. That way you could keep the program code in $E000-$FFFF and just swap out 8k banks in $C000-$DFFF to avoid complication.

And also, if you're in PAL land then Vblank happens 50 times a second, because NMI is executed at 50 Hz. This will probably also change the pitch of DMC samples (bring it down a little). If you're in NTSC land (shown there in the link), then yeah, it's 60 Hz.

Oh, for PCM timing, yes, do what Memblers suggested. You'll want a loop of NOPs or a loop that decreases X until 0. That way you can define what X is without going to a different segment of code. If you want to play the sample at different rates, just change X. Otherwise, for DMC, do frame based or work with the IRQ.
User avatar
Memblers
Site Admin
Posts: 4044
Joined: Mon Sep 20, 2004 6:04 am
Location: Indianapolis
Contact:

Post by Memblers »

For mapper #180, you do need the vectors in every bank. Only 6 bytes at $FFFA. The the other bank is fixed at $8000-$BFFF.
frantik
Posts: 377
Joined: Tue Mar 03, 2009 3:56 pm

Post by frantik »

thanks guys :) I think I got the basics down.. hopefully i'll get a chance to work on this in the next few days and see what I come up with :)

edit:

i'm not sure why i should write 0xC0 to $4017?
Write $C0 to so $4017 to enable it, run a CLI instruction to enable IRQs, then it works.
frantik
Posts: 377
Joined: Tue Mar 03, 2009 3:56 pm

Post by frantik »

hmm I just discovered mmc5 has 8 bit PCM output... perhaps that would be the best mapper to use :idea:


edit: got DPCM samples looping using IRQs :) now on to mapper code
User avatar
Memblers
Site Admin
Posts: 4044
Joined: Mon Sep 20, 2004 6:04 am
Location: Indianapolis
Contact:

Post by Memblers »

i'm not sure why i should write 0xC0 to $4017?
It selects the source of the IRQs I believe. There's also frame IRQs (which I've never understood the usefulness of), DMC IRQ, and external IRQs (from cart).
frantik wrote:hmm I just discovered mmc5 has 8 bit PCM output... perhaps that would be the best mapper to use :idea:
Probably shouldn't, you'd have to modify the NES to hear the sound and MMC5 is also one of the rarest mappers. The sound output is the same as the NES I believe ($5011 register instead of $4011), so if it's 7-bit and you use both of those channels I guess you would have 14-bit sound. If the extra quality is worth the hassle, depends on what you want to do with it.
frantik
Posts: 377
Joined: Tue Mar 03, 2009 3:56 pm

Post by frantik »

Memblers wrote:Probably shouldn't, you'd have to modify the NES to hear the sound
oh, it doesn't play out of the normal nes audio output? I guess i'll stick with doing writes to $4011
If the extra quality is worth the hassle, depends on what you want to do with it.
well any extra sound quality will be worth the hassle.. right now i'm just messing around with emulators but it would be awesome to create my own NES based musical instruments using real hardware. I would want the best quality output possible.

I think the PCM channels DAC is only 7 bits anyways though right? so thats the best audio I can expect from the main NES output?



also, anybody know why i'm getting clicks at the beginning of each loop in FCEUD but not in virtua nes? i guess i need to get a dev cart or something to test the hardware because emulator writers rarely concern themselves with audio accuracy :\
User avatar
Memblers
Site Admin
Posts: 4044
Joined: Mon Sep 20, 2004 6:04 am
Location: Indianapolis
Contact:

Post by Memblers »

Yeah, it's 7-bits. What's more important is the sample-rate. For "CD quality" 44100hz, cpu speed is 1.789Mhz so:
1789000 / 44100 = 40.56 CPU cycles

44.1khz or even higher sample rate is doable in this case. If you want a lot of features like frequency control, volume control, and channel mixing, the sample rate will have to be a little lower.

I've thought all this through because I was working on something similar once.

I wanted an IRQ so I designed a custom cart (Squeedo) and ended up doing much more than that with it, heh. I programmed a sound synthesizer that ran on another CPU/MCU on the cart and had the NES write the output to $4011. I'd thought that I could also have the NES mix samples in with that (since it had a lot more sample memory), but I never tried that out.

If this would be something that you'd be interested in for instrument development, I could build another prototype. MidiNES by wayfar labs used it also (not my sound part though). My synth worked and can do some weird sounds (256 byte samples).
frantik
Posts: 377
Joined: Tue Mar 03, 2009 3:56 pm

Post by frantik »

Memblers wrote:If this would be something that you'd be interested in for instrument development, I could build another prototype.
it sounds interesting but way over my head right now lol.. I'm happy right now that i've got a rom that does nothing but loops 1 dpcm sound over and over lol

for PCM sample rate, 44.1kHz is ideal but i'd be ok with 22.05 at the worst. mixing and volume control both sound like cool options, though to mix stuff I would have to have more than one sample per bank. unless I could swap banks quickly enough to mix two samples together at a decent enough rate i guess..
Celius
Posts: 2158
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

Though if you want 44100 Hz samples, that means 1 sample is one byte, so 1 second of audio = 44100 bytes, doesn't it? In that case, you'd need 3 banks for 1 second of audio without compression.
frantik
Posts: 377
Joined: Tue Mar 03, 2009 3:56 pm

Post by frantik »

1 second / (44100 bytes / 16 kilobytes) in ms is about 370 ms.. that is plenty of time for drum samples.. if each eighth note hit is 370ms, the BPM would be about 80 bpm.

edit: woot got PCM going.. right now it sounds a little faster than 44100 but all i'm doing is counting and looping.

looking at mappers i think MMC3 might make the most sense.. i'll load the PCM into $8000 and $A000 and leave the program code in $C000

edit2: used UNROM instead for right now.. it's working which is pretty cool actually :D
Post Reply