Last active
November 16, 2018 20:31
-
-
Save carltesta/7529749349d934e410fe30d4fac29561 to your computer and use it in GitHub Desktop.
Sway for monome norns
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- 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