Skip to content
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
6 changes: 6 additions & 0 deletions Core/NES/APU/ApuTimer.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ class ApuTimer : public ISerializable
_lastOutput = output;
}
}

__forceinline void SendVolume(uint8_t rawVolume)
{
//Only square channel needs raw volume, for linear mixing
_mixer->RawVolume(_channel, rawVolume);
}

int8_t GetLastOutput()
{
Expand Down
11 changes: 10 additions & 1 deletion Core/NES/APU/SquareChannel.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ class SquareChannel : public INesMemoryHandler, public ISerializable
{ 0, 0, 0, 0, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 0, 0 }
};

static constexpr int8_t _dutySequencesUnbiased[4][8] = {
{-1,-1,-1,-1,-1,-1,-1, 7 },
{-2,-2,-2,-2,-2,-2, 6, 6 },
{-4,-4,-4,-4, 4, 4, 4, 4 },
{ 2, 2, 2, 2, 2, 2,-6,-6 },
};

NesConsole* _console = nullptr;
ApuEnvelope _envelope;
Expand Down Expand Up @@ -84,8 +91,10 @@ class SquareChannel : public INesMemoryHandler, public ISerializable
{
if(IsMuted()) {
_timer.AddOutput(0);
_timer.SendVolume(0);
} else {
_timer.AddOutput(_dutySequences[_duty][_dutyPos] * _envelope.GetVolume());
_timer.AddOutput(_console->GetNesConfig().NonlinearSquareMixer ? _dutySequences[_duty][_dutyPos] * _envelope.GetVolume() : _dutySequencesUnbiased[_duty][_dutyPos] * _envelope.GetVolume());
_timer.SendVolume(_envelope.GetVolume());
}
}

Expand Down
14 changes: 11 additions & 3 deletions Core/NES/NesSoundMixer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,13 @@ double NesSoundMixer::GetChannelOutput(AudioChannel channel, bool forRightChanne
int16_t NesSoundMixer::GetOutputVolume(bool forRightChannel)
{
double squareOutput = GetChannelOutput(AudioChannel::Square1, forRightChannel) + GetChannelOutput(AudioChannel::Square2, forRightChannel);
double tndOutput = GetChannelOutput(AudioChannel::DMC, forRightChannel) + 2.7516713261 * GetChannelOutput(AudioChannel::Triangle, forRightChannel) + 1.8493587125 * GetChannelOutput(AudioChannel::Noise, forRightChannel);
double tndOutput = 2.7516713261 * GetChannelOutput(AudioChannel::Triangle, forRightChannel) + 1.8493587125 * GetChannelOutput(AudioChannel::Noise, forRightChannel) + GetChannelOutput(AudioChannel::DMC, forRightChannel);

uint16_t squareVolume = (uint16_t)((95.88 * 5000.0) / (8128.0 / squareOutput + 100.0));
uint16_t tndVolume = (uint16_t)((159.79 * 5000.0) / (22638.0 / tndOutput + 100.0));
//Non-linear square channel mixer flag
double squareVolume = _console->GetNesConfig().NonlinearSquareMixer ?
95.88 / (8128.0 / squareOutput + 100.0) * 5000.0
: squareOutput / 240.0 * squareSumFactor[_squareVolume[(int)AudioChannel::Square1] + _squareVolume[(int)AudioChannel::Square2]] * 0.258483 * 5000.0;
double tndVolume = 159.79 / (1.0 / (tndOutput / 22638.0) + 100.0) * 5000.0;

return (int16_t)(squareVolume + tndVolume +
GetChannelOutput(AudioChannel::FDS, forRightChannel) * 20 +
Expand All @@ -198,6 +201,11 @@ void NesSoundMixer::AddDelta(AudioChannel channel, uint32_t time, int16_t delta)
}
}

void NesSoundMixer::RawVolume(AudioChannel channel, uint8_t rawVolume)
{
_squareVolume[(int)channel] = rawVolume;
}

void NesSoundMixer::EndFrame(uint32_t time)
{
sort(_timestamps.begin(), _timestamps.end());
Expand Down
3 changes: 3 additions & 0 deletions Core/NES/NesSoundMixer.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class NesSoundMixer : public ISerializable
static constexpr uint32_t MaxSampleRate = 96000;
static constexpr uint32_t MaxSamplesPerFrame = MaxSampleRate / 60 * 4 * 2; //x4 to allow CPU overclocking up to 10x, x2 for panning stereo
static constexpr uint32_t MaxChannelCount = 11;
static constexpr double squareSumFactor[31] = { 1.0, 1.352455, 1.336216, 1.320361, 1.304878, 1.289755, 1.274978, 1.260535, 1.246416, 1.232610, 1.219107, 1.205896, 1.192968, 1.180314, 1.167927, 1.155796, 1.143915, 1.132276, 1.120871, 1.109693, 1.098737, 1.087994, 1.077460, 1.067127, 1.056991, 1.047046, 1.037286, 1.027706, 1.018302, 1.009068, 1.0 };

NesConsole* _console = nullptr;
SoundMixer* _mixer = nullptr;
Expand All @@ -36,6 +37,7 @@ class NesSoundMixer : public ISerializable
vector<uint32_t> _timestamps;
int16_t _channelOutput[MaxChannelCount][CycleLength] = {};
int16_t _currentOutput[MaxChannelCount] = {};
uint8_t _squareVolume[2] = {};

blip_t* _blipBufLeft = nullptr;
blip_t* _blipBufRight = nullptr;
Expand Down Expand Up @@ -66,6 +68,7 @@ class NesSoundMixer : public ISerializable

void PlayAudioBuffer(uint32_t cycle);
void AddDelta(AudioChannel channel, uint32_t time, int16_t delta);
void RawVolume(AudioChannel channel, uint8_t rawVolume);

void Serialize(Serializer& s) override;
};
2 changes: 2 additions & 0 deletions Core/Shared/SettingTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ struct AudioConfig
uint32_t SampleRate = 48000;
uint32_t AudioLatency = 60;

bool NonlinearSquareMixer = true;
bool MuteSoundInBackground = false;
bool ReduceSoundInBackground = true;
bool ReduceSoundInFastForward = false;
Expand Down Expand Up @@ -669,6 +670,7 @@ struct NesConfig
uint32_t PpuExtraScanlinesBeforeNmi = 0;
uint32_t PpuExtraScanlinesAfterNmi = 0;

bool NonlinearSquareMixer = true;
bool DisableNoiseModeFlag = false;
bool ReduceDmcPopping = false;
bool SilenceTriangleHighFreq = false;
Expand Down
3 changes: 3 additions & 0 deletions UI/Config/NesConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ public class NesConfig : BaseConfig<NesConfig>
[Reactive][MinMax(0, 1000)] public UInt32 PpuExtraScanlinesAfterNmi { get; set; } = 0;

//Audio
[Reactive] public bool NonlinearSquareMixer { get; set; } = true;
[Reactive] public bool DisableNoiseModeFlag { get; set; } = false;
[Reactive] public bool ReduceDmcPopping { get; set; } = false;
[Reactive] public bool SilenceTriangleHighFreq { get; set; } = false;
Expand Down Expand Up @@ -207,6 +208,7 @@ public void ApplyConfig()
PpuExtraScanlinesAfterNmi = PpuExtraScanlinesAfterNmi,
PpuExtraScanlinesBeforeNmi = PpuExtraScanlinesBeforeNmi,

NonlinearSquareMixer = NonlinearSquareMixer,
DisableNoiseModeFlag = DisableNoiseModeFlag,
ReduceDmcPopping = ReduceDmcPopping,
SilenceTriangleHighFreq = SilenceTriangleHighFreq,
Expand Down Expand Up @@ -344,6 +346,7 @@ public struct InteropNesConfig
public UInt32 PpuExtraScanlinesBeforeNmi;
public UInt32 PpuExtraScanlinesAfterNmi;

[MarshalAs(UnmanagedType.I1)] public bool NonlinearSquareMixer;
[MarshalAs(UnmanagedType.I1)] public bool DisableNoiseModeFlag;
[MarshalAs(UnmanagedType.I1)] public bool ReduceDmcPopping;
[MarshalAs(UnmanagedType.I1)] public bool SilenceTriangleHighFreq;
Expand Down
1 change: 1 addition & 0 deletions UI/Localization/resources.en.xml
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@
<Control ID="chkSwapDutyCycles">Swap square channels duty cycles (mimics old clones)</Control>
<Control ID="chkReverseDpcmBitOrder">Reverse DPCM bit order (higher or lower quality, depends on game)</Control>
<Control ID="chkSilenceTriangleHighFreq">Mute ultrasonic frequencies on triangle channel (reduces popping)</Control>
<Control ID="chkNonlinearSquareMixer">Use non-linear square channel mixer</Control>
<Control ID="chkReduceDmcPopping">Reduce popping sounds on the DMC channel</Control>
<Control ID="chkDisableNoiseModeFlag">Disable noise channel mode flag (mimics oldest Famicom models)</Control>
<Control ID="grpEffects">Effects</Control>
Expand Down
1 change: 1 addition & 0 deletions UI/Views/NesConfigView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
<StackPanel>
<c:OptionSection Header="{l:Translate grpGeneral}" Margin="0">
<StackPanel>
<CheckBox IsChecked="{Binding Config.NonlinearSquareMixer}" Content="{l:Translate chkNonlinearSquareMixer}" />
<CheckBox IsChecked="{Binding Config.ReduceDmcPopping}" Content="{l:Translate chkReduceDmcPopping}" />
<CheckBox IsChecked="{Binding Config.SilenceTriangleHighFreq}" Content="{l:Translate chkSilenceTriangleHighFreq}" />
<c:CheckBoxWarning IsChecked="{Binding Config.DisableNoiseModeFlag}" Text="{l:Translate chkDisableNoiseModeFlag}" />
Expand Down