Skip to content

Tidal set SC effects

Gil Fuser edited this page Jun 6, 2019 · 1 revision

Tidal set Efects in SuperCollider

Here are the mechanism to link parameters from Tidal to parameters from effects and synths that lives in SuperCollider. This is valid for the VST stuff, the orbits effects.
The extra tidal synths and effects are set in a more usual way and also explained here.

Parameter names are the keys used for the OSC communication between Tidal and SuperCollider. They exist in both sides so one can understand each other and address the values to the right Synths. In the Synths, by the way, they are called arguments which are used for any possible external control.


Parameters

Specs from Orbits-Ndefs

The Ndefs which sound inputs are the Tidal Orbits output have parameters - mapped through Specs - referencing to the effects that are inserted in them through the ProxyChain.

You can check their minimum and maximum values in the files FX/fxLib.scd and FX/FXXX_orbs.scd

Parameters from VSTs

In the way we are controlling the VSTs parameters, through .set, we use values from 0 to 1. There's a way to use the actual parameters values to be more descriptive. I'll explain how more below.


The Tidal side

That's actually the simplest part. All you need to do is to include the parameters you'll use in Tidal's config file.

Your Tidal's config file will be slightly different depending on whether you are using Atom, emacs or Vim-Tidal.

The parameters for the VSTs are always pF, meaning they should be floats (between 0 and 1).

-- VST reberb
vwet = pF "vwet"
vdry = pF "vdry"
vdelay = pF "vdelay"
vsize = pF "vsize"
vlocut = pF "vlocut"
vhicut = pF "vhicut"

Parameters for other effects and synths will be either pI for integers or pF

-- compressor
cctrl = pI "cctrl"
thresh = pF "thresh"
slopea = pF "slopea"
clamp = pF "clamp"
relax = pF "relax"

If you have chosen to control your VSTs MIDI style, unless you want to patternize this, you can have descriptive names instead of using in yout Tidal patterns ccv (control values) and ccn (control numbers) like this:

-- phosycon
cutoff x = ccv x # ccn 2
reson x = ccv x # ccn 3
sweept x = ccv x # ccn 4
envmod x = ccv x # ccn 5
envatt x = ccv x # ccn 6
envdec x = ccv x # ccn 7
accdec x = ccv x # ccn 8
envacc x = ccv x # ccn 9

back to top


The SuperCollider side

To control parameters that are not in SynthDefs loaded into SuperDirt via ~dirt.loadSynthDefs we need either a OSCFunc or a OSCdef. I prefer the latter because its easyer to change it any time without stopping it. You just have to use \play2 as path and it will get the OSC messages from Tidal.

OSCdef(\oscFromTidal, { |msg|
  MFdef(\theHub).value(msg);
}, '/play2').permanent_(true)

Inside the OSCdef we are using there's a MFdef.
MFdef is great and a often overlooked feature of the JitLib. It allows great flexibility specially to hold values and use them across functions you write inside of it.

a named MFunc, a proxy for multiple modal functions

By using the beloved MFdef inside the OSCdef we can use the incoming keys and values in any function.
In our case we are setting the value of the theHub MFdef as the argument msg from its surrounding OSCdef. The OSC messages flow directly from the latter to the former and we can use them in the functions described next by having in them the argument msg again.

Here is the simplest example you will find commented in Tidal_set_FX.scd:

MFdef(\theHub).add(\post, {|msg| msg.postln});
MFdef(\theHub).enable(\post);
MFdef(\theHub).disable(\post);

If you change e.g. the post function at any time, re-evaluating the .add part, it will be substituted JIT style.

setOrb

We need a way to point the incoming values and keys to the right Orbit-Ndef, otherwise when you use a parameter in Tidal it will change the values from all of them. That's what the setOrb function is for. It gets the value from the key \orbit and use it to form the key of one of the four Ndefs we are using, e.g. Ndef(\orb0). It will then set the value of q.orbz with it.

setVST

This function is similar to setOrb but only gets the value of \orbit and set q.vstorb with it.

tidalSetOrbs

That's the function where the real magic happens. Here are the details of how it works.

If I undertand it correctly msg.collect gets the whole OSC msg array then to be used.
We'll be using key and indices i to find what we need.

switch

This is a Control Structure not as often used as if statements but by using it we avoid nested if statements. Take look of the explanation of what it is exactly from the SC Help:

Object implements a switch method which allows for conditional evaluation with multiple cases. These are implemented as pairs of test objects (tested using if this == test.value) and corresponding functions to be evaluated if true. The switch statement will be inlined if the test objects are all Floats, Integers, Symbols, Chars, nil, false, true and if the functions have no variable or argument declarations. The inlined switch uses a hash lookup (which is faster than nested if statements), so it should be very fast and scale to any number of clauses.

Well, we have a great number of clauses here. But there's a lot o repetition too. There are four different trueFunctions:

{ \depth } { q.orbz.set( key, msg[i+1] ) }

\depth is the test value
q.orbz.set will set the Orbit-Ndef we got in the setOrb function key should be \depth because for this function to be evaluated key === depth is true - this was set in switch(key).
msg[i+1] says the the value to be set is the next msg index which means depth's value.

{ \delta } {
    q.orbz.set( key, msg[i+1] );
    q.deltas.setPairs( q.vstorb, msg[i+1].asFloat )
  }

This specific function set the five channel Control Bus in the file FX/fxLib.scd with the delta value sent by Tidal.
setPairs is like set but "set the bus values by pairs of index, value"
q.vstorb gets a number from the setVST function to be used as index.

{ \vlocut } { q.verbs[q.vstorb].synth.set( key, msg[i+1].asFloat ) }

q.verbs is an array that holds all the VST reverb synths.

q.verbs = [~verbOrb0, ~verbOrb1, ~verbOrb2, ~verbOrb3]

q.vstorb is used as q.verbs index.
.synth access the Synth that holds the VST.

note that unlike playing normal synths in Tidal, playing MIDI synths won't create new new synth nodes. That's why we need to access the created synth node itself.

{ \areson } { ~acid.synth.set(key, msg[i+1].asFloat ) }

The VST instruments are being used in one orbit, so, the functions to set them are simpler.

back to top


Use VSTs "native" parameter values

Let's take an example: In TAL-NM the LFO1 Rate parameter goes from 0.01 Hz to 514.22 Hz. You might feel more intuitive dealing with this range of values in Tidal instead of 0 to 1 values. To do this, create a variable that will map the value received from its range to values between 0 and 1:

SynthDef(\vst_tal, { | out bypass = 0 tvolume = 0.5 tcutoff = 1, lfo1rate = 0.5 |
  var lfo1r;
  lfo1r = (lfo1rate).explin(0.01, 514.22, 0, 1);
  OffsetOut.ar( out, VSTPlugin.ar( nil, ~dirt.numChannels, bypass,
    params: [ 1, tvolume, 3, tcutoff, 28, lfo1r ] )
  );
}).add;

In this example the values are mapped using a explin (exponential to linear) because - like most of the frequency related parameters - this is how the LFO1 Rate works. If we had a linear parameter we would have used linlin instead.

back to top

Clone this wiki locally