/* FFT and the PV_Ugens - Spectral Processing*/


// FFT UGens need at least one buffer of memory where the size is a power of 2 between

// 128 and 4096


{SinOsc.ar(440 + SinOsc.ar(200, 0, 440 * 20), 0, 0.05)}.play(s)


// our FFT buffer


b = CtkBuffer.buffer(4096).load;

c = CtkBuffer.buffer(4096).load;

d = CtkBuffer.playbuf("sounds/Perc.aiff").load;


a = CtkNoteObject(

SynthDef(\fftest, {arg buffer, buffer2, sndbuf;

var src, src2, fft;

//src = WhiteNoise.ar(0.1);

//src = PinkNoise.ar(0.1) + SinOsc.ar(440, 0, 0.1);

//src = SinOsc.ar(440 + SinOsc.ar(200, 0, 440 * 20), 0, 0.05);

src = PlayBuf.ar(1, sndbuf, BufRateScale.kr(sndbuf));

fft = FFT(buffer, src);

Out.ar(0, Pan2.ar(IFFT(fft), 0));

})

);

z = a.new.buffer_(b).sndbuf_(d).play;

z.free;



a = CtkNoteObject(

SynthDef(\fftest, {arg buffer, buffer2, sndbuf;

var src, fft;

//src = WhiteNoise.ar(0.1);

//src = PinkNoise.ar(0.1) + SinOsc.ar(440, 0, 0.1);

//src = SinOsc.ar(440 + SinOsc.ar(200, 0, 440 * 20), 0, 0.2);

src = PlayBuf.ar(1, sndbuf, BufRateScale.kr(sndbuf), loop: 1);

fft = FFT(buffer, src);

// scramble bins

fft = PV_BinScramble(fft, MouseX.kr(0, 1), MouseY.kr(0, 1), Impulse.kr(2));

Out.ar(0, Pan2.ar(IFFT(fft), 0));

})

);

z = a.new.buffer_(b).sndbuf_(d).play;

z.free;


a = CtkNoteObject(

SynthDef(\fftest, {arg buffer, buffer2, sndbuf;

var src, fft;

//src = WhiteNoise.ar(0.1);

//src = PinkNoise.ar(0.1) + SinOsc.ar(440, 0, 0.1);

//src = SinOsc.ar(440 + SinOsc.ar(200, 0, 440 * 20), 0, 0.2);

src = PlayBuf.ar(1, sndbuf, BufRateScale.kr(sndbuf), loop: 1);

fft = FFT(buffer, src);

// stretch (mul) and shift (add)

fft = PV_BinShift(fft, MouseX.kr(0, 2), MouseY.kr(0, 20));

Out.ar(0, Pan2.ar(IFFT(fft), 0));

})

)

z = a.new.buffer_(b).sndbuf_(d).play;

z.free;


a = CtkNoteObject(

SynthDef(\fftest, {arg buffer, buffer2, sndbuf;

var src, src2, fft;

//src = WhiteNoise.ar(0.1);

//src = PinkNoise.ar(0.1) + SinOsc.ar(440, 0, 0.1);

// src = SinOsc.ar(440 + SinOsc.ar(200, 0, 440 * 20), 0, 0.2);

src = PlayBuf.ar(1, sndbuf, BufRateScale.kr(sndbuf), loop: 1);

fft = FFT(buffer, src);

fft = PV_BrickWall(fft, MouseX.kr(-1, 1));

Out.ar(0, Pan2.ar(IFFT(fft), 0));

})

);

z = a.new.buffer_(b).sndbuf_(d).play;

z.free;


b = CtkBuffer.buffer(4096).load;


a = CtkNoteObject(

SynthDef(\fftest, {arg buffer, buffer2, sndbuf;

var src, src2, fft;

//src = WhiteNoise.ar(0.1);

//src = PinkNoise.ar(0.1) + SinOsc.ar(440, 0, 0.1);

//src = SinOsc.ar(440 + SinOsc.ar(200, 0, 440 * 20), 0, 0.2);

src = PlayBuf.ar(1, sndbuf, BufRateScale.kr(sndbuf), loop: 1);

fft = FFT(buffer, src);

fft = PV_MagAbove(fft, MouseX.kr(0, 100));

// fft = PV_MagBelow(fft, MouseX.kr(0, 100));

// fft = PV_MagClip(fft, MouseX.kr(0, 100));

// fft = PV_MagNoise(fft);

fft = PV_MagSmear(fft, MouseX.kr(0, 100));

fft = PV_MagFreeze(fft, MouseX.kr(-1, 1));

Out.ar(0, Pan2.ar(IFFT(fft), 0));

})

);


z = a.new.buffer_(b).sndbuf_(d).play;

z.free;


a = CtkNoteObject(

SynthDef(\fftest, {arg buffer, buffer2, sndbuf;

var src, src2, fft;

//src = WhiteNoise.ar(0.1);

//src = PinkNoise.ar(0.1) + SinOsc.ar(440, 0, 0.1);

//src = SinOsc.ar(440 + SinOsc.ar(200, 0, 440 * 20), 0, 0.2);

src = PlayBuf.ar(1, sndbuf, BufRateScale.kr(sndbuf), loop: 1);

fft = FFT(buffer, src);

fft = PV_RandComb(fft, MouseX.kr(0, 1), Impulse.kr(0.25));

Out.ar(0, Pan2.ar(IFFT(fft), 0));

})

);

z = a.new.buffer_(b).sndbuf_(d).play;

z.free;



a = CtkNoteObject(

SynthDef(\fftest, {arg buffer, buffer2, sndbuf;

var src, src2, fft, fft2;

src = WhiteNoise.ar(0.1);

//src = PinkNoise.ar(0.1) + SinOsc.ar(440, 0, 0.1);

//src = SinOsc.ar(440 + SinOsc.ar(200, 0, 440 * 20), 0, 0.2);

src2 = PlayBuf.ar(1, sndbuf, BufRateScale.kr(sndbuf), loop: 1);

fft = FFT(buffer, src);

fft2 = FFT(buffer2, src2);

fft = PV_RandWipe(fft, fft2, 1-(Amplitude.kr(src2, 0.01, 0.2) * 5), Impulse.kr(0.25));

Out.ar(0, Pan2.ar(IFFT(fft), 0));

})

);


z = a.new.buffer_(b).buffer2_(c).sndbuf_(d).play;

z.free;


/* Some PV UGens I built */


a = CtkNoteObject(

SynthDef(\fftest, {arg buffer, buffer2, sndbuf;

var src, src2, fft;

src = PinkNoise.ar(0.1) + SinOsc.ar(440, 0, 0.1);

//src = SinOsc.ar(440 + SinOsc.ar(200, 0, 440 * 20), 0, 0.2);

//src = PlayBuf.ar(1, sndbuf, BufRateScale.kr(sndbuf), loop: 1);

fft = FFT(buffer, src);

// filter out stable freqs

fft = PV_NoiseSynthP(fft, MouseX.kr(0, 2pi));

// filter out unstable freqs

//fft = PV_PartialSynthP(fft, MouseX.kr(0, 2pi));

Out.ar(0, Pan2.ar(IFFT(fft), 0));

})

);


z = a.new.buffer_(b).sndbuf_(d).play;

z.free;


a = CtkNoteObject(

SynthDef(\fftest, {arg buffer, buffer2, sndbuf;

var src, src2, fft;

//src = PinkNoise.ar(0.1) + SinOsc.ar(440, 0, 0.1);

//src = SinOsc.ar(440 + SinOsc.ar(200, 0, 440 * 20), 0, 0.2);

src = PlayBuf.ar(1, sndbuf, BufRateScale.kr(sndbuf), loop: 1);

fft = FFT(buffer, src);

// returns n loudest bins

//fft = PV_MaxMagN(fft, MouseX.kr(0, BufFrames.kr(buffer) * 0.5));

// returns n quietest bins

fft = PV_MinMagN(fft, MouseX.kr(0, BufFrames.kr(buffer) * 0.5));

Out.ar(0, Pan2.ar(IFFT(fft), 0));

})

);


z = a.new.buffer_(b).sndbuf_(d).play;

z.free;


/*

PV_MagMap

*/


/* Triggers */

/*

As we have already mentioned, triggers are usually a signal that changes from a <=0 value to a positive one. We have already seen triggers alter the way something inside a synthdef works, but what if you want something to happen (new notes are created for instance) based on an audio trigger? For that, we actually need to send a mesage back to the language through the OSCresponder or OSCresponderNode (safer).

*/


// A synthdef that sends a trigger message back to the language

a = CtkNoteObject(

SynthDef(\trigger, {

var trig;

// a sample trigger audio signal

trig = Impulse.ar(0.2);

// Use SendTrig to send a '/tr' message to the server, along with a value from an LFNoise2

// SendTrig takes a trigger, an ID and a value to send back to the lang

SendTrig.ar(trig, 0, LFNoise2.ar(0.2).range(0, 10));

Out.ar(0, trig);

})

);

// set up an OSCresponderNode

o = OSCresponderNode(s.addr, '/tr', {arg time, resp, msg;

// our function will just post the data send back from the trigger synthdef

[time, resp, msg].postln;

}).add;

// start your trigger

z = a.new.play;

z.free;


// remove the OSCresponderNode

o.remove;


/*

You can put anything into the OSCresponder function, even new note events

*/


a = CtkNoteObject(

SynthDef(\trigger, {

var trig;

trig = Impulse.ar(0.2);

// values sent back will be between 440 and 880.. trigger sintones

SendTrig.ar(trig, 0, LFNoise2.ar(0.2).range(440, 880));

// No output

})

);


b = CtkNoteObject(

SynthDef(\sintones, {arg freq;

Out.ar(0, SinOsc.ar(freq, 0, XLine.kr(0.2, 0.0001, 2, doneAction: 2)))

})

);

o = OSCresponderNode(s.addr, '/tr', {arg time, resp, msg;

// the value sent back is msg[3]

b.new.freq_(msg[3].postln).play;

}).add;

// start your trigger

z = a.new.play;

z.free;


// remove the OSCresponderNode

o.remove;


/*

Using the above as a basic model, use different signals to trigger. Use Trig1 to limit the number of triggers (since these are audio signals... this can be a good idea).

*/


a = CtkNoteObject(

SynthDef(\trigger, {arg inbus;

var in, trig;

in = In.ar(inbus); // read from the inbus, use Amplitude to trigger

trig = Trig1.ar(

// Booleans (or BinaryOpUGens in this case) return 0 or 1

Amplitude.ar(in) > 0.2, // when Amplitude is greater than 0.2, trigger

1);  // limit to 1 per second

// values sent back will be between 440 and 880.. trigger sintones

SendTrig.ar(trig, 0, LFNoise2.ar(0.2).range(440, 880));

// No output

})

);


b = CtkNoteObject(

SynthDef(\sintones, {arg freq;

Out.ar(0, SinOsc.ar(freq, 0, XLine.kr(0.2, 0.0001, 2, doneAction: 2)))

})

);

o = OSCresponderNode(s.addr, '/tr', {arg time, resp, msg;

// the value sent back is msg[3]

b.new.freq_(msg[3].postln).play;

}).add;

// start your trigger

z = a.new.inbus_(s.options.numOutputBusChannels).play;

z.free;


// remove the OSCresponderNode

o.remove;


/*

I don't like using 'hard-coded' amplitudes... I do like using the difference in signals between now and the very recent past. If the difference is above a threshold, create a trigger

*/


a = CtkNoteObject(

SynthDef(\trigger, {arg inbus;

var in, amp1, amp2, trig;

in = In.ar(inbus); 

amp1 = Amplitude.ar(in); // get the current Amplitude

amp2 = Amplitude.ar(DelayC.ar(in, 0.01, 0.01)); // get the Amplitude from 0.01 seconds ago

trig = Trig1.ar(

(amp1 / amp2.max(0.0001)) > 10, // compare them, and set up a thresh

0.1);     // limit to 0.1 per second

// values sent back will be between 440 and 880.. trigger sintones

SendTrig.ar(trig, 0, LFNoise2.ar(1).range(440, 880));

// No output

})

);


b = CtkNoteObject(

SynthDef(\sintones, {arg freq;

Out.ar(0, SinOsc.ar(freq, 0, XLine.kr(0.2, 0.0001, 2, doneAction: 2)))

})

);


o = OSCresponderNode(s.addr, '/tr', {arg time, resp, msg;

// the value sent back is msg[3]

b.new.freq_(msg[3].postln).play;

}).add;

// start your trigger

z = a.new.inbus_(s.options.numOutputBusChannels).play;

z.free;


// remove the OSCresponderNode

o.remove;


/*

Slope can also make good Amplitude triggers. Using Pitch can be a little trickier:

*/


a = CtkNoteObject(

SynthDef(\trigger, {arg inbus;

var in, freq, hasFreq, trig;

in = In.ar(inbus); 

// pitch outputs two valeus.. we mostly want the first

#freq, hasFreq = Pitch.kr(in, 200, 45.midicps, 60.midicps);

freq.poll(Impulse.kr(1));

// check if freq is InRange

trig = Trig1.ar(

InRange.kr(freq, 56.5.midicps, 57.5.midicps), 

1);     // limit to 0.1 per second

SendTrig.ar(trig, 0, freq); // send the freq out

// No output

})

);


b = CtkNoteObject(

SynthDef(\sintones, {arg freq;

Out.ar(1, 

SinOsc.ar(freq, 0, 

EnvGen.kr(

Env([0.001, 0.2, 0.0001], [2, 2], \exp), 

doneAction: 2)));

})

);


o = OSCresponderNode(s.addr, '/tr', {arg time, resp, msg;

// the value sent back is msg[3]

b.new.freq_((msg[3] * 8).postln).play;

}).add;

// start your trigger

z = a.new.inbus_(s.options.numOutputBusChannels).play;

z.free;


// remove the OSCresponderNode

o.remove;


/* 

Octaves sometimes will cause the algorithm to trigger... I like to filter out as much as I can before sending it to the Pitch UGen

*/


a = CtkNoteObject(

SynthDef(\trigger, {arg inbus;

var in, freq, hasFreq, trig;

in = In.ar(inbus); 

// I know I want pitches around 220, filter out everything above and below

in = HPF.ar(LPF.ar(in, 220), 220);

// pitch outputs two valeus.. we mostly want the first

#freq, hasFreq = Pitch.kr(in, 200, 45.midicps, 60.midicps);

freq.poll(Impulse.kr(1));

// check if freq is InRange

trig = Trig1.ar(

InRange.kr(freq, 56.5.midicps, 57.5.midicps), 

0.1);     // limit to 0.1 per second

SendTrig.ar(trig, 0, freq); // send the freq out

// No output

})

);


b = CtkNoteObject(

SynthDef(\sintones, {arg freq;

Out.ar(1, 

SinOsc.ar(freq, 0, 

EnvGen.kr(

Env([0.001, 0.2, 0.0001], [2, 2], \exp), 

doneAction: 2)));

})

);


o = OSCresponderNode(s.addr, '/tr', {arg time, resp, msg;

// the value sent back is msg[3]

b.new.freq_((msg[3] * 8).postln).play;

}).add;

// start your trigger

z = a.new.inbus_(s.options.numOutputBusChannels).play;

z.free;


// remove the OSCresponderNode

o.remove;


/* Multiple triggers, IDs and ProcMod */


a = CtkNoteObject(

SynthDef(\trigger, {arg inbus;

var in, amp1, amp2, trig;

in = In.ar(inbus); 

amp1 = Amplitude.ar(in); 

amp2 = Amplitude.ar(DelayC.ar(in, 0.01, 0.01)); 

trig = Trig1.ar(

(amp1 / amp2.max(0.0001)) > 10, // compare them, and set up a thresh

0.1);     // limit to 0.1 per second

SendTrig.ar(trig, 0, LFNoise2.ar(0.2).range(440, 880)); // id of 0

SendTrig.ar(Impulse.ar(5.reciprocal), 1, 1); // id of 1, trigger every 5 seconds

// No output

})

);


b = CtkNoteObject(

SynthDef(\sintones, {arg freq;

Out.ar(1, 

SinOsc.ar(freq, 0, 

EnvGen.kr(

Env([0.001, 0.2, 0.0001], [2, 2], \exp), 

doneAction: 2)));

})

);


o = OSCresponderNode(s.addr, '/tr', {arg time, resp, msg;

var id;

// a trigger's id is stored in msg[2]. Check each trigger to see which ID it has

id = msg[2];

case

{id == 0} {

b.new.freq_((msg[3] * 8).postln).play;

}

{id == 1} {

"This is the steady pulse trigger".postln;

}

{true} {

"Hmm... this id wasn't 0 or 1".postln;

}

}).add;

// start your trigger

z = a.new.inbus_(s.options.numOutputBusChannels).play;

z.free;


// remove the OSCresponderNode

o.remove;


// All the OSCresponders we have seen so far will respond to EVERY trigger message, and we have

// also seen that you can set an OSCresponder to check for ids. Also, you have noticed that you

// need to clean up your OSCresponder usage when you are done with it. ProcMod gives you a way

// to set up and destroy OSCresponders as needed for a process. They are created when the ProcMod

// plays, and removed when the ProcMod is released.


// the trigger... pass an id in as an argument

(

a = CtkNoteObject(

SynthDef(\trigger, {arg inbus, id, outbus;

var in, amp1, amp2, trig, trigsig, max, delayed;

in = In.ar(inbus); 

delayed = DelayC.ar(in, 0.01, 0.01); // store the delayed version in a var...

amp1 = Amplitude.ar(in); 

amp2 = Amplitude.ar(delayed); 

// envelope this to avoid the initial trigger from 0's in the delay

trigsig = (amp1 / amp2.max(0.0001)) * 

EnvGen.kr(

Env([0, 0, 1], [0.01, 0.01], [\lin, 10]),

doneAction: 0);

trig = Trig1.ar(trigsig > 5, 0.1); // no more then 0.1 per second

// since we are dealing with spikes of noise, try to grab the most recent max value

max = RunningMax.ar(amp1, trig);

SendTrig.ar(trig, id, max); // send the current Amplitude back to the lang

Out.ar(outbus, delayed); // so you can send it out!

})

);

b = CtkNoteObject(

SynthDef(\burst, {arg envbus, inbus, amp, duration;

var in, delay, noise;

// read in sound for a moment!

in = In.ar(inbus) * 

EnvGen.kr(

Env([1, 1, 0], [0.2, 0.2], [\lin, -10]),

doneAction: 0); // don't free the synth!

delay = CombC.ar(in, 0.2, Line.kr(0.05, 0.2, duration), duration);

// smooth out the delay with an Amplitude UGen, and modulate with noise

noise = Amplitude.ar(delay, releaseTime: 0.1) * GrayNoise.ar(1);

Out.ar(0, Pan2.ar(

noise * EnvGen.kr(

Env([amp, 0], [duration], [-3]), doneAction: 2) *

In.kr(envbus),

LFNoise2.ar(duration.reciprocal)));

})

);

 

z = {

var proc;

proc = ProcMod(Env([0, 1, 0], [0.1, 10], [4, -4], 1), 1)

.function_({arg group, envbus, server;

var outbus;

outbus = server.audioBusAllocator.alloc(1);

// start the node that will listen for jumps in Amplitude

// for an id, pass in the ProcMods group id... it's unique to 

// this ProcMod! outbus will the be input signal delayed by 0.01

a.new(addAction: 0, target: group).inbus_(s.options.numOutputBusChannels)

.id_(group).outbus_(outbus).play;

// we need this 'outbus' info later, store it in ProcMod's data

// slot (a Dictionary)

proc.saveToData(\outbus -> outbus);

})

.responder_(

OSCresponderNode(s.addr, '/tr', {arg time, resp, msg;

var id, amp, duration, inbus;

// check if this trigger is the one we want to use

id = msg[2];

(id == proc.group).if({

amp = msg[3].postln; // the value we sent back

// use the trigger amplitude to decide how long the gesture is

duration = Env([4, 10], [1])[amp];

// grab the \outbus from this ProcMod's data slot

inbus = proc.data.at(\outbus);

b.new(addAction: 1, target: proc.group).inbus_(inbus).amp_(amp)

.duration_(duration.postln).envbus_(proc.envbus).play;

});

});

);

}

)

p = z.value;

p.play;

p.release;