( // SYNTHDEF SynthDef(\flicker, { |automate = 1, bufs = #[0, 1, 2, 3, 4, 5, 6, 7, 8], fadeInTrigBusNum = 0, fadeOutTrigBusNum = 2, wakeTrigBusNum = 1| //~~~~~ Vars ~~~~~ // start times / fade ins var fadeInTimes = [5, 100]; // end times / fade outs (must be same length as fadeInTimes with fadeInTimes[i] < fadeOutTimes[i]) var fadeOutTimes = [80, 150]; // These are the times to wake up with a flourish of activity and the reading of an entry in the journal var wakeTimes = [15, 105]; // These are the frequency ratios of the ensemble parts var freqRatios = [2, 3/2, 5/4, 7/4, 11/8, 13/8, 17/16, 19/16, 23/16]; // Triggers var fadeInTrigs, wakeUpTrigs, fadeOutTrigs; // Rich-get-richer vars var pulse, state, runningPulseCount, norms, wealthGainLag, probs, selects; // Control signal vars var energy, switches, switchesSmoothed; // Sonification vars var lamentations, hums, ensemble, drone, fadeIn, fadeInOutEnv, wakeEnv, fadeOut; // Timed trigger var timedTrigger = {|times| Changed.kr(EnvGen.kr(Env.step({|i| i % 2} ! (times.size + 1), times.differentiate ++ [0.01]), Impulse.kr(0)))}; //~~~~~ Triggers ~~~~~ // Triggers for fadeInTimes fadeInTrigs = Select.kr(automate, [InTrig.kr(fadeInTrigBusNum), timedTrigger.value(fadeInTimes)]); // Triggers for fadeOutTimes fadeOutTrigs = Select.kr(automate, [InTrig.kr(fadeOutTrigBusNum), timedTrigger.value(fadeOutTimes)]); // Triggers for wakeTimes wakeUpTrigs = Select.kr(automate, [InTrig.kr(wakeTrigBusNum), timedTrigger.value(wakeTimes)]); //~~~~~ Rich-get-richer Algorithm ~~~~~ // Update resolution pulse = Impulse.kr(30); // State of consciousness: asleep or awake; a wake up lasts 60 seconds plus a bit of a tail state = EnvGen.kr(Env.sine(60), wakeUpTrigs); // Binary representation of which element was selected selects = LocalIn.kr(9); // Running sum of the times each of the 9 elements has been selected over 120 pulses runningPulseCount = RunningSum.kr(pulse * selects * (state > 0), (ControlRate.ir / 30) * 120); // Normalize the counts over 121 pulses (adding a 1 in the wake states so probs is always > 1) norms = ((0.925 * (state > 0) + 0.075 + runningPulseCount) / 129); // Goes from 0 to 1 over several pulses after an element is chosen ending with 1 to 4 lights on favoring 2 or 3 wealthGainLag = pow((PulseCount.kr(pulse, selects) / TWChoose.kr(wakeUpTrigs, [1, 2, 3, 4], [1, 2, 2, 1], 1)).clip, 4); // Probabilities such that the rich get richer except directly after an element is selected probs = {|i| pow(norms[i] * wealthGainLag[i], state * 4)} ! 9; // Select an element selects = TWindex.kr(pulse, probs, 1); // Feedback binary representation LocalOut.kr({|i| (i <= selects) * (selects <= i) } ! 9); //~~~~~ Control signals ~~~~~ // The norms are basically the amount of energy to each element energy = norms; // In a toy model manner, this mimics voltage to the system switches = {|i| TRand.kr(0, 1, pulse) < energy[i]} ! 9; // Smooths the signal such that the lag time is greater as the element gets richer switchesSmoothed = {|i| Lag2.kr(switches[i], 0.25 + (0.75 * energy[i]))} ! 9; //~~~~~ Sonification ~~~~~ // Playback of the Lassus Lamentations with a LPF as to not overwhelm to overall sound // Each of the Lamentations as a 2 in 3 chance of sounding lamentations = {|i| LPF.ar(PlayBuf.ar(2, bufs[i], 1, wakeUpTrigs, TIRand.kr(0, BufFrames.ir(bufs[i]), wakeUpTrigs), 1), 2880) * switchesSmoothed[i] * TWChoose.kr(wakeUpTrigs, [0, 1], [1, 2], 1)} ! 9; // Toy model of an electric hum hums = {|i| var buf = Array.fill(256, {1.0.rand2}).as(LocalBuf.clear); LPF.ar(Osc.ar(buf, 60, 0), 960) * switchesSmoothed[i]} ! 9; // Sustained tones based on energy to that element ensemble = {|i| SinOsc.ar(240 * freqRatios[i], 0) * energy[i]} ! 9; // Drone in sleep state that gets attenuated in wake state drone = {|i| var harm = pow(2, 2 - (i / 3).trunc), amp = (1 - (0.75 * energy.sum)) * (1 / pow(harm, 2)); SinOsc.ar(60 * harm + TRand.kr(-3, 3, switches[i]), 0, amp)} ! 9; //~~~~~ Mix ~~~~~ // Fade ins (10 secs) / outs (30 secs) fadeInOutEnv = EnvGen.kr(Env.asr(10, 1, 30, \sine), Latch.kr(Select.kr(fadeOutTrigs, [1, 0]), fadeInTrigs + fadeOutTrigs)); // Fade wake sounds in and out based on total energy in the system wakeEnv = pow(0.8 * energy.sum + 0.2, 4); // Final mix currently set to output stereo where the multiplier is the final output level // Send to separate channel for more control of the indivuals sounds (e.g. with a mixer) // The lamentations of a 50 / 50 chance of sounding at all Out.ar([0,1], Mix.new(lamentations) * fadeInOutEnv * wakeEnv * 0.75 * TIRand.kr(0, 1, wakeUpTrigs)); Out.ar([0,1], Mix.new(hums) * fadeInOutEnv * wakeEnv * 0.075); Out.ar([0,1], Mix.new(ensemble) * fadeInOutEnv * wakeEnv * 0.125); Out.ar([0,1], Mix.new(drone) * fadeInOutEnv * 0.25); //~~~~~ Visualization Control ~~~~~ // Send signals to visualizer (these could be sent and scaled appropriated to control real lights) {|i| SendTrig.kr(pulse, i, switchesSmoothed[i] * fadeInOutEnv)} ! 9; {|i| SendTrig.kr(pulse, i + 9, energy[i] * fadeInOutEnv * wakeEnv)} ! 9; //~~~~~ Monitor Time ~~~~~ Poll.kr(PulseDivider.kr(pulse, 30), PulseCount.kr(PulseDivider.kr(pulse, 30)), \time); }).send(s); )