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 and updated FDS audio emulation #33

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
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
1 change: 1 addition & 0 deletions Core/Core.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
<ClInclude Include="NES\HdPacks\HdPackBuilder.h" />
<ClInclude Include="PCE\CdRom\PceCdSeekDelay.h" />
<ClInclude Include="Shared\ColorUtilities.h" />
<ClInclude Include="NES\Mappers\FDS\FDS_LUT_norm.h" />
<ClInclude Include="Shared\Utilities\emu2413.h" />
<ClInclude Include="NES\Mappers\Nintendo\FnsMmc1.h" />
<ClInclude Include="Shared\SaveStateCompatInfo.h" />
Expand Down
3 changes: 3 additions & 0 deletions Core/Core.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -2586,6 +2586,9 @@
<ClInclude Include="NES\Mappers\Nintendo\FnsMmc1.h">
<Filter>NES\Mappers\Nintendo</Filter>
</ClInclude>
<ClInclude Include="NES\Mappers\FDS\FDS_LUT_norm.h">
<Filter>NES\Mappers\FDS</Filter>
</ClInclude>
<ClInclude Include="SMS\SmsConsole.h">
<Filter>SMS</Filter>
</ClInclude>
Expand Down
1 change: 0 additions & 1 deletion Core/NES/Mappers/FDS/BaseFdsChannel.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ class BaseFdsChannel : public ISerializable
_timer--;
if(_timer == 0) {
ResetTimer();

if(_volumeIncrease && _gain < 32) {
_gain++;
} else if(!_volumeIncrease && _gain > 0) {
Expand Down
66 changes: 66 additions & 0 deletions Core/NES/Mappers/FDS/FDS_LUT_norm.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
constexpr 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
};
77 changes: 58 additions & 19 deletions Core/NES/Mappers/FDS/FdsAudio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "NES/APU/NesApu.h"
#include "NES/APU/BaseExpansionAudio.h"
#include "Utilities/Serializer.h"
#include "FDS_LUT_norm.h"

void FdsAudio::Serialize(Serializer& s)
{
Expand All @@ -16,44 +17,55 @@ void FdsAudio::Serialize(Serializer& s)
SVArray(_waveTable, 64);
SV(_volume);
SV(_mod);
SV(_waveWriteEnabled); SV(_disableEnvelopes); SV(_haltWaveform); SV(_masterVolume); SV(_waveOverflowCounter); SV(_wavePitch); SV(_wavePosition); SV(_lastOutput);
SV(_waveWriteEnabled); SV(_disableEnvelopes); SV(_haltWaveform); SV(_masterVolume); SV(_waveAccumulator); SV(_waveM2Counter); SV(_wavePitch); SV(_wavePosition); SV(_lastOutput); SV(_lastGain);
}

void FdsAudio::ClockAudio()
{
int frequency = _volume.GetFrequency();
uint16_t frequency = _volume.GetFrequency();
if(!_haltWaveform && !_disableEnvelopes) {
_volume.TickEnvelope();
if(_mod.TickEnvelope()) {
_mod.UpdateOutput(frequency);
}
}

if(_mod.TickModulator()) {
// TODO: check if modulator and wave units are ticked on the same M2 cycle
if(_mod.TickModulator(_haltWaveform)) {
//Modulator was ticked, update wave pitch
_mod.UpdateOutput(frequency);
}

if(_haltWaveform) {
_wavePosition = 0;
UpdateOutput();
} else {
UpdateOutput();
if(++_waveM2Counter == 16) {
if(_haltWaveform) {
_waveAccumulator = 0;
} else {

if(!_waveWriteEnabled) {
_waveAccumulator += (frequency * _mod.GetOutput()) & 0xFFFFF;
if(_waveAccumulator > 0xFFFFFF) {
_waveAccumulator -= 0x1000000;
}

if(frequency + _mod.GetOutput() > 0 && !_waveWriteEnabled) {
_waveOverflowCounter += frequency + _mod.GetOutput();
if(_waveOverflowCounter < frequency + _mod.GetOutput()) {
_wavePosition = (_wavePosition + 1) & 0x3F;
_wavePosition = (_waveAccumulator >> 18) & 0x3F;
}
}
_waveM2Counter = 0;
}
UpdateOutput();
}

void FdsAudio::UpdateOutput()
{
uint32_t level = std::min((int)_volume.GetGain(), 32) * WaveVolumeTable[_masterVolume];
uint8_t outputLevel = (_waveTable[_wavePosition] * level) / 1152;
// "Changes to the volume envelope only take effect while the wavetable
// pointer (top 6 bits of wave accumulator) is 0."
if(_wavePosition == 0) {
_lastGain = _volume.GetGain();
}
uint8_t level = std::min(_lastGain, uint8_t(32));

// volume level is PWM, but can be approximated linearly
uint8_t outputLevel = uint8_t(DACTable[_waveTable[_wavePosition]][_masterVolume] * float(level));

if(_lastOutput != outputLevel) {
_console->GetApu()->AddExpansionAudioDelta(AudioChannel::FDS, outputLevel - _lastOutput);
Expand All @@ -63,6 +75,15 @@ void FdsAudio::UpdateOutput()

FdsAudio::FdsAudio(NesConsole* console) : BaseExpansionAudio(console)
{
// initialize DAC LUT
// data comes from plgDavid's DC capture of an FDS's DAC output
// data capture shared from the NESDev server
// TODO: generate data based from FDS decap DAC schematics
for(int masterlevel = 0; masterlevel < 4; masterlevel++) {
for(int wavelevel = 0; wavelevel < 64; wavelevel++) {
DACTable[wavelevel][masterlevel] = (FDS_LUT_norm[wavelevel] * 64.0 * float(WaveVolumeTable[masterlevel])) / 1152.0;
}
}
}

uint8_t FdsAudio::ReadRegister(uint16_t addr)
Expand All @@ -79,9 +100,29 @@ uint8_t FdsAudio::ReadRegister(uint16_t addr)
} else if(addr == 0x4090) {
value &= 0xC0;
value |= _volume.GetGain();
} else if(addr == 0x4091) {
// Wave accumulator
value &= 0xC0;
value |= (_waveAccumulator >> 12) & 0xFF;
} else if(addr == 0x4092) {
value &= 0xC0;
value |= _mod.GetGain();
} else if(addr == 0x4093) {
// Mod accumulator
value &= 0xC0;
value |= (_mod.GetModAccumulator() >> 5) & 0x7F;
} else if(addr == 0x4094) {
// wave pitch intermediate result
value &= 0xC0;
value |= (_mod.GetOutput() >> 4) & 0xFF;
} else if(addr == 0x4096) {
// wavetable position
value &= 0xC0;
value |= _wavePosition & 0x3F;
} else if(addr == 0x4097) {
// mod counter value
value &= 0xC0;
value |= _mod.GetCounter() & 0x7F;
}

return value;
Expand Down Expand Up @@ -150,8 +191,7 @@ void FdsAudio::GetMapperStateEntries(vector<MapperStateEntry>& entries)
entries.push_back(MapperStateEntry("$4082/3.0-11", "Frequency", _volume.GetFrequency(), MapperStateValueType::Number16));
entries.push_back(MapperStateEntry("$4083.6", "Volume/Mod Envelopes Disabled", _disableEnvelopes, MapperStateValueType::Bool));

//todo emulation logic + this based on new info
//entries.push_back(MapperStateEntry("$4083.7", "Halt Wave Form", _haltWaveform, MapperStateValueType::Bool));
entries.push_back(MapperStateEntry("$4083.7", "Halt Wave Form", _haltWaveform, MapperStateValueType::Bool));

entries.push_back(MapperStateEntry("", "Gain", _volume.GetGain(), MapperStateValueType::Number8));

Expand All @@ -165,10 +205,9 @@ void FdsAudio::GetMapperStateEntries(vector<MapperStateEntry>& entries)

entries.push_back(MapperStateEntry("$4086/7.0-11", "Frequency", _mod.GetFrequency(), MapperStateValueType::Number16));

//todo emulation logic + this based on new info
//entries.push_back(MapperStateEntry("$4087.6", "???", false, MapperStateValueType::Bool));
entries.push_back(MapperStateEntry("$4087.6", "Force Tick Modulator", _mod.GetForceCarryOut(), MapperStateValueType::Bool));

entries.push_back(MapperStateEntry("$4087.7", "Disabled", _mod.IsModulationDisabled(), MapperStateValueType::Bool));
entries.push_back(MapperStateEntry("$4087.7", "Counter Disabled", _mod.IsModulationCounterDisabled(), MapperStateValueType::Bool));
entries.push_back(MapperStateEntry("", "Gain", _mod.GetGain(), MapperStateValueType::Number8));
entries.push_back(MapperStateEntry("", "Mod Output", std::to_string(_mod.GetOutput())));

Expand Down
5 changes: 4 additions & 1 deletion Core/NES/Mappers/FDS/FdsAudio.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class FdsAudio : public BaseExpansionAudio
{
private:
const uint32_t WaveVolumeTable[4] = { 36, 24, 17, 14 };
float DACTable[64][4] = {};

//Register values
uint8_t _waveTable[64] = {};
Expand All @@ -27,11 +28,13 @@ class FdsAudio : public BaseExpansionAudio
uint8_t _masterVolume = 0;

//Internal values
uint16_t _waveOverflowCounter = 0;
uint32_t _waveAccumulator = 0; //24-bit accumulator
uint8_t _waveM2Counter = 0;
int32_t _wavePitch = 0;
uint8_t _wavePosition = 0;

uint8_t _lastOutput = 0;
uint8_t _lastGain = 0;

protected:
void Serialize(Serializer& s) override;
Expand Down
Loading