// title: GrainOTec // author: Dindoléon // description: // A GUI module to produce granular synthesis textures. Controls are : amplitude, trigger rate, grain duration, number of simultaneous grains, frequency offset between simultaneous grains, random panning and enveloppe. // code: ( // GrainOTec module for SuperCollider, by Dindoléon // licensed under GNU GPL v3 // Have fun, but take care with the DBs ! var win, rout, draw_slider, font; var volume, volume_slider, volume_value, volume_label, volume_color; var lifetime, lifetime_slider, lifetime_value, lifetime_label, lifetime_color; var env, env_array, env_points, env_slider, env_duration, env_multiSlider, env_value, env_label, env_color; var freq, base_freq, freq_value, freq_slider, freq_mul, freq_mul_value, freq_mul_slider, freq_label, freq_mul_label, freq_color, freq_mul_color; var pan, pan_value, pan_slider, pan_label, pan_color; var icon_size = 10, stroke_size = 2, outline_size = 15; font = Font( size: 20 ); base_freq = 220; freq = [base_freq]; freq_value = 0; freq_mul = 1; freq_mul_value = 0; volume = 0; volume_value = 0; lifetime = 0.005; lifetime_value = 0.6; pan = 0; pan_value = 0; env_duration = 0.005; env_value = 0.6; env_points = 8; env_array = Array.fill(env_points+2, 0); volume_color = Color( 0.8, 0, 0.3 ); lifetime_color = Color( 0, 0.3, 0.8 ); env_color = Color( 0, 0.8, 0.3 ); freq_color = Color( 0.8, 0.3, 0 ); freq_mul_color = Color( 0.3, 0, 0.8 ); pan_color = Color( 0.3, 0.8, 0 ); env = Env.new( levels: env_array, times: Array.fill(env_points+1, env_duration/(env_points+1)) ); SynthDef(\grain, { | freq = 220 | var snd, env, envctl, envgen, pan, amp; env = Env.newClear( 10 ); envctl = \env.kr( env.asArray ); envgen = EnvGen.ar( envctl, 1, doneAction: 2 ); snd = LinPan2.ar( SinOsc.ar( freq ) * envgen * \amp.kr(1), \pan.ar(0) ); Out.ar( 0, snd ); }).add; rout = Routine { loop { freq.do( { |f| Synth(\grain, [\freq, f, \amp, volume/freq.size, \env, env, \pan, pan.asFloat.rand] ) }); lifetime.yield; }; }.play(); win = Window("GrainOTec", Rect( 5, 5, 305, 300 ), false ); win.background_(Color.black); win.front; // Volume Slider volume_slider = UserView( win, Rect( 5, 5, 45, 190 )); volume_slider.drawFunc = { draw_slider.value( volume_slider, volume_value, volume_color ) }; volume_slider.mouseDownAction = { | volume_slider, x, y, m | volume_value = (y).linlin( 0, volume_slider.bounds.height, 1, 0 ); volume = volume_value; volume_slider.refresh }; volume_slider.mouseMoveAction = volume_slider.mouseDownAction; volume_label = StaticText( volume_slider, Rect( 25, 165, 28, 28 )); volume_label.string_("a"); volume_label.font_(font); volume_label.stringColor_(Color(1,1,1,0.75)); // Lifetime Slider lifetime_slider = UserView( win, Rect( 55, 5, 45, 190 )); lifetime_slider.drawFunc = { draw_slider.value( lifetime_slider, lifetime_value, lifetime_color ) }; lifetime_slider.mouseDownAction = { | lifetime_slider, x, y, m | lifetime_value = (y).linlin( 0, lifetime_slider.bounds.height, 1, 0 ); lifetime = lifetime_value.linexp( 0, 1, 0.1, 0.001 ); lifetime_slider.refresh }; lifetime_slider.mouseMoveAction = lifetime_slider.mouseDownAction; lifetime_label = StaticText( lifetime_slider, Rect( 25, 165, 28, 28 )); lifetime_label.string_("t"); lifetime_label.font_(font); lifetime_label.stringColor_(Color(1,1,1,0.75)); // Env Slider env_slider = UserView( win, Rect( 105, 5, 45, 190 )); env_slider.drawFunc = { draw_slider.value( env_slider, env_value, env_color ) }; env_slider.mouseDownAction = { | env_slider, x, y, m | env_value = (y).linlin( 0, env_slider.bounds.height, 1, 0 ); env_duration = env_value.linexp( 0, 1, 0.1, 0.001 ); env.times = Array.fill(env_points+1, env_duration/(env_points+1)); env_slider.refresh }; env_slider.mouseMoveAction = env_slider.mouseDownAction; env_label = StaticText( env_slider, Rect( 25, 165, 28, 28 )); env_label.string_("d"); env_label.font_(font); env_label.stringColor_(Color(1,1,1,0.75)); // Freq Slider freq_slider = UserView( win, Rect( 155, 5, 45, 190 )); freq_slider.drawFunc = { draw_slider.value( freq_slider, freq_value, freq_color ) }; freq_slider.mouseDownAction = { | freq_slider, x, y, m | freq_value = (y).linlin( 0, freq_slider.bounds.height, 1, 0 ); freq = Array.fill(freq_value.linlin( 0, 1, 1, 7 ).asInt, base_freq); if( freq_value.linlin( 0, 1, 1, 7 ).asInt > 1, { for( 1, freq_value.linlin( 0, 1, 1, 7 ).asInt -1, { |i| freq[i] = freq[i-1] * freq_mul; } ) }); freq_slider.refresh }; freq_slider.mouseMoveAction = freq_slider.mouseDownAction; freq_label = StaticText( freq_slider, Rect( 25, 165, 28, 28 )); freq_label.string_("h"); freq_label.font_(font); freq_label.stringColor_(Color(1,1,1,0.75)); // Freq Mul Slider freq_mul_slider = UserView( win, Rect( 205, 5, 45, 190 )); freq_mul_slider.drawFunc = { draw_slider.value( freq_mul_slider, freq_mul_value, freq_mul_color ) }; freq_mul_slider.mouseDownAction = { | freq_slider, x, y, m | freq_mul_value = (y).linlin( 0, freq_slider.bounds.height, 1, 0 ); freq_mul = 1 + freq_mul_value; freq = Array.fill(freq_value.linlin( 0, 1, 1, 7 ).asInt, base_freq); if( freq_value.linlin( 0, 1, 1, 7 ).asInt > 1, { for( 1, freq_value.linlin( 0, 1, 1, 7 ).asInt -1, { |i| freq[i] = freq[i-1] * freq_mul; } ) }); freq_mul_slider.refresh }; freq_mul_slider.mouseMoveAction = freq_mul_slider.mouseDownAction; freq_mul_label = StaticText( freq_mul_slider, Rect( 25, 165, 28, 28 )); freq_mul_label.string_("m"); freq_mul_label.font_(font); freq_mul_label.stringColor_(Color(1,1,1,0.75)); // Pan Slider pan_slider = UserView( win, Rect( 255, 5, 45, 190 )); pan_slider.drawFunc = { draw_slider.value( pan_slider, pan_value, pan_color ) }; pan_slider.mouseDownAction = { | lifetime_slider, x, y, m | pan_value = (y).linlin( 0, lifetime_slider.bounds.height, 1, 0 ); pan = pan_value; pan_slider.refresh }; pan_slider.mouseMoveAction = pan_slider.mouseDownAction; pan_label = StaticText( pan_slider, Rect( 25, 165, 28, 28 )); pan_label.string_("p"); pan_label.font_(font); pan_label.stringColor_(Color(1,1,1,0.75)); // Enveloppe MultiSlider env_multiSlider = MultiSliderView( win, Rect( 5, 200, 295, 90 ) ); env_multiSlider.value = Array.fill(env_points, {0.0}); env_multiSlider.isFilled = true; env_multiSlider.elasticMode_(true); env_multiSlider.fillColor = Color(0,0,0.1); env_multiSlider.strokeColor = Color.red; env_multiSlider.background_(Color(0,0,0.2)); env_multiSlider.gap = 0; env_multiSlider.drawRects = false; // Display as bar charts env_multiSlider.drawLines = true; // Display as plot env_multiSlider.action = { arg multi; var index = multi.index; var value = multi.currentvalue; env_array[(index+1)] = value; env.levels = env_array; }; // Slider Draw Function draw_slider = { | slider, value, color | Pen.width = stroke_size; // Draw the frame Pen.strokeColor = Color.white; Pen.fillColor = Color.black; Pen.addRect(Rect(0, 0, slider.bounds.width,slider.bounds.height)); Pen.draw(3); // Draw the losange Pen.moveTo( ( slider.bounds.width/2 ) @ ( ( slider.bounds.height - ( slider.bounds.height * value) ) - icon_size ) ); Pen.lineTo( ( slider.bounds.width/2 -icon_size ) @ ( slider.bounds.height - (slider.bounds.height*value) ) ); Pen.lineTo( ( slider.bounds.width/2 ) @ ( ( slider.bounds.height - ( slider.bounds.height * value) ) + icon_size ) ); Pen.lineTo( ( slider.bounds.width/2 + icon_size ) @ ( slider.bounds.height - (slider.bounds.height*value) ) ); Pen.lineTo( ( slider.bounds.width/2 ) @ ( ( slider.bounds.height - ( slider.bounds.height * value) ) - icon_size ) ); Pen.fillColor = color; Pen.fill; Pen.moveTo( ( slider.bounds.width/2 ) @ ( ( slider.bounds.height - ( slider.bounds.height * value) ) - outline_size ) ); Pen.lineTo( ( slider.bounds.width/2 -outline_size ) @ ( slider.bounds.height - (slider.bounds.height*value) ) ); Pen.lineTo( ( slider.bounds.width/2 ) @ ( ( slider.bounds.height - ( slider.bounds.height * value) ) + outline_size ) ); Pen.lineTo( ( slider.bounds.width/2 + outline_size ) @ ( slider.bounds.height - (slider.bounds.height*value) ) ); Pen.lineTo( ( slider.bounds.width/2 ) @ ( ( slider.bounds.height - ( slider.bounds.height * value) ) - outline_size ) ); Pen.moveTo( ( slider.bounds.width/2 ) @ 0 ); Pen.lineTo( ( slider.bounds.width/2 ) @ ( ( slider.bounds.height - ( slider.bounds.height * value) ) - outline_size ) ); Pen.moveTo( ( slider.bounds.width/2 ) @ ( slider.bounds.height ) ); Pen.lineTo( ( slider.bounds.width/2 ) @ ( ( slider.bounds.height - ( slider.bounds.height * value) ) + outline_size ) ); Pen.strokeColor = color; Pen.stroke; }; )