I don't know if something like this has already been written, but I looked around and didn't find anything. So I made my own table that lists all the appropriate 11-bit to store into the sound registers for the Square Waves to get the desired note. I don't know if this is needed, but I thought I'd release it anyway:
http://www.freewebs.com/the_bott/NotesTable.txt .
EDIT: The first hex value is incorrect. I'll fix that. It should be $7F1, and not $7FF.
EDIT 2: PAL and NTSC versions released/revised:
http://www.freewebs.com/the_bott/NotesTableNTSC.txt
http://www.freewebs.com/the_bott/NotesTablePAL.txt
Ah! Very nice!
This is better than the note table I'm using.
I calculated mine from just knowing that 2047 is an A on the NES, and using (2 ^ (x / 12)) to get the rest of the notes, which are correct notes, but sound slightly too low by just a few cents. :S
Thank you!
Yeah, I'm glad you all like it. I'll be releasing one for the Triangle Wave today, and I'll fix that first byte on the Square Wave.
I did mine all with theory calculations. Since I wanted to do linear vibratoes and glissandoes as well, I did a table where you get much more values than notes (31 pitch values between each note exactly) to get precise periods. (doing only a 12-tone table does allow for pitch bends, but they most likely won't be linear). I did it on the lowest octave available (and even lower than this in fact) and you just shift the values for higher octaves. The triangle plays one octave lower than any channel for the same frequency, but the same table is usable. You can have the software shift the values right one more time, or (I'd recommand this) just take this in account when coding music for the NES.
Here you are WLA code that will generate the table, but it won't work because 2 power a real number results in an integer for some reason. I told the author and he said he fixed it, but this still didn't work as it should have. I eventually did the table by hand, wich was annoying but it's just a bad time to have once and you have a great table forever.
Code:
.define BaseTune 32.7 ;The lowsest C is 32.7 Hz
;even if the NES can't output that frequence, all calculation are based on this one
PeriodTbl
.define Note 0
.rept 12
.redefine FineTune 0
.rept 32 ;Setup period for all possible 32 fine tunes and all possible 12 notes (384 periods in total)
.dw CPUSpeed/(2^((Note/12)+(FineTune/32))*BaseTune*16)
.redef FineTune FineTune+1
.endr
.redef Note Note+1
.endr
BTW I should fix my sound code to have a better enveloppe handling, to add "note enveloppe" (aka arpeggio) while I'm not a fan of this effect it's always a good feature (especially on noise), and to correct a few bugs in other special effects.
I wrote
my period table generator in C.
But in practice, isn't a piecewise linear pitch table (using software linear interpolation between semitones) good enough, or is it on the wrong side of a time/space tradeoff?
You mean software which would calculate the difference between two semitones, and then compute the fractional finetune of it ? Seems much more complicated that just have a table that include the finetunes as well. Of course it would be less a pain to make the table, but in revenche it seems much more a pain to write a routine doing the linar approximation like you said.
It's like when I had to write some code to shot oriented bullets at constant speed, I had to deal with trigonomerty and this was a pain, and I considered using approximate trigonometry by making piece of segments, but this was even more a pain, so I just used a big tangeant table eventually.
Neat. I may use this when I attempt to make music for my game. For some reason I think that's going to be a serious chore...
Is that triangle wave guide anywhere?
Celius wrote:
I don't know if something like this has already been written, but I looked around and didn't find anything. So I made my own table that lists all the appropriate 11-bit to store into the sound registers for the Square Waves to get the desired note. I don't know if this is needed, but I thought I'd release it anyway:
http://www.freewebs.com/the_bott/NotesTable.txt .
EDIT: The first hex value is incorrect. I'll fix that. It should be $7F1, and not $7FF.
This is awesome. Do you have the values for the Triangle channel?? I'd love to have it if you do!!
It's the same values, but the resulting sound will be one octave lower.
Glad you could find it useful
.
But like Bregalad said, they are the same values, but only an octave lower. I should have mentioned that in the document. I'll make that fix.
Also, this only applies to NTSC systems, I'm assuming. What's the formula for calculating the values for a PAL NES?
EDIT: Fixed
.
For PAL, you can usually get away with using the same lookup table and skipping the first entry.
Here's why: The NTSC NES CPU runs at 1789772 Hz. The PAL NES CPU runs at 1662607 Hz. The ratio between these is 1.0764853, which is very close to the 1.0594631 ratio between frequencies of tones one semitone apart in 12edo.
To be more exact, replace the "1790000" in the formula with "1662607".
The PAL CPU is 15/16 slower than it's NTSC counterpart, so multiplying the values by 16/15 worked fine for me, but I don't know how exact this is. It seemed more exact that doing it one semitone higher as tepples mentionned.
Ah, thank you tepples
. Though I'd like to make a legitimate table for PAL, because it does sound slightly flat when you take a NTSC game and run it on PAL in an emulator. And I feel bad because sometimes I feel like I have the mentality that NTSC is "normal" or standard so I don't need to care about PAL when that's just ridiculous, because a very large chunk of people live in PAL areas.
It's fun because in NES-dev scene NTSC is considered normal while PAL is non-standard, but in C64-dev scene it's the other way arround, PAL is considered normal and NTSC is only for those who really cares about it. Anyway I'd be caring about all regions, it's not hard to change the tempo and to adjust the pitch table. Multiply values by 15/16 will do, but you probably want to change the clock by PAL clock frequency to get exact results.
thanks guys! that's very easy to work with!
Well here they are:
http://www.freewebs.com/the_bott/NotesTableNTSC.txt
http://www.freewebs.com/the_bott/NotesTablePAL.txt
You really wouldn't get this precision for the PAL table if you just used the NTSC table plus 1
.
I've tested all the values. They're all good except after about halfway through octave 7 every pitch is so high you can't really tell what you're hearing (if you could though, it'd be right I'm sure).
Hope this is useful.
Also, Bregalad, multiplying by 15/16 seems to give fairly close results, but they weren't the same answers that I got when I used the equation. For example, Multiplying $7F1 by 15/16 gave me $771, when the equation gave me $760.
Nice. Thanks a lot for making these tables Celius!
Quote:
They're all good except after about halfway through octave 7 every pitch is so high you can't really tell what you're hearing (if you could though, it'd be right I'm sure).
You should probably remove octave 8 and half of 7. The pitch values are too close to eachother to sound right, in fact some consecutives notes in octave 8 have the same value, which means that *will* sound wrong. I originally had my high pitched notes sound detuned below the period arround $40, but this is adjustable to certain extent. Detunes notes sounds like they are from some really old instruments and that could sound good for some particular music.
Also high pitched notes sounds better when too high than too low, so it's better to round the values down rather than up if you have to round them in my opinion.
Some error probably come from the -1 offset that I didn't implement for simplicity. Ideally, the tables would contain the values without the -1 so they can be shifted right with exactitude, and substract 1 from it.
MetalSlime wrote:
Nice. Thanks a lot for making these tables Celius!
No problem. Like I said, I decided to make them because I wanted to look at them as references, but I thought the NESDev community might make some use out of them. Glad I was right
.
Bregalad wrote:
You should probably remove octave 8 and half of 7. The pitch values are too close to eachother to sound right, in fact some consecutives notes in octave 8 have the same value, which means that *will* sound wrong.
I don't see the need to remove them. All of the information is valid, and useful to some. I have the really precise value of the calculation displayed in decimal besides the rounded one. So people can look at that value and make use of it somehow (the rounded hex value will stop being precise by this point).
Bregalad wrote:
I originally had my high pitched notes sound detuned below the period arround $40, but this is adjustable to certain extent. Detunes notes sounds like they are from some really old instruments and that could sound good for some particular music.
Were you using a table where you multiply the values by 15/16 instead of (CPU/F*16) - 1? If so, you might be getting something like $2F instead of $2E, or possibly $2D which would be REALLY out of tune. With my table all of the values sounded fine except for when it got to halfway through octave 7. But using these notes anyways is kind of crazy, so it doesn't matter that much.
And also, did you have the assembler make your table? If so, why not just have it make the table with exact results using the formula?
Quote:
Were you using a table where you multiply the values by 15/16 instead of (CPU/F*16) - 1? If so, you might be getting something like $2F instead of $2E, or possibly $2D which would be REALLY out of tune. With my table all of the values sounded fine except for when it got to halfway through octave 7. But using these notes anyways is kind of crazy, so it doesn't matter that much.
Multiply by 15/16 was to get PAL periods from NTSC ones, it does not involve how the table itself was made. The notes sounded wrong on NTSC (which I did my calculations), so I didn't even bother with PAL without having it working on NTSC.
Quote:
And also, did you have the assembler make your table? If so, why not just have it make the table with exact results using the formula?
I didn't use the assembler to make the table, I made it manually. There is only 12 entries in the sound engine I use so it's not really bothersome. The bad pitches comes from the fact I just shifted left to get one octave higher. If you know about math, you should know that (a-1)/2 is not equal to (a/2)-1 but to (a/2)-1/2.
That -1 offset was negligible on low pitches (I only substract 1 to Square channel 1 to get chrous effect). So the pitch did sound wrong on some high notes where the -1 offset wasn't negligible any longer. Anyway I'm not changing the sound engine for Dragon Skill as it is very compact and optmized, and I'm going to use a much larger sound engine with more precise pitch for future NES projects.
Oh, I see. So it's just a matter of making condensed code. If that's the case, then you should just leave your sound code as is.
If you use an entire pitch table (which I do) then you may end up using almost an entire 256 bytes. And then to top it off I have an arpeggio table which takes up the same amount of space (each entry is a 16 bit measure of 1/32 of the space between two notes). So my engine is definitely not very optimal. In order to save space and time with my other project which is really small (NROM 32k platformer), I think I'll cut out all arpeggios and shorten the note table, taking off the top worthless octaves and MAYBE the bottom one.
But for ChateauLeVania, the sound engine I have coming up will be really flexible, and maybe a bit more space consuming. This is okay as I have 512k to work with.
I've seen you guys using different formulas to build your lookup tables, and since I don't know much about music I don't understand what all the differences mean.
For example, Celius' formula uses a rounded NTSC CPU frequency (1790000) instead of the actual one (1789772.666...). Can't that result in wrong values? Also, why subtract 1 from the result of the division? Why does tepples subtract 0.5?
What I want to know is: What is the most straightforward (and correct) formula to calculate the value I have to write in order to generate a note, given that I know it's frequency and the CPU's frequency? Please focus on a single note so that loops and increments don't get in the way of simplicity.
I learned that formula from the NES 101 document, I think. I suppose that it doesn't give you the exact value now that I think about it. They give you very accurate results. I held a tuner once up to a speaker when I played a note made by my table and it read back as exactly in tune.
I honestly am not quite sure why the formula is the way it is. I just know that (CPUSpeed/(Freq * 16)) - 1 gives you the correct value for a given frequency. I think if you just use 1789772 as the CPU speed, it will give you the exact result.
I think you can find a tuner that displays frequency for not too much money. If you can, use the formula and play the value you get, holding the tuner up to the speaker. If the value on the tuner reads back as what you intended, then you'll know it works.
I didn't do much testing, as I play the piano and know a note when I hear it. So once I determined it was right, I thought it was good enough.
EDIT: I just tested the formula. Using 1790000 as the CPU speed with 55 Hz gives a result of 2033.090909. Using 1789772 gives 2032.8318181, which both round to 2033 (aka $7F1). The higher the frequency, the lower the difference. Let's use 8000 Hz for example. Using 1790000, I get 12.984375. Using 1789772, I get 12.98259375. Honestly, the difference by that point is so small that it doesn't matter.
The only thing you'd have to worry about is when the formula generates say 1000.513 using either the rounded or precise speed, and 1000.492 for the other. One would round up and the other would round down. It hasn't made an audible difference for me though.
tokumaru wrote:
For example, Celius' formula uses a rounded NTSC CPU frequency (1790000) instead of the actual one (1789772.666...). Can't that result in wrong values?
Not noticeably. Celius' rounded frequency is only 0.01% off. The octave is divided into 1,200 "cents" (hundredths of a semitone). People can't notice differences smaller than five cents, and the ratio between two frequencies that are five cents apart is 2^(5/1200), or a difference of 0.29%.
The 16 in "Freq * 16" is there because the square period registers effectively get clocked every 2 CPU cycles, and there are eight steps in each waveform.
Quote:
Also, why subtract 1 from the result of the division?
Think of it like this: When you write N to the period register, the period divider counts down from N to 0 and then resets the counter to N one cycle
after the cycle where it is zero, making a total of N+1 steps. So given the number of steps, we need to subtract 1 to get the period value.
Quote:
Why does tepples subtract 0.5?
The proper procedure:
1. Subtract 1.0.
2. Round to the nearest integer.
But rounding to the nearest integer itself has two steps:
1. Add 0.5.
2. Round down.
Plug it into the procedure:
1. Subtract 1.0.
2. Add 0.5.
3. Round down.
Apply
constant folding:
1. Subtract 0.5.
2. Round down.
tepples wrote:
But rounding to the nearest integer itself has two steps:
1. Add 0.5.
2. Round down.
What? I don't understand that.
And it looks like I'm going to have to really study how all the sound really works, because I just realized I don't quite get it. As far as I've been concerned since I've learned how to work with the sound regs, you just plop a value in and it makes a sound. And plus, I always just set the registers to play a constant sound so I can manually adjust the volume for envelopes, and manually silence them.
Celius wrote:
tepples wrote:
But rounding to the nearest integer itself has two steps:
1. Add 0.5.
2. Round down.
What? I don't understand that.
Round 5.7 to the nearest integer:
1. Add 0.5. Result: 6.2.
2. Round down. Result: 6.
Round 8.3 to the nearest integer:
1. Add 0.5. Result: 8.8.
2. Round down. Result: 8.
Further information:
Rounding on Wikipedia
So you can just add .5 and clip off the numbers beyond the decimal point as a method of rounding. That's pretty clever; I don't think I would have thought of that
.
So to get the most possible precision using the "one octave table and shift" method I would have to do the following.
Generate a LUT for the lowest octave (octave 0) using 1662607/(f*16) where f is the frequecy of the notes.
And then the following in the playback code.
get period from LUT
divide period by 2^octave (correctly rounded)
subtract 1
Will this give maximum possible precision?
EDIT:
I live in PAL land.
Yeah I'd say with one octave you can't get much more precise than that. But you might want to store the values as 24 bit, so you can have 8 bits of extra precision.
And feel free to use my table as a reference for making the lower octave. I have the exact values written in decimal, already calculated.
There really is no need to keep 13 bits around that are just going to be shifted away since only the most significant bit that's shifted out matters for correct rounding. It's only in some rare cases where that bit is already rounded it might be off by one, and that is only in the second octave (the one that's only shifted once).
I generated alot of semi notes aswell for vibrato (thanks bregalad for that great idea!), but I did use your table for comparison after generation just in case I got something wrong.
EDIT:
Bregalad: Did you try having 15 seminotes between each note instead of 31? And if so, was that too coarse for nice sounding vibratos? (it would be nice to have it all under 256 bytes for easier indexing)
Anders_A wrote:
There really is no need to keep 13 bits around that are just going to be shifted away since only the most significant bit that's shifted out matters for correct rounding. It's only in some rare cases where that bit is already rounded it might be off by one, and that is only in the second octave (the one that's only shifted once).
Oh, sorry, duh. I knew that the MSb of the decimal place would determine rounding, but I wasn't thinking when it came to defining the extra byte. So yeah, just add the carry after shifting (the MSb will be in the carry, and if you need to round down it will be 0, adding nothing, and it will be 1 for rounding up, adding one, which will all work out in your favor).
But where did the number 13 come from?
Anders_A wrote:
I generated alot of semi notes aswell for vibrato (thanks bregalad for that great idea!), but I did use your table for comparison after generation just in case I got something wrong.
EDIT:
Bregalad: Did you try having 15 seminotes between each note instead of 31? And if so, was that too coarse for nice sounding vibratos? (it would be nice to have it all under 256 bytes for easier indexing)
That's actually what I have too. I can bend a pitch by x/32 of a note. I just have a look up table where each entry is the difference between two tones divided by 32. If I want to bend a pitch up by 7/32, I find the difference between that note and the next /32, multiply it by 7, and add it to the original pitch.
I was able to fit mine in <128 bytes, with some shifting.
Celius wrote:
And it looks like I'm going to have to really study how all the sound really works, because I just realized I don't quite get it. As far as I've been concerned since I've learned how to work with the sound regs, you just plop a value in and it makes a sound. And plus, I always just set the registers to play a constant sound so I can manually adjust the volume for envelopes, and manually silence them.
Ahaha... I can help you with that. Well, the most basic thing you have to know is that musical notes works in a exponetial manner (in regard to frequency), and hopefully for us, rising to an octave higher only require to double the frequency, or, in our case because we mess with timers (period = 1/F) you just have to divide by two (read: LSR) your value to be put in a period register
To calculate exact things, nothing beat a mathematic formula. Because frequency grows exponentially when notes goes up, the formula has to be of exponential type, more precisely in base two. Because there is already a reference in music (A4 = 440 Hz) that would be not much of a trouble calculating the frequency, if you can 'digitalise' note. So, given a system where:
Code:
Note = octave + semi-tones/12 + cents/100
So A 4 = 4 + 9/12 = 4.75, the formula is as easy as:
Code:
Frequency = 440 Hz * 2^(note - 4.75)
And there's its reciprocal:
Code:
Note = log2(frequency/440 Hz) + 4.75
That things beat every table you'll find on the net. Of course, we don't like wasting time on the NES calculating that thing, so a small look-up table of 11 elements is sufficient, since getting the other octaves simply uses LSR,. And because I think you're lazy, I even made a Excel sheet that does the job for you, plus calculate the value to write to the register and even calculate the error.
http://jarhmander.home.googlepages.com/f2n.xls
Celius wrote:
MetalSlime wrote:
Nice. Thanks a lot for making these tables Celius!
No problem. Like I said, I decided to make them because I wanted to look at them as references, but I thought the NESDev community might make some use out of them. Glad I was right
.
Turns out to be useful in more ways than one. I was tracing through a game today (my first one ever actually!), specifically through the sound engine and I actually found their table of note/tone values! It was like an "Aha!" moment for me. They had slightly different values from the ones in your chart, but they were close enough that it was obvious what it was. I wonder how they calculated their values, since they were a bit different from the ones on your chart...
Anyway, if I hadn't printed out your chart beforehand, I might have missed it completely and written question marks in my notes. What a life saver!
It's all coming together. This NES sound stuff is starting to make sense to me. Thanks again!
Hey, sweet!
I'm glad you found it so useful
. Even enough to print it out! That makes me happy to know I made something worth printing on actual paper.
So how off were the values? Did they vary from more than to less than for each entry? Or perhaps they tweaked it by hand to see if rounding up/down made it sound better. It could also be a precision thing where mine has more or less precision than theirs.
Celius wrote:
I'm glad you found it so useful
. Even enough to print it out! That makes me happy to know I made something worth printing on actual paper.
Yes, well worth printing out! I keep it in my backpack right next to blargg's APU reference
Quote:
So how off were the values? Did they vary from more than to less than for each entry? Or perhaps they tweaked it by hand to see if rounding up/down made it sound better. It could also be a precision thing where mine has more or less precision than theirs.
They were pretty close. I don't even know how to begin to calculate if they vary at a constant rate or not. I'd bet that your values are the more precise ones, since you followed the formula and tested the sounds with a tuner.
The game (The Guardian Legend, btw) doesn't sound out of tune to my ears though. I haven't finished tracing the sound engine, so I don't know yet if the game fudges around with the values before committing them to the sound registers. I have seen them write the values to the corresponding sound reg copies in RAM though.
Here are the values from their chart, laid out next to your chart (just the first two octaves. I don't want to annoy people with a long post):
Code:
Octave 1:
TGL Celius
7FE A 7F1
78E A# 780
720 B 713
6BA C 6AD
65A C# 64D
5FE D 5F3
5A8 D# 59D
556 E 54D
50A F 500
4C2 F# 4B8
47E G 475
43C G# 435
Octave 2:
TGL Celius
3FF A 3F8
3C7 A# 3BF
390 B 389
35D C 356
32D C# 326
2FF D 2F9
2D4 D# 2CE
2AB E 2A6
285 F 27F
261 F# 25C
23F G 23A
21E G# 21A
MetalSlime wrote:
Celius wrote:
I'm glad you found it so useful
. Even enough to print it out! That makes me happy to know I made something worth printing on actual paper.
Yes, well worth printing out! I keep it in my backpack right next to blargg's APU reference
Mine? Next to blargg's APU reference? Wow I feel so important!
MetalSlime wrote:
Code:
Octave 1:
TGL Celius
7FE A 7F1
78E A# 780
720 B 713
6BA C 6AD
65A C# 64D
5FE D 5F3
5A8 D# 59D
556 E 54D
50A F 500
4C2 F# 4B8
47E G 475
43C G# 435
Octave 2:
TGL Celius
3FF A 3F8
3C7 A# 3BF
390 B 389
35D C 356
32D C# 326
2FF D 2F9
2D4 D# 2CE
2AB E 2A6
285 F 27F
261 F# 25C
23F G 23A
21E G# 21A
Hmm, those values look like they could be slightly out of tune... Does the game manipulate those values at all before storing them into the sound regs? It could be for pre-arpeggio purposes. Otherwise, it's possible they are all in tune with each other, but just a little flat as a whole.
Celius wrote:
So I made my own table that lists all the appropriate 11-bit to store into the sound registers for the Square Waves to get the desired note.
I apologize in advance for having nothing relevant to add, but this seems oddly appropriate: