Adding new parameters to tidal

I've just tried to use the supercollider generated code snippet:

-- | parameters for the SynthDefs: dirt_grain_envelope2
let (contour, contour_p) = pF "contour" (Nothing)
    (curve, curve_p) = pF "curve" (Nothing)
    (plat, plat_p) = pF "plat" (Nothing)

But that failed. Any idea what is wrong?

that failed how?

• Couldn't match expected type ‘Pattern Double’
                  with actual type ‘Maybe a0’
    • In the second argument of ‘pF’, namely ‘(Nothing)’
      In the expression: pF "plat" (Nothing)
      In a pattern binding: (plat, plat_p) = pF "plat" (Nothing)

And what did you expect? (Genuine question, I can't infer from your code).
What is a "supercollider generated code snippet"?

The type is

pF :: String -> Pattern Double -> ControlPattern

so the second argument must be a pattern (Nothing is not a pattern) and the result will be a pattern (but your code was expecting a pair)

Honestly, I never understood how the pF function worked. But it seems that its signature has changed at some stage?

Looking inot https://github.com/tidalcycles/Tidal/blob/master/src/Sound/Tidal/Params.hs I see that now one writes simply bandq = pF "bandq" etc.

Then perhaps we can improve documentation.

Something like: pF s p takes s :: String and p :: Pattern Float and computes a control pattern that has the same structure as p, where each value v :: Float in p results in a ControlMap with single key s and value v.

pF "foo" "[0.1,2.3]"
(0>1)|foo: 0.1f
(0>1)|foo: 2.3f

Yes things are simpler (and more flexible) now, that should indeed just be

let contour = pF "contour"
    curve = pF "curve"
    plat = pF "plat"
1 Like

On tuesday, I plan to introduce a bit of granular synthesis / microsound to the students, so I've added a grain envelope to superdirt. We'll hav eto think about parameter names in tidal …

SynthDef("dirt_grain_envelope" ++ numChannels, { |out, sustain = 1, tilt = 0.5, plat = 0, curve = -3 |
		var signal = In.ar(out, numChannels);
		var p = plat.clip(0, 1);
		var c = tilt.clip(0, 1);
		var riseAndFall = 1 - p;
		var attack = c * riseAndFall;
		var release = (1 - c) * riseAndFall;
		var hold = p;
		var env = EnvGen.ar(Env.linen(attack, hold, release, 1, curve), timeScale: sustain);
		signal = signal * env;

		ReplaceOut.ar(out, signal);
	}, [\ir, \ir, \ir, \ir, \ir]).add;


(
~dirt.addModule('grenvelo',
	{ |dirtEvent|
		dirtEvent.sendSynth('dirt_grain_envelope' ++ ~numChannels,
			[
				sustain: ~sustain,
				tilt: ~tilt,
				plat: ~plat,
				curve:~curve,
				out: ~out
		])
}, { ~tilt.notNil });
);
let tilt = pF "tilt"
    plat = pF "plat"
    curve = pF "curve"

d1 $ (slow 3) $ sound "hh*20  can*10 uxay:2*18 speakspell*8 "
      # end 0.8
      # begin 0.2
      # tilt (slow 7 tri)
      # plat (slow 12 tri)
      # n 0
      # legato (slow 5 tri * 13)
      # curve (-6)

d1 $ sound "hh*20 bd*56 can*100" # tilt (sin 0.2 + 1 * 0.5) # plat 0 # n "0 4*7 6 0*9 7 -4*10" # legato (sin 0.421 + 1 * 0.5) # curve (-9) # begin 0.2
6 Likes

I think we should think about a better naming scheme for parameters. The effects are treading on each others' feet a little.

I just had a play with this code. Really cool!

I've been looking for a granular synthdef to use with Tidal. A granular engine mixed with Tidal's control sounds like it would be really powerful.

This synthdef seems just as you describe, a single granular envelope that can transverse through the buffer. Do you think it might be possible to modify code like this so that it could create multiple grain streams at once?

Did you have a problem that looked like this?

ERROR: Variable 'numChannels' not defined.
  in interpreted text
  line 31 char 45:

  SynthDef("dirt_grain_envelope" ++ numChannels ,{ |out, sustain = 1, tilt = 0.5, plat = 0, curve = -3 |
                                               
  		var signal = In.ar(out, numChannels);
-----------------------------------

Yes, I had to modify the SC code by adding one line above. Here's the code I ended up with:

(
var numChannels =  ~dirt.numChannels;

SynthDef("dirt_grain_envelope" ++ numChannels, { |out, sustain = 1, tilt = 0.5, plat = 0, curve = -3 |

	    var signal = In.ar(out, numChannels);
		var p = plat.clip(0, 1);
		var c = tilt.clip(0, 1);
		var riseAndFall = 1 - p;
		var attack = c * riseAndFall;
		var release = (1 - c) * riseAndFall;
		var hold = p;
		var env = EnvGen.ar(Env.linen(attack, hold, release, 1, curve), timeScale: sustain);
		signal = signal * env;

		ReplaceOut.ar(out, signal);
	}, [\ir, \ir, \ir, \ir, \ir]).add;
)

(
~dirt.addModule('grenvelo',
	{ |dirtEvent|
		dirtEvent.sendSynth('dirt_grain_envelope' ++ ~numChannels,
			[
				sustain: ~sustain,
				tilt: ~tilt,
				plat: ~plat,
				curve:~curve,
				out: ~out
		])
}, { ~tilt.notNil });
);
3 Likes

That is strange, I can't really get a feeling of what's going on when I have this effect running.

UPDATE: ok, now I get it, that is really cool!

1 Like

I think you can do two things to start with if you want to have parallel streams:

  1. using jux
  2. setting legato quite high so you have event bleeding

This is e.g. something I am trying right now (the sample won't work though, sorry for that)

d1
    $ juxBy 0.5 (slow 4)
    $ jux (slow 2)
    $ every 4 (
        (struct "t(5,16)")
        . (# s "juno_wobble")
        . (|- note 9)
        . (# squiz (slow 2 $ range 0.5 3 saw))
    )
    $ sound "[piano_f*2, piano_f:3*3, piano_f:5*8]"
    # tilt (slow 2 $ perlin)
    # plat (slow 3 $ segment 64 $ rand)
    # curve (irand 9)
    # legato (slow 4 $ range 2 20 tri)

PS: @yaxu I would love to know if there is a way to embed an audio file in a post.

1 Like