mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
199 lines
5.2 KiB
C++
199 lines
5.2 KiB
C++
#include "pch.h"
|
|
#include "SMS/SmsPsg.h"
|
|
#include "SMS/SmsFmAudio.h"
|
|
#include "Utilities/Serializer.h"
|
|
|
|
SmsPsg::SmsPsg(Emulator* emu, SmsConsole* console)
|
|
{
|
|
_console = console;
|
|
_soundMixer = emu->GetSoundMixer();
|
|
_settings = emu->GetSettings();
|
|
|
|
_state.Noise.Lfsr = 0x8000;
|
|
_state.Noise.Volume = 0x0F;
|
|
_state.Tone[0].Volume = 0x0F;
|
|
_state.Tone[1].Volume = 0x0F;
|
|
_state.Tone[2].Volume = 0x0F;
|
|
|
|
_soundBuffer = new int16_t[SmsPsg::MaxSamples * 2];
|
|
memset(_soundBuffer, 0, SmsPsg::MaxSamples * 2 * sizeof(int16_t));
|
|
|
|
_leftChannel = blip_new(SmsPsg::MaxSamples);
|
|
_rightChannel = blip_new(SmsPsg::MaxSamples);
|
|
|
|
blip_clear(_leftChannel);
|
|
blip_clear(_rightChannel);
|
|
|
|
blip_set_rates(_leftChannel, _console->GetMasterClockRate(), SmsPsg::SampleRate);
|
|
blip_set_rates(_rightChannel, _console->GetMasterClockRate(), SmsPsg::SampleRate);
|
|
}
|
|
|
|
void SmsPsg::SetRegion(ConsoleRegion region)
|
|
{
|
|
blip_clear(_leftChannel);
|
|
blip_clear(_rightChannel);
|
|
|
|
blip_set_rates(_leftChannel, _console->GetMasterClockRate(), SmsPsg::SampleRate);
|
|
blip_set_rates(_rightChannel, _console->GetMasterClockRate(), SmsPsg::SampleRate);
|
|
}
|
|
|
|
void SmsPsg::RunNoise(SmsNoiseChannelState& noise)
|
|
{
|
|
if(noise.Timer == 0 || --noise.Timer == 0) {
|
|
noise.LfsrInputBit ^= 1;
|
|
switch(noise.Control & 0x03) {
|
|
case 0: noise.Timer = 0x10; break;
|
|
case 1: noise.Timer = 0x20; break;
|
|
case 2: noise.Timer = 0x40; break;
|
|
case 3: noise.Timer = _state.Tone[2].ReloadValue; break;
|
|
}
|
|
|
|
if(noise.LfsrInputBit) {
|
|
bool useBit3 = noise.Control & 0x04;
|
|
uint16_t newBit = (noise.Lfsr & 0x01) ^ (useBit3 & ((noise.Lfsr >> 3) & 0x01));
|
|
noise.Lfsr = (newBit << 15) | (noise.Lfsr >> 1);
|
|
noise.Output = noise.Lfsr & 0x01;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SmsPsg::Run()
|
|
{
|
|
uint64_t runTo = _console->GetMasterClock();
|
|
uint32_t* volumes = _console->GetModel() == SmsModel::ColecoVision ? _settings->GetCvConfig().ChannelVolumes : _settings->GetSmsConfig().ChannelVolumes;
|
|
|
|
while(_masterClock + 16 < runTo) {
|
|
int16_t outputLeft = 0;
|
|
int16_t outputRight = 0;
|
|
int16_t channelOutput;
|
|
for(int i = 0; i < 3; i++) {
|
|
if(_state.Tone[i].Timer == 0 || --_state.Tone[i].Timer == 0) {
|
|
_state.Tone[i].Output ^= 1;
|
|
_state.Tone[i].Timer = _state.Tone[i].ReloadValue;
|
|
}
|
|
|
|
channelOutput = _state.Tone[i].Output * _volumeLut[_state.Tone[i].Volume] * volumes[i] / 100;
|
|
if(_state.GameGearPanningReg & (0x01 << i)) {
|
|
outputRight += channelOutput;
|
|
}
|
|
if(_state.GameGearPanningReg & (0x10 << i)) {
|
|
outputLeft += channelOutput;
|
|
}
|
|
}
|
|
|
|
RunNoise(_state.Noise);
|
|
channelOutput = _state.Noise.Output * _volumeLut[_state.Noise.Volume] * volumes[3] / 100;
|
|
if(_state.GameGearPanningReg & 0x08) {
|
|
outputRight += channelOutput;
|
|
}
|
|
if(_state.GameGearPanningReg & 0x80) {
|
|
outputLeft += channelOutput;
|
|
}
|
|
|
|
_clockCounter += 16;
|
|
_masterClock += 16;
|
|
|
|
if(_prevOutputLeft != outputLeft || _prevOutputRight != outputRight) {
|
|
blip_add_delta(_leftChannel, _clockCounter, outputLeft - _prevOutputLeft);
|
|
blip_add_delta(_rightChannel, _clockCounter, outputRight - _prevOutputRight);
|
|
_prevOutputLeft = outputLeft;
|
|
_prevOutputRight = outputRight;
|
|
}
|
|
}
|
|
|
|
if(_clockCounter >= 20000) {
|
|
PlayQueuedAudio();
|
|
}
|
|
}
|
|
|
|
void SmsPsg::PlayQueuedAudio()
|
|
{
|
|
blip_end_frame(_leftChannel, _clockCounter);
|
|
blip_end_frame(_rightChannel, _clockCounter);
|
|
|
|
uint32_t sampleCount = (uint32_t)blip_read_samples(_leftChannel, _soundBuffer, SmsPsg::MaxSamples, 1);
|
|
blip_read_samples(_rightChannel, _soundBuffer + 1, SmsPsg::MaxSamples, 1);
|
|
|
|
if(_console->IsPsgAudioMuted()) {
|
|
memset(_soundBuffer, 0, SmsPsg::MaxSamples * 2 * sizeof(int16_t));
|
|
}
|
|
|
|
_soundMixer->PlayAudioBuffer(_soundBuffer, sampleCount, SmsPsg::SampleRate);
|
|
_clockCounter = 0;
|
|
}
|
|
|
|
void SmsPsg::Write(uint8_t value)
|
|
{
|
|
Run();
|
|
|
|
if(value & 0x80) {
|
|
_state.SelectedReg = (value >> 4) & 0x07;
|
|
}
|
|
|
|
uint8_t channel = (_state.SelectedReg >> 1) & 0x03;
|
|
bool volReg = _state.SelectedReg & 0x01;
|
|
|
|
switch(channel) {
|
|
case 0: case 1: case 2:
|
|
if(volReg) {
|
|
_state.Tone[channel].Volume = value & 0x0F;
|
|
} else {
|
|
if(value & 0x80) {
|
|
_state.Tone[channel].ReloadValue = (_state.Tone[channel].ReloadValue & 0x3F0) | (value & 0x0F);
|
|
} else {
|
|
_state.Tone[channel].ReloadValue = (_state.Tone[channel].ReloadValue & 0x0F) | ((value & 0x3F) << 4);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
if(volReg) {
|
|
_state.Noise.Volume = value & 0x0F;
|
|
} else {
|
|
_state.Noise.Control = value & 0x07;
|
|
_state.Noise.Lfsr = 0x8000;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SmsPsg::WritePanningReg(uint8_t value)
|
|
{
|
|
_state.GameGearPanningReg = value;
|
|
}
|
|
|
|
void SmsPsg::Serialize(Serializer& s)
|
|
{
|
|
if(s.IsSaving()) {
|
|
Run();
|
|
} else {
|
|
_clockCounter = 0;
|
|
blip_clear(_leftChannel);
|
|
blip_clear(_rightChannel);
|
|
}
|
|
|
|
SV(_state.SelectedReg);
|
|
SV(_state.GameGearPanningReg);
|
|
|
|
SV(_state.Noise.Timer);
|
|
SV(_state.Noise.Lfsr);
|
|
SV(_state.Noise.LfsrInputBit);
|
|
SV(_state.Noise.Control);
|
|
SV(_state.Noise.Output);
|
|
SV(_state.Noise.Volume);
|
|
|
|
for(int i = 0; i < 3; i++) {
|
|
SVI(_state.Tone[i].ReloadValue);
|
|
SVI(_state.Tone[i].Timer);
|
|
SVI(_state.Tone[i].Output);
|
|
SVI(_state.Tone[i].Volume);
|
|
}
|
|
|
|
if(s.GetFormat() != SerializeFormat::Map) {
|
|
//Hide these entries from the Lua API
|
|
SV(_masterClock);
|
|
SV(_clockCounter);
|
|
SV(_prevOutputLeft);
|
|
SV(_prevOutputRight);
|
|
}
|
|
}
|