PLIdev dynamic scope pattern searching for numbers with integer distance from a source pattern, optionally avoiding repetitions within a span


Part of: miSCellaneous


Inherits from: Pattern


DIdev / PIdef / PLIdev search for numbers with integer distance from a source signal / pattern up to a given deviation. Repetitions within a lookback span are avoided, DIdev / PIdef / PLIdev randomly choose from possible solutions. Intended for search within integer grids (pitches, indices etc.), however applications with non-integer sources are possible, see examples.

 

NOTE: It's the user's responsibility to pass a combination of deviation and lookback values that allows a possible choice, see examples.


NOTE: In contrast to DIdev, PIdev and PLIdev do *not* need to know maximum deviations (minLoDev, maxHiDev) beforehand. Thus the order of arguments is different (here loDev and hiDev before lookBack).


See also: PLx suite, Idev suite, PIdev, DIdev



Creation / Class Methods


Creates a new PLIdev object.

*new (pattern, maxLookBack = 3, loDev = -3, hiDev = 3, lookBack, thr = 1e-3, length = inf, envir = \current)

pattern - Symbol or PIdev pattern arg. The source value pattern to start search from. 

If a Symbol is passed, a pattern/value can be assigned to an envir variable later on. 

Can be dynamically replaced by Patterns or Streams.

maxLookBack - Integer, the maximum lookback span. Fixed, defaults to 3.

loDev - Symbol or PIdev loDev arg. Determines the current low deviation for the search. 

If a Symbol is passed, a pattern/value can be assigned to an envir variable later on. 

Can be dynamically replaced by Patterns or Streams. Defaults to -3.

hiDev - Symbol or PIdev hiDev arg. Determines the current high deviation for the search. 

If a Symbol is passed, a pattern/value can be assigned to an envir variable later on. 

Can be dynamically replaced by Patterns or Streams. Defaults to 3.

lookBack - Symbol or PIdev lookBack arg. Determines the current lookback span for avoiding repetitions.

Can be modulated but must not exceed maxLookBack.

If no value is passed, then maxLookBack is taken.

If a Symbol is passed, a pattern/value can be assigned to an envir variable later on. 

Can be dynamically replaced by Patterns or Streams. Defaults to nil.

thr - Symbol or PIdev thr arg. Threshold for equality comparison. 

If a Symbol is passed, a pattern/value can be assigned to an envir variable later on. 

Can be dynamically replaced by Patterns or Streams. Defaults to 1e-3. 

length - Symbol or PIdev length arg. Number of repeats. 

If a Symbol is passed, a value can be assigned to an envir variable later on. Defaults to inf. 

envir - Dictionary or one of the Symbols \top, \t (topEnvironment), \current, \c (currentEnvironment). 

Dictionary to be taken for variable reference. Defaults to \current.


Examples


(

s = Server.local;

Server.default = s;

s.boot;

)


Ex.1) Basic usage: random choice within region without repetitions

// constant source, max deviation +/- 3

// no repetition within 5 pitches


(

~in = 72;


p = Pbind(

\dur, 0.2,

\midinote, PLIdev(\in, 5, -3, 3).trace(prefix: "midi "),

).play;

)


// change source


~in = 65


p.stop



Ex.2) Variable deviations and lookBack


(

~loDev = -6;

~hiDev = 5;

~lookBack = 2;


p = Pbind(

\dur, 0.2,

\midinote, PLIdev(72, 11, \loDev, \hiDev, \lookBack).trace(prefix: "midi "),

).play;

)



// change on the fly

// as lookBack equals 2, this defines a fixed sequence (up or down anyway)


(

~loDev = -1;

~hiDev = 1;

)



// widen range


(

~loDev = -6;

~hiDev = 5;

)



// force a twelve-tone row


~lookBack = 11;



// contradictory input, lookBack 11 not possible within range, causes repetitions


(

~loDev = -3;

~hiDev = 2;

)


p.stop




Ex.3) Moving source signal


(

~loDev = -1;

~hiDev = 1;

~lookBack = 2;


p = Pbind(

\dur, 1/7,

\midinote, PLIdev(

Pseg(Pseq([65, 90], inf), 5, \sin).round,

11,

\loDev, 

\hiDev, 

\lookBack

).trace(prefix: "midi "),

).play;

)



// widen range and increase lookBack


(

~loDev = -6;

~hiDev = 5;

~lookBack = 10;

)


p.stop




Ex.4) Dynamic deviation range and lookBack


// lookBack and deviations coupled here

// maxLookBack must be large enough


(

~loDev = -1;

~hiDev = 1;

~lookBack = 2;


p = Pbind(

\dur, 1/7,

\midinote, PLIdev(78, 10, \loDev, \hiDev, \lookBack).trace(prefix: "midi "),

).play;

)


// start parameter movement on the fly


(

~loDev = Pseg(Pseq([2, 5].neg, inf), 5, \sin);

~hiDev = Pseg(Pseq([2, 5], inf), 5, \sin).trace(prefix: "absolute deviation and lookBack ");

~lookBack = Pseg(Pseq([2, 5], inf), 5, \sin);

)


p.stop



Ex.5) Non-integer source


(

~loDev = -6;

~hiDev = 5;

~lookBack = 3;

~thr = 1;


p = Pbind(

\dur, 1/7,

\midinote, PLIdev(

Pseg(Pseq([65, 90], inf), 5, \sin),

5,

\loDev,

\hiDev,

\lookBack,

\thr

).trace(prefix: "midi "),

).play;

)



// close floats can occur here

~thr = 0.01



// not here

~thr = 2


p.stop



Ex.6) Multichannel expansion

 

// larger pitch range and lookBack in upper voice 

// as with most patterns, no automatic array expansion of pattern arguments


(

~src = Pseg(Pseq([65, 85], inf), 5, \sin).round;


p = Pbind(

\dur, 1/7,

\midinote, Ptuple([

PLIdev(\src, 1, -1, 1),

PLIdev(\src, 3, -5, 5) + 8.5

]).trace(prefix: "midi "),

).play;

)


p.stop



Ex.7) Application to other params: rhythm

 

// if we have indexed data of whatever, we can slide over it,

// groups of durations as items to be streamed by PLIdev


(

~rhythmBase = [

[1, 1],

[2, 1, 1],

[1, 1, 2]

].collect(_.normalizeSum);


~rhythms = ~rhythmBase *.x [1, 2];

~rhythmNum = ~rhythms.size;

~rhythms = ~rhythms.scramble;


SynthDef(\noise_grain, { |out = 0, freq = 400, att = 0.005, rel = 0.1,

rq = 0.05, pan = 0, amp = 0.1|

var sig = { WhiteNoise.ar } ! 2;

sig = BPF.ar(sig, freq, rq) *

EnvGen.ar(Env.perc(att, rel, amp), doneAction: 2) *

(rq ** -1) * (250 / (freq ** 0.8));

OffsetOut.ar(out, Pan2.ar(sig, pan));

}).add;

)


(

// rhythmic variation and partial pitch repetition

// play for a while to note slow sliding, caused by Pseg


~loDev = -1;

~hiDev = 1;

~maxLookBack = 5; // fixed !

~lookBack = 2;


// be careful not to exceed index bounds

~src = Pseg(Pseq([~loDev.abs, ~rhythmNum - ~hiDev - 1], inf), 10, \sin, inf).round;


p = Pbind(

\instrument, \noise_grain,

\rel, Pexprand(0.05, 0.15),

\dur, PLIdev(\src, ~maxLookBack, \loDev, \hiDev, \lookBack).trace(prefix: "rhythm type: ")

.collect(~rhythms[_]).flatten * 0.3,

\midinote, Pstutter(Pwhite(1, 2), Pclump(3, Pxrand((50..100), inf))).flatten + [0, 12],

\pan, Pstutter(Pwhite(1, 2), Pclump(3, Pwhite(-1.0, 1))).flatten

).play;

)



// more variation by larger deviation from fixed source and larger lookBack


(

~src = 3;

~loDev = -3;

~hiDev = 2;

~lookBack = 4;

)



// fixed cycle with these bounds and lookBack == 1


(

~loDev = -1;

~hiDev = 0;

~lookBack = 1;

)


// other fixed cycle


~src = 2;


p.stop




Ex.8) Proof of concept


(

// Function to check an array for repetitions within a maximum test span


~repetitionCheck = { |array, maxTestSpan|

maxTestSpan.do { |i|

var result = (array.drop(i+1) - array).drop((i+1).neg).includes(0).not;

("no repetitions within a span of " ++ (i+2).asString ++ " items: ").post;

result.postln;

}

}

)


// test case

// no repetitions within a maximum span of 6 (maxLookBack == 5)


(

~src = Pbrown(0, 20, 0.3).round.asInteger;

p = PLIdev(\src, 5, -7, 7).iter;

a = p.nextN(10000);

a.plot;

~repetitionCheck.(a, 10);

)