Skip to content

Instantly share code, notes, and snippets.

@carltesta
Last active November 16, 2018 20:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save carltesta/7529749349d934e410fe30d4fac29561 to your computer and use it in GitHub Desktop.
Save carltesta/7529749349d934e410fe30d4fac29561 to your computer and use it in GitHub Desktop.
Sway for monome norns
Engine_Sway : CroneEngine {
*new { arg context, doneCallback;
^super.new(context, doneCallback);
}
alloc {
NornsSway(\sway);
NornsSway(\sway).input.set(\chan, context.in_b[0].index);
NornsSway(\sway).analysis_input.set(\chan, context.in_b[0].index);
NornsSway(\sway).output.play(context.out_b.index, 2, context.xg, addAction: \addToHead);
NornsSway(\sway).verbose = false;
this.addCommand("amp_thresh", "f", {|msg|
NornsSway(\sway).amp_thresh = msg[1];
});
this.addCommand("clarity_thresh", "f", {|msg|
NornsSway(\sway).clarity_thresh = msg[1];
});
this.addCommand("density_thresh", "f", {|msg|
NornsSway(\sway).density_thresh = msg[1];
});
this.addCommand("verbose", "i", {|msg|
if(msg[1] == 1, {
NornsSway(\sway).verbose = true;
}, {
NornsSway(\sway).verbose = false;
});
});
this.addCommand("polarity", "i", {|msg|
if(msg[1] == 1, {
NornsSway(\sway).polarity = false;
NornsSway(\sway).change_polarity;
}, {
NornsSway(\sway).polarity = true;
NornsSway(\sway).change_polarity;
});
});
this.addCommand("fadetime", "i", {|msg|
NornsSway(\sway).fade_time(msg[1]);
});
this.addCommand("timelimit", "i", {|msg|
NornsSway(\sway).timelimit = msg[1];
});
this.addCommand("map_quadrant", "ii", {|msg|
NornsSway(\sway).map_quadrant(msg[1], msg[2]);
});
this.addCommand("analysis_on", "i", {|msg|
if(msg[1] == 1, {
NornsSway(\sway).analysis_on = true;
}, {
NornsSway(\sway).analysis_on = false;
});
});
this.addCommand("tracker_on", "i", {|msg|
if(msg[1] == 1, {
NornsSway(\sway).tracker_on = true;
}, {
NornsSway(\sway).tracker_on = false;
});
});
this.addCommand("silence", "i", {|msg|
NornsSway(\sway).silence;
});
this.addCommand("reverb", "i", {|msg|
NornsSway(\sway).reverb;
});
this.addCommand("ampmod", "i", {|msg|
NornsSway(\sway).ampmod;
});
this.addCommand("delay", "i", {|msg|
NornsSway(\sway).delay;
});
this.addCommand("filter", "i", {|msg|
NornsSway(\sway).filter;
});
this.addCommand("freeze", "i", {|msg|
NornsSway(\sway).freeze;
});
this.addCommand("pitchbend", "i", {|msg|
NornsSway(\sway).pitchbend;
});
this.addCommand("cascade", "i", {|msg|
NornsSway(\sway).cascade;
});
this.addPoll("x_coord", {NornsSway(\sway).xy[0]});
this.addPoll("y_coord", {NornsSway(\sway).xy[1]});
this.addPoll("avg_amp", {var amp1, amp30; #amp1, amp30 = NornsSway(\sway).amplitude.bus.getnSynchronous(2); amp30;});
this.addPoll("avg_onsets", {var onsets1, onsets30; #onsets1, onsets30 = NornsSway(\sway).onsets.bus.getnSynchronous(2);onsets30;});
this.addPoll("avg_clarity", {var clarity1, clarity30; #clarity1, clarity30 = NornsSway(\sway).clarity.bus.getnSynchronous(2);clarity30;});
this.addPoll("current_processing", {NornsSway(\sway).quadrant_names[(NornsSway(\sway).quadrant[0])]});
this.addPoll("current_quadrant", {NornsSway(\sway).quadrant[0]});
this.addPoll("quadrant0", {NornsSway(\sway).quadrant_names[0]});
this.addPoll("quadrant1", {NornsSway(\sway).quadrant_names[1]});
this.addPoll("quadrant2", {NornsSway(\sway).quadrant_names[2]});
this.addPoll("quadrant3", {NornsSway(\sway).quadrant_names[3]});
this.addPoll("quadrant4", {NornsSway(\sway).quadrant_names[4]});
}
free {
NornsSway(\sway).end;
}
}
NornsSway : Singleton {
//Carl Testa 2018
//Special Thanks to Brian Heim, Joshua Parmenter, Chris McDonald, Scott Carver
classvar <>short_win=1, <>long_win=30, <>refresh_rate=1.0, <>gravity=0.01, <>step=0.05;
var <>xy, <>quadrant, <>quadrant_names, <>quadrant_map, <>input, <>output, <>analysis_input, <>buffer, <>fftbuffer, <>delaybuffer, <>recorder, <>processing, <>fade=30, <>onsets, <>amplitude, <>clarity, <>flatness, <>amfreq, <>rvmix, <>rvsize, <>rvdamp, <>delaytime, <>delayfeedback, <>delaylatch, <>pbtime, <>pbbend, <>graintrig, <>grainfreq, <>grainpos, <>grainsize, <>granpos, <>granenvspeed, <>granrate, <>filtfreq, <>filtrq, <>freezefreq, <>freezefade, <>panpos, <>pansign, <>analysis_loop, <>above_amp_thresh=false, <>above_clarity_thresh=false, <>above_density_thresh=false, <>amp_thresh=4, <>clarity_thresh=0.6, <>density_thresh=1.5, <>tracker, <>count=0, <>analysis_on=true, <>tracker_on=true, <>audio_processing=true, <>verbose=false, <>polarity=false, <>quadrant_flag=false, <>timelimit=200, <>available_processing, <>all_processing, <>global_change=false;
init {
//Setup initial parameters
this.reset;
//audio input with chan argument
input = NodeProxy.audio(Server.default, 1).fadeTime_(fade)
.source = { |chan=0| In.ar(chan) };
//fft
fftbuffer = Buffer.alloc(Server.default, 1024);
//delaybuffer
delaybuffer = Buffer.alloc(Server.default, 10*48000, 1);
//audio recorder
buffer = Buffer.alloc(Server.default, long_win*48000, 1);
recorder = NodeProxy.audio(Server.default, 1)
.source = {
var off = Lag2.kr(A2K.kr(DetectSilence.ar(input.ar(1), 0.1), 0.3));
var on = 1-off;
var fade = MulAdd.new(on, 2, 1.neg);
var out = XFade2.ar(Silent.ar(), input.ar(1), fade);
RecordBuf.ar(out, buffer, loop: 1, run: on);
};
//this is the placeholder for audio procesing
processing = NodeProxy.audio(Server.default, 1).fadeTime_(fade)
.source = { Silent.ar(1) };
//analysis input so there is option to decouple processed audio from analysed audio
analysis_input = NodeProxy.audio(Server.default, 1)
.source = { |chan=0| In.ar(chan) };
//Build the analysis modules
this.build_analysis;
//Begin with an initial nonpolarity mapping of parameters
this.nonpolarity_map;
//audio output to listen and change channel
output = NodeProxy.audio(Server.default, 2)
.source = { Pan2.ar(processing.ar(1), panpos.kr(1)*pansign.kr(1)); };
//Longer term analysis controlling placement on processing grid
analysis_loop = TaskProxy.new({ loop {
//if analysis on flag is set to true then do analysis
if(analysis_on==true, {
//if verbose is on report values
if(verbose==true,{
flatness.bus.get({|val|
(this.name++" flatness: "++val[1]).postln;
});
onsets.bus.get({|val|
(this.name++" onsets: "++val[1]).postln;
});
clarity.bus.get({|val|
(this.name++" clarity: "++val[1]).postln;
});
});
//if signal is above amplitude threshold do analysis
amplitude.bus.get({|val|
if(verbose==true,{(this.name++" amp: "++val[1]).postln});
if( val[1] > amp_thresh, {
above_amp_thresh=true;
if(verbose==true,{(this.name++" amp threshold reached").postln});
clarity.bus.get({|val|
if( val[1] > clarity_thresh,
{above_clarity_thresh=true;
xy[0]=(xy[0]+step).clip(0,1)},
{above_clarity_thresh=false;
xy[0]=(xy[0]-step).clip(0,1)});
});
onsets.bus.get({|val|
if( val[1] > density_thresh,
{above_density_thresh=true;
xy[1]=(xy[1]+step).clip(0,1)},
{above_density_thresh=false;
xy[1]=(xy[1]-step).clip(0,1)});
});
}, {
//else if below threshold drift to center
above_amp_thresh=false;
if(verbose==true,{(this.name++" drift to center").postln});
if(xy[0] > 0.5, {
(xy[0]=xy[0]-gravity).clip(0,0.5)},{
(xy[0]=xy[0]+gravity).clip(0,0.5)});
if(xy[1] > 0.5, {
(xy[1]=xy[1]-gravity).clip(0,0.5)},{
(xy[1]=xy[1]+gravity).clip(0,0.5)});
});
});
this.assign_quadrant(xy[0], xy[1]);
//Checks to see if quadrant has changed, if so, it changes type of processing
if (quadrant[0] == quadrant[1], {
},{this.change_processing});
if (quadrant_flag==true, {
this.change_processing;
quadrant_flag=false;
},{});
//Tracker processing grid changer is implemented here
if (tracker_on==true, {
if( tracker.any({|i,n|i>timelimit}), {//if any item in tracker is above timelimit
//then choose new processing for that quadrant
this.choose_new_processing(tracker.detectIndex({|i|i>timelimit}));
(this.name++": processing grid changing").postln;
if(verbose==false,{global_change=true;(this.name++": global change enabled").postln});
quadrant_flag=true;
tracker[tracker.detectIndex({|i|i>timelimit})]=0;
//Change polarity for the hell of it
this.change_polarity;
},{});
});
});
refresh_rate.wait;
count=count+1;
}}).play;
}
build_analysis {
onsets = NodeProxy.control(Server.default, 2)
.source = {
//Density Tracker
var buf = LocalBuf.new(512,1);
var onsets = Onsets.kr(FFT(buf, analysis_input.ar(1)));
var shortStats = OnsetStatistics.kr(onsets, short_win);
var longStats = OnsetStatistics.kr(onsets, long_win);
var shortValue = (shortStats[0]/short_win);
var longValue = (longStats[0]/long_win);
[shortValue, longValue];
};
amplitude = NodeProxy.control(Server.default, 2)
.source = {
//Amplitude Tracker
var chain = FFT(LocalBuf(1024), analysis_input.ar(1));
var loudness = Loudness.kr(chain);
var shortAverage = AverageOutput.kr(loudness, Impulse.kr(short_win.reciprocal));
var longAverage = AverageOutput.kr(loudness, Impulse.kr(long_win.reciprocal));
[shortAverage, longAverage];
};
clarity = NodeProxy.control(Server.default, 2)
.source = {
var freq, hasFreq, shortAverage, longAverage;
//Pitch hasfreq Tracker
# freq, hasFreq = Pitch.kr(analysis_input.ar(1));
shortAverage = AverageOutput.kr(hasFreq,Impulse.kr(short_win.reciprocal));
longAverage = AverageOutput.kr(hasFreq,Impulse.kr(long_win.reciprocal));
[shortAverage, longAverage];
};
flatness = NodeProxy.control(Server.default, 2)
.source = {
//Spectral Flatness Tracker
var chain = FFT(LocalBuf(1024), analysis_input.ar(1));
var flat = SpecFlatness.kr(chain);
var shortAverage = AverageOutput.kr(flat, Impulse.kr(short_win.reciprocal));
var longAverage = AverageOutput.kr(flat, Impulse.kr(long_win.reciprocal));
[shortAverage, longAverage];
};
//Parameter Controls
//amplitude modulation
amfreq = NodeProxy.control(Server.default, 1).fadeTime_(fade);
//reverb
rvmix = NodeProxy.control(Server.default, 1).fadeTime_(fade);
rvsize = NodeProxy.control(Server.default, 1).fadeTime_(fade);
rvdamp = NodeProxy.control(Server.default, 1).fadeTime_(fade);
//delay
delaytime = NodeProxy.control(Server.default, 1).fadeTime_(fade);
delayfeedback = NodeProxy.control(Server.default, 1).fadeTime_(fade);
delaylatch = TaskProxy.new().play;
//pitch bend
pbbend = NodeProxy.control(Server.default, 1).fadeTime_(fade);
pbtime = NodeProxy.control(Server.default, 1).fadeTime_(fade);
//filter
filtfreq = NodeProxy.control(Server.default, 1).fadeTime_(fade);
filtrq = NodeProxy.control(Server.default, 1).fadeTime_(fade);
//freeze
freezefreq = NodeProxy.control(Server.default, 1).fadeTime_(fade);
freezefade = NodeProxy.control(Server.default, 1).fadeTime_(fade);
//output pan
panpos = NodeProxy.control(Server.default, 1).fadeTime_(fade);
pansign = NodeProxy.control(Server.default, 1).fadeTime_(fade);
}
nonpolarity_map {
//TO DO: Modulate the mapping somehow with additional analysis
//Mapping analysis data to parameter controls
//amplitude modulation
amfreq.source = { amplitude.kr(1,0).linlin(0,30,1,14)};
//reverb
rvmix.source = { onsets.kr(1,0).linlin(0,6,0.3,1) };
rvsize.source = { amplitude.kr(1,0).linlin(0,30,0.3,1) };
rvdamp.source = { clarity.kr(1,0).linlin(0,1,1,0) };
//delay
delaytime.source = { onsets.kr(1,0).linexp(0,7,0.5,9) };
delayfeedback.source = { onsets.kr(1,0).linlin(0,10,0.5,0.05)};
delaylatch.source = { loop {
5.0.wait;
if(0.5.coin, { processing.set(\trigger, 1, \toggle, 1) });
} };
//pitch bend
pbtime.source = { onsets.kr(1,0).linlin(0,6,0.1,1) };
pbbend.source = { amplitude.kr(1,0).linlin(0,10,0.75,1.5) };
//filter
filtfreq.source = { onsets.kr(1,0).linlin(0,6,500,5000) };
filtrq.source = { amplitude.kr(1,0).linlin(0,10,0.3,0.8) };
//freeze
freezefreq.source = { onsets.kr(1,0).linlin(0,6,0.5,6) };
freezefade.source = { onsets.kr(1,0).linlin(0,6,3,0.3) };
//pan position
panpos.source = { amplitude.kr(1,0).linlin(0,10,0,1) };
pansign.source = { onsets.kr(1,0).linlin(0,6,(-1),1) };
}
polarity_map {
//Mapping analysis data to parameter controls
//amplitude modulation
amfreq.source = { amplitude.kr(1,0).linlin(0,30,14,1)};
//reverb
rvmix.source = { onsets.kr(1,0).linlin(0,6,0.8,0.5) };
rvsize.source = { amplitude.kr(1,0).linlin(0,30,0.9,0.7) };
rvdamp.source = { clarity.kr(1,0).linlin(0,1,0.2,1) };
//delay
delaytime.source = { onsets.kr(1,0).linexp(0,7,9,0.5) };
delayfeedback.source = { onsets.kr(1,0).linlin(0,10,0.05,0.5)};
delaylatch.source = { loop {
0.5.wait;
if(0.5.coin, { processing.set(\trigger, 1, \toggle, 1) });
} };
//pitch bend
pbtime.source = { onsets.kr(1,0).linlin(0,6,1,1.1) };
pbbend.source = { amplitude.kr(1,0).linlin(0,10,1.5,0.75) };
//filter
filtfreq.source = { onsets.kr(1,0).linlin(0,6,5000,500) };
filtrq.source = { amplitude.kr(1,0).linlin(0,10,0.9,0.3) };
//freeze
freezefreq.source = { onsets.kr(1,0).linlin(0,6,6,0.5) };
freezefade.source = { onsets.kr(1,0).linlin(0,6,0.3,3) };
//pan position
panpos.source = { amplitude.kr(1,0).linlin(0,10,1,0) };
pansign.source = { onsets.kr(1,0).linlin(0,6,1,(-1)) };
}
//TO DO: Here is where the different types of processing are defined. I'm wondering if I should separate these out into sub-classes. Would that make it easier to extend it and add processing?
//Change processing to amplitude modulation
ampmod {
//control mapping:
//amplitude -> freq
processing.source = {
var off = Lag2.kr(A2K.kr(DetectSilence.ar(input.ar(1),0.05),0.3));
var on = 1-off;
var fade = MulAdd.new(on, 2, 1.neg);
var out = XFade2.ar(Silent.ar(), input.ar(1), fade);
var sine = LFTri.ar(amfreq.kr(1), 0).unipolar;
var am = out*sine;
am;
};
(this.name++": Amplitude Modulation").postln;
}
//Change processing to reverb
reverb {
//control mapping:
//onsets -> mix
//amplitude -> roomsize
processing.source = { FreeVerb.ar(in: input.ar(1), mix: rvmix.kr(1), room: rvsize.kr(1), damp: rvdamp.kr(1)) };
(this.name++": Reverb").postln;
}
//Change processing to freeze
freeze {
//control mapping:
//onsets -> freezefreq
//amplitude ->
processing.source = {
//First use gate on input
var off = Lag2.kr(A2K.kr(DetectSilence.ar(input.ar(1),0.05),0.3));
var on = 1-off;
var fade = MulAdd.new(on, 2, 1.neg);
var out = XFade2.ar(Silent.ar(), input.ar(1), fade);
//then begin freeze
var freeze;
var amplitude = Amplitude.kr(out);
var trig = amplitude > 0.2;
var gate = Gate.kr(LFClipNoise.kr(freezefreq.kr(1)),trig);
var chain = FFT(fftbuffer, out);
chain = PV_Freeze(chain, gate);
freeze = XFade2.ar(Silent.ar(), IFFT(chain), Lag2.kr(gate,freezefade.kr(1)));
freeze = FreeVerb.ar(freeze, rvmix.kr(1), rvsize.kr(1));
freeze = HPF.ar(freeze, 20);
freeze;
};
(this.name++": Freeze").postln;
}
//Change processing to delay
delay {
//control mapping:
//onsets -> delaytime
//onsets -> feedback
processing.source = {
var time = Latch.kr(delaytime.kr(1), \trigger.tr);
var feedback = delayfeedback.kr(1);
var local = LocalIn.ar(1) + input.ar(1);
var select = ToggleFF.kr(\toggle.tr(1.neg));
var delay1 = BufDelayL.ar(delaybuffer, local, Latch.kr(time, 1- select));
var delay2 = BufDelayL.ar(delaybuffer, local, Latch.kr(time, select));
var fade = MulAdd.new(Lag2.kr(select, 4), 2, 1.neg);
var delay = XFade2.ar(delay1, delay2, fade);
LocalOut.ar(delay * feedback);
delay;
};
(this.name++": Delay").postln;
}
//Change processing to pitch bend
pitchbend {
//control mapping:
//onsets -> time
//amplitude -> bend
processing.source = {
PitchShift.ar(input.ar(1), 1, pbbend.kr(1), 0.2, pbtime.kr(1))
};
(this.name++": Pitch Bend").postln;
}
filter {
//control mapping:
//onsets -> filtfreq
//amplitude -> filtrq
processing.source = {
RLPF.ar(input.ar(1), filtfreq.kr(1), filtrq.kr(1)).tanh;
};
(this.name++": Filter").postln;
}
//change processing to cascading playback
cascade {
processing.source = {
//TO DO: analysis control not implemented
//pitch ->
//amp -> number of layers??
//onsets -> how far back in file does it read? Output current frame maybe?
var sound = PlayBuf.ar(1, buffer.bufnum, 1, 0, {buffer.numFrames.rand}!16, 1);
var env = SinOsc.kr(1/16, (0..15).linlin(0,15,8pi.neg,8pi), 0.375);
var mix = Limiter.ar(Mix.new(sound*env), 1);
mix;
};
(this.name++": Cascade").postln;
}
//Silence processing
silence {
processing.source = { Silent.ar(1) };
(this.name++": Silence").postln;
}
//execute change in processing type
change_processing {
(quadrant_map[quadrant[0]]).value;
}
//assign which quadrant source is in based on x/y coordinates
assign_quadrant { |x, y|
quadrant = quadrant.shift(1);
case
{(x<0.45) && (y<0.45)} {quadrant.put(0,3);tracker[3]=tracker[3]+1}//quadrant 3
{(x>0.55) && (y<0.45)} {quadrant.put(0,4);tracker[4]=tracker[4]+1}//quadrant 4
{(x<0.45) && (y>0.55)} {quadrant.put(0,2);tracker[2]=tracker[2]+1}//quadrant 2
{(x>0.55) && (y>0.55)} {quadrant.put(0,1);tracker[1]=tracker[1]+1}//quadrant 1
{(x<0.55) && (x>0.45) && (y<0.55) && (y>0.45)} {quadrant.put(0,0);tracker[0]=tracker[0]+1};//quadrant 0
//quadrant.postln;
}
//map different types of processing to the quadrants using the quadrant names
map_quadrants {|names|
names.do({|item,i|
quadrant_map.put(i,all_processing.at(item));
available_processing.removeAt(item);
quadrant_names.put(i,item);
});
}
//map single quadrant
map_quadrant {|num, name|
quadrant_map.put(num,all_processing.at(name));
quadrant_names.put(num,name);
}
//change polarity
change_polarity {
if(polarity==false, {
this.polarity_map;polarity=true;
(this.name++": polarity mapping set").postln;
},{
this.nonpolarity_map;polarity=false;
(this.name++": non-polarity mapping set").postln;
});
}
//fade change
fade_time { |time|
//sound
processing.fadeTime = time;
//am
amfreq.fadeTime = time;
//reverb
rvmix.fadeTime = time;
rvsize.fadeTime = time;
rvdamp.fadeTime = time;
//delay
delaytime.fadeTime = time;
delayfeedback.fadeTime = time;
//pitch bend
pbbend.fadeTime = time;
pbtime.fadeTime = time;
//filter
filtfreq.fadeTime = time;
filtrq.fadeTime = time;
//freeze
freezefreq.fadeTime = time;
freezefade.fadeTime = time;
//output pan
panpos.fadeTime = time;
pansign.fadeTime = time;
}
choose_new_processing {|qrant|
//choose_new_processing function receives a quadrant as an argument and assigns an available processing to that quadrant
var old, new;
//don't remap the center, keep it silent
if(qrant!=0, {
//get current processing type which is now "old"
old = quadrant_names[qrant];
//change processing to one that is available and capture its symbol
new = available_processing.keys.choose;
quadrant_map.put(qrant, available_processing.at(new));
//update quadrant_names
quadrant_names.put(qrant, new);
//remove new processing from available
available_processing.removeAt(new);
//place old processing in available
available_processing.put(old, all_processing.at(old));
},{});
}
reset {
//reset to initial parameters
//intial placement on processing grid
xy = [0.5,0.5];//random start
tracker = [0,0,0,0,0];//number of times in each quadrant area
//TO DO: the number of data structures I have to keep track of the quadrants and the names of the processing and all the available processing etc feels very clunky. There must be a better way to manage all this information.
quadrant = Array.newClear(2);
quadrant_map = Array.newClear(5);
//change the initial mapping setup here:
quadrant_names = Array.newClear(5);
quadrant_names.put(0,1);
quadrant_names.put(1,2);
quadrant_names.put(2,8);
quadrant_names.put(3,3);
quadrant_names.put(4,4);
all_processing = Dictionary.new;
all_processing.put(1, {this.silence});
all_processing.put(2, {this.delay});
all_processing.put(3, {this.reverb});
all_processing.put(4, {this.ampmod});
all_processing.put(5, {this.pitchbend});
all_processing.put(6, {this.cascade});
all_processing.put(7, {this.filter});
all_processing.put(8, {this.freeze});
//make all processing currently available
available_processing = Dictionary.new;
available_processing.put(1, {this.silence});
available_processing.put(2, {this.delay});
available_processing.put(3, {this.reverb});
available_processing.put(4, {this.ampmod});
available_processing.put(5, {this.pitchbend});
available_processing.put(6, {this.cascade});
available_processing.put(7, {this.filter});
available_processing.put(8, {this.freeze});
this.assign_quadrant(xy[0], xy[1]);
this.map_quadrants(quadrant_names);
polarity=false;
global_change=false;
//quadrant_flag=true;
}
end {
output.free(1);
input.free(1);
analysis_input.free(1);
buffer.free;
fftbuffer.free;
delaybuffer.free;
recorder.free(1);
processing.free(1);
onsets.free(1);
amplitude.free(1);
clarity.free(1);
flatness.free(1);
amfreq.free(1);
rvmix.free(1);
rvsize.free(1);
rvdamp.free(1);
delaytime.free(1);
delayfeedback.free(1);
delaylatch.stop;
pbtime.free(1);
pbbend.free(1);
graintrig.free(1);
grainfreq.free(1);
grainpos.free(1);
grainsize.free(1);
granrate.free(1);
granpos.free(1);
granenvspeed.free(1);
filtfreq.free(1);
filtrq.free(1);
freezefreq.free(1);
freezefade.free(1);
panpos.free(1);
pansign.free(1);
analysis_loop.stop;
this.clear;
//Server.freeAll;
}
}
//Singleton from Singleton SuperCollider Quark by Scott Carver
Singleton {
classvar all, <>know=false, creatingNew=false;
var <>name;
*initClass {
all = IdentityDictionary();
}
*default {
^\default
}
*all {
^all[this] ?? IdentityDictionary()
}
*new {
arg name ...settings;
var sing, classAll, created=false;
name = name ?? this.default;
classAll = all.atFail(this, {
all[this] = IdentityDictionary();
all[this];
});
sing = classAll.atFail(name, {
var newSingleton = this.createNew();
created = true;
newSingleton.name = name;
classAll[name] = newSingleton;
newSingleton.init(name);
newSingleton;
});
if ((settings.notNil && settings.notEmpty) || created) {
sing.set(*settings)
};
if (created) { { this.changed(\added, sing) }.defer(0) };
^sing;
}
*createNew {
arg ...args;
^super.new(*args);
}
*doesNotUnderstand { arg selector ... args;
var item;
if (know && creatingNew.not) {
creatingNew = true; // avoid reentrancy
protect {
if (selector.isSetter) {
selector = selector.asString;
selector = selector[0..(selector.size - 2)].asSymbol;
item = this.new(selector, *args);
} {
item = this.new(selector);
}
} {
creatingNew = false;
};
^item;
} {
^this.superPerformList(\doesNotUnderstand, selector, args);
}
}
init {}
set {
// Override this to receive 'settings' parameter from Singleton.new(name, settings)
}
*clear {
|sing|
var dict = all[this];
if (dict.notNil) {
var key = dict.findKeyForValue(sing);
if (key.notNil) {
dict[key] = nil;
this.changed(\removed, sing);
}
}
}
clear {
this.class.clear(this);
}
}
-- Sway - live processing
-- environment
--
-- Concept and Code by
-- Carl Testa (2018)
--
-- analysis-driven live
-- processing, play into
-- input 1 (L), processing
-- will gradually change
--
-- key 2 toggles processing map
-- and analysis values view
--
-- greater control available
-- within parameter menu
--
-- more info @
-- http://sway.carltesta.net
local amp = 0
local density = 0
local clarity = 0
local x_coord = 0.5
local y_coord = 0.5
local processingtext =""
local screen_number = 0
local quadrants = {1,2,8,3,4}
local q0,q1,q2,q3,q4
engine.name = 'Sway'
function init()
print("Sway")
a = poll.set("avg_amp")
a.callback = function(val) amp = val end
a:start()
o = poll.set("avg_onsets")
o.callback = function(val) density = string.format("%.2f",val*10) end
o:start()
c = poll.set("avg_clarity")
c.callback = function(val) clarity = string.format("%.2f",val*100) end
c:start()
x = poll.set("x_coord")
x.callback = function(val) x_coord = string.format("%.2f",val) end
x:start()
y = poll.set("y_coord")
y.callback = function(val) y_coord = string.format("%.2f",val) end
y:start()
z = poll.set("current_processing")
z.callback = function(val) processingtext = decode(val) end
z:start()
q0 = poll.set("quadrant0")
q0.callback = function(val) quadrants[1] = val end
q0:start()
q1 = poll.set("quadrant1")
q1.callback = function(val) quadrants[2] = val end
q1:start()
q2 = poll.set("quadrant2")
q2.callback = function(val) quadrants[3] = val end
q2:start()
q3 = poll.set("quadrant3")
q3.callback = function(val) quadrants[4] = val end
q3:start()
q4 = poll.set("quadrant4")
q4.callback = function(val) quadrants[5] = val end
q4:start()
t = metro.alloc()
t.count = 0
t.time = 1
t.callback = function(stage)
redraw()
end
params:add{type="number", id="amp_threshold", min=0, max=40,default=4,
action=function(x) engine.amp_thresh(x) end}
params:add{type="number", id="density_threshold", min=0, max=80, default=40,
action=function(x) engine.density_thresh(x/10) end}
params:add{type="number", id="clarity_threshold", min=0, max=100, default=70,
action=function(x) engine.clarity_thresh(x/100) end}
params:add_separator()
params:add{type="option", id="analysis", name="analysis", options={"on", "off"},
action=function(val) engine.analysis_on(val) if val==1 then params:set("processing_type", 1) end end}
params:add{type="number", id="fadetime", min=2, max=120, default=30,
action=function(x) engine.fadetime(x) end}
params:add{type="option", id="processing_type", name="processing_type", options={"---","reverb", "delay", "amp mod", "freeze", "pitchbend", "filter", "cascade"},
action=function(val) if params:get("analysis")==2 then
if val==1 then engine.silence(val) end
if val==2 then engine.reverb(val) end
if val==3 then engine.delay(val) end
if val==4 then engine.ampmod(val) end
if val==5 then engine.freeze(val) end
if val==6 then engine.pitchbend(val) end
if val==7 then engine.filter(val) end
if val==8 then engine.cascade(val) end
end
end}
params:add{type="option", id="polarity", name="polarity", default=2, options={"on", "off"},
action=function(val) engine.polarity(val) end}
params:add_separator()
params:add{type="option", id="evolver", name="evolver", options={"on", "off"},
action=function(val) engine.tracker_on(val) end}
params:add{type="number", id="time_limit", min=30, max=500, default=100,
action=function(x) engine.timelimit(x) end}
params:add_separator()
params:add{type="option", id="center", name="center", default=1, options={"silence","reverb", "delay", "amp mod", "freeze", "pitchbend", "filter", "cascade"},
action=function(val)
if val==1 then engine.map_quadrant(0,1) end
if val==2 then engine.map_quadrant(0,3) end
if val==3 then engine.map_quadrant(0,2) end
if val==4 then engine.map_quadrant(0,4) end
if val==5 then engine.map_quadrant(0,8) end
if val==6 then engine.map_quadrant(0,5) end
if val==7 then engine.map_quadrant(0,7) end
if val==8 then engine.map_quadrant(0,6) end
end}
params:add{type="option", id="quadrant_1", name="quadrant_1", default=3, options={"silence","reverb", "delay", "amp mod", "freeze", "pitchbend", "filter", "cascade"},
action=function(val)
if val==1 then engine.map_quadrant(1,1) end
if val==2 then engine.map_quadrant(1,3) end
if val==3 then engine.map_quadrant(1,2) end
if val==4 then engine.map_quadrant(1,4) end
if val==5 then engine.map_quadrant(1,8) end
if val==6 then engine.map_quadrant(1,5) end
if val==7 then engine.map_quadrant(1,7) end
if val==8 then engine.map_quadrant(1,6) end
end}
params:add{type="option", id="quadrant_2", name="quadrant_2", default=5, options={"silence","reverb", "delay", "amp mod", "freeze", "pitchbend", "filter", "cascade"},
action=function(val)
if val==1 then engine.map_quadrant(2,1) end
if val==2 then engine.map_quadrant(2,3) end
if val==3 then engine.map_quadrant(2,2) end
if val==4 then engine.map_quadrant(2,4) end
if val==5 then engine.map_quadrant(2,8) end
if val==6 then engine.map_quadrant(2,5) end
if val==7 then engine.map_quadrant(2,7) end
if val==8 then engine.map_quadrant(2,6) end
end}
params:add{type="option", id="quadrant_3", name="quadrant_3", default=2, options={"silence","reverb", "delay", "amp mod", "freeze", "pitchbend", "filter", "cascade"},
action=function(val)
if val==1 then engine.map_quadrant(3,1) end
if val==2 then engine.map_quadrant(3,3) end
if val==3 then engine.map_quadrant(3,2) end
if val==4 then engine.map_quadrant(3,4) end
if val==5 then engine.map_quadrant(3,8) end
if val==6 then engine.map_quadrant(3,5) end
if val==7 then engine.map_quadrant(3,7) end
if val==8 then engine.map_quadrant(3,6) end
end}
params:add{type="option", id="quadrant_4", name="quadrant_4", default=4, options={"silence","reverb", "delay", "amp mod", "freeze", "pitchbend", "filter", "cascade"},
action=function(val)
if val==1 then engine.map_quadrant(4,1) end
if val==2 then engine.map_quadrant(4,3) end
if val==3 then engine.map_quadrant(4,2) end
if val==4 then engine.map_quadrant(4,4) end
if val==5 then engine.map_quadrant(4,8) end
if val==6 then engine.map_quadrant(4,5) end
if val==7 then engine.map_quadrant(4,7) end
if val==8 then engine.map_quadrant(4,6) end
end}
t:start()
end
function draw_screen()
if screen_number==1 then
screen.move(0, 24)
screen.text("amp: ".. amp)
screen.move(0, 32)
screen.text("density: ".. density)
screen.move(0, 40)
screen.text("clarity: ".. clarity)
screen.move(0, 48)
screen.text("X: ".. x_coord)
screen.move(32, 48)
screen.text("Y: ".. y_coord)
screen.move(0, 58)
screen.text("Processing: ".. processingtext)
end
if screen_number==0 then
screen.move(0,18)
screen.text("C: "..decode(quadrants[1]))
screen.move(0,26)
screen.text("1: "..decode(quadrants[2]))
screen.move(0,34)
screen.text("2: "..decode(quadrants[3]))
screen.move(0,42)
screen.text("3: "..decode(quadrants[4]))
screen.move(0,50)
screen.text("4: "..decode(quadrants[5]))
screen.move(0,60)
screen.text("P: "..processingtext)
screen.rect(63,0,32,32)
screen.level(8)
screen.stroke()
screen.rect(95,0,32,32)
screen.level(8)
screen.stroke()
screen.rect(63,32,32,32)
screen.level(8)
screen.stroke()
screen.rect(95,32,32,32)
screen.level(8)
screen.stroke()
screen.rect(convertX(x_coord),convertY(y_coord),2,2)
screen.level(16)
screen.stroke()
end
end
function convertX (val)
return (val-0)/(1-0) * (124-65) + 65
end
function convertY (val)
return (val-0)/(1-0) * (2-61) + 61
end
function redraw()
screen.clear()
screen.aa(1)
screen.move(0, 8)
screen.font_size(8)
screen.level(15)
screen.text("SWAY")
draw_screen()
screen.update()
end
function decode(num)
if num==1 then
return "Silence"
end
if num ==2 then
return "Delay"
end
if num ==3 then
return "Reverb"
end
if num ==4 then
return "Amp Mod"
end
if num ==5 then
return "Pitchbend"
end
if num ==6 then
return "Cascade"
end
if num ==7 then
return "Filter"
end
if num ==8 then
return "Freeze"
end
end
function enc(n, delta)
if n == 1 then
mix:delta("output", delta)
end
end
function key(n,z)
if n == 2 and z== 1 then
if screen_number==0 then
screen_number = 1
else
screen_number=0
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment