«NodeProxy PlayControl for parallel sounds» by LFSaw

on 04 Aug'22 05:38 in jitlibrecipy

NodeProxy rule to create parallel versions of the same sound definition.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
(
// \par
//
// create parallel streams of the provided function filling the NodeProxy channels.
// requires the functions output channels to be a multiple of the NodeProxy's numChannels.
AbstractPlayControl.proxyControlClasses.put(\par, SynthDefControl);
AbstractPlayControl.buildMethods.put(\par, #{ | func, proxy, channelOffset = 0, index |
	var funcNumChannels;
	
	func.isKindOf(ArrayedCollection).if({
		funcNumChannels = func[1];
		func = func.first;
	}, {
		try({
			funcNumChannels = (func.value.shape ?? {[1]}).first;
		},{|exeption|
			(exeption.selector == \addKr).if({
				// assume mono
				"NodeProxy: NamedControl in function, please provide numChannels explicitely:".postln;
				"\t \par -> [{...}, <numChannels>]".postln;
				funcNumChannels = 1;
			}, {
				exeption.throw
			})
		})
	});

	if ((proxy.numChannels % funcNumChannels) != 0) {
		Error("NodeProxy input (par): number of proxy channels need to be multiple of function output channels").throw;
	};

	{ | out |
		var env, ctl = NamedControl.kr("mix" ++ (index ? 0), 1.0);

		// create named controls '<argname>s' whilst retaining order of function args
		var funcControls = func.def.keyValuePairsFromArgs.clump(2).collect{|args|
			var key, val;
			// #key, val = args;
			// NamedControl.kr((key ++ $s).asSymbol, (val ? 0)!proxy.numChannels))
			NamedControl.kr(
				(args[0] ++ $s ++ (index ? 0)),
				(args[1] ? 0).dup(proxy.numChannels/funcNumChannels)
			)
		};

		// embed function synthesis
		var outSnd = funcControls.flop.collect{|args, i|
			// args.postln;
			SynthDef.wrap(func, nil, args);
		}.flatten;

		if(proxy.rate === 'audio') {
			env = ctl * EnvGate(i_level: 0, doneAction: 2, curve: \sin);
			Out.ar(out, env * outSnd)
		} {
			env = ctl * EnvGate(i_level: 0, doneAction: 2, curve: \lin);
			Out.kr(out, env * outSnd)
		};
	}.buildForProxy( proxy, channelOffset, index )
});

)


////////////// example usage

// make n 8-channel Ndef
Ndef(\a).ar(8)


// show edit window
Ndef(\a).edit(30)


// play on two channels (this wraps the 8 channels onto 2)
Ndef(\a).play(0, numChannels: 2, vol: 0.2)

// create an 8-channel sound by instantiating 8 times the below monosound
Ndef(\a)[0] = \par -> {|freq = 444, amp = 0.1, lpFreq = 1200, lpRq = 0.1| RLPF.ar(Blip.ar(freq.lag(4), 5), lpFreq.lag(4), lpRq.lag(4)) * amp };

// set some arbitrary parameters of the sound
Ndef(\a).setn(\freqs0, {exprand(100, 400)}!8);
Ndef(\a).setn(\lpFreqs0, {exprand(1000, 4000)}!8);
Ndef(\a).setn(\lpRqs0, {exprand(1, 0.1)}!8);


// add a variation of the first sound in 2nd slot
Ndef(\a)[1] = \par -> {|freq = 444, amp = 0.1, lpFreq = 1200, lpRq = 0.1| RLPF.ar(Blip.ar(freq.lag(4), 5), lpFreq.lag(4), lpRq.lag(4)) * amp };

// set its mix param (amplitude)
Ndef(\a).set(\mix1, 0.3);

// set some arbitrary parameters of the 2nd sound
Ndef(\a).setn(\freqs1, {exprand(1000, 4000)}!8);
Ndef(\a).setn(\lpFreqs1, {exprand(1000, 4000)}!8);
Ndef(\a).setn(\lpRqs1, {exprand(1, 0.1)}!8);


// filter sound globally
Ndef(\a)[10] = \filter -> {|in, lpFreq = 1000, lpRq = 0.1| RLPF.ar(in, lpFreq, lpRq)}

Ndef(\a).release
Ndef(\a).clear
raw 3202 chars (focus & ctrl+a+c to copy)
reception
comments