Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement nonlinear FDS DAC from plgDavid #80

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 13 additions & 15 deletions xgm/devices/Sound/nes_fds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ void NES_FDS::SetStereoMix(int trk, INT16 mixl, INT16 mixr)
ITrackInfo *NES_FDS::GetTrackInfo(int trk)
{
trkinfo.max_volume = 32;
trkinfo.volume = last_vol;
trkinfo.key = last_vol > 0;
trkinfo.volume = vout;
trkinfo.key = vout > 0;
trkinfo._freq = last_freq;
trkinfo.freq = (double(last_freq) * clock) / (65536.0 * 64.0);
trkinfo.tone = env_out[EMOD];
Expand Down Expand Up @@ -79,7 +79,7 @@ void NES_FDS::Reset ()
master_io = true;
master_vol = 0;
last_freq = 0;
last_vol = 0;
vout = 0;

rc_accum = 0;

Expand Down Expand Up @@ -225,35 +225,33 @@ void NES_FDS::Tick (UINT32 clocks)
}

// output volume caps at 32
INT32 vol_out = env_out[EVOL];
if (vol_out > 32) vol_out = 32;
// store for trackinfo and for later DAC calculation
vout = env_out[EVOL];
if (vout > 32) vout = 32;

// final output
if (!wav_write)
fout = wave[TWAV][(phase[TWAV]>>16)&0x3F] * vol_out;
wout = wave[TWAV][(phase[TWAV]>>16)&0x3F];

// NOTE: during wav_halt, the unit still outputs (at phase 0)
// and volume can affect it if the first sample is nonzero.
// haven't worked out 100% of the conditions for volume to
// effect (vol envelope does not seem to run, but am unsure)
// but this implementation is very close to correct

// store for trackinfo
last_vol = vol_out;
}

UINT32 NES_FDS::Render (INT32 b[2])
{
// 8 bit approximation of master volume
const double MASTER_VOL = 2.4 * 1223.0; // max FDS vol vs max APU square (arbitrarily 1223)
const double MAX_OUT = 32.0f * 63.0f; // value that should map to master vol
const INT32 MASTER[4] = {
int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 2.0f),
int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 3.0f),
int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 4.0f),
int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 5.0f) };
const double MASTER[4] = {
((MASTER_VOL / MAX_OUT) * 2.0f / 2.0f),
((MASTER_VOL / MAX_OUT) * 2.0f / 3.0f),
((MASTER_VOL / MAX_OUT) * 2.0f / 4.0f),
((MASTER_VOL / MAX_OUT) * 2.0f / 5.0f) };

INT32 v = fout * MASTER[master_vol] >> 8;
INT32 v = INT32((FDS_LUT_norm[wout] * double(63 * vout) * MASTER[master_vol]) + 0.5);

// lowpass RC filter
INT32 rc_out = ((rc_accum * rc_k) + (v * rc_l)) >> RC_BITS;
Expand Down
74 changes: 72 additions & 2 deletions xgm/devices/Sound/nes_fds.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ class NES_FDS : public ISoundChip
double rate, clock;
int mask;
INT32 sm[2]; // stereo mix
INT32 fout; // current output
UINT32 wout; // current output
UINT32 vout; // current volume
TrackInfoFDS trkinfo;
int option[OPT_END];

bool master_io;
UINT32 master_vol;
UINT32 last_freq; // for trackinfo
UINT32 last_vol; // for trackinfo

// two wavetables
enum { TMOD=0, TWAV=1 };
Expand Down Expand Up @@ -61,6 +61,76 @@ class NES_FDS : public ISoundChip
INT32 rc_k;
INT32 rc_l;

// nonlinear DAC LUT
// data from plgDavid, normalized
// data capture shared from the NESDev server
const float FDS_LUT_norm[64] = {
0.0,
0.011465572752058506,
0.0233255997300148,
0.03682375326752663,
0.04673049971461296,
0.061058409512043,
0.07386837154626846,
0.09098339825868607,
0.09331251680850983,
0.10951442271471024,
0.12239760905504227,
0.1415075808763504,
0.1483096331357956,
0.16800136864185333,
0.18295270204544067,
0.20666064321994781,
0.18730713427066803,
0.20707780122756958,
0.2199448049068451,
0.2434738427400589,
0.24558208882808685,
0.26910510659217834,
0.28437408804893494,
0.312602698802948,
0.29783013463020325,
0.32291364669799805,
0.33768296241760254,
0.3677976429462433,
0.36768317222595215,
0.3981393277645111,
0.4163309335708618,
0.4529191851615906,
0.3774969279766083,
0.40523025393486023,
0.41825342178344727,
0.45081785321235657,
0.44415655732154846,
0.4759543240070343,
0.4921776056289673,
0.5303648114204407,
0.4969009757041931,
0.5296915769577026,
0.5450936555862427,
0.5845786333084106,
0.576058030128479,
0.6141541004180908,
0.6341322660446167,
0.6813235282897949,
0.6059473752975464,
0.6424090266227722,
0.6577944159507751,
0.7020274996757507,
0.6884633898735046,
0.7309963703155518,
0.7510766983032227,
0.802958607673645,
0.7523477077484131,
0.7961556911468506,
0.8153874278068542,
0.869225800037384,
0.8552623987197876,
0.9073793292045593,
0.9342948198318481,
1.0
};

public:
NES_FDS ();
virtual ~ NES_FDS ();
Expand Down