NES chords and intervals

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
NES chords and intervals
by on (#5855)
I'm making my own game and I wanted some music. I found some sheet music for "Little Brown Jug". The music sounded pretty good. I wanted to make it sound better so I tried adding chords or intervals. I've been trying a combination of triangle and both square wave channels playing together in Perfect Fifth (seven semitones above root) intervals and major and minor chords. The Perfect Fifth interval sounds good but both chords sound sort of muddy and dissonant. Any ideas why? I thought chords would sound nice just as they do on my keyboard. Is it because of weird harmonics from square waves? Thanks for any help.

by on (#5858)
Are you coding your sound? well if you are, I have that problem too. There's something weird about balancing things out with 4002 and 4003. I have fixed values in 4003 like this:

music:
.db $90,$80,$70,$60,$50,$40,$30,$20,$30,$40,$50,$60,$70,$80,$90

I have that for a B flat scale that I have at the beggining of my basic minigame. This is what's going on in 4002:


musicc:
.db $BF,$B5,$A0,$90,$80,$7A,$6A,$60,$6A,$7A,$80,$90,$A0,$B5,$BF,$BF,$00

I could understand like musicc going down a value of $8 for like a halfstep, and $F for a wholestep, but I don't know why it did had to be set up like this. When I tried going down 8 for a halfstep and F for a wholestep, it got way screwed up, and there were all sorts of wrong notes, and it did not work at all. I have no idea why.

by on (#5862)
Want to know the true formula behind note periods?

The 2A03 is clocked at M2 = 1789773 Hz (on NTSC), and if you write the 11-bit period p to the period register of a square wave channel, the square wave channels repeat once every 16*p CPU cycles, for a frequency of f = M2/(16*p). So if you want a given period p that will produce a given frequency f, it's p = M2/(16*f).

The frequency of the lowest valid note on the NES is 55 Hz, an A that's a bit more than 2 octaves below middle C. By the formula above, this is a period of 2034.

An octave is a 2:1 ratio (e.g. 2034 and 1017). A perfect fifth is a 3:2 ratio (e.g. 1017 and 678), and its complement is a perfect fourth, a 4:3 ratio (e.g. 1356 and 1017).

There are twelve semitones in an octave. To calculate their frequencies, you can use the rule of equal temperament. Given frequency f0 of the lowest valid note and an interval above it measuring n semitones, the frequency of this note is f = f0 * 2^(semitone number/12). Invert the formula for period: p = p0 / 2^(semitone number/12)

This post by 0xtob and my post a few column inches down should clarify things. Sample source code was posted here.

by on (#5878)
Thanks for the help. I used the same formulas to make a table of note frequencies and periods in the registers for both the square and triangle channels. I sort of understand the Wolf dissonance (not being any sort of musician). However the same chords played on my keyboard sound much better than on the NES (at least FCEUD emulation, I haven't put it into my devcart yet). I guess it could either be the emulation, the NES sound itself or the Wolf note or some combination. I suppose with a little bit of study I could alter the periods to avoid the Wolf problem. By the way, I made a table with 4 octaves (C2-B6) and the corresponding square and triangle periods if anybody wants it.
Lloyd Gordon

by on (#5892)
tepples wrote:
An octave is a 2:1 ratio (e.g. 2034 and 1017). A perfect fifth is a 3:2 ratio (e.g. 1017 and 678), and its complement is a perfect fourth, a 4:3 ratio (e.g. 1356 and 1017).

Not exactly.
Just try the formula :
2^(6/12)=1.49831 VS 3/2=1.50000
Pretty close, but NOT exactly the same value...
the same is valid about :
2^(5/12)=1.33484 VS 4/3=1.33333
very close but not the exact same. I've found a site explaining that in details and where there was sample tunes with both tuning method. And I founded the rational one pretty bad, while the logarithmic one sound better.

by on (#5896)
Bregalad wrote:
I've found a site explaining that in details and where there was sample tunes with both tuning method. And I founded the rational one pretty bad, while the logarithmic one sound better.

That's because you're just used to the logarithmic one, with its wide-ass major 3rd and its narrow minor 3rd. Renaissance and Baroque music was written for just tuning or well temperament, not the modern equal temperament.

by on (#5902)
tepples wrote:
That's because you're just used to the logarithmic one, with its wide-ass major 3rd and its narrow minor 3rd. Renaissance and Baroque music was written for just tuning or well temperament, not the modern equal temperament.

Interesting, heh. I just found the logarithmic one sounding better.
Some people simply asked why there is 12 notes in an octave... and some answered it is because it's where both rational temperament and lograrithmic temperament were so close.
Anyway, the fact that there is 2 possible way of tuning should be mentionned.

by on (#5947)
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