mirror of
https://github.com/libretro/Mesen.git
synced 2024-12-13 20:42:24 +00:00
116 lines
2.9 KiB
C++
116 lines
2.9 KiB
C++
#pragma once
|
|
#include "stdafx.h"
|
|
#include "APU.h"
|
|
#include "IMemoryHandler.h"
|
|
#include "ApuEnvelope.h"
|
|
|
|
class TriangleChannel : public ApuLengthCounter
|
|
{
|
|
private:
|
|
const 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 };
|
|
|
|
uint8_t _linearCounter = 0;
|
|
uint8_t _linearCounterReload = 0;
|
|
bool _linearReloadFlag = false;
|
|
bool _linearControlFlag = false;
|
|
|
|
uint8_t _sequencePosition = 0;
|
|
|
|
protected:
|
|
void Clock() override
|
|
{
|
|
//The sequencer is clocked by the timer as long as both the linear counter and the length counter are nonzero.
|
|
if(_lengthCounter > 0 && _linearCounter > 0) {
|
|
_sequencePosition = (_sequencePosition + 1) & 0x1F;
|
|
|
|
if(_period >= 2 || !EmulationSettings::CheckFlag(EmulationFlags::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)
|
|
AddOutput(_sequence[_sequencePosition]);
|
|
}
|
|
}
|
|
}
|
|
|
|
public:
|
|
TriangleChannel(AudioChannel channel, SoundMixer* mixer) : ApuLengthCounter(channel, mixer)
|
|
{
|
|
}
|
|
|
|
virtual void Reset(bool softReset) override
|
|
{
|
|
ApuLengthCounter::Reset(softReset);
|
|
|
|
_linearCounter = 0;
|
|
_linearCounterReload = 0;
|
|
_linearReloadFlag = false;
|
|
_linearControlFlag = false;
|
|
|
|
_sequencePosition = 0;
|
|
}
|
|
|
|
virtual void StreamState(bool saving) override
|
|
{
|
|
ApuLengthCounter::StreamState(saving);
|
|
|
|
Stream(_linearCounter, _linearCounterReload, _linearReloadFlag, _linearControlFlag, _sequencePosition);
|
|
}
|
|
|
|
void GetMemoryRanges(MemoryRanges &ranges) override
|
|
{
|
|
ranges.AddHandler(MemoryOperation::Write, 0x4008, 0x400B);
|
|
}
|
|
|
|
void WriteRAM(uint16_t addr, uint8_t value) override
|
|
{
|
|
APU::StaticRun();
|
|
switch(addr & 0x03) {
|
|
case 0: //4008
|
|
_linearControlFlag = (value & 0x80) == 0x80;
|
|
_linearCounterReload = value & 0x7F;
|
|
|
|
InitializeLengthCounter(_linearControlFlag);
|
|
break;
|
|
|
|
case 2: //400A
|
|
_period &= ~0x00FF;
|
|
_period |= value;
|
|
break;
|
|
|
|
case 3: //400B
|
|
LoadLengthCounter(value >> 3);
|
|
|
|
_period &= ~0xFF00;
|
|
_period |= (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;
|
|
}
|
|
}
|
|
|
|
ApuTriangleState GetState()
|
|
{
|
|
ApuTriangleState state;
|
|
state.Enabled = _enabled;
|
|
state.Frequency = CPU::GetClockRate(GetNesModel()) / 32.0 / (_period + 1);
|
|
state.LengthCounter = ApuLengthCounter::GetState();
|
|
state.OutputVolume = _lastOutput;
|
|
state.Period = _period;
|
|
state.Timer = _timer;
|
|
state.SequencePosition = _sequencePosition;
|
|
return state;
|
|
}
|
|
}; |