[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Aw: Re: Re: [sc-users] VstPlugin - new pre-release version!
thanks again for your thoughts!
> You should be able to get the same result, but with fewer UGen arguments, less resources, and more flexibility with something like this:
> SynthDef(\vst, {
> var inChannels = 2, outChannels = 2;
> var input = \in.ar[http://in.ar](0 ! inChannels);
> ReplaceOut.ar(\out.kr[http://out.kr],
> VstPluginUgen.ar(
> input,
> outChannels,
> \bypass.kr[http://bypass.kr](0)
> )
> )
>}).add
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?
> VST's are a weird match for SuperCollider
true! the Pd implementation was a breeze from a design perspective because it maps quite naturally to Pd's architecture (and maybe also because I'm a hardcore Pd user :-)
> 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 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 :-)
> 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.
Christof
Gesendet: Mittwoch, 16. Januar 2019 um 03:53 Uhr
Von: scott@xxxxxxxxxxxxx
An: sc-users@xxxxxxxxxxxxxxxx
Betreff: Re: Re: [sc-users] VstPlugin - new pre-release version!
On Tue, Jan 15, 2019 at 2:28 AM <christof.ressi@xxxxxx[mailto:christof.ressi@xxxxxx]> wrote:
Hi Scott, thanks for your detailed feedback!
> Is there a specific reason to use buses?
after I've decided that there should only be one VstPluginUGen in a Node on the Server, I chose to use busses for input and output so that the needed SynthDef would become trivial (I can create it in *initClass and it works for all plugin configurations). VstPluginUGen is rather an implementation detail and not to be used directly.
You should be able to get the same result, but with fewer UGen arguments, less resources, and more flexibility with something like this:
SynthDef(\vst, {
var inChannels = 2, outChannels = 2;
var input = \in.ar[http://in.ar](0 ! inChannels);
ReplaceOut.ar(\out.kr[http://out.kr], ;
VstPluginUgen.ar(
input,
outChannels,
\bypass.kr[http://bypass.kr](0)
)
)
}).add
This would eliminate a couple arguments from your UGen (and the related complexity w/r/t dealing with buses directly). This might allow you to avoid a copy/fill on VstPluginUgen.cpp:288 and simply pass your audio wirebuf pointer directly in to the VST plugin (maybe the output copy also?). I believe the synth graph will handle allocation, and should even let you know whether you can call processReplacing or just process (e.g. if the input buffer == output buffer pointer).
> - Why is VstPlugin a subclass of Synth rather than a standalone class that e.g. points to a running Synth? For example, why not:
> ~node = { VstPluginUgen.ar(...) }.play;
> ~vstInstance = VstPlugin(~node);
I subclass because currently a VstPlugin instance creates and represents a Node on the Server.
But your example is interesting because I now see that I could use SynthDescLib.at(\foo).children to get the index of the plugin within the Synth. But in case there are more than one VstPluginUGen in the Synth, how would you refer to them in VstPlugin.new? maybe VstPluginUGen.ar could take an optional identifier:
~node = { Out.ar(2, [VstPluginUGen.ar(\chorus, ...), VstPluginUGen.ar(\delay, ...)]) }.play;
~chorus = VstPlugin(~node, \chorus);
~delay = VstPlugin(~node, \delay);
just thinking out loud...
I could imagine a scheme that worked like this:
SynthDef(\foo, {
var input, sig, delayTime;
input = \in.ar[http://in.ar]([0, 0]); // two channels input
delayTime = SinOsc.kr(1).range(1, 4);
// Assume 2 channel input means 2 channel output by default?
sig = VstPluginUgen.ar(\delay, input, args:['delay', delayTime]);
sig = VstPluginUgen.ar(\eq, sig);
Out.ar(\out.kr[http://out.kr], sig);
}).add;
~node = Synth(\foo);
~node.map(\in, ~microphoneInput); // connect some input audio
// Okay, now how do have more control over my vst? Create a control object and point it to the node - you should be able to infer the SynthDef in most cases, but lets set it explicitly here
~delayCtrl = VstPluginControl(~node, SynthDescLib.at(\foo), \delay);
~delayCtrl.set(\feedback, 0.5);
~eqCtrl = VstPluginControl(~node, SynthDescLib.at(\foo), \eq);
~eqCtrl.set(\f1, 440);
You could probably store the linkage between names and UGen index in the SynthDef metadata, which is written out to a file and re-loaded as well. You can access the current SynthDef from a ugen via buildSynthDef (for e.g. storing metadata).
I could also see an initialize-first scheme enforced by the actual sclang objects - where you would do something like:
SynthDef(\vstEq, {
Out.ar(0, VstPluginUgen.ar(\vstIndex.kr, \in.ar[http://in.ar](0));
})
VstPlugin.load("/path/to/eq.vst", doneAction:{
|vst|
~synth = Synth(\vstEq, [\in, ~inputBus.asMap, \vstIndex, vst.index])
});
All just brainstorming! VST's are a weird match for SuperCollider, but incredibly useful - I'm glad this is moving forward.
- S
> - Is it possible to support specifying VST parameters via regular UGen arguments, e.g. a syntax like:
the reasons why plugin parameters are not Synth arguments are exactly those you've mentioned. number of parameters varies *widely* between plugins (lets say 0-160).
you can modulate every parameter with either -setParameter or you can directly map it to a control bus with -mapParameter. I think this gives you lots of flexibility already, but if I rewrite the thing so that VstPluginUGen can be used freely inside a SynthDef, I see that it would be handy to set the parameters via UGen inputs. However, I don't see how this should behave together with the plugin GUI...
OTOH, inside your SynthDef you can easily write to Control Busses which the VstPlugin can read from.
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 (usually, the UI updates as you play?). It might be good to ignore /setParameter changes if you're driving a parameter with some kind of input, but that's straightforward to do.
> - You mention plugin open/close and bank load/save as non-realtime-safe operations. Can these be performed asynchronously, rather than in a way that blocks the audio thread as they are now?
opening/closing a plugin: very difficult; reading/saving programs: also difficult, but actually you can load your preset file in the language into an Int8Array and pass it to -setProgramData. this should be more realtime safe (but it depends really on what the plugin is doing).
I forget that UGens don't really have a non-realtime setup phase, so everything has to happen on the realtime clock. 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 - but at least from the sc side, it would need to work like:
1. Tell plugin to initialize a new foo.vst instance with id=123
2. Plugin reports back when initialization is done
3. Lang creates a Synth with a VstPluginUgen.ar(123, ...)
That should at least provide a path to create and remove VST's without dropouts. Anyone really wanting to use VST's is going to have to build this functionality anyway (e.g. init first, then use later), so it might as well be solved by the core classes?
> - The ugen index in your /u_cmd is hard-coded to 2. This definitely isn't safe
right now, VstPluginUGen is the only UGen in the SynthDef, so it will always have the same index (I think?). getting the index with SynthDef.children would be more save of course!
SynthDef graph generation code changes sometimes, and that can have an effect on the ordering of nodes - this can in turn affect the index. It's *definitely* possible that a change in core sclang code could break your plugin if you're using hardcoded indexes.
- S
_______________________________________________
sc-users mailing list
info (subscription, etc.): http://www.birmingham.ac.uk/facilities/ea-studios/research/supercollider/mailinglist.aspx
archive: https://listarc.bham.ac.uk/marchives/sc-users/
search: https://listarc.bham.ac.uk/lists/sc-users/search/