I could totally imagine this! (actually, I had implemented VstPluginUGen like that before). I could (re)add a class method to quickly create a SynthDef for a single VST plugin with a given number of inputs/outputs:
VstPlugin.makeSynthDef(name: \vst, nin: 2, nout: 2, replacing: true).add;
this would be equivalent to your SynthDef above. if users want more complex SynthDefs with several VstPluginUGens and/or other UGens, they can create their own SynthDef.
creating a VstPlugin then becomes:
~synth = Synth.new(\vst, [\in, ~input, \out, ~output, \bypass, 0]);
// if there's only one VstPluginUGen we don't need an identifier:
~plugin = VstPlugin.new(~synth);
// otherwise it has to be something like this:
~plugin = VstPlugin.new(~synth, \eq);
// the SynthDef is automatically inferred with SynthDescLib.at(synth.defName) but it could be provided as an optional argument.
// the first version could be combined into a single line:
~plugin = VstPlugin(Synth(\vst, [\in, ~input, \out, ~output, \bypass, 0]));
this doesn't look so bad. it is not as dead-simple as the current implementation but it opens up more possibilities. creating a single VST plugin is still simple enough not to frighten beginners.
what do you think?
Yeah, I think that's definitely heading in the right direction. If there was a need for a more expressive syntax, you can always add something like:
~synth = Synth(...);
~synth.vstUgens[\eq].setParameter(\freq1, 100);
This would allow you to keep Synth and VstPlugin as separate classes and keep their constructors clear and unambiguous, but still make accessing them very natural and easy - and you could manage the VstPlugin objects, rather than making the user keep track of all of them.
> Hmm - I would expect that setting a parameter via a UGen input would be exactly the same as it works now when setting it via \setParameter. In terms of UI, I would expect parameters that are being set in an automated way would work the same as params being driven by automation in a DAW
when I use UGen inputs to automate parameters, how do I tell which one I want to automate? when my plugin has 100 parameters (some have 2000!) and I only want to automate a couple of them? and maybe I want to change which parameters I want to automate? I could use pairs of UGen inputs like 'parameter ID', 'parameter value'... but how do I tell that I don't want to automate a parameter anymore? setting the parameter ID to -1? honestly, mapping/unmapping parameters to control busses like in the current implementation seems like the most straightforward solution to me regarding the abstract nature of VST plugin parameters. but I have to think more about this...
I could definitely see the arguments being key value pairs, of either symbols or numbers (where the symbols are looked up from the list of parameter names once the plugin is loaded?) ... VstPluginUgen.ar(args:[\freq1, 100]). It doesn't seem very useful to allow for switching between the params being controlled, but depending on how it's implemented it would be just as easy to support this as not.
I suppose I'm thinking about this a lot because the FIRST thing I'd want to do is map a VST parameter to some external control - a pattern, a MIDI device, a slider. All the code in the core SuperCollider class lib and external quarks to do this by either writing control data to a bus and mapping that to a synth arg, or sending it directly to a synth or a parent group. All of these methods would need at least some new server interaction code to work correctly - it would be nice if it was possible to more or less use existing libraries or techniques to connect up VST's, at least in simple cases.
> I guess my inclination would be to provide a custom plugin command that would initialize a plugin instance on a background thread, and then call back when finished (like buffer allocation) - that way you could load and unload VST's and have a firm guarantee that you won't break playback or drop audio. Not sure how possible this is
unfortunately, this won't be possible if you want to use the VST GUI because the GUI runs in it's own thread with a Win32 / X11 message pump and some plugin (e.g. all JUCE plugins) expect the plugin to be created and destroyed it that thread (otherwise they crash). it might be less a headache for plugins with the SuperCollider GUI. Let's leave this for later :-)
Ah, I see in your code that you're already doing initialization on a background thread in certain cases, and blocking the audio thread to wait. I think all you'd need to make this thread safe is to make the plugin ptr atomic, and then not block while waiting for the initialization thread (just have it set the plugin pointer). The plugin would have to just output 0's if the plugin pointer isn't set, which is far preferable to blocking the audio thread, which can be disastrous.
> SynthDef graph generation code changes sometimes, and that can have an effect on the ordering of nodes - this can in turn affect the index.
I see! I've already changed the code to deduce the index from the SynthDef.
Awesome! Sorry to be pedantic about this - but the graph generation code is some of the most sophisticated, sensitive, and least understood in SuperCollider. It can take ages to track down issues related to this kind of thing, so it's good to have a solid base to start from!
- S