From f3c346024eb6a25a070017189fa119e23a930f33 Mon Sep 17 00:00:00 2001 From: Persune Date: Mon, 8 Apr 2024 15:05:35 +0800 Subject: [PATCH] Implement nonlinear FDS DAC from plgDavid --- xgm/devices/Sound/nes_fds.cpp | 28 ++++++------- xgm/devices/Sound/nes_fds.h | 74 ++++++++++++++++++++++++++++++++++- 2 files changed, 85 insertions(+), 17 deletions(-) diff --git a/xgm/devices/Sound/nes_fds.cpp b/xgm/devices/Sound/nes_fds.cpp index 9d74c62f..627bf376 100644 --- a/xgm/devices/Sound/nes_fds.cpp +++ b/xgm/devices/Sound/nes_fds.cpp @@ -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]; @@ -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; @@ -225,21 +225,19 @@ 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]) @@ -247,13 +245,13 @@ 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; diff --git a/xgm/devices/Sound/nes_fds.h b/xgm/devices/Sound/nes_fds.h index 212e472b..0e969c3e 100644 --- a/xgm/devices/Sound/nes_fds.h +++ b/xgm/devices/Sound/nes_fds.h @@ -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 }; @@ -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 ();