Demand to Pmono

hey, could someone please help me to get the same results from the Pmono as with Demand Ugens? triggers and Patterns are really confusing me. thanks a lot.

(
SynthDef(\demand_clicks, {

	var trig = {
		TDuty.ar
		(
			Dxrand(((1/8!8) ++ (1/4!8) ++ [Dseq([1/16, 1/16], Drand([1, 2], inf))]) * 1.25, inf),
			0,
			Dwhite(0.5, 1, inf)
		)
	} ! 2;
	
	var sig, env;
	
	env = Decay.ar(trig, 0.001 * LFNoise1.ar(4).abs);

	sig = PinkNoise.ar(1) * env;
	sig = BPF.ar(sig, 13289.75, 0.9, 25 * LFNoise1.ar(8).range(0, 1)).fold(-1, 1);

	sig = MidEQ.ar(sig, 13289.75, 0.7, 8);

	sig = sig * \amp.kr(0.25);

	sig = LeakDC.ar(sig);
	Out.ar(\out.kr(0), sig);
}).add;
)

x = Synth(\demand_clicks);
x.free;

// trying to convert to Pmono

(
SynthDef(\pmono_clicks, {

	var trig = \trig.tr(0)!2;
	var sig, env;

	trig = Trig.ar(trig, SampleDur.ir);
	env = Decay.ar(trig, 0.001 * LFNoise1.ar(4).abs);

	sig = PinkNoise.ar(1) * env;
	sig = BPF.ar(sig, 13289.75, 0.9, 25 * LFNoise1.ar(8).range(0, 1)).fold(-1, 1);

	sig = MidEQ.ar(sig, 13289.75, 0.7, 8);

	sig = sig * \amp.kr(0.25);

	sig = LeakDC.ar(sig);
	Out.ar(\out.kr(0), sig);
}).add;
)

(
Pdef(\pmono_clicks,
	Pmono(\clicks,
		
		\dur, Pxrand(((1/8!8) ++ (1/4!8) ++ Pseq([1/16, 1/16], Prand([1, 2], 1))) * (1/1.25), inf),
	)
).play;
)

Pdef(\pmono_clicks).stop;

in your demand version you create two different streams via the ! 2, in the pmono version you double a trigger generated from a single stream.

thanks alot :slight_smile: do you have an idea how i can create two different streams with Pmono then?
Im also wondering about Dwhite(0.5, 1.0, inf) inside TDuty which is acting as a attenuator, right? and if its right to multiply the \dur value by 1 / 1.25

If they have different rhythms, you have to use Ppar (or Ptpar or Pspawner).

It isn’t supported to have one Pbind or Pmono produce multiple simultaneous rhythms – never in any case. Multichannel expansion won’t work for this.

hjh

hey thanks.
im struggeling with this multichannel trigger / pattern logic for a while now.
if somebody could share some code, how this specific example could be converted into pattern logic as a blueprint would be really nice :slight_smile:
thanks so much.

EDIT: also posted this one a while ago. which i havent been able to figure out completly in pattern logic:

(
{
	var trig, snd, lfo;
	trig = Impulse.ar(12 * (1 + (LFNoise2.kr(20 ! 2) * 0.5)));
	lfo = LFSaw.ar(0.3 * (1 + (LFNoise2.kr(0.3 ! 2) * 0.5)));
	snd = SinOsc.ar(lfo.linexp(-1, 1, 100, 8000)) + SinOsc.ar(lfo.linexp(-1, 1, 1000, 4000));
	snd = Pluck.ar(snd, trig, 0.1, [125, 130].reciprocal, 0.1);
	snd = Limiter.ar(snd);
	snd * 0.2;
}.play(fadeTime: 0);
)

I think this sounds very similar flamenco-like-rhythmically:

(
Pdef(\pmono_clicks,
Ppar({ arg i;
Pmono(\pmono_clicks,
\out, i,
\dur, Pxrand(((1/8!8) ++ (1/4!8) ++ Pseq([1/16, 1/16], Prand([1, 2], 1))) * 1.25, inf),

) }!2)
).play;

)

¡Olé!

thanks. whats about attenuating with Dwhite(0.5, 1.0) ?

¡Olé!

haha. its not about this specific rhythm. its just an example with Demand Ugens, which i would like to drive with Patterns and thought of replicating the original with Pmono is a first step.

so when dealing with multichannel triggers and Pmono use:

trig = \trig.tr;
trig = Trig.ar(trig, SampleDur.ir);

in the SynthDef and use Ppar (Ptpar or Pspawner) and a \dur value with additional randomness or an array of values?

and with triggers and Pbind use:

trig = Impulse.ar(0);

and an Envelope with doneAction:2 and Ppar (Ptpar or Pspawner)?

in the second example you have:

trig = Impulse.ar(12 * (1 + (LFNoise2.kr(20 ! 2) * 0.5)));`

how do you transform LFNoise to pattern logic?

does this whole converting thing even make sense? it seems to me that no one else is doing this.
Im using a fractal system for creating the rhythms with patterns and transform the sound from one set of values to another set using automations in the DAW ( which works the best at the moment). I also made some attempts to use Pspawner and transform the parameters with a transformation function which could not handle individual keys inside Ppar.
Having the rhythm logic in the SynthDef and mainly relying on randomness makes it imo impossible to compose a sophisticated piece of music which is not only presenting the sounds but also transforming them. Thats the reason behind it.

EDIT: ive once more compared the two approaches Demand vs. Pmono unfortunately they are not the same (probably wrong use of seed here).

~seed = 2000;

(
thisThread.randSeed = ~seed;
SynthDef(\demand_clicks, {

	var sig, env, trig;

	RandID.ir(1);

	RandSeed.kr(\seedtrg.kr(1), \seed.kr(~seed));

	trig = {
		TDuty.ar
		(
			Dxrand(((1/8!8) ++ (1/4!8) ++ [Dseq([1/16, 1/16], Drand([1, 2], inf))]) * 1.25, inf),
			0,
			Dwhite(0.5, 1, inf)
		)
	} !2;

	env = Decay.ar(trig, 0.001 * LFNoise1.ar(4).abs);

	sig = PinkNoise.ar(1) * env;
	sig = BPF.ar(sig, 13289.75, 0.9, 25 * LFNoise1.ar(8).range(0, 1)).fold(-1, 1);

	sig = MidEQ.ar(sig, 13289.75, 0.7, 8);

	sig = sig * \amp.kr(0.25);

	sig = LeakDC.ar(sig);
	Out.ar(\out.kr(0), sig);
}).add;
)

x = Synth(\demand_clicks);
x.free;

(
thisThread.randSeed = ~seed;
SynthDef(\pmono_clicks, {

	var trig = \trig.tr(0);
	var sig, env;

	RandID.ir(1);

	RandSeed.kr(\seedtrg.kr(1), \seed.kr(~seed));

	trig = Trig.ar(trig, SampleDur.ir);
	env = Decay.ar(trig, 0.001 * LFNoise1.ar(4).abs);

	sig = PinkNoise.ar(1) * env;
	sig = BPF.ar(sig, 13289.75, 0.9, 25 * LFNoise1.ar(8).range(0, 1)).fold(-1, 1);

	sig = MidEQ.ar(sig, 13289.75, 0.7, 8);

	sig = sig * \amp.kr(0.25);

	sig = LeakDC.ar(sig);
	Out.ar(\out.kr(0), sig);
}).add;
)

(
Pdef(\pmono_clicks,
	Pseed(~seed,
		Ppar({ |i|
			Pmono(\pmono_clicks,
				\dur, Pxrand(((1/8!8) ++ (1/4!8) ++ Pseq([1/16, 1/16], Prand([1, 2], 1))) * 1.25, inf).trace,
				\out, i,
		) } !2 )
	)
).play;
)

Pdef(\pmono_clicks).stop;

EDIT: tried to solve the other example like this. the result is also different. dont know. i accepted it and played around with some parameters :slight_smile:

(
SynthDef(\pluck, {

	var trig = \trig.tr(0);
	var sig, lfo;

	trig = Trig.ar(trig, SampleDur.ir) * (1 + (LFNoise2.kr(20) * 0.5));
	lfo = LFSaw.ar(0.3 * (1 + (LFNoise2.kr(0.3) * 0.5)));
	
	sig = SinOsc.ar(lfo.linexp(-1, 1, 100, 8000)) + SinOsc.ar(lfo.linexp(-1, 1, 1000, 4000));
	sig = Pluck.ar(sig, trig, 0.1, \freq.kr(50).reciprocal, \dec.kr(0.1), \coef.kr(0.5));

	sig = sig * \amp.kr(0.25);
	Out.ar(\out.kr(0), sig);
}).add;
)

(
Pdefn(\durs, Pbind(\dur, 1/12));
Pdefn(\notes, Pbind(\freq, [125, 130]));
Pdefn(\coefs, Pbind(\coef, [0.5, 0.5]));
Pdefn(\decs, Pbind(\dec, [0.2, 0.3]));

Pdef(\poly,
	Ppar({ |i|
		Pmono(\pluck,
			\dur, Pkey(\dur),
			\freq, Pkey(\freq).collect(_[i]),
			\dec, Pkey(\dec).collect(_[i]),
			\coef, Pkey(\coef).collect(_[i]),
			\out, i,
	) } !2 ) <> Pdefn(\notes) <> Pdefn(\durs) <> Pdefn(\coefs) <> Pdefn(\decs);
).play;
)

Pdef(\poly).stop

how do you transform LFNoise to pattern logic?

LFNoise(0,1,2…) are simply generating interpolated random values, so I guess you could make use of the array interpolation methods included in the wslib quark:

// extend array of 5 random values between -1 and 1 to array of length 100
// using cubic (hermite) interpolation 
(
var len = 5;
var frand2arr = { 1.0.rand2 } ! len;
frand2arr.resize(100, \hermite, false).plot;
)

{ LFNoise2.ar(500) }.plot(0.01); // for comparison

Here, the len variable loosely corresponds to LFNoise’s rate arg.

I don’t have time to figure out how to properly turn this into a pattern right now, but two tentative ideas on how you could do that:

  1. Wrap the array interpolation code in a Pn(Plazy({...}) or PLx patterns from miSCellaneous_lib.
// very hacky
(
Pbind(
	\note, Pn(Plazy({
		var len = 5;
		var lfnArr = { 12.rand2 }.dup(len);
		lfnArr = lfnArr.resize(100, \hermite);
		Pseq(lfnArr);
	})).trace,
	\dur, 0.1
).play;
)
  1. Look at the implementation of Pseg and try to replace the instances of Env.at with interpolated arrays and wslib’s intAt method. You might want to read the Pattern Internals reference first, which has a lot of detailed information on how to write your own Patterns.

Forgot to add, there are a bunch of built-in pattern classes which generate random values, maybe they are already sufficient for your use case.
And here’s an interesting thread on handling continuous control changes in the pattern paradigm.

hey, thanks a lot for the detailed information. i will have a look at the wslib quark.

I actually thought that one could do a hybrid solution with Pmono, where you have a \dur pattern and modulate the triggers like this. which is not working.

var trig = \trig.tr(0);
trig = Trig.ar(trig, SampleDur.ir) * (1 + (LFNoise2.kr(20) * 0.5));

ive already tried to use the auto-map approach see this thread ctrlEnv merge Event \type. But i use PbindFx for sequencing fx and a function for data sharing between source and fx and PbindFx doesnt like the busses created automatically. every external input has to be passed via \otherBusArgs. so i skipped this solution. see this thread DXEnvFan Bus in PbindFx - #3 by dietcv

But ive combined some things and sending LFOs to a control Bus and using In.ar for sending a modulation signal:

(
~makeBusses = {
	~bus = Dictionary.new;
	~bus.add(\fx -> Array.fill(3, { Bus.audio(s, 2) }));
	~bus.add(\ctrl -> Array.fill(3, { Bus.control(s, 1) }));
};

~makeNodes = {
	s.bind({

		if(~mainGrp.isNil) {
			~mainGrp = Group.basicNew(s, -1);
		};

		if(~ctrlGrp.isNil) {
			~ctrlGrp = Group.basicNew(s, -1);
		};

		if(~fxGrp.isNil) {
			~fxGrp = Group.basicNew(s, -1);
		};

		~mainGrp.nodeID = s.nodeAllocator.alloc;
		~ctrlGrp.nodeID = s.nodeAllocator.alloc;
		~fxGrp.nodeID = s.nodeAllocator.alloc;

		s.sendBundle(nil, ~mainGrp.newMsg(nil,\addToHead));
		s.sendBundle(nil, ~ctrlGrp.newMsg(~mainGrp,\addAfter));
		s.sendBundle(nil, ~fxGrp.newMsg(~ctrlGrp,\addAfter));
	});
};
)

(
SynthDef(\pluck, {
	var trig = \trig.tr(0);
	var lfo = \lfo.kr(0);
	var sig;

	trig = Trig.ar(trig, SampleDur.ir);// * (1 + (LFNoise2.kr(20) * 0.5));

	sig = SinOsc.ar(lfo.linexp(-1, 1, 100, 8000)) + SinOsc.ar(lfo.linexp(-1, 1, 1000, 4000));
	sig = Pluck.ar(sig, trig, 0.1, \freq.kr(50).reciprocal, \dec.kr(0.1), \coef.kr(0.5));

	sig = sig * \amp.kr(0.25);

	sig = sig * (1 + In.ar(\modIn.kr(0)));

	//sig = SafetyLimiter.ar(sig);
	Out.ar(\out.kr(0), sig);
}).add;

SynthDef(\lfo, {
	arg gate=1, rate=0.3, mul=1;
	var sig;
	FreeSelf.kr(gate <= 0);
	sig = LFSaw.ar(rate * (1 + (LFNoise2.kr(rate) * 0.5)), mul: mul);
	Out.kr(\out.kr(0), sig)
}).add;

SynthDef(\ampMod, {
	var lfoGain = EnvGen.ar(Env([0, 1], [0.02], \sin));
	var sig = SinOsc.ar(\freq.kr(150)).range(1 - lfoGain, 1);
	Out.ar(\out.kr(0), sig)
}).add;
)

(
Pdefn(\durs, Pbind(\dur, 1/12));
Pdefn(\notes, Pbind(\freq, [125, 130]));
Pdefn(\coefs, Pbind(\coef, [0.5, 0.5]));
Pdefn(\decs, Pbind(\dec, [0.2, 0.3]));
Pdefn(\amps, Pbind(\amp, [0.25, 0.25]));

Pdef(\poly,
	Ppar({|i|
		Pmono(\pluck,
			\dur, Pkey(\dur),
			\freq, Pkey(\freq).collect(_[i]),
			\dec, Pkey(\dec).collect(_[i]),
			\coef, Pkey(\coef).collect(_[i]),

			\lfo, ~bus[\ctrl][i].asMap,

			\modIn, ~bus[\fx][i],
			\otherBusArgs, [\modIn],
			\group, ~mainGrp,

			\amp, Pkey(\amp).collect(_[i]),
			\out, i,

			\cleanupDelay, Pkey(\dur) * Pkey(\legato),
			\fxOrder, [1]
	) } !2) <> Pdefn(\notes) <> Pdefn(\coefs) <> Pdefn(\decs) <> Pdefn(\durs) <> Pdefn(\amps);
);

Pdefn(\rates, Pbind(\rate, [0.3, 0.2]));
Pdefn(\muls, Pbind(\mul, [1, 1]));

Pdef(\lfo,
	Ppar({|i|
		Pbind(
			\instrument, \lfo,
			\dur, inf,
			\rate, Pkey(\rate).collect(_[i]),
			\mul, Pkey(\mul).collect(_[i]),
			\out, ~bus[\ctrl][i],
			\group, ~ctrlGrp,
	) } !2) <> Pdefn(\rates) <> Pdefn(\muls);
);

Pdef(\poly_ctrl,
	Ptpar([0, Pdef(\poly), 0.0001, Pdef(\lfo)])
);
)

Pdef(\poly_ctrl).play;
Pdef(\poly_ctrl).stop

(
x = Synth(\ampMod, [\freq, 300, \out, ~bus[\fx][0]], target: ~mainGrp, addAction: \addBefore);
y = Synth(\ampMod, [\freq, 400, \out, ~bus[\fx][1]], target: ~mainGrp, addAction: \addBefore);
)

(
x.free;
y.free;
)

and searching a good way for composing the Pdefn values:

(
r = Routine({
	Pdef(\poly_ctrl).play;

	10.wait;

	loop {
		Pdefn(\notes, Pbind(\freq, Env([[125,130], [145,165]], 10)));

		10.wait;

		Pdefn(\notes, Pbind(\freq, Env([[145,165], [125,130]], 10)));

		10.wait;
	}

}).play(AppClock, quant:1)
)

(
Pdef(\poly_ctrl).stop;
r.stop;
)

but the initial SynthDef(\demand_clicks) vs. SynthDef(\pmono_clicks) is still unsolved.

Most of it was TL;DR for me so I don’t know what is happening exactly, but you can modulate the trigger value from the pattern itself and from the synth as well.

(
SynthDef(\click, {
	var trig = Trig.ar(\trig.tr(1), SampleDur.ir);
	Latch.ar(trig, trig).poll;
	Out.ar(\out.kr(0), trig);
}).add;
)

(
Ppar({ arg i;
	Pmono(
		\click, \out, i,
		\trig, Pseq((0.1, 0.2..1), inf),
		\dur, 0.1
	);
}!2).play;
)


(
SynthDef(\click, {
	var trig = Trig.ar(\trig.tr(1), SampleDur.ir) * LFNoise2.ar(10).range(0, 1);
	Latch.ar(trig, trig).poll;
	Out.ar(\out.kr(0), trig);
}).add;
)

(
Ppar({ arg i;
	Pmono(
		\click, \out, i,
		// event type set sends a trigger value of 0.5 to the trig key by default.
		\dur, 0.1
	);
}!2).play;
)

im sorry for the confusion. and try to combine all the useful information here.

i think this was the missing piece and you also solved another question i had in mind about polling the trig values with:


Latch.ar(trig, trig).poll;

one last thing:
how would you go about attenuating the triggers in the SynthDef / Pmono like in the original example with Dwhite(0.5, 1, inf) inside TDuty.ar?

thanks a lot for your help :slight_smile: @lucas @bovil43810

how would you go about attenuating the triggers in the SynthDef / Pmono like in the original example with Dwhite(0.5, 1, inf) inside TDuty.ar?

(
SynthDef(\click, {
	var trig = Trig.ar(\trig.tr(1), SampleDur.ir);
	Latch.ar(trig, trig).poll;
	Out.ar(\out.kr(0), trig);
}).add;
)
(
Ppar({ arg i;
	Pmono(
		\click, \out, i,
		\trig, Pwhite(0.5, 1.0),
		\dur, 0.1
	);
}!2).play;
)

Or am I missing something obvious here?

no, you are right. both examples have the same result:

(
SynthDef(\click, {
	var trig = Trig.ar(\trig.tr(1), SampleDur.ir);
	Latch.ar(trig, trig).poll;
	Out.ar(\out.kr(0), trig);
}).add;
)

(
Ppar({ arg i;
	Pmono(
		\click, \out, i,
		\trig, Pwhite(0.5, 1.0),
		\dur, 0.5
	);
}!2).play;
)

(
SynthDef(\click, {
	var trig = TDuty.ar(Dseq([0.5], inf), 0, Dwhite(0.5, 1, inf));
	Latch.ar(trig, trig).poll;
	Out.ar(\out.kr(0), trig);
}).add;
)
(
Ppar({ arg i;
	Pmono(
		\click, \out, i,
	);
}!2).play;
)