DIdev pseudo drate ugen, searches for numbers with integer distance from a source signal, optionally avoiding repetitions within a span


Part of: miSCellaneous


Inherits from: DUGen


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: DIdev needs at least a SC version >= 3.7.0 for proper working.


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 PIdev and PLIdev, DIdev needs to know maximum deviations (minLoDev, maxHiDev) beforehand. Together with maxLookBack they determine multichannel sizes, so a relatively high number of ugens might be involved. 


See also: Idev suite, PIdev, PLIdev



Creation / Class Methods


Creates a new DIdev object.

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

in - The source signal, integer distances are calculated from the value of this signal with each trigger.

Can be demand rate or other ugen.

maxLookBack - Integer, the maximum lookback span. Cannot be modulated, defaults to 3.

minLoDev - Integer, the minimum low deviation (must not be exceeded by any loDev value). 

Should be negative, cannot be modulated, defaults to -3.

maxHiDev - Integer, the maximum high deviation (must not be exceeded by any hiDev value).  

Should be positive, cannot be modulated, defaults to 3.

lookBack - Determines the current lookback span for avoiding repetitions.

Can be modulated (demand rate or other ugen, no ar) but must not exceed maxLookBack.

If no value is passed, then maxLookBack is taken.

loDev - Determines the current low deviation for the search.

Can be modulated (demand rate or other ugen) but must not exceed minLoDev.

If not specified, them minLoDev is taken. 

hiDev - Determines the current high deviation for the search.

Can be modulated (demand rate or other ugen) but must not exceed maxHiDev.

If not specified, then maxHiDev is taken. 

thr - Threshold for equality comparison. Can be modulated (demand rate or other ugen).

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


(

x = {

var trig = Impulse.ar(5);

var midi = Demand.ar(trig, 0, DIdev(72, 2, -1, 2));

midi.poll(5, label: \midi);

SinOsc.ar(midi.midicps.lag(0.007)) ! 2 * 0.1

}.play;

)


x.release




Ex.2) Variable deviations and lookBack


(

x = {

|loDev = -6, hiDev = 5, lookBack = 2|

var trig = Impulse.ar(5);

// define maxLookBack, minLoDev and maxHiDev

var midi = Demand.ar(trig, 0, DIdev(72, 11, -6, 5, lookBack, loDev, hiDev));

midi.poll(5, label: \midi);

SinOsc.ar(midi.midicps.lag(0.007)) ! 2 * 0.1

}.play;

)



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


(

x.set(\loDev, -1);

x.set(\hiDev, 1)

)



// widen range


(

x.set(\loDev, -6);

x.set(\hiDev, 5);

)



// force a twelve-tone row


x.set(\lookBack, 11)



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


(

x.set(\loDev, -3);

x.set(\hiDev, 2)

)


x.release



Ex.3) Moving source signal


(

x = {

|loDev = -1, hiDev = 1, lookBack = 2|

var trig = Impulse.ar(7);

// define maxLookBack, minLoDev and maxHiDev

// source is rounded to integers here

var midi = Demand.ar(trig, 0, DIdev(SinOsc.ar(0.2, 0, 15, 82).round, 11, -6, 5, lookBack, loDev, hiDev));

midi.poll(7, label: \midi);

SinOsc.ar(midi.midicps.lag(0.007)) ! 2 * 0.1

}.play;

)



// widen range and increase lookBack


(

x.set(\loDev, -6);

x.set(\hiDev, 5);

x.set(\lookBack, 10);

)


x.release




Ex.4) Dynamic deviation range and lookBack


// lookBack and deviations coupled here

// maxLookBack, minLoDev and maxHiDev must be large enough


(

x = {

var trig = Impulse.ar(7);

var dev = SinOsc.ar(0.1, -pi/2).range(1, 5);

var midi = Demand.ar(trig, 0, 

DIdev(78, 10, -5, 5, 

SinOsc.kr(0.1, -pi/2).range(1, 5).round.poll(label: \lookBack),

dev.neg.poll(label: \loDev),

dev.poll(label: \hiDev)

)

);

    SinOsc.ar(midi.lag(0.007).midicps, 0, 0.1) ! 2

}.play;

)


x.release



// loDev and hiDev can be demand rate


(

x = {

    var trig = Impulse.ar(5);

var hiDev = Dseq([1, 10], inf);

    var midi = Demand.ar(trig, 0,

        DIdev(78, 10, -10, 10,

1,

            Dseq([-10, 5], inf),

            Dseq([-5, 10], inf)

        )

    );

    SinOsc.ar(midi.lag(0.007).midicps, 0, 0.1) ! 2

}.play;

)


x.release



// lookBack can also be demand rate


(

x = {

    var trig = Impulse.ar(5);

var hiDev = Dseq([1, 10], inf);

    var midi = Demand.ar(trig, 0,

DIdev(70, 10, -15, 15,

Dstutter(4, Dseq([1, 3], inf)),

Dstutter(4, Dseq([-9, 7], inf)),

Dstutter(4, Dseq([-8, 10], inf))

        )

    );

midi.poll(trig);

SinOsc.ar(midi.lag(0.007).midicps, 0, 0.1) ! 2 * EnvGen.ar(Env.asr(0.15))

}.play;

)


x.release




Ex.5) Non-integer source


(

x = {

|lookBack = 3, thr = 1|

var trig = Impulse.ar(7);

// for a non-integer source it makes sense to take a sufficiently large threshold thr

var midi = Demand.ar(trig, 0, DIdev(SinOsc.ar(0.2, 0, 15, 82), 5, -6, 5, lookBack, thr: thr));

midi.poll(7, label: \midi);

SinOsc.ar(midi.midicps.lag(0.007)) ! 2 * 0.1

}.play;

)


// close floats can occur here 

x.set(\thr, 0.01)


// not here

x.set(\thr, 2)


x.release




Ex.6) Multichannel expansion

 


// nothing especially implemented


(

x = {

var trig = Impulse.ar(7);

var in = [0, 8.5];

var maxLookBack = [1, 3];

var loDev = [-1, -5];

var hiDev = [1, 5];

var midi = { |i| Demand.ar(trig, 0,

DIdev(

in[i] + SinOsc.ar(0.1, -pi/2).range(65, 85).round,

maxLookBack: maxLookBack[i],

loDev: loDev[i],

hiDev: hiDev[i]

).dpoll(label: "midi_" ++ (i == 0).if { "lo" }{ "hi"})

) } ! 2;

SinOsc.ar(midi.lag(0.007).midicps, 0, 0.1)

}.play;

)


x.release



Ex.7) Application to other params: rhythm

 

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

// prepare some rhythms in order

// use them for SynthDef


(

~rhythmBase = [

[1, 1],

[2, 1, 1],

[1, 1, 2],

].collect(_.normalizeSum);


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

~rhythmNum = ~rhythms.size;

~rhythms = ~rhythms.scramble;


"rhythm types: ".postln;

~rhythms.do { |r, i| i.post; ": ".post; r.postln };


SynthDef(\sine_rhythm, { |out = 0, freq = 400, att = 0.01, rel = 0.1, gate = 1, amp = 0.2|

var trig, loDev = -1, hiDev = 1, sig, src,

rhy = ~rhythmNum.collect { |i| Dseq(~rhythms[i], 1) };


trig = TDuty.ar(

Dswitch(rhy,

// be careful not to exceed range of rhythm indices, which can result in server quit

Dpoll(

DIdev(SinOsc.ar(0.1).range(loDev.abs, ~rhythmNum - hiDev - 1).round, 2, loDev, hiDev),

'rhythm type'

)

) * 0.3

);

src = SinOsc.ar(Demand.ar(trig, 0, LFDNoise3.ar([0.5, 3], [10, 15], [60, 85])).midicps);

sig = Decay2.ar(trig, att, rel) * src;

Out.ar(out, sig * EnvGen.ar(Env.asr(att, 1, rel), gate, doneAction: 2) * amp);

}).add;

)


x = Synth(\sine_rhythm)


x.release



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;

}

};


// prepare buffer to store DIdev values 


n = 10000; // buffer size

r = 10000; // trigger rate

b = Buffer.alloc(s, n);

)



// run to store, wait until finished (a bit more than 1 second)

(

{

var trig = Impulse.ar(r);

Demand.ar(trig, 0, Dbufwr(DIdev(Dbrown(0, 20, 0.3).round, 5, -7, 7), b, Dseries(0, 1, n), 0));

Line.ar(dur: n / r + 0.1, doneAction: 2);

0

}.play;

)


// move data to language


b.loadToFloatArray(action: { |x| a = x.asInteger; "array filled \n".postln; })



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


~repetitionCheck.(a, 10);




Ex.9) Switching signals with DXMix


// source of 10 stereo granulations:

// they differ in position movement, trigrate and rate


b = Buffer.read(s, Platform.miSCellaneousDirs[0] +/+ "Sounds" +/+ "kitchen_sounds_1.wav");


(

x = {

var pos = { |i| SinOsc.ar(0.02, pi/5 * i).range(0.1, 0.8) } ! 10;

var sig;


sig = pos.collect { |p, i|

TGrains.ar(

2,

trigger: Impulse.ar(i * 10 + 30),

bufnum: b,

rate: LFDNoise3.ar(0.1).range(0.3, 1.5),

centerPos: p * BufDur.ir(b),

dur: 0.1,

pan: Dseq([-1, 1], inf)

)

};

// switch between stereo sources with DXMix

DXMix.ar(

Dpoll(DIdev(SinOsc.ar(0.1).range(2, 7).round, 2, -2, 1)),

`sig,

fadeMode: 1,

stepTime: 0.03,

fadeTime: 0.002

) * 5

}.play

)


x.release



// sine stereo sources


(

x = {

var sig = (1..20).scramble.collect { |i| 

SinOsc.ar(

[i, 20-i] * 100 * LFDNoise3.ar(0.1).range(0.97, 1.03), 

0, 

0.05 * LFDNoise3.ar(1 ! 2).range(0.1, 1)

)

};

DXMix.ar(

Dpoll(DIdev(SinOsc.ar(0.05).range(2, 17).round, 2, -2, 2)),

`sig,

fadeMode: 1,

stepTime: 0.03,

fadeTime: 0.002

) ;

}.play

)


x.release