Dynamic Arrays for Patterns?


#1

Pseq doesn’t like having an array passed into it from a Pdefn:

(
SynthDef (\sine) {
	arg freq = 440;
	var sig = SinOsc.ar (freq);
	sig = Pan2.ar (sig, 0, 0.2);
	Out.ar (0, sig);
}.add;

TempoClock.tempo_ (91 / 60);

Pmono (\sine,
	\delta, 1 / 4,
	\freq, Pdefn (\arpeggio),
).play;
)

(
Pdefn (\freq_array) {
	{ ([ 0, 3, 7, 10, 19] + 57).midicps.yield }.loop;
};

Pdefn (\arpeggio) {
	Pseq (([ 0, 3, 7, 10, 19] + 57).midicps, inf); // works
	// Pseq (Pdefn (\freq_array), inf);  // doesn't work
};
)

is there a way around this? … I want to be able to change the number of steps in the arpeggio dynamically.


#2

Pseq accept an array of pattern, but the array cannot be the pattern itself

You can always use a Prout to do what you want:

(
Pdefn (\freq_array) {
	{ ([ 0, 3, 7, 1,10, 19] + 57).midicps.yield }.loop;
};

Pdefn (\arpeggio, 
	Prout({ arg ev;
		Pdefn(\freq_array).asStream.do({ arg arr;
			Pseq(arr).embedInStream
			//arr.do({ arg val; val.yield }) // alternate way
		})
	})
);
)

or with your syntax i didn’t know about (but i believe it just create a Prout under the hood)

(
Pdefn (\freq_array) {
	{ ([ 0, 3, 7, 1,10, 19] + 57).midicps.yield }.loop;
};

Pdefn (\arpeggio) {
	Pdefn(\freq_array).asStream.do({ arg arr;
		Pseq(arr).embedInStream
		//arr.do({ arg val; val.yield }) // alternate way
	})
}
)

#3

A friend and I tracked down an elegant way to do this recently:

(
Pdefn(\notes, 40 + [0, 3, 5, 7]);

Pdef(\seq, Pbind(
	\scale, Scale.chromatic,
	\dur, 0.25, \legato, 3,
	\midinote, Pdefn(\notes).composeBinaryOp(\wrapAt, Pseries()).trace
)).play;

fork {
	loop {
		Pdefn(\notes, 40 + [0, 3, 5, 9]);
		8.rand.wait;
		Pdefn(\notes, 40 + [0, 3, 5, 8]);
		8.rand.wait;
		Pdefn(\notes, 40 + [0, 3, 5, 7]);
		8.rand.wait;
	}
}
)

Not only does this allow runtime redefinition of \notes, it actually keeps the same position in the sequence when you change the list. If you want something other than a Pseq style sequence, you’ve only got to change the Pseries to some other pattern that produces indexes that reflect how you want to iterate through the list.


#4

You might check PLx patterns from miSCellaneous_lib.

(
SynthDef (\sine) {
	arg freq = 440;
	var sig = SinOsc.ar (freq);
	sig = Pan2.ar (sig, 0, 0.2);
	Out.ar (0, sig);
}.add;

TempoClock.tempo_ (91 / 60);

~a = 57 + [0, 3, 7, 10, 19];

p = Pmono (\sine,
	\delta, 1 / 4,
	\midinote, PLseq(\a),
).play;
)

// replace

~a = 57 + [2, 4, 9]

p.stop

Note that PLx patterns in general follow an immediate replacement paradigm (replace with next item from stream), for preservation of full loops see PLn or take Pn + Plazy.


#5

this helped me, thank you. but I am still having trouble conceptualising how I need to structure my Pdefns to generate the array dynamically. take the following:

(
Pdefn (\low_freq) { { 220.yield }.loop };
Pdefn (\high_freq) { { 440.yield }.loop };
Pdefn (\tet) { { 12.yield }.loop };
Pdefn (\delt) {
	log2 (Pdefn (\high_freq) / Pdefn (\low_freq)) / Pdefn (\tet);
};

Pdefn (\new_array) {
	Prout {
		Array.fill (Pdefn (\tet)) {
			arg i;
			Pdefn (\low_freq) * (2 ** (Pdefn (\delt) * i));
		};
	};
};
)

1 / 12; // hoping for this
Pdefn (\delt).asStream.next; // works

(57..69).midicps; // hoping for this
Pdefn (\new_array).asStream.next; // doesn't work

I want to derive the semitones between the A below middle C and the A above it dynamically, so I can stretch the end points, change the TET number, etc. arbitrarily as it is playing.


cute. I’ll probably end up doing it this way - thanks !!

doesn’t solve for dynamic array generation however (see above).


woah cool. just installed the quark. ima have a look at PLx suite - looks really nice. thanks !!


#6

My impression is that you could save typing by switching to Pbindef, e.g.

(
Pbindef(\a,
	\loFreq, 220,
	\hiFreq, 440,
	\tet, 12,
	\delt, (Pkey(\hiFreq) / Pkey(\loFreq)).log2 / Pkey(\tet),
	\new_array, Pfunc { |e| 
		{ |i| e[\loFreq] * (2 ** (e[\delt] * i)) } ! e[\tet] 
	},
	\freq, Pkey(\new_array).collect(_.choose),
	\dur, 0.2
).trace.play
)
	
// exchange param

Pbindef(\a, \hiFreq, 300)

As I mentioned PLx before, you could also try PLbindef, where the replacement reduces to

~a.hiFreq = 300

Also a number of conversion params are already built into the Event framework, so you could use ‘stepsPerOctave’, ‘root’ etc, but I see the point that you might prefer to define your customized calculations.


#7

edit 2: worked it out. disregard!

(
~sine_synth = Synth (\sine);
Pbindef(\a,
	\type, \set,
	\id, ~sine_synth.nodeID,
	\loFreq, 220,
	\hiFreq, 440,
	\tet, 12,
	\delt, (Pkey(\hiFreq) / Pkey(\loFreq)).log2 / Pkey(\tet),
	\new_array, Pfunc { |e| 
		{ |i| e[\loFreq] * (2 ** (e[\delt] * i)) } ! e[\tet] 
	},
	\freq, Pkey(\new_array).collect(_.choose),
	\dur, 0.2
).play
)

edit:

is there a Pbindef for Pmono ?


yeh this looks awesome. many thanks Daniel !! :pray: :pray:


#8

You can combine Pbindef with Pmono via Pchain:

Pchain(Pbindef(\a, \foo, 1, \bar, 2), Pmono(\a)).trace.play;
Pbindef(\a, \foo, 2, \bar, 3);