commit 46fda32eb755abd459bef1d079e8c10615ebf328 Author: Michael Winter Date: Fri Sep 1 15:11:51 2017 -0500 first commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..4a245ab --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# generator_hierarchical_dust_and_necklaces + +note that the release with all the audio and video releases is available +at: +http://www.unboundedpress.org/code_releases/generator_hierarchical_dust_and_necklaces_source.zip diff --git a/audio/.gitignore b/audio/.gitignore new file mode 100644 index 0000000..d8dd753 --- /dev/null +++ b/audio/.gitignore @@ -0,0 +1 @@ +*.wav diff --git a/generator_hierarchical_dust_and_necklaces_score.pdf b/generator_hierarchical_dust_and_necklaces_score.pdf new file mode 100644 index 0000000..d5d48f3 Binary files /dev/null and b/generator_hierarchical_dust_and_necklaces_score.pdf differ diff --git a/supercollider/generator_hierarchical_dust_and_necklaces_main.scd b/supercollider/generator_hierarchical_dust_and_necklaces_main.scd new file mode 100644 index 0000000..2a83862 --- /dev/null +++ b/supercollider/generator_hierarchical_dust_and_necklaces_main.scd @@ -0,0 +1,12 @@ +( +// MAIN LAUNCH +~dir = thisProcess.nowExecutingPath.dirname; +"generator_hierarchical_dust_and_necklaces_synthdef.scd".loadRelative(true, { + "generator_hierarchical_dust_and_necklaces_visuals.scd".loadRelative(true, { + Buffer.read(s, thisProcess.nowExecutingPath.dirname +/+ "../audio/generator.wav", action: { + |buf| + ~buf = buf; + {~generateVisuals.value(buf)}.defer; + }); +})}); +) diff --git a/supercollider/generator_hierarchical_dust_and_necklaces_synthdef.scd b/supercollider/generator_hierarchical_dust_and_necklaces_synthdef.scd new file mode 100644 index 0000000..a6a9757 --- /dev/null +++ b/supercollider/generator_hierarchical_dust_and_necklaces_synthdef.scd @@ -0,0 +1,56 @@ +( +SynthDef(\hierarchical_dust, { + arg stable = 10, unstable = 10, buf = 0, loop = 0, cycle_len = 10; + var local_in, hold, change, state, latch, hierarchical_dust, generator, env_master, env_spectrum, env_vol, chain, spectrum_mult; + + // Feedback in state + local_in = LocalIn.kr(2, 0); + // Make each state last at least 15 seconds, however these variables could be different / manipulated in the array [15, 15, 15] + // That is, they could be turned into user variables + // Note the second two account for the fade in and fade out of the necklace, so it will be at least 30 seconds long + hold = PulseCount.kr(Impulse.kr(1), Changed.kr(local_in[0])) > Select.kr(local_in[0], [15, 15, 15]); + // Change state trigger + change = TWChoose.kr(Impulse.kr(1), [0, 1], [Select.kr(local_in[0] > 0, [unstable, stable]), 1].normalizeSum) * hold; + // Change state + state = Stepper.kr(change + TDelay.kr(local_in[0] > 1, 15), 0, 0, 2); + + // Monitor + Poll.kr(Impulse.kr(1), hold, \hold); + Poll.kr(Impulse.kr(1), change, \change); + Poll.kr(Impulse.kr(1), state, \state); + + // Trigger for filter changes + hierarchical_dust = ( + Impulse.kr(8) * + (TRand.kr(0, 1, Impulse.kr(8)) <= 0.5) * + (TRand.kr(0, 1, Impulse.kr(1)) <= 0.75) * + (state <= 0) + ); + + // Playback the soundfile + generator = PlayBuf.ar(1, buf, BufRateScale.kr(buf), 1, 0, 1); + latch = Impulse.kr(Latch.kr((60 * cycle_len).reciprocal, local_in[1])); + env_master = EnvGen.kr(Env.sine(Latch.kr(60 * cycle_len, latch)), latch * (loop + Impulse.kr(0))); + Poll.kr(Impulse.kr(1), env_master, \env); + + // Feedback out state + LocalOut.kr([state, latch]); + + // Gate bins of the FFT + env_spectrum = pow(env_master, 3) * 0.75; + chain= FFT(LocalBuf(128).clear, generator); + spectrum_mult = { |i| TRand.kr(0, 1, hierarchical_dust) > Latch.kr(env_spectrum, hierarchical_dust)} ! 64; + chain = chain.pvcalc(64, {|mags, phases| [mags * spectrum_mult, phases] }); + + // Output + env_vol = pow(env_master, 2); + Out.ar([0,1], IFFT(chain).dup * env_vol * (1 - EnvGen.kr(Env.asr(15, 0.9, 15, \sine), state % 2))); + + // Send info to Visuals + SendTrig.kr(Impulse.kr(24), 0, + 1 - EnvGen.kr(Env.sine(1/6.0, env_vol), Select.kr(state > 0, [hierarchical_dust, state - Delay1.kr(state) < 0]))); + SendTrig.kr((state - Delay1.kr(state) < 0) * PulseCount.kr(Changed.kr(local_in[0])) >= 1, 1); + SendTrig.kr(Changed.kr(state), 2, state > 0); + SendReply.kr(Impulse.kr(24), '/tr', spectrum_mult, 3); +}).send(s); +) \ No newline at end of file diff --git a/supercollider/generator_hierarchical_dust_and_necklaces_visuals.scd b/supercollider/generator_hierarchical_dust_and_necklaces_visuals.scd new file mode 100644 index 0000000..8ff178d --- /dev/null +++ b/supercollider/generator_hierarchical_dust_and_necklaces_visuals.scd @@ -0,0 +1,186 @@ +( +~generateVisuals = { + arg buf; + var control_window, width_cw = 600, height_cw = 100, + stable_slider, unstable_slider, cycle_slider, stable_val, unstable_val, cycle_val, + border = true, border_button, loop_button,start_button, + hierarchical_dust_window, width_hdw = 600, height_hdw = 600, shade_hd = 1, spectrum_mult_hd = Array.fill(128, {1}), state_hd = 0, + necklaces_window, width_nw = 400, height_nw = 600, shade_n = 0, reset_hd_window, + + + // All the possible necklaces + necklaces = [[[2,0,1],[2,1,0]], + [[2,0,1,0],[2,1,0,1],[2,2,0,1],[2,1,2,0],[2,2,1,0]], + [[2,0,1,0,1],[2,1,0,1,0],[2,2,0,1,0],[2,0,2,0,1],[2,1,2,0,1],[2,2,1,0,1], + [2,2,2,0,1],[2,0,2,1,0],[2,1,2,1,0],[2,1,2,2,0],[2,2,1,2,0],[2,2,2,1,0]], + [[2,0,1,0,1,0],[2,1,0,1,0,1],[2,2,0,1,0,1],[2,0,2,0,1,0],[2,1,2,0,1,0],[2,2,1,0,1,0], + [2,2,2,0,1,0],[2,0,2,1,0,1],[2,0,2,2,0,1],[2,1,0,2,0,1],[2,1,2,1,0,1],[2,1,2,2,0,1], + [2,2,0,2,0,1],[2,2,1,2,0,1],[2,2,2,1,0,1],[2,2,2,2,0,1],[2,0,2,1,2,0],[2,0,2,2,1,0], + [2,1,0,2,2,0],[2,1,2,1,2,0],[2,1,2,2,1,0],[2,1,2,2,2,0],[2,2,1,2,1,0],[2,2,1,2,2,0], + [2,2,2,1,2,0],[2,2,2,2,1,0]], + [[2,0,1,0,1,0,1],[2,1,0,1,0,1,0],[2,2,0,1,0,1,0],[2,0,2,0,1,0,1],[2,1,2,0,1,0,1], + [2,2,1,0,1,0,1],[2,2,2,0,1,0,1],[2,0,1,2,0,1,0],[2,0,2,1,0,1,0],[2,0,2,2,0,1,0], + [2,1,0,2,0,1,0],[2,1,2,1,0,1,0],[2,1,2,2,0,1,0],[2,2,0,2,0,1,0],[2,2,1,2,0,1,0], + [2,2,2,1,0,1,0],[2,2,2,2,0,1,0],[2,0,1,2,1,0,1],[2,0,1,2,2,0,1],[2,0,2,0,2,0,1], + [2,0,2,1,2,0,1],[2,0,2,2,1,0,1],[2,0,2,2,2,0,1],[2,1,0,2,1,0,1],[2,1,0,2,2,0,1], + [2,1,2,0,2,0,1],[2,1,2,1,2,0,1],[2,1,2,2,1,0,1],[2,1,2,2,2,0,1],[2,2,0,2,1,0,1], + [2,2,0,2,2,0,1],[2,2,1,0,2,0,1],[2,2,1,2,1,0,1],[2,2,1,2,2,0,1],[2,2,2,0,2,0,1], + [2,2,2,1,2,0,1],[2,2,2,2,1,0,1],[2,2,2,2,2,0,1],[2,0,2,0,2,1,0],[2,0,2,1,2,1,0], + [2,0,2,1,2,2,0],[2,0,2,2,1,2,0],[2,0,2,2,2,1,0],[2,1,0,2,1,2,0],[2,1,0,2,2,1,0], + [2,1,0,2,2,2,0],[2,1,2,0,2,2,0],[2,1,2,1,2,1,0],[2,1,2,1,2,2,0],[2,1,2,2,1,2,0], + [2,1,2,2,2,1,0],[2,1,2,2,2,2,0],[2,2,0,2,2,1,0],[2,2,1,2,1,2,0],[2,2,1,2,2,1,0], + [2,2,1,2,2,2,0],[2,2,2,1,2,1,0],[2,2,2,1,2,2,0],[2,2,2,2,1,2,0],[2,2,2,2,2,1,0]]], + + binary_map = [[0, 0], [0, 1], [1, 0]], binary_rep, + necklace_len_count = Array.fill(necklaces.size, {1}), + necklace_count = all {: Array.fill(n.size, {1}), n <- necklaces }, + synth, osc_func, necklace_func, necklace, necklace_transition, necklace_fade_in, necklace_fade_out, run = true; + + // Generate the necklaces + necklace_func = {var necklace_len_index, necklace_index; + necklace_len_index = ({|i| i} ! necklaces.size).wchoose(necklace_len_count.normalizeSum); + necklace_len_count = necklace_len_count + 1; + necklace_len_count[necklace_len_index] = 0; + + necklace_index = ({|i| i} ! necklace_count[necklace_len_index].size).wchoose(necklace_count[necklace_len_index].normalizeSum); + necklace_count[necklace_len_index] = necklace_count[necklace_len_index] + 1; + necklace_count[necklace_len_index][necklace_index] = 0; + necklace = necklaces[necklace_len_index][necklace_index]; + }; + + // Fade necklaces in and out + necklace_fade_out = Routine { + 26.do({ arg i; shade_n = (25-i).abs/25.0; (1.0/25).wait;}); + necklace_fade_out.yieldAndReset; + }; + necklace_fade_in = Routine { + 2.wait; necklace_func.value; 26.do({ arg i; shade_n = i/25.0; (1.0/25).wait;}); + necklace_fade_in.yieldAndReset; + }.play; + necklace_transition = Routine { + necklace_fade_out.play; 1.wait; necklace_fade_in.play; + necklace_transition.yieldAndReset; + }; + + // Get messages from SynthDef + osc_func = OSCFunc({ arg msg, time; + switch(msg[2], + 0, {shade_hd = msg[3]}, + 1, {necklace_transition.play}, + 2, {state_hd = msg[3]}, + 3, {spectrum_mult_hd = msg[3..]} + )},'/tr', s.addr); + + reset_hd_window = { + arg border = true, isLaunch = true; + // Create window for projection + hierarchical_dust_window = Window("hierarchical dust window", + if(isLaunch, + {Rect(100, Window.availableBounds.height - height_hdw, width_hdw, height_hdw)}, + {hierarchical_dust_window.bounds}), true, border); + hierarchical_dust_window.background = Color.white; + hierarchical_dust_window.front; + + // Animate + hierarchical_dust_window.drawFunc = { + Pen.use { + Pen.color = Color.gray(1-shade_hd); + Pen.addRect(Rect(0, 0, hierarchical_dust_window.bounds.width, hierarchical_dust_window.bounds.height)); + Pen.perform(\fill); + + Pen.color = Color.gray(shade_hd); + if(state_hd == 1, { + binary_rep = (all {: binary_map[x], x <- necklace }).flatten; + + { |i| + Pen.line( + hierarchical_dust_window.bounds.width-if(binary_rep[i] == 1,{40},{30})@(((hierarchical_dust_window.bounds.height-40) + / 128) * i + 150), + hierarchical_dust_window.bounds.width-20@(((hierarchical_dust_window.bounds.height-40) / 128) * i + 150)) + } ! binary_rep.size;}); + + { |i| if(spectrum_mult_hd[i] == 1, + {Pen.line( + 20@(((hierarchical_dust_window.bounds.height-40) / 128) * i + 150), + 40@(((hierarchical_dust_window.bounds.height-40) / 128) * i + 150))})} ! 64; + Pen.stroke; + }; + }; + }; + reset_hd_window.value; + + // Create window for score + necklaces_window = Window("necklaces window", + Rect(width_hdw + 100, Window.availableBounds.height - height_nw, width_nw, height_nw)); + necklaces_window.background = Color.white; + necklaces_window.onClose = { run = false; hierarchical_dust_window.close; control_window.close; ~synth.free }; + necklaces_window.front; + + // Animate + necklaces_window.drawFunc = { + Pen.use { + Pen.color = Color.gray(if(state_hd == 1,{0}, {0.5}), shade_n); + { |i| Pen.line((200-75)@((i-1)*15 + 300), (200+75)@((i-1)*15 + 300)) } ! 3; + Pen.stroke; + + { |i| Pen.addOval( + Rect(200 - 75 + ((i+1) * (150.0 / (necklace.size + 2)) + 4), + (2-necklace[i]-1) * 15 + 300 - 7, 14, 14)) } ! necklace.size; + Pen.perform(\fill); + Pen.stroke; + }; + }; + + // Refresh + { while { run } { hierarchical_dust_window.refresh; necklaces_window.refresh; 24.reciprocal.wait; } }.fork(AppClock); + + // Create window for user controls + control_window = Window.new("control window", + Rect(100, Window.availableBounds.height - height_hdw - height_cw - 30, width_cw, height_cw)); + control_window.onClose = { run = false; hierarchical_dust_window.close; necklaces_window.close; ~synth.free }; + control_window.front; + unstable_val = TextField().fixedWidth_(50).string_("15"); + stable_val = TextField().fixedWidth_(50).string_("10"); + cycle_val = TextField().fixedWidth_(50).string_("10"); + unstable_slider = Slider(control_window).orientation_(\horizontal).action_({ + var scaled_val = (unstable_slider.value * 25 + 5).trunc; + ~synth.set(\unstable, scaled_val); + unstable_val.string = scaled_val;}); + unstable_slider.value = 0.4; + stable_slider = Slider(control_window).orientation_(\horizontal).action_({ + var scaled_val = (stable_slider.value * 25 + 5).trunc; + ~synth.set(\stable, scaled_val); + stable_val.string = scaled_val;}); + stable_slider.value = 0.2; + cycle_slider = Slider(control_window).orientation_(\horizontal).action_({ + var scaled_val = (cycle_slider.value * 25 + 5).trunc; + ~synth.set(\cycle_len, scaled_val); + cycle_val.string = scaled_val;}); + cycle_slider.value = 0.2; + border_button = Button(control_window).states_([["border", Color.black], ["border", Color.black, Color.grey]]).value_(1).action_( + {|v| hierarchical_dust_window.close; reset_hd_window.value(if(v.value == 1,{true},{false}), false)}); + loop_button = Button(control_window).states_([["loop", Color.black], ["loop", Color.black, Color.grey]]).action_( + {|v| ~synth.set(\loop, v.value)}); + start_button = Button(control_window).states_([["start", Color.black]]).action_( + {|v| Routine{ + state_hd = 0; + necklace_fade_in.play; + ~synth.free; + (1).wait; + ~synth = Synth.newPaused(\hierarchical_dust, [\buf, buf]); + (1).wait; + {stable_slider.valueAction = stable_slider.value; + unstable_slider.valueAction = unstable_slider.value; + cycle_slider.valueAction = cycle_slider.value; + loop_button.valueAction = loop_button.value;}.defer; + (2).wait; + ~synth.run; + }.play;}); + control_window.layout = VLayout( + HLayout([StaticText().string="unstable -> stable", stretch: 1], [HLayout(unstable_slider, unstable_val), stretch: 4]), + HLayout([StaticText().string="stable -> unstable", stretch: 1], [HLayout(stable_slider, stable_val), stretch: 4]), + HLayout([StaticText().string="cycle length", stretch: 1], [HLayout(cycle_slider, cycle_val), stretch: 4]), + HLayout(nil, nil, nil, border_button, loop_button, start_button) + ); +}; +) diff --git a/video/.gitignore b/video/.gitignore new file mode 100644 index 0000000..8f6528a --- /dev/null +++ b/video/.gitignore @@ -0,0 +1 @@ +*.mov