Use NDef with Tidal

Hi

Is it possible to use a synth defined with NDef from Tidal?

Thanks Fabian

Yes, this might be a bit out of date (I haven't done this in a while) but an example to get started might be something like:

// Tidal drone
(
b = Bus.control(s);
Ndef(\x, {|freq=60, drive=1, filter=1, portamento=0.2, wrate=1|
	var freqlag = Lag.kr(freq, portamento);
	var sig = Lag.ar(Impulse.ar(freqlag+[0,1], [0,0.01*SinOsc.kr(1)]), [0.1,0.11], 100);
	sig = LeakDC.ar(sig);
	sig = 0.3*atan(sig*10*drive);
	sig = RLPF.ar(sig, ([1300, 1100, 800]*filter+SinOsc.ar(wrate).range(100,500)).clip(20,8000), 0.3, [0.2, 0.3, 0.7]);
	sig = Mix.ar(sig);
	Out.ar(0,SplayAz.ar(2, sig, 0.5));
}).gui;
~dirt.soundLibrary.addSynth(\sbass, (play: {
	Ndef(\x).set(\freq, (~n+60).midicps);
	b.set(1.0);
}));
)
4 Likes

Thanks!

Worked fine

wow this amazingg! I didn't know it was so easy to use Ndefs with tidal, for doing drones and otther continuous stuff. This comes really handy <3

does any body know a way of triggering envelopes in Ndef with tidal? I think that maybe detecting a change in an input param (like freq) may do the trick???

EDIT:

I mean, something like this is working, but you need the note value to be changing all the time, which is silly :stuck_out_tongue:

(
b = Bus.control(s);
Ndef(\x, {|freq=60, drive=1, filter=1, portamento=0.2, wrate=1, sustain=1|
	var freqlag = Lag.kr(freq, portamento);
	var sig = Lag.ar(VarSaw.ar(freqlag+[0,1], [0,0.01*SinOsc.kr(1)]), [0.1,0.11], 100);
	sig = LeakDC.ar(sig);
	sig = 0.8*atan(sig*20*drive);
	sig = RLPF.ar(sig, ([1300, 1100]*filter+SinOsc.ar(wrate).range(100,500)).clip(20,8000), 0.2, [0.3, 0.7]);
	sig = Mix.ar([sig,DelayN.ar(sig,delaytime:30)]);
  sig = EnvGen.ar(Env.perc(0.02,sustain), gate:Changed.kr(freq), timeScale:sustain) * sig;
	Out.ar(0,SplayAz.ar(2, sig, 0.9));
});
)

(
~dirt.soundLibrary.addSynth(\sbass, (play: {
	Ndef(\x).set(\freq, (~n+60).midicps);
	Ndef(\x).set(\filter, (~filter));
	Ndef(\x).set(\portamento, (~portamento));
	Ndef(\x).set(\sustain, (~sustain));
	b.set(1.0);
}));
)

I think the closest thing I've done is you can use InTrig.kr(b) in the Ndef to make a trigger whenever the "control bus" is touched, which I think will happen whenever Tidal sends any event.

Thanks! I didn't know about InTrig, Changed was the best I could do but this will do <3

This should work:

// you can call any sclang function from tidal, like this
~dirt.soundLibrary.addSynth(\lizzy, (play: { "hello lizzy".postln }));
// so also a trigger
~dirt.soundLibrary.addSynth(\lizzy, (play: { Ndef(\lizzy).set(\trigger, 1) }));
// any control rate trigger
Ndef(\lizzy, { var trig = \trigger.tr; Decay2.kr(trig) * Blip.ar(67) * 0.2 }).play;
// envelope
Ndef(\lizzy, { var trig = \trigger.tr; Env.linen(0, 0.3, 1).kr(0, trig) * Blip.ar(67) * 0.2 }).play;
// if you need to convert the trigger to audio rate 
Ndef(\lizzy, { var trig = T2A.ar(\trigger.tr); Decay2.ar(trig) * Blip.ar(67) * 0.2 }).play;

1 Like

You can use a trigger control (\foo.tr), then it will always trigger even if it doesn't change.

Anyone else having trouble with timing while doing this?
The sync is drifting all over the place. I feel like there should to be a step of grabbing the osc timedata maybe?
Tried with s.makeBundle in SC but it didn't help. Any ideas?

Ok this is maybe outside the topic of this thread but it relates to my above question.

I realized everything I send to the function in ~dirt.soundLibrary.addSynth(\aSynth, (play: {...
is "out of sync" and it also completely ignores whatever latency I put in BootTidal.hs.
It just starts and stops as soon as I tell it to.

If i do this in SC:

(
SynthDef(\blopp, {
	var sig, env;
	env = EnvGen.ar(Env([0,1,0], [0.001, 0.1]), doneAction:2);
	sig = SinOsc.ar(200);
	Out.ar(0, sig*env);
}).add;
)

(
~dirt.soundLibrary.addSynth(\test, (play: {
Synth(\blopp);
}))
)

and then in Tidal:

d1 $ s "test*8"

d2 $ s "bd*8"

d1 comes out like a drunk trying to play a shuffle while d2 is solid.

Any help with this is appreciated.

Solved!

Grabbing the latency data from the OSC-stream was easier than I thought.
Then I just passed that to .makeBundle and voila.

(
~dirt.soundLibrary.addSynth(\testSynth, (play:
	{|dirtEvent|
		~dirt.server.makeBundle(dirtEvent.event[\latency], {
			Ndef(\tidalPad).set(\freq, (~n+(12*~octave)).midicps);
			Ndef(\tidalPad).set(\amp, (~amp));
			Ndef(\tidalPad).set(\trigger, (~trigger));
			b.set(1.0);
		})
		
}));
)
2 Likes

good solution!

Maybe we should add a message bind to DirtEvent? So that we could write:

~dirt.soundLibrary.addSynth(\testSynth, (play: {|dirtEvent| dirtEvent.bind { <...> }}))

or more gnomic:

~dirt.soundLibrary.addSynth(\testSynth, (play: _.bind { <...> }))

Yeah that might be neater ::))

That should not be necessary, because the event only gets called when s = \testSynth. Or not?

It's in the develop branch now.

example:



(
SynthDef(\blopp, {
	var sig, env;
	env = EnvGen.ar(Env([0,1,0], [0.001, 0.1]), doneAction:2);
	sig = SinOsc.ar(200);
	Out.ar(0, sig*env);
}).add;
)


~dirt.soundLibrary.addSynth(\blopp, (play: { |e| e.bind { Synth(\blopp) } }))

Hmm, yes you're right. I got some wierd triggering behavior and the if-statement solved it for some reason. Recompiled and now it works as expected.

just to avoiding cargo cult programming :slight_smile:

Haha jupp.
I just like to make sure.

if(one == 1){return 1;}
1 Like

If you want to play through the routing system:



(
SynthDef(\blopp, { |out|
	var sig, env;
	env = EnvGen.ar(Env([0,1,0], [0.001, 0.1]), doneAction:2);
	sig = SinOsc.ar(200);
	Out.ar(out, sig*env);
}).add;
)


~dirt.soundLibrary.addSynth(\blopp, 
	(play: { |e| 
		e.bind { Synth(\blopp, [\out, e.orbit.dryBus]) } 
	})
)



I wonder if should better pass this as ~out in the event itself.