Closed Catsvilles closed 4 years ago
normally, in realtime mode I would reuse ~fx1 variable to "attach" the effect, something like:
I don't think this works even in RT mode. The first argument of VSTPluginController.new
must be a Synth
, not a VSTPluginController
*). So generally have to do the following:
~synth = Synth.basicNew(\first);
~ins= VSTPluginController(~synth, \Ins);
~filter = VSTPluginController(~synth, \Filter);
// etc.
BUT I think you've actually discovered a NRT bug. The above doesn't work if I use SynthDef.store
(for some reason, the VSTPlugin
id becomes nil
), but it works with SynthDef.add
. I have to investigate why...
In the meantime, I've updated the NRT example in the develop branch. It was pretty outdated. You don't need to delay the openMsg
anymore and there's now a built-in vst_midi
Event type. Here's the new example:
nrt_example.zip
Would be really cool to see more examples using NRT
Unfortunately, NRT mode in SuperCollider is pretty awkward to work with. You have to think completely different. The problem is the asynchronous nature of a Client Server app. In a synchronous app like Pd, you can mostly use the exact same patch both for RT and NRT rendering.
On the other hand, you can simply "record" patterns (as my example shows), which makes composing NRT scores less awkward.
*) Actually, this would be a nice feature to pass VSTPluginController
for easier chaining. I'm putting this on my list: https://git.iem.at/pd/vstplugin/-/issues/75
Actually, turns out that the problem with .store
also occurs in RT synthesis! I just happened to always use SynthDef.add
. It is strange: it can find the SynthDef and get the UGens list, but members like id
are always nil
, although they are definitely set in the SynthDef function...
@Spacechild1
I don't think this works even in RT mode. The first argument of VSTPluginController.new must be a Synth
Yes, this is exactly how I do in RT, sorry to confuse, just wanted to show the examples of NRT which do not work... Anyway, good to know I'm not going crazy and the issue is with SC, because I really tried for hours to solve this and most of the the ways were perfectly working in RT but not NRT :D
You don't need to delay the openMsg anymore and there's now a built-in vst_midi Event type.
Wow, cool! That probably means we don't have to rely on custom Event and now easily can play chords? Because before that, I had to hack into the custom Even function in the NRT example to make chords work.
Another thing I would like to ask now but didn't want to create issue for is if there is a way to to switch between banks of midi programs? Say, the VST plugin allows only 127 midi programs to switch between, but what we could do is make 127 folder and fill them with the according number of presets. Switching to folder is easy with ~fx.program_(1); but after that I have no idea how to select the program within.
From what I understand the first thing to do is actually send midi command to switch to the bank and then we should be able to switch between programs within the bank. Is there a way to do this with VSTplugin?
That probably means we don't have to rely on custom Event and now easily can play chords?
Yes! Also, vst_midi
schedules MIDI messages as OSC bundles, like all other Server Event types), which guarantees proper timing. For example, VSTis now sound exactly at the same time as other Synths scheduled for the same logical time. I think I have introduced this in v0.3.
Anyway, good to know I'm not going crazy and the issue is with SC
I now understand what's going on... SynthDesc.store
reconstructs itself from the file it has written, losing all extra information stored in the UGens. I think I have to either work with metadata, or tell people to only use SynthDef.add
:-)
From what I understand the first thing to do is actually send midi command to switch to the bank and then we should be able to switch between programs within the bank. Is there a way to do this with VSTplugin?
Yes, just send the appropiate MIDI CC messages (read about "MIDI Bank Select"). Given that your plugin actually supports MIDI program and bank changes in the first place!
Say, the VST plugin allows only 127 midi programs to switch between, but what we could do is make 127 folder and fill them with the according number of presets. Switching to folder is easy with ~fx.program_(1); but after that I have no idea how to select the program within.
Generally, the behavior of program
highly depends on the plugin. Some (VST2) plugins allow to save preset banks (see readBank
/writeBank
) and you can use program
to switch between individual programs in the given bank. Many plugins just use program
for (read-only) factory presets. Some plugins don't use program
at all, instead they only use VST preset data/files.
Of course, you can always simply use loadPreset
/savePreset
and organize the presets in any way you want for a given project. For example, a "bank" could simply be an Array of preset names and then you can have an Array of "banks". Assuming that there's a good reason for accessing programs numerically in the first place.
That being said, program
and MIDI program/bank changes have the advantage that they work synchronously (in the RT thread), while the preset methods are asynchronous. On the other hand, you can fake synchronous "presets" by saving/sending the parameter changes you're actually interested in.
@Spacechild1 Unfortunately, the new example of using VST effects and instruments in NRT doesn't seem to work + I receive errors: FAILURE IN SERVER /u_cmd failed
I will keep digging but logically the code should be working, have no idea what's wrong...
The errors seem to come from VST effects somehow.
can you post a full code example and the exact error messages?
Basically the code is the same as in the example you provided. I tried with less VST effects too, so I don't think the issues is using too many of them :)
`// SynthDef for playing the VSTi SynthDef.new(\helm, { arg out; var sig = VSTPlugin.ar(nil, 2, id: \vsti); sig = VSTPlugin.ar(sig, 2, id: \fx); Out.ar(out, sig); }).add; // don't use .store!
// 1) create the VST Synths in the language
// make two Synths
~synth1 = Synth.basicNew(\helm); ~synth2 = Synth.basicNew(\helm);
// get handles to the plugin controllers ~vsti1 = VSTPluginController(~synth1, \vsti); ~fx1 = VSTPluginController(~synth1, \fx); ~low = VSTPluginController(~synth1, \fx); ~hi = VSTPluginController(~synth1, \fx);
~vsti2 = VSTPluginController(~synth2, \vsti); ~fx2 = VSTPluginController(~synth2, \fx);
// 2) create the score:
~score = Score.new;
~score.add([0.0, VSTPlugin.searchMsg(verbose: true)]); // search for plugins in default search paths // ~score.add([0.0, VSTPlugin.searchMsg(["/my/plugin/directory"], useDefault: false, verbose: true)]); // use custom search path
// create the synths: ~score.add([0.0, ~synth1.newMsg]); ~score.add([0.0, ~synth2.newMsg]);
// load VSTi plugins ~score.add([0.0, ~vsti1.openMsg("Helm")]); ~score.add([0.0, ~vsti2.openMsg("Helm")]);
// choose programs ~score.add([0.0, ~vsti1.programMsg(1)]); ~score.add([0.0, ~vsti2.programMsg(2)]);
//load FX ~score.add([0.0, ~fx1.openMsg("GLFO")]); ~score.add([0.0, ~fx2.openMsg("GChorus")]); ~score.add([0.01, ~low.openMsg("GLow")]); ~score.add([0.01, ~low.programMsg(rrand(0, 6))]); ~score.add([0.01, ~hi.openMsg("GHi")]); ~score.add([0.01, ~hi.programMsg(rrand(0, 2))]);
// set some FX params ~score.add([0.0, ~fx1.programMsg(0)]); ~score.add([0.0, ~fx2.setMsg(0, 0.9)]); // chorus depth // make a random melody ~melody = Pbind( \type, \vst_midi, \dur, Pxrand(#[8, 16, 24, 32], inf), \legato, 1, \amp, Pexprand(0.5, 1.0, inf), \midinote, Pxrand(Scale.major.degrees + 48, inf) );
// helper function for recording a Pbind to a Score ~render = { arg score, vsti, pbind, start, dur; var list = pbind.asScore(dur, start, (vst: vsti)).score; score.score = score.score.addAll(list[1..(list.size-1)]); // we have to remove first and last bundle! };
~render.(~score, ~vsti1, ~melody, 1, 16); // render 8 beats of the first voice, starting at beat 1 ~render.(~score, ~vsti2, ~melody, 16, 8); // render 7 beats of the second voice, starting at beat 4
// end ~score.sort; // important! ~score.add([~score.endTime + 4, [0]]);
// 3) render stereo aiff file
~score.recordNRT("~/nrt_test".standardizePath, "~/nrttest.aiff".standardizePath, options: ServerOptions.new.numOutputBusChannels(2));
// 4) save the Score to a file
~score.saveToFile("~/nrt_test.txt".standardizePath); )`
Full console output:
-> a Score Faust: supercollider.cpp: sc_api_version = 3 Faust: FaustGreyholeRaw numControls=7 Faust: supercollider.cpp: sc_api_version = 3 Faust: FaustJPverbRaw numControls=11 Found 0 LADSPA plugins VSTPlugin 0.3.3start time 0 nextOSCPacket 0 nextOSCPacket 0 vst_search: bufnum -1 out of range nextOSCPacket 0 nextOSCPacket 0 nextOSCPacket 0 nextOSCPacket 0 nextOSCPacket 0 nextOSCPacket 0 nextOSCPacket 0 FAILURE IN SERVER /u_cmd failed nextOSCPacket 0 FAILURE IN SERVER /u_cmd failed nextOSCPacket 0 FAILURE IN SERVER /u_cmd failed nextOSCPacket 0 FAILURE IN SERVER /u_cmd failed nextOSCPacket 0.01 FAILURE IN SERVER /u_cmd failed nextOSCPacket 0.01 FAILURE IN SERVER /u_cmd failed nextOSCPacket 0.01 FAILURE IN SERVER /u_cmd failed nextOSCPacket 0.01 FAILURE IN SERVER /u_cmd failed nextOSCPacket 1 nextOSCPacket 16 nextOSCPacket 17 nextOSCPacket 17 nextOSCPacket 24 nextOSCPacket 24 nextOSCPacket 28 nextOSCPacket 28 X11: terminated UI thread
OS Ubuntu 18, using latest SC compiled from source and latest release version of VSTplugin.
Very curios, if I want to add SC effects too, should I create a different Synthdef for every VSTi routed to it's effects or it's somehow possible with just one Synthdef? Actually doesn't seem possible, because what if we want some VSTi to have JPverb and some not? ¯_(ツ)_/¯
~fx1 = VSTPluginController(~synth1, \fx);
~low = VSTPluginController(~synth1, \fx);
~hi = VSTPluginController(~synth1, \fx);
you're getting three handles to the same VSTPlugin instance. this can't work.
~score.add([0.0, ~fx1.openMsg("GLFO")]);
~score.add([0.0, ~fx2.openMsg("GChorus")]);
~score.add([0.01, ~low.openMsg("GLow")]);
~score.add([0.01, ~low.programMsg(rrand(0, 6))]);
~score.add([0.01, ~hi.openMsg("GHi")]);
~score.add([0.01, ~hi.programMsg(rrand(0, 2))]);
~fx1, ~low and ~how point to the same VSTPlugin UGen instance... also, the 0.01 delay shouldn't be necessary anymore.
Very curios, if I want to add SC effects too, should I create a different Synthdef for every VSTi routed to it's effects or it's somehow possible with just one Synthdef?
You can of course add any kinds of SC effects within the Synthdef. Sometimes it's ok to have a static FX chain in a single SynthDef, but often it's better to have a dedicated Synth for each effect (SC or VST), so you can route them flexibly. This is especially true for send effects (like a reverb), like in a DAW, where you usually don't put one reverb plugin on each track, but rather send the tracks to an FX bus which contains the reverb plugin. Unless, of course, you want a different room acoustic on each track. Same thing for master FX.
Actually doesn't seem possible, because what if we want some VSTi to have JPverb and some not?
That's exactly the point. Only put a VSTi and an FX into the same SynthDef if they really form a conceptual unit.
Personally, I always make a very general SynthDef like
SynthDef(\insert_2ch, { arg bus;
ReplaceOut(bus, VSTPlugin.ar(In.ar(bus, 2), 2));
}).add;
and then create a seperate Synth for each plugin, so I can connect them freely. See also the "Serial FX chains" section in VSTPluginController.schelp.
Wow, this is getting out of hand, I'm literally using the code from the example, without changing anything and still getting bunch of
FAILURE IN SERVER /u_cmd Node 1 not found
Yes, first thing it doesn't like from the code is the name \my_instrument which I change to \helm, I guess this is some SC specific stuff.
Also, following your comments I tried to assign every effect to it's unique id, when using many of them but as I said when trying to use even VST effect the errors still occur. Without any everything works fine. I'm sorry, this is taking way to much for such (you would think) an easy thing. Have no idea, if this is my OS, SC problem, will reboot to start again now.
I think I found the problem! SynthDef.add
doesn't store the SynthDef on disk, but then the scsynth subprocess can't find the SynthDef, so we need to use SynthDef.store
(that's why I used in the first place, I just forgot :-). I think you were getting all those weird errors because the scsynth process was loading an outdated SynthDef file, while the language was seeing the new SynthDef.
The solution is simple: use SynthDef.store
and pass the SynthDef to VSTPluginController.open
. See the updated example:
nrt_example.zip
This this work now?
@Spacechild1 Yes, it works! No errors this time, thank you very much! For now it seems that only 1 effect is working and even if adding more effects with unique IDs, the sound is not changing and responding. Can only hear the first effect. But with the examples you provided and told me to look through (like "Serial FX chains" ) I will surely figure it out on my own, need to go deeper into SC and learn more, anyway :) Thank you very much for your time and valuable advises and efforts! I hope more people will find all these examples here helpful in future. Cheers!
For now it seems that only 1 effect is working and even if adding more effects with unique IDs, the sound is not changing and responding.
For me it certainly is :-). Anyway, thanks a lot for the report! I will now update the example.
@Spacechild1 Hey, sorry to bother you again but I just wanted to ask if it's possible somehow to specify attack and/or release times when playing a note using \type, \vst_midi
with Pbind? I tried to search the whole project and help files but couldn't find anything on how to approach this.
From what I understand this should be possible via midi CC messages. But it's possible I misunderstood something... Anyway, will really appreciate if there is an easier way to do it and yo can point me to it! Thanks, again :)
From what I understand this should be possible via midi CC messages
This has nothing to do with MIDI messages.
Almost every synthesizer has some kind of envelope-generator which can be controlled with VST parameters, but it's usually the same for all voices. If you want to play polyphonically and need different envelopes for each voice, you can create several VSTis and do your own voice allocation in Supercollider.
Note that you can always create custom envelopes in Supercollider with EnvGen
, but obviously this only works if you play the synthesizer monophonically, because the envelope affects the total output of the synthesizer.
@Spacechild1 Thanks again! I think I will try to use EnvGen for now.
Hi, I'm following the help file with an example of rendering score in NRT thread, but unfortunately the example shows using instruments only and I would like to use both SC effects and VST effects in one Synthdef.
I don't know if this is the right approach but I'm trying to achieve something like "channels" in traditional DAW where we would have multiple effects and instrument per channel. So, I have multiple Synthdefs that look something like that:
`SynthDef(\first, { |out = 0| var snd;
snd = VSTPlugin.ar(nil, 2, id: \Ins); snd = VSTPlugin.ar(snd, 2, id: \Filter); snd = VSTPlugin.ar(snd, 2, id: \Reverb); snd = VSTPlugin.ar(snd, 2, id: \Compressor); snd = JPverb.ar(snd, 2, 0.7, 4, modDepth:1); snd = Limiter.ar(snd, level: 0.5, dur: 0.1); Out.ar(out, snd); }).store;`
then I'm trying to create the synth:
~fx1 = VSTPluginController(Synth.basicNew(\first));
and this is where problems start, normally, in realtime mode I would reuse ~fx1 variable to "attach" the effect, something like:
~filter = VSTPluginController.new(~fx1, \Filter);
but this doesn't work in NRT mode. I'm sure there is an easy solution to solving this, I just cannot find it due to the lack of experience. Also, not clear how to manage VST effects since we should probably create only one synth per Synthdef?
~score.add([0.0, ~fx1.synth.newMsg]);
Thanks a lot! Would be really cool to see more examples using NRT, I cannot seem even to find/grasp the appropriate way of composing a song in NRT using rendering per beats, seems a bit messy to call every instrument per beat, but ofc, it's just me not fully understanding the things, for sure it will get better with time/experience. :)