Problems recalling plugin state versus current preset


#1

Hi all:

I am trying to implement a proper preset mechanism in my plugin. Right now, if I include my preset code (i.e. implement getNumPrograms(), getCurrentProgram(), setCurrentProgram(int index), getProgramName (int index), and changeProgramName (int index, const String& newName)), every time I save a project in Live, the project will open up with the settings from the last preset chosen, instead of the actual plugin settings at the time.

I am not calling setCurrentProgram() from within my plugin code, so this is clearly the host calling this, presumably after setStateInformation() is called. If I remove all of my code for getNumPrograms() etc., and just use setStateInformation() and getStateInformation(), the plugin saves and recalls its state properly, so I know this is working (my code is essentially the same as the JuceDemoPlugin in this case, with the one difference being that I have an array of parameters rather than parameters I access by different names).

Any idea what I am doing wrong, or what I am missing? I just found out about getCurrentProgramStateInformation() and setCurrentProgramStateInformation(), but I am unclear on what these actually do.

Thanks for any help on this.

Sean Costello


Cubase calling setCurrentProgram after setStateInformation
#2

On a related note: I notice that the AU wrapper calls getCurrentProgramStateInformation() and setCurrentProgramStateInformation(). However, the example plugin doesn’t even define these functions. So, how does the host get and save state, by default, in Juce-based Audio Units?

Sean


#3

As I sat bored out of my skull in my Javascript class last night, I realized that the alert() method that we were using to test our code would be useful in debugging Juce plugins. So I put AlertWindow code in my setStateInformation() and setCurrentProgram() functions. Here’s what I found:

In Live (8.1.5 - I need to start using 8.2, but anyway), here’s what happens with the VST build of ValhallaShimmer:

  • When adding a new instance of the plugin to a project, neither of the functions are called.
  • If I mess with the controls without choosing a preset, save the project, and reopen it, setStateInformation() is called. Then setCurrentProgram() is called, which wipes out the saved parameters and selects the first preset in my list.
  • If I mess with the controls AFTER choosing a preset, save the project, and reopen it, setStateInformation() is called, and then setCurrentProgram() is called TWICE.

I don’t call setCurrentProgram() in my plugin code, so the calls are being generated by Live. To be more precise, setCurrentProgram() is called by the Juce VST wrapper, in response to calls from Live.

My goal is to make sure that, when restoring the state of the plugin, that the calls to setCurrentProgram() don’t wipe out the stored state of the sliders. Anyone have any ideas? Help is greatly appreciated.

Thanks,

Sean Costello


#4

In case anyone is paying attention, no such issues happen in Audio Units. The plugin state is recalled correctly, whether or not a preset has been selected. The current behavior of the ValhallaShimmer AU release, where the preset is recalled in some audio hosts instead of the desired saved state, is due to a bug in my older code.

So the problem seems linked to the VST function setProgram() being called after setChunk(), and clobbering whatever was set in setChunk().

Sean Costello


#5

I asked about this issue on KVR, as it seems to be a VST issue. I was pointed to this thread:

Urs Heckmann provided this solution:

So, a question for Jules (or whoever else has an answer): What would be the best way to implement this in Juce? I considered the Timer class, but it seems like I want to simply look at the time elapsed, rather than have a function that is called every x milliseconds.

Thanks,

Sean Costello


#6

Yes, VST hosts can be a bit of a pain sometimes…

Easy enough to measure the time - just use Time::getMillisecondCounter()


#7

[quote=“jules”]Yes, VST hosts can be a bit of a pain sometimes…
[/quote]

“a bit”…

[quote]
Easy enough to measure the time - just use Time::getMillisecondCounter()[/quote]

I just implemented this. Here’s what I’ve done:

  • In setStateInformation(), I store the value of Time:getMillisecondCounter() in a variable, setChunkCalled.
  • in my setCurrentProgram() function, I perform an initial check:

if((Time::getMillisecondCounter()-setChunkCalled)<200) return;

This should bypass any setCurrentProgram calls within the first 200 milliseconds of a project being loaded into the DAW, which is presumably a short enough time that the user can’t select another preset by then.

Things seem to work great in Live. I will test in Reaper and Cubase, in OS 10.6 and Win 7, to make sure that 200 milliseconds is enough time for those DAWS.

It’s 3:04 am here. Time for bed. Tomorrow, more tests, then port the changes to 64 bit AU and x64 Win VST. I have those builds running - they were waiting for this bug fix.

Sean Costello


#8

Sean, do you still use this trick in your VSTs - or has something else changed in the last two years?


#9

I'm having the same problem with my plug-in, but for me it also happens when loading a user preset directly and not just when re-opening a session.  I haven't tried the timer hack, but it seems like there should be a better solution.  I can also repro in Studio One, but not Ableton 9 and Reaper.

Sean, what solution did you end up going with?  

Thanks,

Chris


#10

 

 

Man, I can't beleive this is still an issue.  I'm putting the timer solution in there today.  

 It's clearly a hack though, how in the world is ableton/etc dumb enough to load the program AFTER the other state info?  gah!!!!

 

Are we sure there isn't some better solution?


#11

I've experienced this too. In Cubase 8 (OSX) setCurrentProgram(int index) is called when a new MIDI track is created... There are similar problems on Cubase 8/Windows, but haven't had the time to investigate exactly what's happening there yet (hopefully the same things as on OSX...).  My solution was to do nothing if the index is the same in combination with the timer solution above:

void MyAudioProcessor::setCurrentProgram(int index)
{
 if (currentProgram_m == index) return;
 if (Time::getMillisecondsCounter()-timeLastSetStateCall_m) < 200) return;
 ...
}

Another idea is to always save the current index value along with the parameters in setStateInformation() and restore it to currentProgram_m when setStateInformation() gets called. This might let us remove the timer-solution. Haven't tested it yet though...