iPlug2 Forum

Using JSON to store presets

Hi all,

My plugin uses a mix of params and ByteChunks in its presets. To save/restore these, I have overridden the plugin class SerializeState and UnserializeState methods as follows:

bool LoudNES::SerializeState(IByteChunk &chunk) const {
  for (auto channel : mDSP.mNesChannels->allChannels) {
    channel->Serialize(chunk);
  }
  return SerializeParams(chunk);
}

int LoudNES::UnserializeState(const IByteChunk &chunk, int startPos) {
  int pos = startPos;
  for (auto channel : mDSP.mNesChannels->allChannels) {
    pos = channel->Deserialize(chunk, pos);
  }
  return UnserializeParams(chunk, pos);
}

Simple enough; the param data is concatenated to the binary chunk. But this serialization seems very brittle. I would like to define my factory presets as files on disk. If I change any params or byte chunk parts, it will break all presets.

Has anyone tried using a serialization format like JSON with IPlug2 to make forward compatibility a little easier? And would this just be a matter of overriding the Serialize/Unserialize methods?

I’ve thought the same thing about parameter storage being very brittle this way, did you end up trying JSON or something similar? I’ve implemented a very simple versioning scheme which just writes a version number first and then calls a specific deserializer - maybe that would be another option? But adding parameters would potentially require multiple deserializers, or at least chaining them. Annoying work nonetheless. Or maybe you could just write the number of parameters up front and always deserialize them in a specific order? Sorry, I guess I’m asking for guidance on this subject, too! :slight_smile:

I defined a MaxUserChunkSize, and after I filled the chunk with “data”, I fill it up with NULL’s till the MaxUserChunkSize. In that way I have a buffer for “new to be added data” without messing up my presets, as the chunk has always the same size, and “new” data is just appended to existing data:

bool RRE_UMasterK::SerializeState(IByteChunk& chunk) const
{
  chunk.PutBytes(//Your data 1);
  chunk.PutBytes(//Your data 1);
  //etc.etc.

  // Reserv bytes for future user data, so that adding user data does not mess up existing presets
  cSize = chunk.Size() ;
  char zero[1];
  memset(zero, 0, 1);
  for (int i = 0; i < mMaxUserChunkSize - cSize; ++i) chunk.PutBytes(zero, 1);

  TRACE return SerializeParams(chunk);
}
int RRE_UMasterK::UnserializeState(const IByteChunk& chunk, int startPos)
{
  //Get Data
    startPos = chunk.GetBytes(//Get your user DATA 1, startPos);
    startPos = chunk.GetBytes(//Get your user DATA 2, startPos);
    //etc.etc.

  // Get rest of chunk
  char buffer[1];
  memset(buffer, 0, 1);
  int size = mMaxUserChunkSize - startPos;
  for (int i = 0; i < size; ++i) startPos = chunk.GetBytes(buffer, 1, startPos);

  TRACE return UnserializeParams(chunk, startPos);

Of course when your mMaxUserChunkSize is set to small, and you run out of it at some point, you still end up messing your presets.