( ~sin_grain = CtkSynthDef(\sin_grain, {arg dur = 1, amp = -3, freq = 440, pos = 0; var env, halfdur, car; halfdur = dur * 0.5; amp = amp.dbamp; env = EnvGen.kr( Env([0, amp, 0], [halfdur, halfdur], \sin)); car = SinOsc.ar(freq, 0, 1); OffsetOut.ar(0, Pan2.ar(car * env, pos)); }); ~fm_grain = CtkSynthDef(\fm_grain, {arg dur = 1, amp = -3, freq = 440, cm_ratio = 1, index = 1, pos = 0; var env, halfdur, mod, car, dev, mod_freq; halfdur = dur * 0.5; amp = amp.dbamp; mod_freq = freq * cm_ratio; dev = index * mod_freq; env = EnvGen.kr( Env([0, amp, 0], [halfdur, halfdur], \sin)); mod = SinOsc.ar(mod_freq, 0, dev); car = SinOsc.ar(freq + mod); OffsetOut.ar(0, Pan2.ar(car * env, pos)); }); ) ( // function that generates periodic and non-periodic grains, // grains can be both sinusoidal or fm ~grains = {arg score, synth, start_time, duration, grain_density, grain_amp, grain_dur, grain_freq, grain_pos, grain_cm_ratio, grain_index; var now = 0.0, n, idur, amp, freq, dur, dens, pos, event; idur = duration.reciprocal; while({ now < duration; }, { n = now * idur; // all these parameters accept Env or Tendency objects freq = grain_freq.at(n); amp = grain_amp.at(n); dur = grain_dur.at(n); pos = grain_pos.at(n); dens = grain_density.at(n); event = synth.new(now + start_time, dur).freq_(freq).amp_(amp).dur_(dur).pos_(pos); // check if synth is fm_grain and set extra slots for the event (synth == ~fm_grain).if({ event.cm_ratio = grain_cm_ratio.at(n); event.index = grain_index.at(n); }); // add the event to the score score.add(event); // increment time by the inverse of the grain density now = now + dens.reciprocal; }) }; ) // ServerOptions to be used by CtkScore's write method ~options = ServerOptions.new.numOutputBusChannels_(2); ( // Low density (10 grains per second) grains last 10ms // and are periodic, their frequency is within the 400-500 range var score; score = CtkScore.new; ~grains.value(score, ~sin_grain, 1.0, 15.0, Tendency(10.0, 10.0), Env([-24.0, -3.0, -24.0], [0.5, 0.5]), Tendency(0.01, 0.01), Tendency(400.0, 500.0), Tendency(-0.5, 0.5)); // write sound file out score.write("~/Desktop/grains_1.aiff".standardizePath, options: ~options); ) // open file Player.new("~/Desktop/grains_1.aiff".standardizePath).gui; ( // Same but changing grain duration and position over time var score; score = CtkScore.new; ~grains.value(score, ~sin_grain, 1.0, 15.0, Tendency(10.0, 10.0), Env([-24.0, -3.0, -24.0], [0.5, 0.5]), Env([0.01, 0.1, 0.01],[0.5, 0.5]), Tendency(400.0, 500.0), Env([-1, 0, 1],[0.5, 0.5])); // write sound file out score.write("~/Desktop/grains_2.aiff".standardizePath, options: ~options); ) // open file Player.new("~/Desktop/grains_2.aiff".standardizePath).gui; ( // Same but changing grain density over time // note that we change only the uper bound of the tendency // panning is also controlled with a tendency mask var score; score = CtkScore.new; ~grains.value(score, ~sin_grain, 1.0, 15.0, Tendency(10.0, Env([10, 100, 10],[0.5, 0.5])), Env([-24.0, -3.0, -24.0], [0.5, 0.5]), Tendency(0.01, 0.01), Tendency(400.0, 500.0), Tendency(Env([0, -1, 0],[0.5, 0.5], 4), Env([0, 1, 0],[0.5, 0.5], 4))); // write sound file out score.write("~/Desktop/grains_3.aiff".standardizePath, options: ~options); ) // open file Player.new("~/Desktop/grains_3.aiff".standardizePath).gui; ( // Longer grains, enhancing band of 1300 and 1500 Hz var score; score = CtkScore.new; ~grains.value(score, ~sin_grain, 1.0, 15.0, // grain density Tendency( Env([100, 100, 500, 500], [0.3, 0.4, 0.3]), Env([200, 200, 700, 700], [0.3, 0.4, 0.3])), // grain amplitude Env([-60.0, -30.0, -30.0, -60.0], [0.3, 0.4, 0.3]), // grain duration Env([0.1, 0.2, 0.1], [0.5, 0.5]), // grain frequency Tendency(1300.0, 1500.0), // grain position Tendency( Env([-1, -1, 0, 0], [0.3, 0.4, 0.3]), Env([0, 0, 1, 1], [0.3, 0.4, 0.3])) ); // write sound file out score.write("~/Desktop/grains_4.aiff".standardizePath, options: ~options); ) // open file Player.new("~/Desktop/grains_4.aiff".standardizePath).gui; ( // Same but moving the frequency band of the grains var score; score = CtkScore.new; ~grains.value(score, ~sin_grain, 1.0, 15.0, // grain density Tendency( Env([100, 100, 500, 500], [0.3, 0.4, 0.3]), Env([200, 200, 700, 700], [0.3, 0.4, 0.3])), // grain amplitude Env([-60.0, -30.0, -30.0, -60.0], [0.3, 0.4, 0.3]), // grain duration Env([0.1, 0.2, 0.1], [0.5, 0.5]), // grain frequency Tendency( Env([1300.0, 1000.0], [1.0]), Env([1500.0, 3500.0], [1.0])), // grain position Tendency( Env([-1, 0, 0, -1], [0.3, 0.4, 0.3]), Env([1, 0, 0, 1], [0.3, 0.4, 0.3])) ); // write sound file out score.write("~/Desktop/grains_5.aiff".standardizePath, options: ~options); ) // open file Player.new("~/Desktop/grains_5.aiff".standardizePath).gui; // A more complex example using fm_grain ( var score; score = CtkScore.new; ~grains.value(score, ~fm_grain, 1.0, 15.0, // grain density Tendency( Env([100, 100, 500, 500], [0.3, 0.4, 0.3]), Env([200, 200, 700, 700], [0.3, 0.4, 0.3])), // grain amplitude Env([-60.0, -30.0, -30.0, -60.0], [0.3, 0.4, 0.3]), // grain duration Env([0.01, 0.1, 0.01], [0.5, 0.5]), // grain frequency Tendency( Env([1300.0, 1000.0], [1.0]), Env([1500.0, 3500.0], [1.0])), // grain position Tendency(-1.0, 1.0), // grain cm ratio, Env([1.0, 1.66666, 1.66666, 1.0], [0.3, 0.4, 0.3], \exp), // grain index Env([1.0, 2.0, 2.0, 1.0], [0.3, 0.4, 0.3], \exp) ); // write sound file out score.write("~/Desktop/grains_6.aiff".standardizePath, options: ~options); ) // open file Player.new("~/Desktop/grains_6.aiff".standardizePath).gui;