Implement libretro save state serialization/deserialization for audio state

This commit is contained in:
刘皓 2025-06-03 17:41:51 -04:00
parent 1c83c69f7f
commit e19ee7f60a
No known key found for this signature in database
GPG key ID: 7901753DB465B711
7 changed files with 238 additions and 0 deletions

View file

@ -28,6 +28,7 @@
#include "eventthread.h"
#include "mkxp-polyfill.h" // std::to_string
#include "wasm-types.h"
#include <string>
#include <utility>
@ -35,6 +36,7 @@
#ifdef MKXPZ_RETRO
# include "graphics.h"
# include "sandbox-serial-util.h"
#else
# include "sdl-util.h"
# include <SDL_mutex.h>
@ -606,3 +608,41 @@ void Audio::reset()
}
Audio::~Audio() { delete p; }
bool Audio::sandbox_serialize(void *&data, mkxp_sandbox::wasm_size_t &max_size)
{
if (!mkxp_sandbox::sandbox_serialize((mkxp_sandbox::wasm_size_t)p->bgmTracks.size(), data, max_size)) return false;
for (AudioStream *track : p->bgmTracks) {
if (!track->sandbox_serialize(data, max_size)) return false;
}
if (!p->bgs.sandbox_serialize(data, max_size)) return false;
if (!p->me.sandbox_serialize(data, max_size)) return false;
if (!p->se.sandbox_serialize(data, max_size)) return false;
return true;
}
bool Audio::sandbox_deserialize(const void *&data, mkxp_sandbox::wasm_size_t &max_size)
{
{
mkxp_sandbox::wasm_size_t count;
if (!mkxp_sandbox::sandbox_deserialize(count, data, max_size)) return false;
if (count != p->bgmTracks.size()) return false;
}
for (AudioStream *track : p->bgmTracks) {
if (!track->sandbox_deserialize(data, max_size)) return false;
}
if (!p->bgs.sandbox_deserialize(data, max_size)) return false;
if (!p->me.sandbox_deserialize(data, max_size)) return false;
if (!p->se.sandbox_deserialize(data, max_size)) return false;
return true;
}

View file

@ -34,6 +34,7 @@
#ifdef MKXPZ_RETRO
# include "mkxp-polyfill.h"
# include "wasm-types.h"
#else
# include <SDL_mutex.h>
#endif // MKXPZ_RETRO
@ -124,6 +125,11 @@ public:
std::string getLastError();
#ifdef MKXPZ_RETRO
bool sandbox_serialize(void *&data, mkxp_sandbox::wasm_size_t &max_size);
bool sandbox_deserialize(const void *&data, mkxp_sandbox::wasm_size_t &max_size);
#endif // MKXPZ_RETRO
#ifndef MKXPZ_RETRO
private:
#endif // MKXPZ_RETRO

View file

@ -26,6 +26,7 @@
#ifdef MKXPZ_RETRO
# include "core.h"
# include "sandbox-serial-util.h"
#else
# include <SDL_mutex.h>
# include <SDL_thread.h>
@ -435,6 +436,72 @@ void AudioStream::render()
stream.render();
}
bool AudioStream::sandbox_serialize(void *&data, mkxp_sandbox::wasm_size_t &max_size)
{
AudioMutexGuard guard(mutex);
ALStream::State state = stream.queryState();
if (!mkxp_sandbox::sandbox_serialize(current.filename, data, max_size)) return false;
if (!mkxp_sandbox::sandbox_serialize(state, data, max_size)) return false;
if (!mkxp_sandbox::sandbox_serialize(playingOffset(), data, max_size)) return false;
if (!mkxp_sandbox::sandbox_serialize(current.pitch, data, max_size)) return false;
for (int i = 0; i < AudioStream::VolumeType::VolumeTypeCount; ++i) {
if (!mkxp_sandbox::sandbox_serialize(volumes[i], data, max_size)) return false;
}
return true;
}
bool AudioStream::sandbox_deserialize(const void *&data, mkxp_sandbox::wasm_size_t &max_size)
{
AudioMutexGuard guard(mutex);
{
std::string value;
if (!mkxp_sandbox::sandbox_deserialize(current.filename, data, max_size)) return false;
if (current.filename != value) {
Exception e;
stream.open(e, current.filename);
if (e.is_error()) return false;
}
}
ALStream::State state;
if (!mkxp_sandbox::sandbox_deserialize(state, data, max_size)) return false;
double offset;
if (!mkxp_sandbox::sandbox_deserialize(offset, data, max_size)) return false;
if (state != stream.queryState()) {
stream.stop();
switch (state) {
case ALStream::State::Playing:
stream.needsRewind.set();
stream.play(offset);
break;
case ALStream::State::Paused:
stream.needsRewind.set();
stream.play(offset);
stream.pause();
break;
case ALStream::State::Stopped:
default:
break;
}
}
if (!mkxp_sandbox::sandbox_deserialize(current.pitch, data, max_size)) return false;
stream.setPitch(current.pitch);
for (int i = 0; i < AudioStream::VolumeType::VolumeTypeCount; ++i) {
if (!mkxp_sandbox::sandbox_deserialize(volumes[i], data, max_size)) return false;
}
updateVolume();
return true;
}
#else
void AudioStream::fadeOutThread()
{

View file

@ -29,6 +29,10 @@
#include <string>
#ifdef MKXPZ_RETRO
# include "wasm-types.h"
#endif // MKXPZ_RETRO
struct AudioStream
{
struct
@ -152,6 +156,9 @@ struct AudioStream
#ifdef MKXPZ_RETRO
void render();
bool sandbox_serialize(void *&data, mkxp_sandbox::wasm_size_t &max_size);
bool sandbox_deserialize(const void *&data, mkxp_sandbox::wasm_size_t &max_size);
#endif // MKXPZ_RETRO
private:

View file

@ -29,9 +29,11 @@
#include "config.h"
#include "util.h"
#include "debugwriter.h"
#include "wasm-types.h"
#ifdef MKXPZ_RETRO
# include <sndfile.hh>
# include "sandbox-serial-util.h"
#else
# include <SDL_sound.h>
#endif // MKXPZ_RETRO
@ -99,6 +101,7 @@ SoundEmitter::SoundEmitter(const Config &conf)
srcCount(conf.SE.sourceCount),
alSrcs(srcCount),
atchBufs(srcCount),
filenames(srcCount),
srcPrio(srcCount)
{
for (size_t i = 0; i < srcCount; ++i)
@ -182,6 +185,8 @@ void SoundEmitter::play(const std::string &filename,
AL::Source::setPitch(src, _pitch);
AL::Source::play(src);
filenames[srcIndex] = filename;
}
void SoundEmitter::stop()
@ -336,3 +341,100 @@ SoundBuffer *SoundEmitter::allocateBuffer(const std::string &filename)
return buffer;
}
}
bool SoundEmitter::sandbox_serialize(void *&data, mkxp_sandbox::wasm_size_t &max_size)
{
if (!mkxp_sandbox::sandbox_serialize((mkxp_sandbox::wasm_size_t)srcCount, data, max_size)) return false;
for (size_t i = 0; i < srcCount; ++i) {
if (!mkxp_sandbox::sandbox_serialize(filenames[i], data, max_size)) return false;
AL::Source::ID source = alSrcs[i];
ALenum state = AL::Source::getState(source);
if (!mkxp_sandbox::sandbox_serialize((int32_t)state, data, max_size)) return false;
{
ALfloat value;
alGetSourcef(source.al, AL_SEC_OFFSET, &value);
if (!mkxp_sandbox::sandbox_serialize(value, data, max_size)) return false;
}
{
ALfloat value;
alGetSourcef(source.al, AL_PITCH, &value);
if (!mkxp_sandbox::sandbox_serialize(value, data, max_size)) return false;
}
{
ALfloat value;
alGetSourcef(source.al, AL_GAIN, &value);
if (!mkxp_sandbox::sandbox_serialize(value, data, max_size)) return false;
}
}
return true;
}
bool SoundEmitter::sandbox_deserialize(const void *&data, mkxp_sandbox::wasm_size_t &max_size)
{
{
mkxp_sandbox::wasm_size_t count;
if (!mkxp_sandbox::sandbox_deserialize(count, data, max_size)) return false;
if (count != srcCount) return false;
}
for (size_t i = 0; i < srcCount; ++i) {
AL::Source::ID source = alSrcs[i];
{
std::string value = filenames[i];
if (!mkxp_sandbox::sandbox_deserialize(filenames[i], data, max_size)) return false;
if (atchBufs[i] != nullptr) {
SoundBuffer::deref(atchBufs[i]);
}
if (filenames[i] != value) {
if (filenames[i].empty()) {
atchBufs[i] = nullptr;
} else {
SoundBuffer *buffer = allocateBuffer(filenames[i]);
if (buffer == nullptr) return false;
atchBufs[i] = SoundBuffer::ref(buffer);
AL::Source::attachBuffer(source, buffer->alBuffer);
}
}
}
int32_t state;
if (!mkxp_sandbox::sandbox_deserialize(state, data, max_size)) return false;
if (state != AL::Source::getState(source)) {
switch (state) {
case AL_PLAYING:
AL::Source::play(source);
break;
case AL_PAUSED:
AL::Source::pause(source);
break;
case AL_STOPPED:
default:
AL::Source::stop(source);
break;
}
}
{
ALfloat value;
if (!mkxp_sandbox::sandbox_deserialize(value, data, max_size)) return false;
alSourcef(source.al, AL_SEC_OFFSET, value);
}
{
ALfloat value;
if (!mkxp_sandbox::sandbox_deserialize(value, data, max_size)) return false;
alSourcef(source.al, AL_PITCH, value);
}
{
ALfloat value;
if (!mkxp_sandbox::sandbox_deserialize(value, data, max_size)) return false;
alSourcef(source.al, AL_GAIN, value);
}
}
return true;
}

View file

@ -29,6 +29,10 @@
#include <string>
#include <vector>
#ifdef MKXPZ_RETRO
# include "wasm-types.h"
#endif // MKXPZ_RETRO
struct SoundBuffer;
struct Config;
@ -45,6 +49,7 @@ struct SoundEmitter
const size_t srcCount;
std::vector<AL::Source::ID> alSrcs;
std::vector<SoundBuffer*> atchBufs;
std::vector<std::string> filenames;
/* Indices of sources, sorted by priority (lowest first) */
std::vector<size_t> srcPrio;
@ -58,6 +63,11 @@ struct SoundEmitter
void stop();
#ifdef MKXPZ_RETRO
bool sandbox_serialize(void *&data, mkxp_sandbox::wasm_size_t &max_size);
bool sandbox_deserialize(const void *&data, mkxp_sandbox::wasm_size_t &max_size);
#endif // MKXPZ_RETRO
private:
SoundBuffer *allocateBuffer(const std::string &filename);
};

View file

@ -1732,6 +1732,9 @@ extern "C" RETRO_API bool retro_serialize(void *data, size_t len) {
ADVANCE(shState->graphics().frozenPixels.size());
}
// Write the audio state
if (!audio->sandbox_serialize(data, max_size)) return false;
std::memset(data, 0, max_size);
return true;
}
@ -2079,6 +2082,9 @@ extern "C" RETRO_API bool retro_unserialize(const void *data, size_t len) {
ADVANCE((size_t)shState->graphics().width() * (size_t)shState->graphics().height());
}
// Read the audio state
if (!audio->sandbox_deserialize(data, max_size)) DESER_FAIL;
return true;
}