It is currently Wed Jan 23, 2019 4:40 am

 All times are UTC - 7 hours

 Page 2 of 3 [ 44 posts ] Go to page Previous  1, 2, 3  Next
 Print view Previous topic | Next topic
Author Message
 Post subject: Posted: Fri Apr 06, 2012 9:21 pm

Joined: Fri Sep 22, 2006 9:52 am
Posts: 160
Location: philly
Sorry for the bump, but there's good discussion here and I'd rather not clutter up the board with a closely-related topic.

I was wondering if someone can answer this question:
I'm curious about﻿ the gaps in the scale when downtuning. Know of any reasons it was designed that way?

How would you explain to a layperson what a "timer system" is, and why there are larger gaps up at the higher pitches? Is there a reason it starts off with a major scale and then eventually starts taking bigger leaps?

Top

 Post subject: Posted: Fri Apr 06, 2012 11:19 pm

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 7120
Well, there are some decisions in the 2A03 design that are extremely arbitrary. Take a look at the possible length counter values, for instance: http://wiki.nesdev.com/w/index.php/APU_Length_Counter.

I don't think there's really a good explanation for the DPCM frequencies. I think they're just frequencies somebody at Nintendo thought might be useful.

Top

 Post subject: Posted: Sat Apr 07, 2012 5:30 am

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 21011
Location: NE Indiana, USA (NTSC)
My wild guess about the larger gaps at the higher pitches is that there is less precision in the sample periods once it goes that high.

Top

 Post subject: Posted: Thu Apr 12, 2012 6:41 pm
 Formerly ~J-@D!~

Joined: Sun Mar 12, 2006 12:36 am
Posts: 479
Location: Rive nord de Montréal
Tepples' totally right.

Here my GNU Octave code (can work in Matlab with few modifications):
Code:
0; % if you want to put in a m file
function hz = midi2freq(note)
hz = 440 * 2 .^((note - 69) / 12);
end

nesfreq = 1.7897725e6;
dmcperiods = [0x36; 0x48; 0x54; 0x6A; 0x80; 0x8E; 0xA0; 0xBE; 0xD6; 0xE2; 0xFE; 0x11E; 0x140; 0x154; 0x17C; 0x1AC];

notes = [12*12; 11*12+7; 11*12+4; 11*12; 10*12+9; 10*12+7; 10*12+5; 10*12+2; 10*12; 9*12+11; 9*12+9; 9*12+7; 9*12+5; 9*12+4; 9*12+2; 9*12];

most_tuned_dmcperiods = round(nesfreq ./ midi2freq(notes));

fprintf("difference between original DMC periods and the most tuned possible DMC periods:\n")

disp( most_tuned_dmcperiods - dmcperiods)

Output:
Code:
difference between original DMC periods and the most tuned possible DMC periods:
-1
-1
1
1
-1
1
0
0
0
0
0
-1
0
-1
1
0

As you can see, they didn't get the most tuned possible values, but they're very close to it. And if you change the 'round' by 'floor' (simple truncation) it outputs:

Code:
difference between original DMC periods and the most tuned possible DMC periods:
-1
-1
0
0
-1
0
0
0
-1
0
0
-1
0
-1
0
-1

which shows that it is even closer to that algorithm.

Top

 Post subject: Posted: Fri Apr 13, 2012 7:14 am

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 21011
Location: NE Indiana, USA (NTSC)
Notice that all the values in dmcperiods are even. This means the counter is probably triggered by the APU clock, which is half the CPU clock. (Quietust traced the circuit, and it turned out that pulse, noise, and DMC period dividers are clocked by the APU clock.) So what happens when you round most_tuned_dmcperiods to the nearest even period? I don't know Octave, so I have no idea whether the following is working syntax:
Code:
most_tuned_dmcperiods = 2 * round(nesfreq ./ (2 * midi2freq(notes)));

And there's an exact formula for nesfreq: 315000000/176

Top

 Post subject: Posted: Fri Apr 13, 2012 6:45 pm
 Formerly ~J-@D!~

Joined: Sun Mar 12, 2006 12:36 am
Posts: 479
Location: Rive nord de Montréal
You know Tepples, Octave is really like Matlab, with just less restrictions on the language and of course, Octave is not backed with powerful toolboxes; nevertheless, you should apt-get installthat, and also some useful packages like octave-control and octave-signal.

And yeah, you're right again:

Code:
0; % if you want to put in a m file
function hz = midi2freq(note)
hz = 440 * 2 .^((note - 69) / 12);
end

nesfreq = 315000000 / 176;
dmcperiods = [0x36; 0x48; 0x54; 0x6A; 0x80; 0x8E; 0xA0; 0xBE; 0xD6; 0xE2; 0xFE; 0x11E; 0x140; 0x154; 0x17C; 0x1AC];

notes = [12*12; 11*12+7; 11*12+4; 11*12; 10*12+9; 10*12+7; 10*12+5; 10*12+2; 10*12; 9*12+11; 9*12+9; 9*12+7; 9*12+5; 9*12+4; 9*12+2; 9*12];

most_tuned_dmcperiods = 2 * round(nesfreq ./ (2 * midi2freq(notes)));

fprintf("difference between original DMC periods and the most tuned possible DMC periods:\n")

disp( most_tuned_dmcperiods - dmcperiods)

Output:
Code:
difference between original DMC periods and the most tuned possible DMC periods:
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0

So now it's apparent that it really correlate with notes, no doubts. That wasn't random at all.

Top

 Post subject: Posted: Sat Apr 14, 2012 12:57 am

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 7120
So what about the "gaps" after the first several chromatic pitches? How many cents off are they? (Would the best case for the skipped pitches be really bad?)

Top

 Post subject: Posted: Sat Apr 14, 2012 8:32 am
 Formerly ~J-@D!~

Joined: Sun Mar 12, 2006 12:36 am
Posts: 479
Location: Rive nord de Montréal
Data is posted on the (old) 2A03.org forums, but since it has proved instable, I'll post the results here, and recalc them to be sure, posting at the same time my GNU Octave code to get that data, so anyone can verify how the result was obtained.

Code:
0; % if you want to put in a m file

function hz = midi2freq(note)
hz = 440 * 2 .^((note - 69) / 12);
end

% an helper
function notestruct = make_notestruct(octave, scale, cents)
notestruct = struct("octave", octave, "scale", scale, "cents", cents);
end

% note that it can output an invalid MIDI notes (values outside 0~127)
% as well as non integer values.
function midinote = freq2midi(freq)
midinote = log2(freq ./ 440) .* 12 + 69;
end

function notestruct = midi2notestruct(midinote)
closestnote = round(midinote);
oct_offs    = floor(closestnote/12);
closestnote = closestnote - oct_offs*12;
octave      = oct_offs - 1;
scale       = closestnote;
cents       = (midinote - oct_offs*12 - scale) * 100;

notestruct = make_notestruct(octave, scale, cents);
end

function str = scale2disp(scale)
scaledisp = ["C";"C#";"D";"D#";"E";"F";"F#";"G";"G#";"A";"A#";"B"];
str = scaledisp(scale+1);
end

nesfreq = 315000000 / 176;
dmcperiods = [0x36; 0x48; 0x54; 0x6A; 0x80; 0x8E; 0xA0; 0xBE; 0xD6; 0xE2; 0xFE; 0x11E; 0x140; 0x154; 0x17C; 0x1AC];

dmcfreqs = nesfreq ./ dmcperiods;

dmcmidi = freq2midi(dmcfreqs);

dmcnotes = midi2notestruct(dmcmidi);

fprintf("DMC period  octave     scale   cents\n");
fprintf("-----------------------------------------\n");
for i=1:length(dmcperiods)
fprintf("0x%03X       %2d        %2s       %+2.6g\n", dmcperiods(i), ...
dmcnotes.octave(i), ...
scale2disp(dmcnotes.scale)(i), ...
dmcnotes.cents(i));
end

Results:
Code:
DMC period  octave     scale   cents
-----------------------------------------
0x036       11         C       -17.8827
0x048       10         G       -15.9277
0x054       10         E       +17.2014
0x06A       10         C       +14.4778
0x080        9         A       -12.0177
0x08E        9         G       +8.28576
0x0A0        9         F       +1.66859
0x0BE        9         D       +4.15558
0x0D6        9         C       -1.77808
0x0E2        8         B       +3.76755
0x0FE        8         A       +1.56068
0x11E        8         G       -3.8633
0x140        8         F       +1.66859
0x154        8         E       -3.28682
0x17C        8         D       +4.15558
0x1AC        8         C       -1.77808

Top

 Post subject: Posted: Sat Apr 14, 2012 1:29 pm

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 7120
I don't have octave, but I wrote a similar python program to answer the question I was asking. What I wanted to know was what are the best-fit values that could have been used for any given note.

http://rainwarrior.ca/projects/nes/dpcm_freqs.py

Code:
DMC   Octave   Note   Cents
-----------------------------------
0x002 16       A      -12.01769334
0x004 15       A      -12.01769334
0x006 15       D      -13.97269421
0x008 14       A      -12.01769334
0x00A 14       F      +1.66859279
0x00C 14       D      -13.97269421
0x00E 13       B      +19.15640019
0x010 13       A      -12.01769334
0x012 13       G      -15.92769507
0x014 13       F      +1.66859279
0x016 13       D#     +36.66436429
0x018 13       D      -13.97269421
0x01A 13       C      +47.45464489
0x01C 12       B      +19.15640019
0x01E 12       A#     -0.28640807
0x020 12       A      -12.01769334
0x022 12       G#     -16.97310284
0x024 12       G      -15.92769507
0x026 12       F#     -9.53070947
0x028 12       F      +1.66859279
0x02A 12       E      +17.20139932
0x02C 12       D#     +36.66436429
0x030 12       D      -13.97269421
0x032 12       C#     +15.35487893
0x036 12       C      -17.88269594   *****
0x038 11       B      +19.15640019
0x03C 11       A#     -0.28640807
0x040 11       A      -12.01769334
0x044 11       G#     -16.97310284
0x048 11       G      -15.92769507   *****
0x04C 11       F#     -9.53070947
0x050 11       F      +1.66859279
0x054 11       E      +17.20139932   *****
0x05A 11       D#     -2.24140894
0x060 11       D      -13.97269421
0x064 11       C#     +15.35487893
0x06A 11       C      +14.47776118   *****
0x072 10       B      -11.48571034
0x078 10       A#     -0.28640807
0x080 10       A      -12.01769334   *****
0x086 10       G#     +8.67527811
0x08E 10       G      +8.28576325    *****
0x098 10       F#     -9.53070947
0x0A0 10       F      +1.66859279    *****
0x0AA 10       E      -3.28681671
0x0B4 10       D#     -2.24140894
0x0BE 10       D      +4.15557666    *****
0x0CA 10       C#     -1.87147264
0x0D6 10       C      -1.77807702    *****
0x0E2 9        B      +3.76755176    *****
0x0F0 9        A#     -0.28640807
0x0FE 9        A      +1.56068253    *****
0x10E 9        G#     -4.19640980
0x11E 9        G      -3.86329747    *****
0x12E 9        F#     +1.89661947
0x140 9        F      +1.66859279    *****
0x154 9        E      -3.28681671    *****
0x168 9        D#     -2.24140894
0x17C 9        D      +4.15557666    *****
0x194 9        C#     -1.87147264
0x1AC 9        C      -1.77807702    *****
0x1C4 8        B      +3.76755176
0x1E0 8        A#     -0.28640807
0x1FC 8        A      +1.56068253
-----------------------------------

Does it shed any light on why they picked the particular notes they did? I dunno, by this the choices still look kinda arbitrary to me.

Top

 Post subject: Posted: Sat Apr 14, 2012 3:20 pm

Joined: Sun Apr 13, 2008 11:12 am
Posts: 7978
Location: Seattle
Musically the note choice makes a fair amount of sense-

The entire Ionian octave at the bottom.
Most of the next octave, missing the 3rd and 7th scale degrees
The major triad of the next octave up
The root scale degree at top.

Because any desired detune and transposition can be encoded when the sample is made, this allows any song to play notes over most of two octaves in Ionian or Dorian modes.

Is it be as good as a fully programmable divisor as on the triangle and pulse wave channels? Obviously not. Would other decisions that only used 4 bits for specifying period make more sense? Yes; I think including one octave of a full chromatic scale at plus a few higher notes would probably have been a better design. But I think this is far more explicable than the restrictions on noise frequency or the duplicate pulse duty (1/4 and 3/4 have the same sound).

Top

 Post subject: Posted: Sat Apr 14, 2012 6:09 pm

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 7120
You can build large samples at any frequency, at whichever samplerate from the table is good for you, but interestingly it doesn't seem to be based around doing that kind of thing. In that sort of situation it would be more useful to keep your samplerates in tune with each other, rather than trying to keep them in tune with an arbitrary standard like A440 (which is clearly in use here). Note that only the lowest two Cs are exactly an octave apart; the rest are jittered up and down trying to match to A440 rather than matching each other.

It's not particularly good for short looped samples either, since the sample length is 16n+1 bytes. A 1 byte sample would be too quiet to be useful. If it was an even 16, with 16 bytes you could make a decent DPCM triangle 3-7 octaves down from the frequencies mentioned, and play it all over that major scale. However, it's 17 bytes, which detunes it by 16/17... which kind of makes the scale mostly pointless (unless you want to tune your square/triangle at A=414hz). Kinda seems like it's not intended for that either. The only place I've seen this tried was in Rob Hubbard's The Immortal, which used a 17 byte bass, but doesn't really bother to compensate for the 16/17 detune, just kinda uses it sparingly, and accompanies it with vibrato generally. 16/17 is most of a semitone off, so it's still close enough to be usable. (So for example, in track 2 he uses DPCM pitch 2, an E frequency, to play a tone close enough to E-flat to be acceptable.)

Honestly I think they're rather poorly chosen for any purpose. Sunsoft tried to make do with them, and they do an okay job, but you'll notice their basslines jump in octaves all the time and they bottom and top of the octave really never sound in tune with each other. They just live with the approximation. Note that they for the most part don't use the low samplerates where that pre-fab major scale resides, because they'd sound too muddy at such a low samplerate anyway. They're forced to use the more poorly tuned higher ones, and do the tuning via multiple samples.

If you ask what my guess is, the designer originally presumed sample lengths wouldn't have the +1, and designed the scale for short looped samples in an A440 tuning. After the first octave, just chose arbitrary pitches; F and G are good because they're in the middle but also an important scale note, I guess trying to space out the higher frequencies so there are more to work with? Other ones are just haphazardly chosen, in my opinion. Maybe they were chosen early on, intended for a different purpose, and then as a feature it was de-emphasized later on in the design process and it didn't ever get a rework.

Top

 Post subject: Posted: Sat Apr 14, 2012 6:40 pm

Joined: Sun Apr 13, 2008 11:12 am
Posts: 7978
Location: Seattle
Sure; they evidently knew just enough to make decisions for the audio hardware that are correct at first glimpse and inadequate when a person who knows more about the situation looks at it. See also: the looped noise mode's interactions with the restrictions on noise rate.

Top

 Post subject: Posted: Sat Apr 14, 2012 7:28 pm

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 21011
Location: NE Indiana, USA (NTSC)
At least on the Game Boy, looped noise makes acceptable C, D, F, and G# in each octave, which allows for passable covers of the Super Mario Bros. 2 and the end of "Wish" by Nine Inch Nails (which uses a D, C, G#, F descending scale).

Top

 Post subject: Posted: Sat Apr 14, 2012 7:55 pm

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 7120
On a whim I decided I would try to make an in-tune 17-byte looped DPCM sample. Or, well, at least as in tune as is possible on the NES.

http://rainwarrior.ca/projects/nes/lately_tuned.nsf

So, this is a short tune with a 17-byte looped DPCM sample. It makes sure to use all 16 possible pitches. The pitch tables used for the other channels have been custom built with A-flat = 440hz * 16 / 17, to compensate for the detune due to sample length.

As you can hear, for the high notes especially, the DPCM really doesn't have a lot of resolution to get pitches accurate anyway. I'm bent over as far as I can to try and sound in-tune with the DPCM's chosen scale, and even with this the results aren't particularly magical.

The NES has never really been an instrument for precision tuning anyway, I suppose, but if I did this correctly, this is a best-case example for DPCM tuning.

Top

 Post subject: Posted: Sat Apr 14, 2012 9:59 pm

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 7120
Just to point out how bad this tuning scheme actually is, check out the four Cs:

Code:
0x036 12       C      -17.88269594
0x06A 11       C      +14.47776118
0x0D6 10       C      -1.77807702
0x1AC 9        C      -1.77807702

Note that 0x036 and 0x06A are a total of 31 cents apart, because one rounds up and the other down! This is almost comic. You cannot use the DPCM's pitches to play an in-tune octave, except between the lowest two (which are the least likely to be used since the samplerate is so low down there).

It would have been much better to just forget about trying to hit the A440 scale and make these power of 2 multiples of each other. Sunsoft games could have had much nicer tuning in their basslines.

Also, Tepples, I'd be interested in hearing that GB Wish cover, if you have a link.

Top

 Display posts from previous: All posts1 day7 days2 weeks1 month3 months6 months1 year Sort by AuthorPost timeSubject AscendingDescending
 Page 2 of 3 [ 44 posts ] Go to page Previous  1, 2, 3  Next

 All times are UTC - 7 hours

#### Who is online

Users browsing this forum: No registered users and 2 guests

 You cannot post new topics in this forumYou cannot reply to topics in this forumYou cannot edit your posts in this forumYou cannot delete your posts in this forumYou cannot post attachments in this forum

Search for:
 Jump to:  Select a forum ------------------ NES / Famicom    NESdev    NESemdev    NES Graphics    NES Music    Homebrew Projects       2018 NESdev Competition       2017 NESdev Competition       2016 NESdev Competition       2014 NESdev Competition       2011 NESdev Competition    Newbie Help Center    NES Hardware and Flash Equipment       Reproduction    NESdev International       FCdev       NESdev China       NESdev Middle East Other    General Stuff    Membler Industries    Other Retro Dev       SNESdev       GBDev    Test Forum Site Issues    phpBB Issues    Web Issues    nesdevWiki