PIdev 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: Idev suite, PLIdev, DIdev



Creation / Class Methods


Creates a new PIdev object.

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

pattern - The source value pattern to start search from.

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

loDev - Determines the current low deviation for the search. Defaults to -3.

hiDev - Determines the current high deviation for the search. Defaults to 3.

lookBack - 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.

thr - Threshold for equality comparison. Can be modulated, defaults to 1e-3. 

length - Number of repeats. Defaults to inf. 


Examples


(

s = Server.local;

Server.default = s;

s.boot;

)



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

// constant source (72), max deviation +/- 3

// no repetition within 5 pitches


(

p = Pbind(

\dur, 0.2,

\midinote, PIdev(72, 5, -3, 3).trace(prefix: "midi "),

).play;

)


p.stop




Ex.2) Variable deviations and lookBack


(

~loDev = -6;

~hiDev = 5;

~lookBack = 2;


p = Pbind(

\dur, 0.2,

\midinote, PIdev(72, 11, Pfunc { ~loDev }, Pfunc { ~hiDev }, Pfunc { ~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, PIdev(

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

11,

Pfunc { ~loDev },

Pfunc { ~hiDev },

Pfunc { ~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, PIdev(

        78,

        10,

        Pn(Plazy { ~loDev }),

        Pn(Plazy { ~hiDev }).trace(prefix: "absolute deviation "),

        Pn(Plazy { ~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, PIdev(

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

5,

Pfunc { ~loDev },

Pfunc { ~hiDev },

Pfunc { ~lookBack },

Pfunc { ~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


(

p = Pbind(

\dur, 1/7,

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

\midinote, Ptuple([

PIdev(Pkey(\src), 1, -1, 1),

PIdev(Pkey(\src) + 8.5, 3, -5, 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 PIdev


(

~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;


p = Pbind(

\instrument, \noise_grain,

\rel, Pexprand(0.05, 0.15),

\dur, PIdev(

// be careful not to exceed index bounds

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

2, // lookBack span, no repetition within 3 items

~loDev, 

~hiDev

).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;

)


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 (lookBack == 5)


(

p = PIdev(Pbrown(0, 20, 0.3).round.asInteger, 5, -7, 7).iter;

a = p.nextN(10000);

a.plot;

~repetitionCheck.(a, 10);

)