Cubase calling setCurrentProgram after setStateInformation


#1

I’m making sure that get/set stateInformation is working properly for my plugin in every host I can and it is in everything…except Cubase on the Mac. For whatever reason, it seems that Cubase is calling setCurrentProgram after setStateInformation when it loads a project with my plugin in it, and therefore destroying the state that the project was saved in.

I tested other plugins and they seem work properly, but not mine…and only in Cubase. Does anyone have any clues here?

Thanks.


Calling order of setStateInformation(...) and prepareToPlay(...)?
#2

Is this VST3? Are you using JUCE_VST3_CAN_REPLACE_VST2?


#3

I had the same problem in bitwig studio with a JUCE VST 2 plugin. Bitwig did also call setCurrentProgram after loading the state. I asked the bitwig support and we came to the conclusion that something in the JUCE framework maybe triggers the host to call setCurrentProgram(int) after or while setting the state. Other 3rd party plugins did not have the issues.

I really wanted to support the VST program list in our new plugin, but i skipped it after the issues came up. I don’t implement the setCurrentProgram() in my plugins anymore. It seems to be to risky…


#4

Isn’t this a matter of re-instating all programs with setStateInformation / getStateInformation? If all programs are captured in the plugin state, a subsequent program change should not cause any problems? Or perhaps I’m missing something…?

I was under the impression that get/setStateInformation was intended for all programs anyway, since there is a separate function to get/set the state for one program only…


#5

I think that’s up to you to decide that. .i did it this way in older plugins.

But i don’t like that the user don’t have any access to the original factory presets anymore. It loads the modified ones. Some plugins have the factory presets saved in preset files. That would not work anymore too.

I’m pretty sure that it also should work when you only save one preset within the plugin state. The host should not call set current program after setting the state. And it seems that he doesn’t in other 3rd party plugins.

Edit: The Bitwig Support team told me that they do not explicit call setCurrentProgram() after setting the state. It seems that our framework triggers something that let the host think that he has to set the current program again. Maybe the same also happens with some cubase versions.


#6

@fabian No, this is VST2, and I am not using JUCE_VST3_CAN_REPLACE_VST2.

This doesn’t happen in my AU or AAX, and it’s making me a little crazy. It also works perfectly in Plugin Host.

And for what it’s worth, after calling setCurrentProgram, Cubase calls prepareToPlay 6 times in a row.


#7

I had the same issue only in Bitwig Studio and also only with the VST and they wrote me that they don’t call setCurrentProgram() after setting the state. So i guess something similar to UpdateHostDisplay with some unlucky notification maybe triggers the setCurrentProgram() call. But only a guess.


#8

FWIW although some of the posts above are suggesting that the JUCE wrapper is triggering setCurrentProgram() spuriously, you can see from a quick search of the VST wrapper code that we only call it from one place, which is a direct callback from the host.

Has anyone tried debugging it to see the stack trace when this unwanted callback happens?


#9

@jules I’m going to do that today.


#10

As a starting place, just to make sure I wasn’t doing anything crazy, I added some DBG statements to the JUCE Demo Plugin and loaded it into MainStage (as an AU), JUCE Plugin Host (as a VST) and Cubase (as a VST). I created an empty project, loaded the demo plugin and saved the project. This was the log when I reopened the projects in each host. It confirms that Cubase is indeed calling setCurrentProgram after setStateInformation.

I’ll get to a stack trace soon.

Mainstage (using AU version)
----------

JUCE v4.3.1
*** constructor
*** prepareToPlay
*** reset
*** getNumPrograms
*** getProgramName
*** reset
*** reset
*** setState (load)
*** getState (save)
*** process
----------
*** process
*** releaseResources
*** destructor




Cubase (using VST version)
-------

JUCE v4.3.1
*** constructor
*** getNumPrograms
*** prepareToPlay
*** reset
*** releaseResources
*** getNumPrograms
*** getNumPrograms
*** getProgramName
*** getNumPrograms
*** getNumPrograms
*** getState (save)
*** setState (load)
*** getNumPrograms
*** setCurrentProgram
*** getNumPrograms
*** getNumPrograms
*** getProgramName
*** getNumPrograms
*** prepareToPlay
*** reset
*** releaseResources
*** prepareToPlay
*** reset
*** releaseResources
*** prepareToPlay
*** reset
*** releaseResources
*** prepareToPlay
*** reset
*** releaseResources
*** prepareToPlay
*** reset
*** releaseResources
*** prepareToPlay
*** reset
*** process
-----------
*** process
*** releaseResources
*** prepareToPlay
*** reset
*** destructor


JUCE Plugin Host (using VST version)
-----------------
JUCE v4.3.1
*** constructor
*** getNumPrograms
*** destructor
*** constructor
*** getNumPrograms
*** getNumPrograms
*** setCurrentProgram
*** getNumPrograms
*** getProgramName
*** setState (load)
*** getNumPrograms
*** getProgramName
*** prepareToPlay
*** reset
*** process
------------
*** process
*** releaseResources
*** destructor

#11

And here’s the stack trace from setCurrentProgram, and you can see that’s called after setStateInformation:

JUCE v4.3.1
*** constructor
*** getNumPrograms
*** prepareToPlay
*** reset
*** releaseResources
*** getNumPrograms
*** getNumPrograms
*** getProgramName
*** getNumPrograms
*** getNumPrograms
*** getStateInformation (save)
*** setStateInformation (load)
*** getNumPrograms
*** setCurrentProgram
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
  * frame #0: 0x0000000151013c33 JuceDemoPlugin`JuceDemoPluginAudioProcessor::setCurrentProgram(this=0x0000000150948200, index=0) at PluginProcessor.h:67
    frame #1: 0x0000000151005f25 JuceDemoPlugin`JuceVSTWrapper::handleSetCurrentProgram(this=0x000000015094ca00, args=(index = 0, value = 0, ptr = 0x0000000000000000, opt = 0)) at juce_VST_Wrapper.cpp:1551
    frame #2: 0x0000000151004cb3 JuceDemoPlugin`JuceVSTWrapper::dispatcher(this=0x000000015094ca00, opCode=2, args=(index = 0, value = 0, ptr = 0x0000000000000000, opt = 0)) at juce_VST_Wrapper.cpp:1134
    frame #3: 0x0000000151002d32 JuceDemoPlugin`JuceVSTWrapper::dispatcherCB(vstInterface=0x000000015094ca58, opCode=2, index=0, value=0, ptr=0x0000000000000000, opt=0) at juce_VST_Wrapper.cpp:1188
    frame #4: 0x000000011cb5c038 VSTPlugManager`___lldb_unnamed_symbol2033$$VSTPlugManager + 24
    frame #5: 0x000000011cb6450f VSTPlugManager`___lldb_unnamed_symbol2193$$VSTPlugManager + 431
    frame #6: 0x000000011cb64308 VSTPlugManager`___lldb_unnamed_symbol2192$$VSTPlugManager + 632
    frame #7: 0x000000011cb5e3aa VSTPlugManager`___lldb_unnamed_symbol2049$$VSTPlugManager + 234
    frame #8: 0x0000000100f7614a Cubase LE AI Elements 9`___lldb_unnamed_symbol84272$$Cubase LE AI Elements 9 + 26
    frame #9: 0x0000000100f7fb3d Cubase LE AI Elements 9`___lldb_unnamed_symbol84542$$Cubase LE AI Elements 9 + 2365
    frame #10: 0x0000000100f8ca03 Cubase LE AI Elements 9`___lldb_unnamed_symbol84614$$Cubase LE AI Elements 9 + 467
    frame #11: 0x000000010045caa9 Cubase LE AI Elements 9`___lldb_unnamed_symbol24657$$Cubase LE AI Elements 9 + 41
    frame #12: 0x00000001000558cf Cubase LE AI Elements 9`___lldb_unnamed_symbol2725$$Cubase LE AI Elements 9 + 959
    frame #13: 0x0000000100072003 Cubase LE AI Elements 9`___lldb_unnamed_symbol3308$$Cubase LE AI Elements 9 + 115
    frame #14: 0x000000010045caa9 Cubase LE AI Elements 9`___lldb_unnamed_symbol24657$$Cubase LE AI Elements 9 + 41
    frame #15: 0x00000001006896e8 Cubase LE AI Elements 9`___lldb_unnamed_symbol39434$$Cubase LE AI Elements 9 + 360
    frame #16: 0x000000010064c283 Cubase LE AI Elements 9`___lldb_unnamed_symbol37840$$Cubase LE AI Elements 9 + 643
    frame #17: 0x000000010064bf3a Cubase LE AI Elements 9`___lldb_unnamed_symbol37838$$Cubase LE AI Elements 9 + 202
    frame #18: 0x0000000100930be3 Cubase LE AI Elements 9`___lldb_unnamed_symbol52813$$Cubase LE AI Elements 9 + 19
    frame #19: 0x0000000100680a82 Cubase LE AI Elements 9`___lldb_unnamed_symbol39194$$Cubase LE AI Elements 9 + 434
    frame #20: 0x000000010064f2d2 Cubase LE AI Elements 9`___lldb_unnamed_symbol37900$$Cubase LE AI Elements 9 + 114
    frame #21: 0x000000010092aabe Cubase LE AI Elements 9`___lldb_unnamed_symbol52742$$Cubase LE AI Elements 9 + 254
    frame #22: 0x000000010067fa96 Cubase LE AI Elements 9`___lldb_unnamed_symbol39170$$Cubase LE AI Elements 9 + 22
    frame #23: 0x00000001006671f4 Cubase LE AI Elements 9`___lldb_unnamed_symbol38397$$Cubase LE AI Elements 9 + 100
    frame #24: 0x00000001005fb20e Cubase LE AI Elements 9`___lldb_unnamed_symbol35937$$Cubase LE AI Elements 9 + 30
    frame #25: 0x0000000100624318 Cubase LE AI Elements 9`___lldb_unnamed_symbol37001$$Cubase LE AI Elements 9 + 840
    frame #26: 0x0000000100620d5e Cubase LE AI Elements 9`___lldb_unnamed_symbol36895$$Cubase LE AI Elements 9 + 1086
    frame #27: 0x0000000100ba5fac Cubase LE AI Elements 9`___lldb_unnamed_symbol64856$$Cubase LE AI Elements 9 + 8812
    frame #28: 0x0000000100ba75f5 Cubase LE AI Elements 9`___lldb_unnamed_symbol64859$$Cubase LE AI Elements 9 + 885
    frame #29: 0x00000001013c718e Cubase LE AI Elements 9`___lldb_unnamed_symbol108989$$Cubase LE AI Elements 9 + 14
    frame #30: 0x00000001017aca09 Cubase LE AI Elements 9`___lldb_unnamed_symbol134631$$Cubase LE AI Elements 9 + 329
    frame #31: 0x00007fffb104152c CoreFoundation`__CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 12
    frame #32: 0x00007fffb104142b CoreFoundation`_CFXRegistrationPost + 427
    frame #33: 0x00007fffb1041192 CoreFoundation`___CFXNotificationPost_block_invoke + 50
    frame #34: 0x00007fffb0fff772 CoreFoundation`-[_CFXNotificationRegistrar find:object:observer:enumerator:] + 2018
    frame #35: 0x00007fffb0ffe75b CoreFoundation`_CFXNotificationPost + 667
    frame #36: 0x00007fffb2a3f997 Foundation`-[NSNotificationCenter postNotificationName:object:userInfo:] + 66
    frame #37: 0x00007fffaec68b1f AppKit`-[NSApplication _postDidFinishNotification] + 297
    frame #38: 0x00007fffaec68884 AppKit`-[NSApplication _sendFinishLaunchingNotification] + 208
    frame #39: 0x00007fffaeb2bbe9 AppKit`-[NSApplication(NSAppleEventHandling) _handleAEOpenEvent:] + 552
    frame #40: 0x00007fffaeb2b83b AppKit`-[NSApplication(NSAppleEventHandling) _handleCoreEvent:withReplyEvent:] + 661
    frame #41: 0x00007fffb2a8ae1d Foundation`-[NSAppleEventManager dispatchRawAppleEvent:withRawReply:handlerRefCon:] + 290
    frame #42: 0x00007fffb2a8ac97 Foundation`_NSAppleEventManagerGenericHandler + 102
    frame #43: 0x00007fffb1e8ff26 AE`aeDispatchAppleEvent(AEDesc const*, AEDesc*, unsigned int, unsigned char*) + 544
    frame #44: 0x00007fffb1e8fc9d AE`dispatchEventAndSendReply(AEDesc const*, AEDesc*) + 39
    frame #45: 0x00007fffb1e8fba9 AE`aeProcessAppleEvent + 312
    frame #46: 0x00007fffb059cddf HIToolbox`AEProcessAppleEvent + 55
    frame #47: 0x00007fffaeb270ed AppKit`_DPSNextEvent + 1833
    frame #48: 0x00007fffaf2a285e AppKit`-[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 2796
    frame #49: 0x00007fffaeb1b7ab AppKit`-[NSApplication run] + 926
    frame #50: 0x00000001017aa4be Cubase LE AI Elements 9`___lldb_unnamed_symbol134574$$Cubase LE AI Elements 9 + 110
    frame #51: 0x00000001015d860d Cubase LE AI Elements 9`___lldb_unnamed_symbol123193$$Cubase LE AI Elements 9 + 77
    frame #52: 0x00000001013c74ea Cubase LE AI Elements 9`___lldb_unnamed_symbol108991$$Cubase LE AI Elements 9 + 314
    frame #53: 0x00000001013cd7ab Cubase LE AI Elements 9`___lldb_unnamed_symbol109074$$Cubase LE AI Elements 9 + 91
    frame #54: 0x00007fffc6d42235 libdyld.dylib`start + 1
    frame #55: 0x00007fffc6d42235 libdyld.dylib`start + 1
(lldb)

#12

Sorry to keep piling on information, but I just found out something…

In my examples above, I had changed getNumPrograms() to return 1 with the assumption that any plugin that responds to setCurrentProgram would report more than 0. But if getNumPrograms() returns 0, then setCurrentProgram is never called, and therefore setStateInformation is never ruined. But it also means that Cubase does not fill it’s own presetName list.


#13

Can you maybe ignore the setCurrentProgram call if the program index hans’t changed? Keep track of the current program index in a member variable and set it to zero in the constructor.


#14

It turns out that this has been discussed here since 2010…and on KVR since 2005…

The threads basically say that this is a somewhat known issue with some VST hosts including Cubase, Bitwig Studio and Ableton Live and likely not a JUCE issue at all.

It looks like the solution is possibly two fold: not changing the program if setCurrentProgram() is called with the same index, and also setting a timer in setStateInformation() and not changing the program if setCurrentProgram() is called within about 500 milliseconds. I’ll be putting this in my code base as soon as the coffee takes effect.

Unreal.


#15

Thanks for keep looking at that. This are really bad news and the work around is risky too. There is nothing worse than a user that looses it’s plugin settings when opening a project. I think i will never support VST presets in this case :slight_smile:


#16

I just found that if I save the current program number with the state, and do the equals test in setCurrentProgram, I can avoid the over-ride problem. This works in FL Studio anyway. You can’t use program 0 because FL independantly remembers the current preset number I used last when re-loading the plug-in.