mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
164 lines
No EOL
4 KiB
C++
164 lines
No EOL
4 KiB
C++
#pragma once
|
|
#include "pch.h"
|
|
#include "NES/NesConsole.h"
|
|
#include "NES/NesConstants.h"
|
|
#include "NES/APU/ApuTimer.h"
|
|
#include "NES/APU/ApuLengthCounter.h"
|
|
#include "NES/INesMemoryHandler.h"
|
|
#include "Utilities/ISerializable.h"
|
|
#include "Utilities/Serializer.h"
|
|
|
|
class TriangleChannel : public INesMemoryHandler, public ISerializable
|
|
{
|
|
private:
|
|
static constexpr uint8_t _sequence[32] = { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
|
|
|
|
NesConsole* _console;
|
|
ApuLengthCounter _lengthCounter;
|
|
ApuTimer _timer;
|
|
|
|
uint8_t _linearCounter = 0;
|
|
uint8_t _linearCounterReload = 0;
|
|
bool _linearReloadFlag = false;
|
|
bool _linearControlFlag = false;
|
|
|
|
uint8_t _sequencePosition = 0;
|
|
|
|
public:
|
|
TriangleChannel(NesConsole* console) : _lengthCounter(AudioChannel::Triangle, console), _timer(AudioChannel::Triangle, console->GetSoundMixer())
|
|
{
|
|
_console = console;
|
|
}
|
|
|
|
void Run(uint32_t targetCycle)
|
|
{
|
|
while(_timer.Run(targetCycle)) {
|
|
//The sequencer is clocked by the timer as long as both the linear counter and the length counter are nonzero.
|
|
if(_lengthCounter.GetStatus() && _linearCounter > 0) {
|
|
_sequencePosition = (_sequencePosition + 1) & 0x1F;
|
|
|
|
if(_timer.GetPeriod() >= 2 || !_console->GetNesConfig().SilenceTriangleHighFreq) {
|
|
//Disabling the triangle channel when period is < 2 removes "pops" in the audio that are caused by the ultrasonic frequencies
|
|
//This is less "accurate" in terms of emulation, so this is an option (disabled by default)
|
|
_timer.AddOutput(_sequence[_sequencePosition]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Reset(bool softReset)
|
|
{
|
|
_timer.Reset(softReset);
|
|
_lengthCounter.Reset(softReset);
|
|
|
|
_linearCounter = 0;
|
|
_linearCounterReload = 0;
|
|
_linearReloadFlag = false;
|
|
_linearControlFlag = false;
|
|
|
|
_sequencePosition = 0;
|
|
}
|
|
|
|
void Serialize(Serializer& s) override
|
|
{
|
|
SV(_linearCounter); SV(_linearCounterReload); SV(_linearReloadFlag); SV(_linearControlFlag); SV(_sequencePosition);
|
|
SV(_timer);
|
|
SV(_lengthCounter);
|
|
}
|
|
|
|
void GetMemoryRanges(MemoryRanges &ranges) override
|
|
{
|
|
ranges.AddHandler(MemoryOperation::Write, 0x4008, 0x400B);
|
|
}
|
|
|
|
void WriteRam(uint16_t addr, uint8_t value) override
|
|
{
|
|
_console->GetApu()->Run();
|
|
|
|
switch(addr & 0x03) {
|
|
case 0: //4008
|
|
_linearControlFlag = (value & 0x80) == 0x80;
|
|
_linearCounterReload = value & 0x7F;
|
|
|
|
_lengthCounter.InitializeLengthCounter(_linearControlFlag);
|
|
break;
|
|
|
|
case 2: //400A
|
|
_timer.SetPeriod((_timer.GetPeriod() & 0xFF00) | value);
|
|
break;
|
|
|
|
case 3: //400B
|
|
_lengthCounter.LoadLengthCounter(value >> 3);
|
|
|
|
_timer.SetPeriod((_timer.GetPeriod() & 0xFF) | ((value & 0x07) << 8));
|
|
|
|
//Side effects Sets the linear counter reload flag
|
|
_linearReloadFlag = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TickLinearCounter()
|
|
{
|
|
if(_linearReloadFlag) {
|
|
_linearCounter = _linearCounterReload;
|
|
} else if(_linearCounter > 0) {
|
|
_linearCounter--;
|
|
}
|
|
|
|
if(!_linearControlFlag) {
|
|
_linearReloadFlag = false;
|
|
}
|
|
}
|
|
|
|
void TickLengthCounter()
|
|
{
|
|
_lengthCounter.TickLengthCounter();
|
|
}
|
|
|
|
void ReloadLengthCounter()
|
|
{
|
|
_lengthCounter.ReloadCounter();
|
|
}
|
|
|
|
void EndFrame()
|
|
{
|
|
_timer.EndFrame();
|
|
}
|
|
|
|
void SetEnabled(bool enabled)
|
|
{
|
|
_lengthCounter.SetEnabled(enabled);
|
|
}
|
|
|
|
bool GetStatus()
|
|
{
|
|
return _lengthCounter.GetStatus();
|
|
}
|
|
|
|
uint8_t GetOutput()
|
|
{
|
|
return _timer.GetLastOutput();
|
|
}
|
|
|
|
ApuTriangleState GetState()
|
|
{
|
|
ApuTriangleState state;
|
|
state.Enabled = _lengthCounter.IsEnabled();
|
|
state.Frequency = NesConstants::GetClockRate(NesApu::GetApuRegion(_console)) / 32.0 / (_timer.GetPeriod() + 1);
|
|
state.LengthCounter = _lengthCounter.GetState();
|
|
state.OutputVolume = _timer.GetLastOutput();
|
|
state.Period = _timer.GetPeriod();
|
|
state.Timer = _timer.GetTimer();
|
|
state.SequencePosition = _sequencePosition;
|
|
state.LinearCounterReload = _linearCounterReload;
|
|
state.LinearCounter = _linearCounter;
|
|
state.LinearReloadFlag = _linearReloadFlag;
|
|
return state;
|
|
}
|
|
|
|
uint8_t ReadRam(uint16_t addr) override
|
|
{
|
|
return 0;
|
|
}
|
|
}; |