By the way, I made a programm to get the precise pitch on the NES, based from a note number (0-11), an octave number, and some different detune value to made pitch LFO, pitch bends and detunes. I foud fair to publish it there, because people is just talking about that :
This routine has 4 steps :
- Mix all different fine tunes (LFO, pitch bend and detune) together, to end up with a main detune value
- Add the corresponding main note to the fine detune value calculated before and make sure that the final value is in the correct octave range. If it's not, then modity the octave number to make it do.
- Add the table pointer to the tune pointer, and read data. Shift it right the number of octaves, if octave isn't minus.
- If octave is minus or if the period is above $800, just output with a pitch of $1 to make an inaudible sound, beacause the NES isn't able to produce very low frequenceys.
Code:
;Get a pitch (11 bit period)
;NoteOctave= Octave (0-8)
;PitchBend= Main pitch bend (-32768 - +32767)
;FineTune= Detune index (-128 - +127)
;VibFineTune = Vibrato detune index (-128 - +127)
;NotePitch = Main pitch (0-12)
;Output with PitchL, PitchH = walue to write in the period regs
GetPitch
lda #$00
sta PitchH
lda FineTune.w,X
clc
adc LFOFineTune.w,X ;Add LFO fine tune to main fine tune
sta PitchL ;Both are 8-bit signed
bpl +
bvs _fineTuneDone
dec PitchH ;If result is neg and if there is no overflow, the main thing is negative
bne _fineTuneDone
+ bvc _fineTuneDone
dec PitchH ;If result is positive and if there is overflow, the main thing is also negatinve
_fineTuneDone
lda PitchBendL.w,X
clc
adc PitchL
sta PitchL
lda PitchBendH.w,X
adc PitchH ;Add pitch bend (16 bit signed) to get a 16 bit signed result
asl PitchL
rol A
asl PitchL
rol A
asl PitchL
rol A ;Multiply the whole detune index by 8
;Add the note to the main pitch, then divide by 8
;This is the equivelent to multiply note by 32
clc
adc NotePitch
bmi _octaveAdjustNeg
_octaveAdjustPos
cmp #$0c
bcc _octaveAdjustDone
sec
sbc #$0c ;Add as many octaves than needed scince the detunes is higher than one octave
inc NoteOctave ;To avoid geting out of the pitch table because the detune would change the octave
jmp _octaveAdjustPos
_octaveAdjustNeg
clc
adc #$0c ;Same but remove octaves if the pitch goes one or more octave below
dec NoteOctave ;Note than PitchH is forcely positive from now
cmp #$0c
bcs _octaveAdjustNeg
_octaveAdjustDone
lsr A
ror PitchL
lsr A
ror PitchL
sta PitchH
lda PitchL
clc
adc #<PeriodTbl
sta PitchL
lda PitchH ;Load the pointer instead of pitch variables
adc #>PeriodTbl
sta PitchH
ldy #$00
lda [Pitch],Y
pha
iny
lda [Pitch],Y
sta PitchH
pla
ldy NoteOctave
bmi _PitchOutofRange
beq _octaveZero
cpy #$05
bcs _veryHighOctave
- lsr PitchH
ror A
dey
bne -
_octaveZero
sta PitchL
lda PitchH
cmp #$08 ;If the period is over 11 bits, the pitch is out of range
bcc +
_PitchOutofRange
lda #$01
sta PitchL ;Make an unaudible tone if pitch is out of range
lsr A
sta PitchH
+ rts
_veryHighOctave
- asl A
rol PitchH
iny
cpy #$08
bcc -
lda PitchH
sta PitchL
lda #$00
sta PitchH
rts
About the table, the values should be CPUSpeed/2^(n/384)*32.7*16, where n equals 0, then 1, then 2, up to 384 for each entry on the table.
If someone want to use it, I'll just post the some .dw line it does for NTSC, however, it's wrong in pal. (I used Excel to have those values fastly).
Code:
.dw 3420, 3414, 3408, 3402, 3396, 3390, 3383, 3377, 3371, 3365, 3359, 3353, 3347, 3341, 3335, 3329
.dw 3323, 3317, 3311, 3305, 3299, 3293, 3287, 3281, 3275, 3269, 3263, 3258, 3252, 3246, 3240, 3234
.dw 3228, 3223, 3217, 3211, 3205, 3199, 3194, 3188, 3182, 3176, 3171, 3165, 3159, 3153, 3148, 3142
.dw 3136, 3131, 3125, 3119, 3114, 3108, 3103, 3097, 3091, 3086, 3080, 3075, 3069, 3064, 3058, 3053
.dw 3048, 3042, 3037, 3031, 3026, 3020, 3015, 3009, 3004, 2998, 2993, 2988, 2982, 2977, 2972, 2966
.dw 2961, 2956, 2950, 2945, 2940, 2934, 2929, 2924, 2918, 2913, 2908, 2903, 2897, 2892, 2887, 2882
.dw 2877, 2871, 2866, 2861, 2856, 2851, 2846, 2840, 2835, 2830, 2825, 2820, 2815, 2810, 2805, 2800
.dw 2795, 2790, 2785, 2780, 2775, 2770, 2765, 2760, 2755, 2750, 2745, 2740, 2735, 2730, 2725, 2720
.dw 2715, 2710, 2705, 2700, 2696, 2691, 2686, 2681, 2676, 2671, 2667, 2662, 2657, 2652, 2647, 2643
.dw 2638, 2633, 2628, 2624, 2619, 2614, 2609, 2605, 2600, 2595, 2591, 2586, 2581, 2577, 2572, 2567
.dw 2563, 2558, 2553, 2549, 2544, 2540, 2535, 2531, 2526, 2521, 2517, 2512, 2508, 2503, 2499, 2494
.dw 2490, 2485, 2481, 2476, 2472, 2467, 2463, 2459, 2454, 2450, 2445, 2441, 2436, 2432, 2428, 2423
.dw 2419, 2415, 2410, 2406, 2401, 2397, 2393, 2389, 2384, 2380, 2376, 2371, 2367, 2363, 2359, 2354
.dw 2350, 2346, 2342, 2337, 2333, 2329, 2325, 2321, 2316, 2312, 2308, 2304, 2300, 2296, 2291, 2287
.dw 2283, 2279, 2275, 2271, 2267, 2263, 2259, 2254, 2250, 2246, 2242, 2238, 2234, 2230, 2226, 2222
.dw 2218, 2214, 2210, 2206, 2202, 2198, 2194, 2190, 2186, 2182, 2178, 2175, 2171, 2167, 2163, 2159
.dw 2155, 2151, 2147, 2143, 2139, 2136, 2132, 2128, 2124, 2120, 2116, 2113, 2109, 2105, 2101, 2097
.dw 2094, 2090, 2086, 2082, 2079, 2075, 2071, 2067, 2064, 2060, 2056, 2052, 2049, 2045, 2041, 2038
.dw 2034, 2030, 2027, 2023, 2019, 2016, 2012, 2008, 2005, 2001, 1998, 1994, 1990, 1987, 1983, 1980
.dw 1976, 1973, 1969, 1965, 1962, 1958, 1955, 1951, 1948, 1944, 1941, 1937, 1934, 1930, 1927, 1923
.dw 1920, 1916, 1913, 1910, 1906, 1903, 1899, 1896, 1892, 1889, 1886, 1882, 1879, 1875, 1872, 1869
.dw 1865, 1862, 1858, 1855, 1852, 1848, 1845, 1842, 1838, 1835, 1832, 1829, 1825, 1822, 1819, 1815
.dw 1812, 1809, 1806, 1802, 1799, 1796, 1793, 1789, 1786, 1783, 1780, 1776, 1773, 1770, 1767, 1764
.dw 1761, 1757, 1754, 1751, 1748, 1745, 1742, 1738, 1735, 1732, 1729, 1726, 1723, 1720, 1717, 1713