mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
188 lines
4.6 KiB
C++
188 lines
4.6 KiB
C++
#include "pch.h"
|
|
#include "PCE/PcePsg.h"
|
|
#include "PCE/PceConsole.h"
|
|
#include "Shared/Emulator.h"
|
|
#include "Shared/EmuSettings.h"
|
|
#include "Shared/MessageManager.h"
|
|
#include "Shared/Audio/SoundMixer.h"
|
|
#include "Utilities/Audio/blip_buf.h"
|
|
#include "Utilities/Serializer.h"
|
|
|
|
PcePsg::PcePsg(Emulator* emu, PceConsole* console)
|
|
{
|
|
_emu = emu;
|
|
_console = console;
|
|
_soundMixer = emu->GetSoundMixer();
|
|
|
|
_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);
|
|
}
|
|
|
|
UpdateSoundOffset();
|
|
|
|
_leftChannel = blip_new(PcePsg::MaxSamples);
|
|
_rightChannel = blip_new(PcePsg::MaxSamples);
|
|
|
|
blip_clear(_leftChannel);
|
|
blip_clear(_rightChannel);
|
|
|
|
blip_set_rates(_leftChannel, PcePsg::PsgFrequency, PcePsg::SampleRate);
|
|
blip_set_rates(_rightChannel, PcePsg::PsgFrequency, PcePsg::SampleRate);
|
|
}
|
|
|
|
PcePsg::~PcePsg()
|
|
{
|
|
blip_delete(_leftChannel);
|
|
blip_delete(_rightChannel);
|
|
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();
|
|
|
|
switch(addr & 0x0F) {
|
|
case 0: _state.ChannelSelect = value & 0x07; break;
|
|
case 1:
|
|
_state.RightVolume = value & 0x0F;
|
|
_state.LeftVolume = (value >> 4) & 0x0F;
|
|
break;
|
|
|
|
case 2: case 3: case 4: case 5: case 6:
|
|
{
|
|
if(_state.ChannelSelect < 6) {
|
|
_channels[_state.ChannelSelect].Write(addr, value);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 7:
|
|
//Only channels 5/6 have noise
|
|
if(_state.ChannelSelect == 4 || _state.ChannelSelect == 5) {
|
|
_channels[_state.ChannelSelect].Write(addr, value);
|
|
}
|
|
break;
|
|
|
|
case 8: _state.LfoFrequency = value; break;
|
|
case 9: _state.LfoControl = value; break;
|
|
}
|
|
|
|
UpdateOutput(_emu->GetSettings()->GetPcEngineConfig());
|
|
}
|
|
|
|
void PcePsg::Run()
|
|
{
|
|
uint64_t clock = _console->GetMasterClock();
|
|
uint32_t clocksToRun = clock - _lastClock;
|
|
PcEngineConfig& cfg = _emu->GetSettings()->GetPcEngineConfig();
|
|
while(clocksToRun >= 6) {
|
|
uint32_t minTimer = clocksToRun / 6;
|
|
for(int i = 0; i < 6; i++) {
|
|
uint16_t timer = _channels[i].GetTimer();
|
|
if(timer != 0 && timer < minTimer) {
|
|
minTimer = timer;
|
|
}
|
|
}
|
|
|
|
for(int i = 0; i < 6; i++) {
|
|
_channels[i].Run(minTimer);
|
|
}
|
|
|
|
_clockCounter += minTimer;
|
|
clocksToRun -= minTimer * 6;
|
|
|
|
UpdateOutput(cfg);
|
|
}
|
|
|
|
if(_clockCounter >= 20000) {
|
|
PlayQueuedAudio();
|
|
}
|
|
|
|
_lastClock = clock - clocksToRun;
|
|
}
|
|
|
|
void PcePsg::UpdateOutput(PcEngineConfig& cfg)
|
|
{
|
|
int16_t leftOutput = 0;
|
|
int16_t rightOutput = 0;
|
|
for(int i = 0; i < 6; i++) {
|
|
PcePsgChannel& ch = _channels[i];
|
|
leftOutput += (int32_t)ch.GetOutput(true, _state.LeftVolume) * (int32_t)cfg.ChannelVol[i] / 100;
|
|
rightOutput += (int32_t)ch.GetOutput(false, _state.RightVolume) * (int32_t)cfg.ChannelVol[i] / 100;
|
|
}
|
|
|
|
if(_prevLeftOutput != leftOutput) {
|
|
blip_add_delta(_leftChannel, _clockCounter, leftOutput - _prevLeftOutput);
|
|
_prevLeftOutput = leftOutput;
|
|
}
|
|
|
|
if(_prevRightOutput != rightOutput) {
|
|
blip_add_delta(_rightChannel, _clockCounter, rightOutput - _prevRightOutput);
|
|
_prevRightOutput = rightOutput;
|
|
}
|
|
}
|
|
|
|
void PcePsg::UpdateSoundOffset()
|
|
{
|
|
uint8_t offset = _emu->GetSettings()->GetPcEngineConfig().UseHuC6280aAudio ? 0x10 : 0;
|
|
for(int i = 0; i < 6; i++) {
|
|
_channels[i].SetOutputOffset(offset);
|
|
}
|
|
}
|
|
|
|
void PcePsg::PlayQueuedAudio()
|
|
{
|
|
blip_end_frame(_leftChannel, _clockCounter);
|
|
blip_end_frame(_rightChannel, _clockCounter);
|
|
|
|
uint32_t sampleCount = (uint32_t)blip_read_samples(_leftChannel, _soundBuffer, PcePsg::MaxSamples, 1);
|
|
blip_read_samples(_rightChannel, _soundBuffer + 1, PcePsg::MaxSamples, 1);
|
|
_soundMixer->PlayAudioBuffer(_soundBuffer, sampleCount, PcePsg::SampleRate);
|
|
_clockCounter = 0;
|
|
|
|
UpdateSoundOffset();
|
|
}
|
|
|
|
void PcePsg::Serialize(Serializer& s)
|
|
{
|
|
SV(_state.ChannelSelect);
|
|
SV(_state.LeftVolume);
|
|
SV(_state.LfoControl);
|
|
SV(_state.LfoFrequency);
|
|
SV(_state.RightVolume);
|
|
|
|
if(s.GetFormat() != SerializeFormat::Map) {
|
|
//Hide these entries from the Lua API
|
|
SV(_lastClock);
|
|
SV(_prevLeftOutput);
|
|
SV(_prevRightOutput);
|
|
SV(_clockCounter);
|
|
}
|
|
|
|
for(int i = 0; i < 6; i++) {
|
|
SVI(_channels[i]);
|
|
}
|
|
}
|