diff --git a/Core/Console.cpp b/Core/Console.cpp index ffa8633e..29d4fbb2 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -551,6 +551,11 @@ void Console::SaveState(ostream &saveStream) Instance->_apu->SaveSnapshot(&saveStream); Instance->_controlManager->SaveSnapshot(&saveStream); Instance->_mapper->SaveSnapshot(&saveStream); + if(Instance->_hdAudioDevice) { + Instance->_hdAudioDevice->SaveSnapshot(&saveStream); + } else { + Snapshotable::WriteEmptyBlock(&saveStream); + } } } @@ -567,6 +572,11 @@ void Console::LoadState(istream &loadStream) Instance->_apu->LoadSnapshot(&loadStream); Instance->_controlManager->LoadSnapshot(&loadStream); Instance->_mapper->LoadSnapshot(&loadStream); + if(Instance->_hdAudioDevice) { + Instance->_hdAudioDevice->LoadSnapshot(&loadStream); + } else { + Snapshotable::SkipBlock(&loadStream); + } MessageManager::SendNotification(ConsoleNotificationType::StateLoaded); } @@ -636,11 +646,11 @@ bool Console::IsHdPpu() void Console::LoadHdPack(VirtualFile &romFile, VirtualFile &patchFile) { _hdData.reset(); + _hdAudioDevice.reset(); if(EmulationSettings::CheckFlag(EmulationFlags::UseHdPacks)) { _hdData.reset(new HdPackData()); if(!HdPackLoader::LoadHdNesPack(romFile, *_hdData.get())) { _hdData.reset(); - _hdAudioDevice.reset(); } else { auto result = _hdData->PatchesByHash.find(romFile.GetSha1Hash()); if(result != _hdData->PatchesByHash.end()) { diff --git a/Core/HdAudioDevice.cpp b/Core/HdAudioDevice.cpp index 19698a95..9038fcb4 100644 --- a/Core/HdAudioDevice.cpp +++ b/Core/HdAudioDevice.cpp @@ -11,22 +11,43 @@ HdAudioDevice::HdAudioDevice(HdPackData * hdData) _oggMixer = SoundMixer::GetOggMixer(); } -bool HdAudioDevice::PlayBgmTrack(uint8_t track) +void HdAudioDevice::StreamState(bool saving) { - auto result = _hdData->BgmFilesById.find(_album * 256 + track); + int32_t trackOffset = 0; + if(saving) { + trackOffset = _oggMixer->GetBgmOffset(); + if(trackOffset < 0) { + _lastBgmTrack = -1; + } + Stream(_album, _lastBgmTrack, trackOffset); + } else { + Stream(_album, _lastBgmTrack, trackOffset); + if(_lastBgmTrack != -1 && trackOffset > 0) { + PlayBgmTrack(_lastBgmTrack, trackOffset); + } + } +} + +bool HdAudioDevice::PlayBgmTrack(uint8_t track, uint32_t startOffset) +{ + int trackId = _album * 256 + track; + auto result = _hdData->BgmFilesById.find(trackId); if(result != _hdData->BgmFilesById.end()) { - return !_oggMixer->Play(result->second, false); + if(_oggMixer->Play(result->second, false, startOffset)) { + _lastBgmTrack = trackId; + return true; + } } else { MessageManager::Log("[HDPack] Invalid album+track combination: " + std::to_string(_album) + ":" + std::to_string(track)); - return false; } + return false; } bool HdAudioDevice::PlaySfx(uint8_t sfxNumber) { auto result = _hdData->SfxFilesById.find(_album * 256 + sfxNumber); if(result != _hdData->SfxFilesById.end()) { - return !_oggMixer->Play(result->second, true); + return !_oggMixer->Play(result->second, true, 0); } else { MessageManager::Log("[HDPack] Invalid album+sfx number combination: " + std::to_string(_album) + ":" + std::to_string(sfxNumber)); return false; @@ -98,7 +119,7 @@ void HdAudioDevice::WriteRAM(uint16_t addr, uint8_t value) //Play BGM track (0-255 = track number) //Stop the current BGM and starts a new track - case 5: _trackError = PlayBgmTrack(value); break; + case 5: _trackError = PlayBgmTrack(value, 0); break; //Play sound effect (0-255 = sfx number) //Plays a new sound effect (no limit to the number of simultaneous sound effects) diff --git a/Core/HdAudioDevice.h b/Core/HdAudioDevice.h index 6b1cd695..43b15865 100644 --- a/Core/HdAudioDevice.h +++ b/Core/HdAudioDevice.h @@ -3,10 +3,11 @@ #include "IMemoryHandler.h" #include "SoundMixer.h" #include "OggMixer.h" +#include "Snapshotable.h" struct HdPackData; -class HdAudioDevice : public IMemoryHandler +class HdAudioDevice : public IMemoryHandler, public Snapshotable { private: HdPackData *_hdData; @@ -14,11 +15,15 @@ private: uint8_t _flags; bool _trackError; OggMixer* _oggMixer; + int32_t _lastBgmTrack; - bool PlayBgmTrack(uint8_t track); + bool PlayBgmTrack(uint8_t track, uint32_t startOffset); bool PlaySfx(uint8_t sfxNumber); void ProcessControlFlags(uint8_t flags); +protected: + void StreamState(bool saving) override; + public: HdAudioDevice(HdPackData *hdData); diff --git a/Core/OggMixer.cpp b/Core/OggMixer.cpp index 7b5bfb17..401095c1 100644 --- a/Core/OggMixer.cpp +++ b/Core/OggMixer.cpp @@ -80,11 +80,11 @@ void OggMixer::SetSampleRate(int sampleRate) } } -bool OggMixer::Play(string filename, bool isSfx) +bool OggMixer::Play(string filename, bool isSfx, uint32_t startOffset) { shared_ptr reader(new OggReader()); bool loop = !isSfx && (_options & (int)OggPlaybackOptions::Loop) != 0; - if(reader->Init(filename, loop, _sampleRate)) { + if(reader->Init(filename, loop, _sampleRate, startOffset)) { if(isSfx) { _sfx.push_back(reader); } else { @@ -108,3 +108,12 @@ void OggMixer::ApplySamples(int16_t * buffer, size_t sampleCount) } _sfx.erase(std::remove_if(_sfx.begin(), _sfx.end(), [](const shared_ptr& o) { return o->IsPlaybackOver(); }), _sfx.end()); } + +int OggMixer::GetBgmOffset() +{ + if(_bgm) { + return _bgm->GetOffset(); + } else { + return -1; + } +} \ No newline at end of file diff --git a/Core/OggMixer.h b/Core/OggMixer.h index 42d55dfc..6786b016 100644 --- a/Core/OggMixer.h +++ b/Core/OggMixer.h @@ -22,7 +22,7 @@ public: void ApplySamples(int16_t* buffer, size_t sampleCount); void Reset(); - bool Play(string filename, bool isSfx); + bool Play(string filename, bool isSfx, uint32_t startOffset); void SetPlaybackOptions(uint8_t options); void SetPausedFlag(bool paused); void StopBgm(); @@ -31,4 +31,5 @@ public: void SetSfxVolume(uint8_t volume); bool IsBgmPlaying(); bool IsSfxPlaying(); + int32_t GetBgmOffset(); }; diff --git a/Core/OggReader.cpp b/Core/OggReader.cpp index f20866d6..128b263f 100644 --- a/Core/OggReader.cpp +++ b/Core/OggReader.cpp @@ -22,7 +22,7 @@ OggReader::~OggReader() } } -bool OggReader::Init(string filename, bool loop, int sampleRate) +bool OggReader::Init(string filename, bool loop, uint32_t sampleRate, uint32_t startOffset) { int error; VirtualFile file = filename; @@ -32,6 +32,9 @@ bool OggReader::Init(string filename, bool loop, int sampleRate) if(_vorbis) { _loop = loop; _oggSampleRate = stb_vorbis_get_info(_vorbis).sample_rate; + if(startOffset > 0) { + stb_vorbis_seek(_vorbis, startOffset); + } blip_set_rates(_blipLeft, _oggSampleRate, sampleRate); blip_set_rates(_blipRight, _oggSampleRate, sampleRate); return true; @@ -100,3 +103,8 @@ void OggReader::ApplySamples(int16_t * buffer, size_t sampleCount, uint8_t volum buffer[i] += (int16_t)(_outputBuffer[i] * (EmulationSettings::GetMasterVolume() * volume / 255 / 10)); } } + +uint32_t OggReader::GetOffset() +{ + return stb_vorbis_get_file_offset(_vorbis); +} \ No newline at end of file diff --git a/Core/OggReader.h b/Core/OggReader.h index 199ef37b..9622c606 100644 --- a/Core/OggReader.h +++ b/Core/OggReader.h @@ -31,9 +31,10 @@ public: OggReader(); ~OggReader(); - bool Init(string filename, bool loop, int sampleRate); + bool Init(string filename, bool loop, uint32_t sampleRate, uint32_t startOffset = 0); bool IsPlaybackOver(); void SetSampleRate(int sampleRate); void SetLoopFlag(bool loop); void ApplySamples(int16_t* buffer, size_t sampleCount, uint8_t volume); + uint32_t GetOffset(); }; diff --git a/Core/Snapshotable.cpp b/Core/Snapshotable.cpp index 77af826f..cdf8cbf9 100644 --- a/Core/Snapshotable.cpp +++ b/Core/Snapshotable.cpp @@ -97,4 +97,17 @@ void Snapshotable::LoadSnapshot(istream* file) if(_blockBuffer) { throw new std::runtime_error("A call to StreamEndBlock is missing."); } +} + +void Snapshotable::WriteEmptyBlock(ostream* file) +{ + int blockSize = 0; + file->write((char*)&blockSize, sizeof(blockSize)); +} + +void Snapshotable::SkipBlock(istream* file) +{ + int blockSize = 0; + file->read((char*)&blockSize, sizeof(blockSize)); + file->seekg(blockSize, ios::cur); } \ No newline at end of file diff --git a/Core/Snapshotable.h b/Core/Snapshotable.h index fd069033..cc2a6d67 100644 --- a/Core/Snapshotable.h +++ b/Core/Snapshotable.h @@ -158,4 +158,7 @@ protected: public: void SaveSnapshot(ostream* file); void LoadSnapshot(istream* file); + + static void WriteEmptyBlock(ostream* file); + static void SkipBlock(istream* file); }; \ No newline at end of file