FDS modulator formula revision

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

User avatar
rainwarrior
Posts: 8732
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

FDS modulator formula revision

Post by rainwarrior »

I've been making audio tests of my FDS over the last few days, and I've spent a lot of time trying to verify the currently used formula for modulation strength outlined in disch's old document (and currently on the wiki FDS audio page), which always struck me as a very odd formula, especially since disch himself had a disclaimer that he'd never peresonally tested the real hardware. I found it to be mostly correct, but requiring a few tweaks to get it to match the empirical data I gathered from my test recordings.

Code: Select all

// pitch = $4082/4083 (12-bit unsigned pitch value)
// bias = $4085 (7-bit signed bias)
// gain = $4084 (6-bit unsigned gain)

// 1. multiply bias by gain, lose lowest 4 bits of result but "round" in a strange way
temp = bias * gain;
remainder = temp & 0xF;
temp >>= 4;
if ((remainder > 0) && ((temp & 0x80) == 0))
{
    if (bias < 0) temp -= 1;
    else temp += 2;
}

// 2. wrap if a certain range is exceeded
if (temp >= 192) temp -= 256;
else if (temp < -64) temp += 256;

// 3. multiply result by pitch, then round to nearest while dropping 6 bits
temp = pitch * temp;
remainder = temp & 0x3F;
temp >>= 6;
if (remainder >= 32) temp += 1;

// final mod result is in temp
The notable changes to disch's formula are:
  • the weird early "rounding" step is only applied if bit 7 of the result is clear
  • the wrapping step no longer uneven, the fix to the rounding makes this wrap really just an 8-bit result biased by -64
  • the final result appears to be rounded to nearest, rather than simply truncating
So... at least the wrapping step got simpler (and common-sensified), even though there are two new rounding complications. I have no idea why it only adjusts if bit 7 is clear. The -1/+2 thing is also strange to me, but that was already there. I can't explain why it's this way, but it definitely matches my tests now.

I will add this to the wiki in a few days, as I am still doing another round of tests at the moment. In the meantime, is there anybody here who can independently verify this? I can detail the methods I used to determine this if anyone's interested, but I would appreciate verification by a different experiment if possible.
User avatar
rainwarrior
Posts: 8732
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: FDS modulator formula revision

Post by rainwarrior »

Okay, I think I've tested everything I can, and I'm pretty confident in the current state of my emulator. (Beta available here: https://dl.dropboxusercontent.com/u/883 ... ay23b3.zip)

Other than the mod formula revision, there are a couple of things I've noticed in my tests:

1. There is no simple way to reset the phase of the mod table. $4085 only sets the "bias" accumulator, nothing more. When appending to the mod table (which can only be done when the mod table is halted via $4087), new entries will be written at its current playback position (there is no separate write position). Thus, the only way to reset the phase of the table is to halt it (via $4087) and write all 32 entries; when it is unhalted, the playback position will be at the current waveform (since it's gone a full once around). A lot of FDS NSFs seem to write $4085 immediately before writing the mod-table, so maybe it was presumed this could reset the phase.

2. Halting the unit via $4083 resets the wave phase to 0. It does not disable its output, so if the sample at $4040 is a value of 63, for example, it will be outputting that 63 constantly. You can affect this output with the volume and master volume. (Volume envelopes seem to be disabled when $4083 bit 7 is set, however, so you can only change the volume with a direct set.)

3. There appears to be a simple 1-pole lowpass on the output. In trying to match it the cutoff appears to be ~2000Hz.

4. The $4089 wave write bit will hold the waveform output at its current level, though the waveform will continue to play underneath if not halted via $4083, so when the write bit is cleared, the waveform output will simply jump to wherever it would have been, as if it had never been stopped (because it hadn't).

There are some other little details probably, but I'm going to do a full pass on the wiki page and explain.


I have a request: if anyone with a PowerPak and an FDS would like to do a hotswap test, I want to make sure that the lack of $4085 phase reset isn't just a peculiar revision of my FDS only. The test is attached (fds_test_7.nes), run it, after the first buzz you have 5 seconds to hotswap to the FDS before the second buzz. After the second buzz it tries 12 times to reset the phase via $4085 and it should fail each time (you'll hear the characteristic mod loop continue unscathed).

Here is what the test sounded like through my Famicom+FDS: https://dl.dropboxusercontent.com/u/883 ... est_7.flac

The only reason I'm unsure about this is that the Bio Miracle Bokutte Upa NSF track 2 sounds broken because of $4085 writes while the mod table is playing (which other emulators presume should reset phase). At this point it seems either my FDS is special, or the NSF is broken, but I'd like to hear some other recordings of this test to verify that it's not just my FDS.
Attachments
fds_test_7.zip
(4.88 KiB) Downloaded 710 times
Last edited by rainwarrior on Fri Dec 26, 2014 8:53 pm, edited 1 time in total.
ccovell
Posts: 1045
Joined: Sun Mar 19, 2006 9:44 pm
Location: Japan
Contact:

Re: FDS modulator formula revision

Post by ccovell »

Here is a recording from my Twin Famicom: http://www.chrismcovell.com/data/FDS_Chan_Test.rar

The Twin Fami apparently has quite the lowpass filter as well, but anyway, clicks and whatnot are quite audible.
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: FDS modulator formula revision

Post by lidnariq »

rainwarrior wrote:3. There appears to be a simple 1-pole lowpass on the output. In trying to match it the cutoff appears to be ~2000Hz.
Are there pictures of both sides of the FDS PCB somewhere? It would be nice to see if I can find the components that cause the lowpass.
User avatar
rainwarrior
Posts: 8732
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: FDS modulator formula revision

Post by rainwarrior »

Thank very much!

Yeah, it's pretty clear from that that there's no phase reset at all going on there.

After doing some more investigating, I believe what the problem I have with Bio Miracle is it's extra write of $4085 just after it re-enables the mod unit. On some notes, the mod frequency is set very high, so there's a chance it could tick before $4085 resets the mod accumulator, throwing the pitch off. I think the reason I couldn't eliminate it entirely is it's a very precise number of cycles, and it's right on the cusp of where it would tick before the $4085. The other thing I believe I missed is that I think $4088 resets the accumulator that ticks the mod table, so it actually makes that timing consistent if you've got a cycle-accurate emulator.

Unfortunately, NSFPlay alternates between clocking the CPU and clocking the audio units, so it's not cycle-precise in that way. It's far too slow if I do it all cycle by cycle.

I think I'll probably put in an option (disabled by default) to let $4085 reset the phase, and document that it's really just there to work around this problem.

Also in the future I'll probably add a full-clock-samplerate WAV export for people who want to make cycle-precise renders.
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: FDS modulator formula revision

Post by tepples »

Cycle accurate operation can be made efficient. Run the CPU ahead of the audio units, generating a list of writes represented as (cycle, address, data) tuples. Then catch up the audio units by feeding in each write at the appropriate cycle. CPU reads of readable registers complicate things though, I'll admit, but you can just catch up on each $4015 or expansion synthesizer read. I imagine that NSFs seldom spin on a readable register for long enough to make this slow it down.
User avatar
rainwarrior
Posts: 8732
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: FDS modulator formula revision

Post by rainwarrior »

The problem isn't that cycle accurate CPU/audio can't be made efficient, it's that cycle accurate isn't efficient enough with the current code, and I'm not going to do the necessary rewrite for this version. The solution is earmarked for a future revision.

For what it's worth, the current implementation is generally very accurate (CPU and audio are synced at least once per sample, and independently they are cycle accurate to themselves), it's just the efficiency really breaks down quickly when I take it to single clocks. This is the first and only problem I've found relating to their very slight desynch.
Last edited by rainwarrior on Mon Jul 08, 2013 6:31 pm, edited 1 time in total.
User avatar
rainwarrior
Posts: 8732
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: FDS modulator formula revision

Post by rainwarrior »

lidnariq, see what you can do with this:

Image
Image
Image
Image
Image

The electrolytic caps are 47µF 16V (big) and 1µF 50V (small x2). All the ceramics say 104Z 25V.

The board markings for resistors:
R1: 47
R2: 1.2M
R3: 56K
R4: 2M
R5: 56K
R6: 3.3K
R7: 100
R8: 100K

If there's anything else you need read, let me know.
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: FDS modulator formula revision

Post by lidnariq »

I should have figured that it was just a matter of figuring out what to search for.
This fellow has a reverse engineered schematic: http://green.ap.teacup.com/junker/119.html

The two highpasses are obvious in the schematic: one at 0.796 Hz (2MΩ R4 and 1µF C3), and one at 5.68Hz (56 kiloohms from each audio source (R3, R5) and a 1 microfarad capacitor (C5)).
But none of the lowpass filters are obvious.

So I threw it into LTspice to see if I could coax it out. (Schematic:
bss145-schem.png
)
I faked the BU4069 using nMOSFET-pMOSFET pairs, and ended up with this transfer function:
bss145-xfer.png
Finding the exact corner frequencies in that complicated of a transfer function is difficult, so here's the numbers using a bunch of different heuristics:
* using phase=45°+k·90°: highpasses at .0774Hz and 5.78Hz and lowpasses at 1.36kHz, 10.5kHz, and 51.7kHz
* using intersection of tangent lines: highpasses at .0796Hz, 5.74Hz, and lowpasses at 1.57kHz, 11.5kHz, and 39.8kHz
Both of those methods underestimate the frequency of the 1.4kHz and 11kHz lowpasses (because they all stacked on top of each other)
* by -6dB points: highest highpass at 3.27Hz; lowest lowpass at 2.86kHz. (This is probably what you found)

The top two lowpasses seem to be relatively sensitive to the specifics of the inverter: replacing the nMOSFET (from a BSS145 to a 2N7002) moved from 11.5kHz to 18.2kHz and from 51.7kHZ to 193kHz. The other three corner frequencies are comparatively fixed.
Last edited by lidnariq on Sun Feb 23, 2020 4:16 pm, edited 1 time in total.
User avatar
rainwarrior
Posts: 8732
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: FDS modulator formula revision

Post by rainwarrior »

That's pretty neat. A bit more complex than I was expecting! I remember someone suggested that the volume on the FDS is controlled via PWM, are these additional high-frequency lowpass filters intended to smooth the effects of PWM?

From your transfer function and description, I'm wondering what the best way to approximate this is. The highpass probably isn't worth bothering with since the emulator already has a simple one, and the differences between one or the other probably aren't very audible. That curve in the region between 1kHz - 20kHz is important, and I assume beyond there we needn't bother. Is a 1-pole filter good enough to match in this range? Do we need 2? More?

I don't have much background in circuit analysis; my approximation of a 2kHz lowpass was just based on comparing against a recording with a couple of square waves at different frequencies until I found something that seemed to match reasonably well. At 1.6kHz it looked too weak, at 2.4kHz too strong, and that's about as far as I went. I kinda wish I could just make it produce white noise so I could compare a spectral analysis.

The other funny thing I forgot to mention, probably not worth emulating, but also the DAC in my FDS is clearly less than ideal. Check out this 0-63 saw wave:
Image
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: FDS modulator formula revision

Post by lidnariq »

rainwarrior wrote:I remember someone suggested that the volume on the FDS is controlled via PWM, are these additional high-frequency lowpass filters intended to smooth the effects of PWM?
The lowpass caused by the 0.1µF capacitor in the middle of the schematic is clearly intentional. But the rest? I can't tell if they're intentional, just artifacts of the amplifiers used, or serendipitous. Regardless, they'd help with PWM noise.
That curve in the region between 1kHz - 20kHz is important, and I assume beyond there we needn't bother. Is a 1-pole filter good enough to match in this range? Do we need 2? More?
Over the range of 10Hz to 7kHz, a 1-pole lowpass with a corner frequency of 1750 Hz looks like the best fit in simulation.
After that, I get an exceptionally good fit for everything above 10Hz by adding two poles at 20.5kHz. (BSS145 nMOSFET)
Switching to the 2N7002 moves the first pole down to 1670Hz and splits the other two poles to 19.1kHz and 160kHz.
Instead removing the 0.1µF capacitor produces two poles at 1.80kHz and 21.3kHz
Then again, 5% tolerance resistors with 20% tolerance capacitors means that many of these numbers vary from unit to unit. I think these are gaussian distributions, so they shouldn't ever hit the full ±25% worst-case range, but...
I don't have much background in circuit analysis; my approximation of a 2kHz lowpass was just based on comparing against a recording with a couple of square waves at different frequencies until I found something that seemed to match reasonably well.
Other versions of SPICE provide automatic pole-zero analysis. (not LTSpice, but, hey, it's free)
I kinda wish I could just make it produce white noise so I could compare a spectral analysis.
Impulse trains and frequency sweeps are good, and probably more attainable.
The other funny thing I forgot to mention, probably not worth emulating, but also the DAC in my FDS is clearly less than ideal. Check out this 0-63 saw wave
O_o
I can't remember whether that shape is more congruous with R2R or weighted binary DACs. And the down-only RC decay is just weird to me.
User avatar
rainwarrior
Posts: 8732
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: FDS modulator formula revision

Post by rainwarrior »

lidnariq wrote:I can't remember whether that shape is more congruous with R2R or weighted binary DACs. And the down-only RC decay is just weird to me.
The discontinuities do look like they're kind of at binary locations in the waveform, with decreasing intensity along the binary tree (i.e. strongest at 32, next strongest at 48/16, etc...). I dunno what that suggests for a DAC circuit though.

What do you mean by down-only RC decay? The discontinuity in the saw wave only goes down, the up part is smooth so you don't really see the decay. (I could show you a square wave if you want to see it going both ways.)
lidnariq wrote:Impulse trains and frequency sweeps are good, and probably more attainable.
Ah, yeah a frequency sweep would be easy to do. I will probably try that later.
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: FDS modulator formula revision

Post by lidnariq »

rainwarrior wrote:The discontinuities do look like they're kind of at binary locations in the waveform, with decreasing intensity along the binary tree (i.e. strongest at 32, next strongest at 48/16, etc...). I dunno what that suggests for a DAC circuit though.
It's kinda ... cauliflowery? Consistent with each next-most-significant bit being overrepresented in the binary weights. By a comparable amount for each bit? I see something very similar using an binary-weighted DAC where each next-most-significant bit is 55% of the previous (e.g. 10k, 5.5k, 3k, 1.7k, 920, 500). But getting it consistent across multiple scales probably implies R2R DAC; I can't see how else it'd be consistently the similar amount of error at the 31-32 edge and the 47-48 edge and the 55-56 edge.

With an R2R dac, I get something fairly similar to your graph using a "2R" of 3R. It'd be nice to make sure that your DAC is exceptional; the distortion there is kinda significant.
What do you mean by down-only RC decay? The discontinuity in the saw wave only goes down, the up part is smooth so you don't really see the decay. (I could show you a square wave if you want to see it going both ways.)
Well, I thought there'd be signficant smoothing on the top peak as well as the bottom peak of the sawtooth if this were simply a lowpass... but when I actually put a capacitor in my sim it turns out what you show is just an ordinary lowpass.
ccovell
Posts: 1045
Joined: Sun Mar 19, 2006 9:44 pm
Location: Japan
Contact:

Re: FDS modulator formula revision

Post by ccovell »

Keep this going, guys. Very neat analysis.

I remember way back in like 1997-8 or so, poking around the FDS' chips and finding the spot from the main chip where the unfiltered audio first comes out -- it was really harsh, so I'm glad there's some kind of filter in there somewhere.
jsr
Posts: 42
Joined: Thu Oct 07, 2004 2:47 am
Contact:

Re: FDS modulator formula revision

Post by jsr »

rainwarrior wrote:The other funny thing I forgot to mention, probably not worth emulating, but also the DAC in my FDS is clearly less than ideal. Check out this 0-63 saw wave:
This is actually one thing I wanted to investigate, because the DAC is a lot less linear in my unit:

Image

And this is very audible, especially when playing a sine wave. But then I assume the quality varies a lot among different units.
Post Reply