SuperDirt and VSTPlugin integration

Hello community,
this is my first post here so please let me know if I can make it better.


I have been exploring VSTPlugin and so far I have been able to send MIDI messages to a VSTPluginController from within TidalCycles. The next desirable step would be to have parameter automation from within TidalCycles as well.

I learnt that I can automate plugin parameter in different ways:

  • using set e.g. ~vsti.set(1, 0.5);
  • using an Event e.g. (vst: ~vsti, type: \vst_set, params: 1, 1: 0.5).play;

given that ~vsti is the instance of VSTPluginController.

What I have been experiencing is that I see the change in the vst when I execute the statements myself, but when I wrap them inside SuperDirt they have no effect at all.

I have two ideas that I have been trying:

  • having parameter automation as effect in SuperDirt so I do not have to waste orbits just for the sake of automating parameter, here for documentation on how to add an effect to SuperDirt.
  • having SuperDirt execute a function that triggers the parameter change, here for documentation on how to call a function from SuperDirt.

Following the first idea, this is the code I wrote:

(
var diversions = ();
~diversions = diversions;
// d1 is a DirtOrbit
~d1.defaultParentEvent[\diversion] = { diversions[~s].value };
)

(
~diversions[\autoParam]  = {
	~vsti.set(1, 0.5);
    // alternative: (vst: ~vsti, type: \vst_set, params: [1], 1: 0.5).play;
};
)

// try it out
(type: \dirt, s: \autoParam, dirt: ~dirt).play;

This does not trigger any change in the vst parameter, plus I do not really know how to give the parameter/value pair as input.

Following the second idea, this is the code I wrote:

/* Parameter automation from within TidalCycles */
(
// add module in SuperDirt, instance is ~dirt
(
~dirt.addModule('param-mod', { |dirtEvent|
	dirtEvent.sendSynth('param-mod' ++ ~dirt.numChannels,
		[
			param: ~param,
			value: ~value,
		]
	)
}, { ~param.notNil and: { ~value.notNil } });
);
// create fx function
(
SynthDef("param-mod" ++ ~dirt.numChannels, {| param, value |
	(vst: ~vsti, type: \vst_set, params: [param], param: value).play;
}).add;
);
)

This does not produce any change in the vst parameter, although I see the event being triggered from within TidalCycles.

I would really appreciate if anyone could point me where/what I am doing wrong, or whether there is a better way as I am not super familiar with SuperDirt, and more generally SuperCollider, concepts.

Thank you :slight_smile:

I learnt that I can automate plugin parameter in different ways:

  • using set e.g. ~vsti.set(1, 0.5);
  • using an Event e.g. (vst: ~vsti, type: \vst_set, params: 1, 1: 0.5).play;

There are two more ways:

  • via UGen inputs, see the params argument in VSTPlugin.ar
  • mapping a Control bus to a parameter, see VSTPluginController.map

Maybe one of those methods map more naturally to TidalCycles/SuperDirt?


I don’t know anything about TidalCycles, so I can’t help much, but I remember various people trying to use it together with VSTPlugin. Unfortunately, I can’t find those messages/posts anymore…

Anyway, if anyone has good examples for how to control VSTPlugin with TidalCycles/SuperDirt, feel free to send them to me and I’ll include them in the documentation. Or even better: write a Guide and make a PR to the repo :slight_smile:

Christof

1 Like

Also without knowing much of superdirt, I’d guess that either of these sounds more likely to work.

Particularly the first: If, in a normal superdirt synth, the parameters are SynthDef arguments, then the easiest way to integrate VSTPlugin would be to create a SynthDef where the parameters are SynthDef arguments and use tidal’s existing mechanism to set synth controls.

The diversions idea… is this connected to tidal parameters? It seems to be about playing new notes.

The second idea:

This will definitely not work. (...).play is a language-side only operation; the server does not know how to do this. A SynthDef defines server-side calculations – so a SynthDef cannot and will never be able to do what you’re asking here.

hjh

Thank you both for your replies! I’ll try to shed some light on how (I think) SuperDirt works and what I tried based on your input.

I can’t exactly say whether SuperDirt is a synth or not. I tried to make some sense from the class and it seems like that SuperDirt class is responsible to connecting to the Tidal client so to receive OSC messages and playing a DirtEvent. What I understand is that there is a 1:1 correspondence between messages coming from Tidal and a DirtEvent i.e. a DirtEvent instance is created for each Tidal scheduled message.
If I look further into the DirtEvent script, I see that the instance is playing a synth. I also assume it is responsible of freeing resources when the event is ended.

Quoting from the documentation: “You can define arbitrary functions to be called from Tidal”.
What I thought is that I can wrap something that I execute from within SuperCollider and it works e.g.

~vsti.synth.set(\idx, 25, \value, 0);

inside a function and have it executed.


Coming back to the attempts:

I have been trying this and now the definition looks like this:

SynthDef(\vstToTidal, {|out, idx, value|
	var sound = VSTPlugin.ar(nil, ~dirt.numChannels, 
		id: \Arp2600,
		params: [idx, value]);
	Out.ar(out, sound);
}).add;

and I then use your ~vsti.synth.set(\idx, 25, \value, 0.2); to automate parameters.
This works inside SuperCollider but still does not when I call it from Tidal e.g. see above.

Furthermore, I also tried to encapsulate this into a SynthDef and call it from Tidal the way it is shown in some tutorial. The definition looks like this:

(
SynthDef(\paramChange, {
	~vsti.synth.set(\idx, 25, \value, 0);
}).add;
)

but still I get ERROR: Message <some word> not understood. where <some word> changes.

I would love to try this but I do not know how to map a DirtEvent to a Bus.
(Sorry I am not super skilled :slight_smile:)

I will definitely do so if we are able to crack the code :slight_smile:

Again, this won’t work. Every method you call in a UGen graph function is only called once (when the function is evaluated). This is certainly not what you want here. I think you might have misinterpreted the tutorial you’ve read.

Maybe @julian can give you some hints.

IIUC Tidal is sending messages to SuperDirt, and SuperDirt responds by dispatching to some hook to handle the message.

You now have a line of SC code that changes the parameter, but it isn’t being called from Tidal.

This means that you/we haven’t found the appropriate hook.

Yes, there is a feature in SuperDirt to evaluate user code – but if this exists in some hook other than the one handling parameter automation, then it won’t be called from parameter automation.

Here, my ability to help is limited by the fact that I don’t know Tidal internals at all. Probably very few people on this forum do. It may be worthwhile to ask the Tidal forum how parameter automation is handled – to find out which hook to modify.

hjh

We are trying to do so so we can get the best from both worlds.
I hope Julian can help us understand a bit better.

Thank you for your support so far, I love this community :slight_smile:

1 Like