PCE: Added LFO support

This commit is contained in:
Sour 2022-05-18 21:06:01 -04:00
parent c307f06449
commit 284c4f959c
6 changed files with 67 additions and 15 deletions

View file

@ -14,6 +14,10 @@ PcePsg::PcePsg(Emulator* emu)
_soundBuffer = new int16_t[PcePsg::MaxSamples * 2];
memset(_soundBuffer, 0, PcePsg::MaxSamples * 2 * sizeof(int16_t));
for(int i = 0; i < 6; i++) {
_channels[i].Init(i, this);
}
_leftChannel = blip_new(PcePsg::MaxSamples);
_rightChannel = blip_new(PcePsg::MaxSamples);
@ -31,6 +35,26 @@ PcePsg::~PcePsg()
delete[] _soundBuffer;
}
bool PcePsg::IsLfoEnabled()
{
return (_state.LfoControl & 0x80) == 0 && (_state.LfoControl & 0x03);
}
uint16_t PcePsg::GetLfoFrequency()
{
return _state.LfoFrequency ? _state.LfoFrequency : 0x100;
}
uint32_t PcePsg::GetLfoCh1PeriodOffset()
{
//When LFO is enabled, the period of channel 1 is altered
//based on channel's 2 current output, multiplied by either 1, 4 or 16
//depending on the value in the lower 2 bits of the LFO's control register
int shift = ((_state.LfoControl & 0x03) - 1) * 2;
int8_t ch2Out = _channels[1].GetState().CurrentOutput;
return (uint32_t)(ch2Out << shift);
}
void PcePsg::Write(uint16_t addr, uint8_t value)
{
Run();
@ -57,7 +81,6 @@ void PcePsg::Write(uint16_t addr, uint8_t value)
}
break;
//TODO, LFO is not implemented
case 8: _state.LfoFrequency = value; break;
case 9: _state.LfoControl = value; break;
}

View file

@ -33,6 +33,10 @@ public:
PcePsg(Emulator* emu);
~PcePsg();
bool IsLfoEnabled();
uint16_t GetLfoFrequency();
uint32_t GetLfoCh1PeriodOffset();
PcePsgState& GetState() { return _state; }
PcePsgChannelState& GetChannelState(uint8_t ch) { return _channels[ch].GetState(); }

View file

@ -1,5 +1,6 @@
#include "stdafx.h"
#include "PcePsgChannel.h"
#include "PCE/PcePsgChannel.h"
#include "PCE/PcePsg.h"
PcePsgChannel::PcePsgChannel()
{
@ -8,7 +9,13 @@ PcePsgChannel::PcePsgChannel()
}
}
uint16_t PcePsgChannel::GetPeriod()
void PcePsgChannel::Init(uint8_t index, PcePsg* psg)
{
_chIndex = index;
_psg = psg;
}
uint32_t PcePsgChannel::GetPeriod()
{
if(_state.DdaEnabled) {
return 0;
@ -19,10 +26,22 @@ uint16_t PcePsgChannel::GetPeriod()
return ((~_state.NoiseFrequency) & 0x1F) * 128;
}
} else {
if(_state.Frequency == 0) {
return 0xFFF + 1;
uint32_t period = _state.Frequency;
if(_chIndex == 0 && _psg->IsLfoEnabled()) {
//When enabled, LFO alters channel 1's frequency/period
period = (period + _psg->GetLfoCh1PeriodOffset()) & 0xFFF;
}
return _state.Frequency;
period = period ? period : 0x1000;
if(_chIndex == 1 && _psg->IsLfoEnabled()) {
//When enabled, LFO acts as a clock divider for channel 2
//which reduces its frequency (increases its period)
period *= _psg->GetLfoFrequency();
}
return period;
}
}
@ -30,7 +49,7 @@ void PcePsgChannel::Run(uint32_t clocks)
{
if(_state.Enabled) {
if(_state.DdaEnabled) {
_state.CurrentOutput = (int16_t)_state.DdaOutputValue - 0x10;
_state.CurrentOutput = (int8_t)_state.DdaOutputValue - 0x10;
} else {
_state.Timer -= clocks;
@ -44,9 +63,9 @@ void PcePsgChannel::Run(uint32_t clocks)
}
if(_state.NoiseEnabled) {
_state.CurrentOutput = (int16_t)_noiseData[_noiseAddr] - 0x10;
_state.CurrentOutput = (int8_t)_noiseData[_noiseAddr] - 0x10;
} else {
_state.CurrentOutput = (int16_t)_state.WaveData[_state.ReadAddr] - 0x10;
_state.CurrentOutput = (int8_t)_state.WaveData[_state.ReadAddr] - 0x10;
}
}
} else {
@ -65,7 +84,7 @@ int16_t PcePsgChannel::GetOutput(bool forLeftChannel, uint8_t masterVolume)
return 0;
}
return _state.CurrentOutput * volumeReduction[reductionFactor];
return (int16_t)_state.CurrentOutput * volumeReduction[reductionFactor];
}
uint16_t PcePsgChannel::GetTimer()

View file

@ -3,18 +3,24 @@
#include "PCE/PceTypes.h"
#include "Utilities/RandomHelper.h"
class PcePsg;
class PcePsgChannel
{
private:
PcePsgChannelState _state = {};
PcePsg* _psg = nullptr;
uint8_t _chIndex = 0;
uint8_t _noiseData[0x1000];
uint16_t _noiseAddr = 0;
uint16_t GetPeriod();
uint32_t GetPeriod();
public:
PcePsgChannel();
void Init(uint8_t index, PcePsg* psg);
PcePsgChannelState& GetState() { return _state; }
void Run(uint32_t clocks);

View file

@ -204,8 +204,8 @@ struct PcePsgChannelState
uint8_t WriteAddr;
uint8_t ReadAddr;
uint16_t Timer;
int16_t CurrentOutput;
uint32_t Timer;
int8_t CurrentOutput;
//Channel 5 & 6 only
bool NoiseEnabled;

View file

@ -1449,8 +1449,8 @@ namespace Mesen.Interop
public byte WriteAddr;
public byte ReadAddr;
public UInt16 Timer;
public Int16 CurrentOutput;
public UInt32 Timer;
public byte CurrentOutput;
//Channel 5 & 6 only
[MarshalAs(UnmanagedType.I1)] public bool NoiseEnabled;