MSU1 support

This commit is contained in:
Sour 2019-11-01 21:15:11 -04:00
parent 305ec6e446
commit 6b85b6a579
15 changed files with 427 additions and 16 deletions

View file

@ -33,6 +33,7 @@
#include "MovieManager.h"
#include "SystemActionManager.h"
#include "SpcHud.h"
#include "Msu1.h"
#include "../Utilities/Serializer.h"
#include "../Utilities/Timer.h"
#include "../Utilities/VirtualFile.h"
@ -241,6 +242,7 @@ void Console::Stop(bool sendNotification)
_controlManager.reset();
_memoryManager.reset();
_dmaController.reset();
_msu1.reset();
_soundMixer->StopAudio(true);
@ -331,6 +333,8 @@ bool Console::LoadRom(VirtualFile romFile, VirtualFile patchFile, bool stopRom)
_dmaController.reset(new DmaController(_memoryManager.get()));
_spc.reset(new Spc(this));
_msu1.reset(Msu1::Init(romFile, _spc.get()));
if(_cart->GetSpcData()) {
_spc->LoadSpcFile(_cart->GetSpcData());
_spcHud.reset(new SpcHud(this, _cart->GetSpcData()));
@ -553,6 +557,9 @@ void Console::Serialize(ostream &out)
serializer.Stream(_cart.get());
serializer.Stream(_controlManager.get());
serializer.Stream(_spc.get());
if(_msu1) {
serializer.Stream(_msu1.get());
}
serializer.Save(out);
}
@ -567,7 +574,9 @@ void Console::Deserialize(istream &in, uint32_t fileFormatVersion)
serializer.Stream(_cart.get());
serializer.Stream(_controlManager.get());
serializer.Stream(_spc.get());
if(_msu1) {
serializer.Stream(_msu1.get());
}
_notificationManager->SendNotification(ConsoleNotificationType::StateLoaded);
}
@ -666,6 +675,11 @@ shared_ptr<DmaController> Console::GetDmaController()
return _dmaController;
}
shared_ptr<Msu1> Console::GetMsu1()
{
return _msu1;
}
shared_ptr<Debugger> Console::GetDebugger(bool autoStart)
{
shared_ptr<Debugger> debugger = _debugger;

View file

@ -30,6 +30,7 @@ class MovieManager;
class SpcHud;
class FrameLimiter;
class DebugStats;
class Msu1;
enum class MemoryOperationType;
enum class SnesMemoryType;
@ -49,6 +50,8 @@ private:
shared_ptr<InternalRegisters> _internalRegisters;
shared_ptr<ControlManager> _controlManager;
shared_ptr<DmaController> _dmaController;
shared_ptr<Msu1> _msu1;
shared_ptr<Debugger> _debugger;
@ -143,6 +146,7 @@ public:
shared_ptr<InternalRegisters> GetInternalRegisters();
shared_ptr<ControlManager> GetControlManager();
shared_ptr<DmaController> GetDmaController();
shared_ptr<Msu1> GetMsu1();
shared_ptr<Debugger> GetDebugger(bool autoStart = true);
void StopDebugger();

View file

@ -114,6 +114,7 @@
<ClInclude Include="MessageType.h" />
<ClInclude Include="MovieDataMessage.h" />
<ClInclude Include="MovieTypes.h" />
<ClInclude Include="Msu1.h" />
<ClInclude Include="Multitap.h" />
<ClInclude Include="MesenMovie.h" />
<ClInclude Include="MovieManager.h" />
@ -124,6 +125,7 @@
<ClInclude Include="NetMessage.h" />
<ClInclude Include="NtscFilter.h" />
<ClInclude Include="Obc1.h" />
<ClInclude Include="PcmReader.h" />
<ClInclude Include="PlayerListMessage.h" />
<ClInclude Include="PpuTools.h" />
<ClInclude Include="RecordedRomTest.h" />
@ -261,12 +263,14 @@
<ClCompile Include="MessageManager.cpp" />
<ClCompile Include="MovieManager.cpp" />
<ClCompile Include="MovieRecorder.cpp" />
<ClCompile Include="Msu1.cpp" />
<ClCompile Include="Multitap.cpp" />
<ClCompile Include="NecDsp.cpp" />
<ClCompile Include="NecDspDisUtils.cpp" />
<ClCompile Include="NotificationManager.cpp" />
<ClCompile Include="NtscFilter.cpp" />
<ClCompile Include="Obc1.cpp" />
<ClCompile Include="PcmReader.cpp" />
<ClCompile Include="Ppu.cpp" />
<ClCompile Include="PpuTools.cpp" />
<ClCompile Include="RecordedRomTest.cpp" />

View file

@ -479,6 +479,12 @@
<ClInclude Include="IDebugger.h">
<Filter>Debugger\Debuggers</Filter>
</ClInclude>
<ClInclude Include="Msu1.h">
<Filter>SNES\Coprocessors\MSU1</Filter>
</ClInclude>
<ClInclude Include="PcmReader.h">
<Filter>SNES\Coprocessors\MSU1</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp" />
@ -761,6 +767,12 @@
<ClCompile Include="GameServer.cpp">
<Filter>Netplay</Filter>
</ClCompile>
<ClCompile Include="PcmReader.cpp">
<Filter>SNES\Coprocessors\MSU1</Filter>
</ClCompile>
<ClCompile Include="Msu1.cpp">
<Filter>SNES\Coprocessors\MSU1</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="SNES">
@ -829,5 +841,8 @@
<Filter Include="Netplay\Messages">
<UniqueIdentifier>{c58f689e-a6af-4b9c-bb77-0be9d2b1142c}</UniqueIdentifier>
</Filter>
<Filter Include="SNES\Coprocessors\MSU1">
<UniqueIdentifier>{9fb8db1f-a7b5-4570-99ca-bbc15d262632}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>

View file

@ -272,7 +272,7 @@ uint8_t MemoryManager::ReadDma(uint32_t addr, bool forBusA)
uint8_t value;
IMemoryHandler* handler = _mappings.GetHandler(addr);
if(handler) {
if(forBusA && handler == _registerHandlerB.get()) {
if(forBusA && addr >= 0x2100 && addr <= 0x21FF && handler == _registerHandlerB.get()) {
//Trying to read from bus B using bus A returns open bus
value = _openBus;
} else if(handler == _registerHandlerA.get()) {

117
Core/Msu1.cpp Normal file
View file

@ -0,0 +1,117 @@
#include "stdafx.h"
#include "Msu1.h"
#include "Spc.h"
#include "../Utilities/Serializer.h"
#include "../Utilities/FolderUtilities.h"
Msu1* Msu1::Init(VirtualFile romFile, Spc* spc)
{
string romFolder = romFile.GetFolderPath();
string romName = FolderUtilities::GetFilename(romFile.GetFileName(), false);
if(ifstream(FolderUtilities::CombinePath(romFolder, romName + ".msu"))) {
return new Msu1(romFile, spc);
} else if(ifstream(FolderUtilities::CombinePath(romFolder, "msu1.rom"))) {
return new Msu1(romFile, spc);
} else {
return nullptr;
}
}
Msu1::Msu1(VirtualFile romFile, Spc* spc)
{
_spc = spc;
_romFolder = romFile.GetFolderPath();
_romName = FolderUtilities::GetFilename(romFile.GetFileName(), false);
_dataFile.open(FolderUtilities::CombinePath(_romFolder, _romName) + ".msu", ios::binary);
if(_dataFile) {
_trackPath = FolderUtilities::CombinePath(_romFolder, _romName);
} else {
_dataFile.open(FolderUtilities::CombinePath(_romFolder, "msu1.rom"), ios::binary);
_trackPath = FolderUtilities::CombinePath(_romFolder, "track");
}
if(_dataFile) {
_dataFile.seekg(0, ios::end);
_dataSize = (uint32_t)_dataFile.tellg();
} else {
_dataSize = 0;
}
}
void Msu1::Write(uint16_t addr, uint8_t value)
{
switch(addr) {
case 0x2000: _tmpDataPointer = (_tmpDataPointer & 0xFFFFFF00) | value; break;
case 0x2001: _tmpDataPointer = (_tmpDataPointer & 0xFFFF00FF) | (value << 8); break;
case 0x2002: _tmpDataPointer = (_tmpDataPointer & 0xFF00FFFF) | (value << 16); break;
case 0x2003:
_tmpDataPointer = (_tmpDataPointer & 0x00FFFFFF) | (value << 24);
_dataPointer = _tmpDataPointer;
_dataFile.seekg(_dataPointer, ios::beg);
break;
case 0x2004: _trackSelect = (_trackSelect & 0xFF00) | value; break;
case 0x2005:
_trackSelect = (_trackSelect & 0xFF) | (value << 8);
LoadTrack();
break;
case 0x2006: _volume = value; break;
case 0x2007:
if(!_audioBusy) {
_repeat = (value & 0x02) != 0;
_paused = (value & 0x01) == 0;
_pcmReader.SetLoopFlag(_repeat);
}
break;
}
}
uint8_t Msu1::Read(uint16_t addr)
{
switch(addr) {
case 0x2000:
//status
return (_dataBusy << 7) | (_audioBusy << 6) | (_repeat << 5) | ((!_paused) << 4) | (_trackMissing << 3) | 0x01;
case 0x2001:
//data
if(!_dataBusy && _dataPointer < _dataSize) {
_dataPointer++;
return (uint8_t)_dataFile.get();
}
return 0;
case 0x2002: return 'S';
case 0x2003: return '-';
case 0x2004: return 'M';
case 0x2005: return 'S';
case 0x2006: return 'U';
case 0x2007: return '1';
}
return 0;
}
void Msu1::MixAudio(int16_t* buffer, size_t sampleCount, uint32_t sampleRate)
{
if(!_paused) {
_pcmReader.SetSampleRate(sampleRate);
_pcmReader.ApplySamples(buffer, sampleCount, _spc->IsMuted() ? 0 : _volume);
}
}
void Msu1::LoadTrack(uint32_t startOffset)
{
_trackMissing = !_pcmReader.Init(_trackPath + "-" + std::to_string(_trackSelect) + ".pcm", _repeat, startOffset);
}
void Msu1::Serialize(Serializer &s)
{
uint32_t offset = _pcmReader.GetOffset();
s.Stream(_trackSelect, _tmpDataPointer, _dataPointer, _repeat, _paused, _volume, _trackMissing, _audioBusy, _dataBusy, offset);
if(!s.IsSaving()) {
_dataFile.seekg(_dataPointer, ios::beg);
LoadTrack(offset);
}
}

44
Core/Msu1.h Normal file
View file

@ -0,0 +1,44 @@
#pragma once
#include "stdafx.h"
#include "PcmReader.h"
#include "../Utilities/ISerializable.h"
#include "../Utilities/VirtualFile.h"
class Spc;
class Msu1 : public ISerializable
{
private:
Spc * _spc;
PcmReader _pcmReader;
uint8_t _volume = 100;
uint16_t _trackSelect = 0;
uint32_t _tmpDataPointer = 0;
uint32_t _dataPointer = 0;
string _romName;
string _romFolder;
string _trackPath;
bool _repeat = false;
bool _paused = false;
bool _audioBusy = false; //Always false
bool _dataBusy = false; //Always false
bool _trackMissing = false;
ifstream _dataFile;
uint32_t _dataSize;
void LoadTrack(uint32_t startOffset = 8);
public:
Msu1(VirtualFile romFile, Spc* spc);
static Msu1* Init(VirtualFile romFile, Spc* spc);
void Write(uint16_t addr, uint8_t value);
uint8_t Read(uint16_t addr);
void MixAudio(int16_t *buffer, size_t sampleCount, uint32_t sampleRate);
void Serialize(Serializer &s);
};

150
Core/PcmReader.cpp Normal file
View file

@ -0,0 +1,150 @@
#include "stdafx.h"
#include "PcmReader.h"
#include "../Utilities/VirtualFile.h"
PcmReader::PcmReader()
{
_done = true;
_loop = false;
_prevLeft = 0;
_prevRight = 0;
_loopOffset = 8;
_sampleRate = 48000;
_blipLeft = blip_new(10000);
_blipRight = blip_new(10000);
_outputBuffer = new int16_t[20000];
}
PcmReader::~PcmReader()
{
blip_delete(_blipLeft);
blip_delete(_blipRight);
delete[] _outputBuffer;
}
bool PcmReader::Init(string filename, bool loop, uint32_t startOffset)
{
if(_file) {
_file.close();
}
_file.open(filename, ios::binary);
if(_file) {
_file.seekg(0, ios::end);
_fileSize = (uint32_t)_file.tellg();
if(_fileSize < 12) {
return false;
}
_file.seekg(4, ios::beg);
uint32_t loopOffset = (uint8_t)_file.get();
loopOffset |= ((uint8_t)_file.get()) << 8;
loopOffset |= ((uint8_t)_file.get()) << 16;
loopOffset |= ((uint8_t)_file.get()) << 24;
_loopOffset = (uint32_t)loopOffset;
_prevLeft = 0;
_prevRight = 0;
_done = false;
_loop = loop;
_fileOffset = startOffset;
_file.seekg(_fileOffset, ios::beg);
blip_clear(_blipLeft);
blip_clear(_blipRight);
return true;
} else {
_done = true;
return false;
}
}
bool PcmReader::IsPlaybackOver()
{
return _done;
}
void PcmReader::SetSampleRate(uint32_t sampleRate)
{
if(sampleRate != _sampleRate) {
_sampleRate = sampleRate;
blip_clear(_blipLeft);
blip_clear(_blipRight);
blip_set_rates(_blipLeft, PcmReader::PcmSampleRate, _sampleRate);
blip_set_rates(_blipRight, PcmReader::PcmSampleRate, _sampleRate);
}
}
void PcmReader::SetLoopFlag(bool loop)
{
_loop = loop;
}
void PcmReader::ReadSample(int16_t &left, int16_t &right)
{
uint8_t val[4];
_file.get(((char*)val)[0]);
_file.get(((char*)val)[1]);
_file.get(((char*)val)[2]);
_file.get(((char*)val)[3]);
left = val[0] | (val[1] << 8);
right = val[2] | (val[3] << 8);
}
void PcmReader::LoadSamples(uint32_t samplesToLoad)
{
uint32_t samplesRead = 0;
int16_t left = 0;
int16_t right = 0;
for(uint32_t i = _fileOffset; i < _fileSize && samplesRead < samplesToLoad; i+=4) {
ReadSample(left, right);
blip_add_delta(_blipLeft, samplesRead, left - _prevLeft);
blip_add_delta(_blipRight, samplesRead, right - _prevRight);
_prevLeft = left;
_prevRight = right;
_fileOffset += 4;
samplesRead++;
if(samplesRead < samplesToLoad && i + 4 >= _fileSize) {
if(_loop) {
i = _loopOffset * 4 + 8;
_fileOffset = i;
_file.seekg(_fileOffset, ios::beg);
} else {
_done = true;
}
}
}
blip_end_frame(_blipLeft, samplesRead);
blip_end_frame(_blipRight, samplesRead);
}
void PcmReader::ApplySamples(int16_t *buffer, size_t sampleCount, uint8_t volume)
{
if(_done) {
return;
}
LoadSamples((uint32_t)sampleCount * PcmReader::PcmSampleRate / _sampleRate + 1 - blip_samples_avail(_blipLeft));
int samplesRead = blip_read_samples(_blipLeft, _outputBuffer, (int)sampleCount, 1);
blip_read_samples(_blipRight, _outputBuffer + 1, (int)sampleCount, 1);
for(size_t i = 0, len = samplesRead * 2; i < len; i++) {
buffer[i] += (int16_t)((int32_t)_outputBuffer[i] * volume / 255);
}
}
uint32_t PcmReader::GetOffset()
{
return _fileOffset;
}

43
Core/PcmReader.h Normal file
View file

@ -0,0 +1,43 @@
#pragma once
#include "stdafx.h"
#include "../Utilities/stb_vorbis.h"
#include "../Utilities/blip_buf.h"
class PcmReader
{
private:
static constexpr int PcmSampleRate = 44100;
static constexpr int SamplesToRead = 100;
int16_t* _outputBuffer;
ifstream _file;
uint32_t _fileOffset;
uint32_t _fileSize;
uint32_t _loopOffset;
int16_t _prevLeft;
int16_t _prevRight;
bool _loop;
bool _done;
blip_t* _blipLeft;
blip_t* _blipRight;
uint32_t _sampleRate = 0;
void LoadSamples(uint32_t samplesToLoad);
void ReadSample(int16_t &left, int16_t &right);
public:
PcmReader();
~PcmReader();
bool Init(string filename, bool loop, uint32_t startOffset = 0);
bool IsPlaybackOver();
void SetSampleRate(uint32_t sampleRate);
void SetLoopFlag(bool loop);
void ApplySamples(int16_t* buffer, size_t sampleCount, uint8_t volume);
uint32_t GetOffset();
};

View file

@ -5,6 +5,7 @@
#include "Spc.h"
#include "BaseCartridge.h"
#include "Sa1.h"
#include "Msu1.h"
#include "CheatManager.h"
#include "../Utilities/Serializer.h"
@ -15,6 +16,7 @@ RegisterHandlerB::RegisterHandlerB(Console *console, Ppu * ppu, Spc * spc, uint8
_sa1 = console->GetCartridge()->GetSa1();
_ppu = ppu;
_spc = spc;
_msu1 = console->GetMsu1().get();
_workRam = workRam;
_wramPosition = 0;
_memoryType = SnesMemoryType::Register;
@ -33,6 +35,8 @@ uint8_t RegisterHandlerB::Read(uint32_t addr)
return value;
} else if(addr >= 0x2300 && addr <= 0x23FF && _console->GetCartridge()->GetSa1()) {
return _console->GetCartridge()->GetSa1()->CpuRegisterRead(addr);
} else if(_msu1 && addr <= 0x2007) {
return _msu1->Read(addr);
} else {
return _ppu->Read(addr);
}
@ -69,6 +73,8 @@ void RegisterHandlerB::Write(uint32_t addr, uint8_t value)
}
} else if(addr >= 0x2200 && addr <= 0x22FF && _console->GetCartridge()->GetSa1()) {
_console->GetCartridge()->GetSa1()->CpuRegisterWrite(addr, value);
} else if(_msu1 && addr <= 0x2007) {
return _msu1->Write(addr, value);
} else {
_ppu->Write(addr, value);
}

View file

@ -7,6 +7,7 @@ class Console;
class Ppu;
class Spc;
class Sa1;
class Msu1;
class CheatManager;
class RegisterHandlerB : public IMemoryHandler, public ISerializable
@ -17,6 +18,7 @@ private:
Ppu *_ppu;
Spc *_spc;
Sa1 *_sa1;
Msu1 *_msu1;
uint8_t *_workRam;
uint32_t _wramPosition;

View file

@ -44,7 +44,7 @@ public:
// a pair of samples is be generated.
void run();
void setEchoWriteEnabled(bool enabled);
bool isMuted() { return (m.regs[r_flg] & 0x40) != 0; }
// Sound control

View file

@ -6,6 +6,7 @@
#include "RewindManager.h"
#include "VideoRenderer.h"
#include "WaveRecorder.h"
#include "Msu1.h"
#include "../Utilities/Equalizer.h"
#include "../Utilities/blip_buf.h"
@ -69,25 +70,30 @@ void SoundMixer::PlayAudioBuffer(int16_t* samples, uint32_t sampleCount)
_leftSample = samples[0];
_rightSample = samples[1];
int16_t *out = nullptr;
uint32_t count = 0;
if(cfg.SampleRate == SoundResampler::SpcSampleRate && cfg.DisableDynamicSampleRate) {
out = samples;
count = sampleCount;
} else {
count = _resampler->Resample(samples, sampleCount, cfg.SampleRate, _sampleBuffer);
out = _sampleBuffer;
}
shared_ptr<Msu1> msu1 = _console->GetMsu1();
if(msu1) {
msu1->MixAudio(out, count, cfg.SampleRate);
}
if(masterVolume < 100) {
//Apply volume if not using the default value
for(uint32_t i = 0; i < sampleCount * 2; i++) {
samples[i] = (int32_t)samples[i] * (int32_t)masterVolume / 100;
for(uint32_t i = 0; i < count * 2; i++) {
out[i] = (int32_t)out[i] * (int32_t)masterVolume / 100;
}
}
shared_ptr<RewindManager> rewindManager = _console->GetRewindManager();
if(rewindManager && rewindManager->SendAudio(samples, (uint32_t)sampleCount)) {
int16_t *out = nullptr;
uint32_t count = 0;
if(cfg.SampleRate == SoundResampler::SpcSampleRate && cfg.DisableDynamicSampleRate) {
out = samples;
count = sampleCount;
} else {
count = _resampler->Resample(samples, sampleCount, cfg.SampleRate, _sampleBuffer);
out = _sampleBuffer;
}
if(rewindManager && rewindManager->SendAudio(out, count)) {
bool isRecording = _waveRecorder || _console->GetVideoRenderer()->IsRecording();
if(isRecording) {
if(_waveRecorder) {

View file

@ -342,6 +342,11 @@ SpcState Spc::GetState()
return _state;
}
bool Spc::IsMuted()
{
return _dsp->isMuted();
}
AddressInfo Spc::GetAbsoluteAddress(uint16_t addr)
{
if(addr < 0xFFC0 || !_state.RomEnabled) {

View file

@ -298,6 +298,7 @@ public:
void ProcessEndFrame();
SpcState GetState();
bool IsMuted();
AddressInfo GetAbsoluteAddress(uint16_t addr);
int GetRelativeAddress(AddressInfo & absAddress);