Squiz Wave squeezer. Maybe a kind of pitch shifter.


Squiz.ar(in, pitchratio, zcperchunk, memlen)


A simplistic pitch-raising algorithm. It's not meant to sound natural; its sound is reminiscent of some weird mixture of filter, ring-modulator and pitch-shifter, depending on the input.


pitchratio is the ratio by which pitch will be raised (e.g. pitchratio of 2 will raise by one octave). Only upwards pitch-shifts are possible so a value below 1 will have no effect.


The algorithm works by cutting the signal into fragments (delimited by upwards-going zero-crossings) and squeezing those fragments in the time domain (i.e. simply playing them back faster than they came in), leaving silences imbetween. 


zcperchunk indicates how many positive-going zero-crossings are used to delimit a chunk. (Only positive integer values make sense.)


memlen is the amount (in seconds) of memory the unit allocates internally to remember each fragment, i.e. the longest a given fragment can become. The default should be fine for most signals. Raising it higher will use more real-time memory and probably won't sound very different (especially if zcperchunk is low).


All the parameters apart from memlen can be modulated.


This UGen is dedicated to Suburban Base Records. (It doesn't sound like them, but was half-inspired by them.)



Examples


In both these examples, moving the mouse left/right varies pitchratio, while moving it up/down varies  zcperchunk. If you watch the scope it illustrates fairly well what's happening.


s = Server.internal;

s.boot;

// On a simple sine wave

x = {Squiz.ar(SinOsc.ar, MouseX.kr(1, 10, 1), zcperchunk: MouseY.kr(1, 10), mul:0.1).dup}.play(s);

x.free;

// Scope is helpful for visualising what's going on

x = {Squiz.ar(SinOsc.ar, MouseX.kr(1, 10, 1), zcperchunk: MouseY.kr(1, 10), mul:0.1).dup}.scope;

x.free;



// On a sample of some sort - choose one...

b = Buffer.read(s,"sounds/a11wlk01.wav");

b = Buffer.read(s,"sounds/amenfast.wav");

(

x = {

var sig;

sig = PlayBuf.ar(1, b.bufnum, BufRateScale.kr(b.bufnum) * 0.5, startPos: 92898, loop: 1);

Squiz.ar(sig, MouseX.kr(1, 100, 1), zcperchunk: MouseY.kr(1, 10), mul:0.5).dup

}.play(s);

)

x.free;

b.free;



// Let's make it a bit more complicated. By analysing the signal we can change the Squiz sound algorithmically.

s.boot;

b = Buffer.read(s,"sounds/amenfast.wav");

c = Buffer.alloc(s, 512);

(

x = {

var sig, chain, centroid, hicent, locent, fraccent, fraccentl, heaviness, heaviness2, kick2trig, kick2;

sig = PlayBuf.ar(1, b.bufnum, BufRateScale.kr(b.bufnum) * 0.5, /*startPos: 92898, */loop: 1);

// kick2 to emphasise the main downbeats

kick2trig = Impulse.kr(BufDur.kr(b.bufnum).reciprocal * 4);

kick2 = SinOsc.ar(50 + EnvGen.ar(Env.perc(0.001, 0.01, 1000, -10), kick2trig));

kick2 = EnvGen.ar(Env.perc, kick2trig) * kick2 * 0.3;

chain = FFT(c.bufnum, sig);

//centroid = FFTCentroid.kr(chain).log.max(0.000001);

centroid = FFTPercentile.kr(chain, 0.9);//.log.max(0.000001);

hicent =     Amplitude.kr(centroid, 0, 10);

locent = 10000 - Amplitude.kr(10000 - centroid, 0, 10);

fraccent = ((centroid - locent) / (hicent - locent)).min(1).max(0);

fraccentl = Latch.kr(fraccent, Onsets.kr(chain));

Out.kr(0, fraccentl);

// fraccent.poll;

//heaviness = MouseX.kr(0,1);

//heaviness2 = MouseY.kr(0,1);

heaviness  = LFPar.kr(0.016, 0, 0.5, 0.5);

heaviness2 = LFPar.kr(0.016, 1, 0.5, 0.5);

// we don't go really mental until we've got going

# heaviness, heaviness2 = [heaviness, heaviness2] * Line.kr(0.1, 1, 100);

kick2 + Squiz.ar(sig, 

(   fraccentl  * LFNoise2.kr(0.1).exprange(1, 100) * heaviness) + 

((1-fraccentl) * LFNoise2.kr(0.1).exprange(1, 100) * heaviness2) +

0.8, 

zcperchunk: LFNoise2.kr(0.1).range(1, 10), mul:0.5).dup

//sig

}.play(s);

)

//s.scope5

x.free;

b.free;