Why B+21 Cents?

I keep getting this question, about why I make SNES samples tuned exactly to B+21 Cents.

Let’s start with these premises:

  1. There is a limited amount of ARAM space.
  2. In order to make best use of the available space, we need short, LOOPED samples
  3. BRR samples are made of blocks of 16 samples
  4. End point and Loop point will be exactly a multiple of 16
  5. We don’t want clicks or buzz at every loop point
  6. SNES internal sample rate is 32000 Hz.

Therefore, we must make looped samples that perfectly loop at a multiple of 16 samples. Unlooped samples (drums) can be any frequency, and need no special tuning.

Look at the sine wave here. This is one cycle.

So we need this to exactly be a multiple of 16 samples. The shortest (highest pitch) looped sample we can make is 16 samples per cycle. 32000 samples per second / 16 samples = 2000 Hz. Which is B6+21 cents. Here’s a chart of all the possible tunings for simple, short, looped samples.

32000 / 16 sample loop = 2000 Hz = B6+21 Cents

32000 / 32 sample loop = 1000 Hz = B5+21 Cents

32000 / 48 sample loop = 666.7 Hz = E5+19 Cents

32000 / 64 sample loop = 500 Hz = B4+21 Cents

32000 / 80 sample loop = 400 Hz = G4+35 Cents

32000 / 96 sample loop = 333.3 Hz = E4+19 Cents

32000 / 112 sample loop = 285.7 Hz = D4-48 Cents

…(note, middle C would be here at 261.6 Hz)

32000 / 128 sample loop = 250 Hz = B3+21 Cents

32000 / 144 sample loop = 222.2 Hz = A3+17 Cents

32000 / 160 sample loop = 200 Hz = G3+35 Cents

32000 / 176 sample loop = 181.8 Hz = F#3-30 Cents

32000 / 192 sample loop = 166.7 Hz = E3+19 Cents

32000 / 208 sample loop = 153.8 Hz = D#3-20 Cents

32000 / 224 sample loop = 142.9 Hz = D3-47 Cents

32000 / 240 sample loop = 133.3 Hz = C3+33 Cents

32000 / 256 sample loop = 125 Hz = B2+21 Cents

Given this chart, the most common tuning found is B+21.

So, if we make a perfect loop, the wave will continue infinitely, and a short sample can play as long as you need it with no problems. But, if the loop is even a little bit off, you get weird clicks and buzz.

And it makes sense to tune ALL samples to B+21 so that they are in tune with each other. To do that, I record at C and then slow down the speed by 4.5-4.6% in Audacity. (Effect/Change Speed… Percent Change = -4.6%).

And, I’m sure some naysayer will argue that you could use a number of other tunings, if the loop was longer… say 1000-10000 samples long. Yes, you could just record at C and make a very large loop, and cross fade them, but remember premise 1 above, there is limited ARAM space. We want each sample to be as short as possible.

There might be a few exceptions. Strings (violins) don’t sound very good with a short loop. The best sounding string samples I have heard have a very long loop period… 10000+ samples. But for 99% of samples, I find it’s best to just copy the last cycle, cut to the nearest 16, and then loop that last single cycle repeatedly.

Side note, I usually resample my original samples to 16000 Hz (mono), before importing to SNESGSS. You do that in Audacity by opening the WAV at whatever rate it is. Select and copy it. Open a new file, and change the project sample rate (bottom left) to 16000, then paste the sample. This usually works best in order to reduce file size without losing too much high end. But, sometimes you need 32000 Hz if the sample has a lot of high frequency sounds (for example, cymbals).


I have made a ton of samples freely available. Check them out. You need SNESGSS to open these files, and go to instruments to hear them. You can save the instrument (to mix and match). You can extract the original WAV. Or, you can export an SPC file, and then open that with Echo4GSS (an app I made), which can extract all the BRR files.


(SNESGSS is in the MUSIC folder also = snesgssQv2.exe)


Most of them were made with Famitracker or Deflemask. The drums are all royalty free samples.

Update 2022

I made some changes to the neslib and nesdoug library files.

There was a bug where, if you called one of the sprite functions at the exact end of the frame, it could have placed only half of a sprite’s data in the sprite buffer before the nmi sent it to the OAM RAM for the next frame. This could cause an incorrect sprite to be be shown.

The nmi code has been modified so that it will only DMA the OAM (sprite data) if you have reached the next ppu_wait_frame() or ppu_wait_nmi(). Now, a lag frame will just result in the previous frame being shown twice, with no changes.

Another change — I removed clear_vram_buffer(). I feel like this is something that should be done automatically, when the vram buffer is copied to the PPU. I have seen people confused as to when to use clear_vram_buffer(), so this should clear up any confusion, it will manage itself.

If you need clear_vram_buffer() for some reason, the code is still there, it’s just commented out (in nesdoug.h and nesdoug.s… and I removed the .export _clear_vram_buffer). You can easily put it back in.

And, I changed the name of flush_vram_update_nmi() to flush_vram_update2(). The name was misleading, the way I was using it — entirely outside of the nmi (ie. pushing updates to the PPU with the screen off).

One caution — split screens still can’t handle lag frames. If you are using a sprite zero split screen, make sure that the code isn’t so complex that it runs past the bottom of the frame. I suppose you could rewrite the nmi code (in assembly) and put the split screen code directly there, to fix it.

I did make sure that the MMC3 code can handle lag frames and do split screens. That is because you can handle the split with IRQs. The nmi code still runs the IRQ function on lag frames.

Minor change — I redid the Full BG and Fade examples. The code is the same, but I thought I could make it look nicer if I used the NESIFIER tool that I made.

old was

new is