PDA

View Full Version : tale SerializePresets


cc_
08-13-2010, 02:41 AM
I'm trying to figure out why you did this:

Find:
//savedOK = _this->SerializePresets(pChunk);
savedOK = _this->SerializeState(pChunk);

After, add
if (savedOK) savedOK = _this->SerializePresets(pChunk);

Find:
//pos = _this->UnserializePresets(pChunk, pos);
pos = _this->UnserializeState(pChunk, pos);

After, add:
if (pos >= 0 && pos < pChunk->Size()) pos = _this->UnserializePresets(pChunk, pos);

Find:
if (!(_this->DoesStateChunks())) {
_this->ModifyCurrentPreset();
}

replace with:
//if (!(_this->DoesStateChunks())) {
_this->ModifyCurrentPreset();
//}


The code I have been using (which seems to work fine) is a bit different, I do this instead of the regular iplug code:


savedOK = _this->SerializePresets(pChunk);
//savedOK = _this->SerializeState(pChunk);


so I don't call SerializeState at all and it seems to work. I think your code is saving one preset twice, but I could be wrong.

Also I inverted the test rather than commenting it out:

if (_this->DoesStateChunks()) {
_this->ModifyCurrentPreset();
}

Tale
08-16-2010, 06:41 AM
I'm trying to figure out why you did this:
...
so I don't call SerializeState at all and it seems to work. I think your code is saving one preset twice, but I could be wrong.
You are right. The "current" settings are saved, but these same settings are also stored in the currently selected preset. However, my plug-in's SerializeState() saves not just the VST parameters, but also some internal state variables.

Alternatively I guess I could (should?) have made SerializePresets() virtual, so I could override it, and make it store the internal state variables along with the presets.

Also I inverted the test rather than commenting it out:

if (_this->DoesStateChunks()) {
_this->ModifyCurrentPreset();
}

If you do some custom settings, and then select another preset, then your custom settings should be saved before switching to the other preset. I don't think this should depend on whether or not you are using state chunks.

cc_
08-17-2010, 12:42 AM
The way I did it was to override SerializeState (already virtual I think), the code is in my version of the example too, to save the scribble strip state:


bool PlugExample::SerializeState(ByteChunk* pChunk)
{
IMutexLock lock(this);
if ( ScribbleStrip == NULL ) {
if (pChunk->PutStr("default") <= 0)
return false;
}
else {
if (pChunk->PutStr(ScribbleStrip->GetText()) <= 0)
return false;
}
return SerializeParams(pChunk);
}

int PlugExample::UnserializeState(ByteChunk* pChunk, int startPos)
{
IMutexLock lock(this);
WDL_String MyStr("xxx");
startPos = pChunk->GetStr(&MyStr, startPos);
char *s = MyStr.Get();
if (ScribbleStrip) ScribbleStrip->SetTextFromPlug( MyStr.Get() );
return UnserializeParams(pChunk, startPos);
}



If you do some custom settings, and then select another preset, then your custom settings should be saved before switching to the other preset. I don't think this should depend on whether or not you are using state chunks.


My brain seems to be resisting remembering this preset stuff, but I think I was assuming that when you are not using state chunks all the information about the preset is managed by the host, so no need to call ModifyCurrentPreset. I could very easily be very wrong though.

Tale
08-17-2010, 02:00 AM
My brain seems to be resisting remembering this preset stuff, but I think I was assuming that when you are not using state chunks all the information about the preset is managed by the host, so no need to call ModifyCurrentPreset. I could very easily be very wrong though.
When I disable PLUG_DOES_STATE_CHUNKS and I comment out the _this->ModifyCurrentPreset(), then changes aren't saved when switching presets (in REAPER on Windows).

cc_
08-25-2010, 03:51 AM
Ah yes, I think you are right.

I have switched to using your save chunks mods instead of mine. It seems to work, I have implemented SerializeState and UnserializeState to save/restore my stuff and then call (Un)SerializeParams.

But I'm not sure what (if anything) I should be doing with SerializePresetsState and UnserializePresetsState.

Tale
08-25-2010, 04:48 AM
SerializeState() saves one preset, the current preset. SerializePresetsState() saves all presets. The host could call SerializeState() when e.g. it saves a patch to an .FXP file, and SerializePresetsState() to save a bank to a .FXB file.

By default SerializeState() simply calls SerializeParams(), which makes perfect sense. It would even more sense if the default SerializePresetsState() would call SerializePresets(). Unfortunately it doesn't, but you can easily override SerializePresetsState() and make it call SerializePresets().

cc_
08-25-2010, 05:07 AM
I see.

So where did SerializePresetsState() come from? I don't see it in the cockos latest WDL zip.

cc_
08-25-2010, 05:17 AM
I'm very confused now... your IPlugBase::RestorePreset is calling SerializeParams, shouldn't it be calling SerializeState instead? Otherwise only the params will get restored?

Edit: also in MakeDefaultPreset().

Tale
08-25-2010, 05:22 AM
SerializePresetsState() is currently only in the "next" branch of my Git repository.

cc_
08-25-2010, 05:35 AM
Ok, maybe I have totally lost the plot here, but I'm not sure why you introduced that new function.
I used the existing SerializePresets() function in IPlugVST - in fact the line was there already, I just uncommented it.

The other thing I did was went through and changed all the SerializeParams to SerializeState, so they all go through that function (which you can override).

That seemed to work. Did I miss something?

Tale
08-25-2010, 05:41 AM
I'm very confused now... your IPlugBase::RestorePreset is calling SerializeParams, shouldn't it be calling SerializeState instead? Otherwise only the params will get restored?

Edit: also in MakeDefaultPreset().
Yeah, that can't be right. I (or you?) will have to take another look at this.

cc_
08-25-2010, 05:45 AM
I found my post on this a while back

http://forum.cockos.com/showpost.php?p=480299&postcount=14

I still think that is right. If you want to try it it is in my WDL.git. Unfortunately I fucked up the check in and it is 3 different commits. I know I should be able to mush them into one, but at the time I was too scared of breaking something.


713ce7f GetChunk / SetChunk more missing changes
fe6036c GetChunk / SetChunk - missed a change
adaf6a3 GetChunk / SetChunk with VST presets working.

Tale
08-25-2010, 07:10 AM
Your changes seem very promising. I would make (Un)SerializeParams() virtual, so you can override it on a per plug-in basis. However, your version has the same downside as mine; it may not be compatible with existing projects.

BTW, I see your adaf6a3 commit contains changes to the default example. This is not a good idea IMHO, because if I would change the example with every feature I add, the example would quickly become way too complex.

cc_
08-25-2010, 07:40 AM
I take your point about the example.

I think it will be pretty compatible. For the non-preset version it does the same thing (when people loading the plugin state). For the preset version (switching between presets) it doesn't do the same thing, but it was broken before, so I don't think it matters that people can't load the broken presets in their old projects.

Edit: also this only affects people who are using chunks and multiple presets - if they are doing that won't they have noticed it doesn't work, come here and be using my code from that thread anyway?

Tale
08-25-2010, 08:19 AM
Yes, perhaps we should give up on backward compatibility. After all, if SerializeParams() is virtual, one could just as easily override it and let it call SerializeState() to fix a project that depends on SerializeState() being called for banks, right?

Tale
08-25-2010, 09:06 AM
If you want to try it it is in my WDL.git. Unfortunately I fucked up the check in and it is 3 different commits. I know I should be able to mush them into one, but at the time I was too scared of breaking something.


713ce7f GetChunk / SetChunk more missing changes
fe6036c GetChunk / SetChunk - missed a change
adaf6a3 GetChunk / SetChunk with VST presets working.

I have just temporarily undone my changes, and then I have committed your changes (using git cheery-pick, hoping this is the right way), but now my test project crashes. Grrr...

cc_
08-25-2010, 09:40 AM
Because it has the current state saved before all the presets in the bank? I guess that would happen!

I think you will be the only one with this problem though, maybe the fix should be specific to this one plugin?

cc_
08-25-2010, 09:52 AM
Actually, maybe you could stop it crashing by adding a check to see if the preset name string looks sensible, and if not refuse to read the bank. That might not be a thing to have in there anyway.

Tale
08-25-2010, 12:51 PM
It could well just be this one project, although I think it does work with the original Cokcos WDL code. Still, I don't understand... Shouldn't SerializeState() be able to save any data whatever, as long as you provide an UnserializeState() that reads back the same data?

cc_
08-25-2010, 01:11 PM
Yes, SerializeState can save anything. The problem for you is in UnserializePresets() - that expects each bank to be a series of presets and each preset is a name followed by a bool flag (for initialized) followed by the SerializeState data.

The banks you have in your current project start with the current plugin state's SerializeState data (without a name) followed by the presets in the proper format. So when they get read they will almost certainly break as UnserializePresets() tries to treat the first part of the SerializeState data as a WDL string.

cc_
08-25-2010, 01:16 PM
BTW I didn't really write any of this code, it was mostly there commented out, with just a few steps needed to make it all work.

It seems like a sensible way to do it all to me though.

cc_
08-25-2010, 01:40 PM
And just before I go... I think the best thing to do would be for you to just add #ifdefed code into SerializePresets and UnserializePresets that does a SerializeState or UnserializeState before doing the presets for real. Then next plugin you can just turn it off.

Tale
08-25-2010, 03:29 PM
Thanks for explaining. However, that was not it (storing data in any order you want works just fine). The problem was that I called GetGUI()->GetControl() from within SerializeState() at a moment the GUI wasn't available yet. I can easily fix this by checking GetGUI(). However, I have to think a little harder to see if this could cause any other issues.

But now I have stumbled upon a new problem: I use MakePresetFromNamedParams(), which doesn't work anymore (as also explained in your comments). Oh well... I will leave it for a couple of days; perhaps I can come up with another (better?) solution then.

cc_
08-26-2010, 02:48 AM
Thanks for explaining. However, that was not it (storing data in any order you want works just fine). The problem was that I called GetGUI()->GetControl() from within SerializeState() at a moment the GUI wasn't available yet. I can easily fix this by checking GetGUI(). However, I have to think a little harder to see if this could cause any other issues.


Ah, that's good. I had that problem in the Example I did, I ended up with this (ScribbleStrip is an editable text control which is the only non-parameter data that was being put in the state):


bool PlugExample::SerializeState(ByteChunk* pChunk)
{
IMutexLock lock(this);
if ( ScribbleStrip == NULL ) {
if (pChunk->PutStr("default") <= 0)
return false;
}
else {
if (pChunk->PutStr(ScribbleStrip->GetText()) <= 0)
return false;
}
return SerializeParams(pChunk);
}





But now I have stumbled upon a new problem: I use MakePresetFromNamedParams(), which doesn't work anymore

Well, it works as well as it used to: if you are using chunks, not at all :D

I didn't implement it because I'm not using it and so wouldn't test it. But I think the answer is pretty simple, just rename that function to MakePresetParamsFromNamedParams(), then add a new virtual function called MakePresetFromNamedParams() that just calls MakePresetParamsFromNamedParams() by default. Then overide MakePresetFromNamedParams() in your plugin to do whatever you need before calling MakePresetParamsFromNamedParams().

cc_
08-26-2010, 05:19 AM
I could have a go at implementing this if you like.

Tale
08-26-2010, 05:29 AM
Well, it works as well as it used to: if you are using chunks, not at all :D
Not exactly... With the original WDL implementation you can actually use MakePresetFromNamedParams(), as long as you don't add extra data within each preset. You might want to do this if you are using state chunks to save some "global" setting (e.g. GUI state).

cc_
08-26-2010, 06:01 AM
Interesting, I would have thought it would work if you didn't add any data.

cc_
08-27-2010, 06:03 AM
I just switched from the 8da3756 commit version back to my version and opening projects that had been saved with 8da3756 caused reaper to crash like this:


0x18940b72 WDL_HeapBuf::Resize(int, bool) + 458 (heapbuf.h:245)
0x18940f02 WDL_TypedBuf<unsigned char>::Resize(int, bool) + 44 (heapbuf.h:294)
0x18940f56 int ByteChunk::Put<int>(int const*) + 56
0x18940fbe ByteChunk::PutStr(char const*) + 62 (Containers.h:194)
0x1893e636 Indus::SerializeState(ByteChunk*) + 188 (Indus.cpp:228)
0x1897e69e IPlugBase::RestorePreset(int) + 158
0x1897e83a IPlugBase::UnserializePresets(ByteChunk*, int) + 326
0x18982b0f IPlugVST::VSTDispatcher(AEffect*, int, int, int, void*, float) + 2079


I added this hacky code into UnserializePresets:


char *t = name.Get();
for (int j=0; ; j++) {
if (t[j]='\0') break;
if (j > MAX_PRESET_NAME_LEN) return 0;
}


Which got rid of the crashes, I could then resave the projects and then take the hacky code out. And all is well again.

Or maybe we could keep some check like that in there anyway?

Tale
08-27-2010, 06:49 AM
If would take the "hack" code out again. I would however double-check whether all the plug-in settings in your projects are still what you expect them to be.

cc_
08-27-2010, 07:55 AM
The presets are toast, as they have been saved in the project without the extra data my plug needs. That's OK though, luckily it's only me testing at the moment.

cc_
08-27-2010, 10:10 AM
But I think the answer is pretty simple, just rename that function to MakePresetParamsFromNamedParams(), then add a new virtual function called MakePresetFromNamedParams() that just calls MakePresetParamsFromNamedParams() by default. Then overide MakePresetFromNamedParams() in your plugin to do whatever you need before calling MakePresetParamsFromNamedParams().

Who is this idiot?

I took a look and it is nowhere near simple :D

Hmm... think again.

cc_
08-27-2010, 01:32 PM
OK, I got MakePresetFromNamedParams() working with chunks, and updated my git.

There's now a function you can overide to do whatever extra processing you need before the rest of the args are used for the parameters, the default one in IPlugBase looks like this:


virtual void MakePresetStateFromNamedParams(ByteChunk *pChunk, int nParamsNamed, va_list vp) {
MakePresetParamsFromNamedParams(pChunk, nParamsNamed, vp);
}


There's also an example of using it in IPlug/Example/chunks .

Tale
08-28-2010, 03:36 AM
I like the way the new MakePreset*() look in your example. However, I have managed to fix my original changes with very little effort: I have just made SerializeParams() and UserializeParams() virtual, and I have added a MakePresetFromChunk(). So now I can:

a) Save a customized chunk of data (e.g. a hidden parameter, or an unsupported data type) in each preset.

b) Save a customized chunk of data (e.g. a global setting like a GUI state variable) along with all presets (or along with one patch).

MakePresetFromChunk() is pretty ugly to use, but you only need it if you override SerializeParams(). I have been thinking of adding a MakePresetFromFormat(name, "%d%f%s%f%f", ...), so you can do something like:

#define PRESET_FORMAT "%d%f%s%d"
MakePresetFromFormat("Foo", PRESET_FORMAT, 5, -6.0, "bar.wav", true);

This is a lot prettier (but still not as pretty as cc_'s MakePreset stuff). Then again, if you define a helper function in your plug-in that creates a preset chunk for you, it will not only look pretty but it will also be type safe.

cc_
08-28-2010, 06:22 AM
I guess the rational behind SerializeState being virtual and SerializeParams not is that SerializeParams' job is to serialize the parameters, and there's no need to ever change how that is done. Of course changing how any state outside the params is handled is something you need to change per plugin, so that is handled in a virtual function SerializeState. This isn't my idea, this is how the original IPlug is set up.

It would be nice to have these things type safe. I know gcc has some extensions that can allow type checking printf like format strings (with some __attribute__ type thing), but I'm not sure about MS compilers.