/* 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;