.play a Score with VSTPlugin

Hi!

would it be possible to play a Score that contains VSTPlugins in the realtime server?

when I am trying to play a NRT Score with VSTPlugins (like the VSTPluginController NRT example) the realtime server is crashing… This is expected because it is not guarantee that one can load VSTs in realtime, right?

Is there any possible workaround?

This is the example:

(
// 1) temporary Server (for Node ID allocation)
~server = Server(\nrt,
    options: ServerOptions.new
    .numOutputBusChannels_(2)
);

// 2) SynthDef for playing the VSTi
SynthDef.new(\my_instrument, { arg out;
    var sig = VSTPlugin.ar(nil, 2, id: \vsti);
    sig = VSTPlugin.ar(sig, 2, id: \fx);
    Out.ar(out, sig);
}).store; // so that the subprocess can find the SynthDef!

// 3) create the VSTi Synths (in the language)

~synth = { Synth.basicNew(\my_instrument, ~server) } ! 2;

// get handles to the plugin controllers
~vst = ~synth.collect { |x| VSTPluginController.collect(x) };

// 4) create the Score:

~score = Score.new;

// search for plugins in default search paths (only needed the first time)
~score.add([0.0, VSTPlugin.searchMsg(verbose: false)]);

// create the synths:
~score.add([0.0, ~synth[0].newMsg]);
~score.add([0.0, ~synth[1].newMsg]);

// open VSTi plugins
~score.add([0.0, ~vst[0].vsti.openMsg("Dexed.vst3", editor: false)]);
~score.add([0.0, ~vst[1].vsti.openMsg("Dexed.vst3", editor: false)]);

// choose programs
~score.add([0.0, ~vst[0].vsti.programMsg(2)]);
~score.add([0.0, ~vst[1].vsti.programMsg(3)]);

// open FX plugins
~score.add([0.0, ~vst[0].fx.openMsg("Dragonfly Early Reflections", editor: false)]);
~score.add([0.0, ~vst[1].fx.openMsg("Dragonfly Plate Reverb", editor: false)]);

// set some FX params
~score.add([0.0, ~vst[0].fx.setMsg(0, 0.2)]); // delay time
~score.add([0.0, ~vst[0].fx.setMsg(1, 0.5)]); // delay feedback
~score.add([0.0, ~vst[1].fx.setMsg(0, 0.9)]); // chorus depth

// (optionally) put plugins into offline processing mode (might yield better results)
~score.add([0.0, ~vst[0].vsti.setOfflineMsg(true)]);
~score.add([0.0, ~vst[0].fx.setOfflineMsg(true)]);
~score.add([0.0, ~vst[1].vsti.setOfflineMsg(true)]);
~score.add([0.0, ~vst[1].fx.setOfflineMsg(true)]);

// make a random melody
~melody = Pbind(
    \type, \vst_midi,
    \dur, Prand(#[0.5, 1], inf),
    \legato, 1,
    \amp, Pexprand(0.5, 1.0, inf),
    \midinote, Pwhite(48, 72, inf)
);

// helper function for recording a Pbind to a Score
~render = { arg vsti, pbind, start, dur;
    var list = pbind.asScore(dur, start, (vst: vsti)).score;
    ~score.score = ~score.score.addAll(list[1..(list.size-2)]); // we have to remove the first and last bundle (/g_new + /c_set)!
};

~render.(~vst[0].vsti, ~melody, 1, 8); // render 8 beats of the first voice, starting at beat 1
~render.(~vst[1].vsti, ~melody, 4, 7); // render 7 beats of the second voice, starting at beat 4

// finally sort the score!
~score.sort;

// 5) render as stereo aiff file

~score.recordNRT("~/nrt_test".standardizePath, "~/nrt_test.aiff".standardizePath,
    options: ~server.options, duration: 15,     action: { "done".postln });

// or save the Score to a file
~score.saveToFile("~/nrt_test.txt".standardizePath);

// remove temporary Server
~server.remove;
)

~score.play;

And this is the error message:

-> a Score
FAILURE IN SERVER /s_new duplicate node ID
FAILURE IN SERVER /u_cmd failed
FAILURE IN SERVER /u_cmd failed
FAILURE IN SERVER /u_cmd failed
FAILURE IN SERVER /u_cmd failed
FAILURE IN SERVER /u_cmd failed
FAILURE IN SERVER /u_cmd failed
FAILURE IN SERVER /u_cmd failed
searching in 'C:\Program Files\VSTPlugins'...
WARNING: VSTPlugin: no plugin loaded!
found 5 plugins
searching in 'C:\Program Files (x86)\VSTPlugins'...
found 5 plugins
searching in 'C:\Program Files\Steinberg\VSTPlugins'...
found 5 plugins
searching in 'C:\Program Files (x86)\Steinberg\VSTPlugins'...
found 5 plugins
searching in 'C:\Program Files\Common Files\VST3'...
Server 'localhost' exited with exit code -1073741819.
server 'localhost' disconnected shared memory interface

The crash itself is not directly related to Score playing, you just happened to trigger it by accident :slight_smile: I have already pushed a fix to the develop branch. Thanks for the report!

Without the crash, you would still get lots of messages that say WARNING: VSTPlugin: no plugin loaded!. Also, the sonic result would not match the NRT version.

Generally, playing a Score only works if all commands are synchronous (e.g. creating/destroying Synths, setting Synth parameters, setting control busses, etc.). Asynchronous commands, however, don’t work as expected. In RT code you typically have to wait for asynchronous commands to finish, either by using an action function, or by calling Server.sync. In NRT mode this is not necessary since asynchronous commands execute synchronously. But if you take such OSC messages and try to execute them on a RT Server, you will run into problems because of the mising synchronization.

With regard to VSTPlugin, you definitely can “play” those parts of the Score that are synchrononous, like setting Synth parameters or sending MIDI events to a VSTi. But anything that is asynchronous, like VSTPlugin.search or VSTPluginController.open, has to be done in advance and with proper synchronization.