{
   "author" : "emergent",
   "name" : "Vocal Clouds (work in progress)",
   "description" : "A sample-based granular synthesizer to make a cloud-like texture from short vocal samples, which can then be played with a MIDI controller\r\n\r\n## ToDo:\r\n\r\n * add MIDI controls for more parameters (e.g. density, detune, pan, grain position)\r\n * add mechanism to choose different vocal samples\r\n * add mechanism to balance amplitude variations brought about by different grain durations and densities\r\n * possibly add more controls for rate manipulation/overtone generation & grain position\r\n * add mechanism to choose between Dust and Impulse as grain triggers (Dust for a slightly more glitchy texture, Impulse for smoother sound)",
   "ancestor_list" : [],
   "labels" : [
      "instrument",
      "granularsynthesis"
   ],
   "code" : "// Vocal clouds - Proof of concept\r\n\r\n(\r\n// configure server\r\ns = Server.local;\r\ns.options.sampleRate_(44100);\r\ns.options.memSize_(65536 * 4);\r\ns.newBusAllocators;\r\n\r\n// global variables\r\n~out = 0;\r\n~path = PathName(thisProcess.nowExecutingPath).parentPath++\"/vocal-samples/\"; // expects folder \"vocal-samples\" with audio samples in the same folder as the project file\r\n\r\n// make buffers\r\n\r\n~makeBuffers = {\r\n\t~buf = Array.new;\r\n\tPathName(~path).entries.do({\r\n\t\targ path;\r\n\t\t~buf = ~buf.add(Buffer.read(s, path.fullPath));\r\n\t});\r\n};\r\n\r\n// make buses\r\n~makeBuses = {\r\n\t~revBus = Bus.audio(s, 2); // effects bus\r\n};\r\n\r\n// make groups\r\n~makeNodes = {\r\n\ts.bind({\r\n\t\t~src = Group.new(s);\r\n\t\t~efx = Group.after(~src);\r\n\t\t~reverb = Synth.new(\\reverb, [\\in, ~revBus, \\out, ~out], ~efx);\r\n\t});\r\n};\r\n\r\n// clean up on ServerQuit\r\n~cleanup = {\r\n\ts.freeAll;\r\n\tMIDIdef.freeAll;\r\n\ts.newBusAllocators;\r\n\tServerBoot.removeAll;\r\n\tServerTree.removeAll;\r\n\tServerQuit.removeAll;\r\n};\r\n// MIDI stuff!\r\n~bend = 8192; // initializing global variable for pitch bend wheel\r\n~mod = 0; // global variable\r\n\r\nMIDIIn.connectAll;\r\n\r\n~notes = Array.fill(128, {nil});\r\n\r\nMIDIdef.noteOn(\\on, {\r\n\targ val, num, chan, src;\r\n\t~notes[num] = Synth.new(\\voxgrains, [\r\n\t\t\\baserate, (num-60).midiratio,\r\n\t\t\\amp, val.linexp(1,127,0.02,0.3), //velocity detection\r\n\t\t\\gate, 1,\r\n\t\t\\bend, ~bend.linlin(0,16383,-2,2),\r\n\t\t\\basedur, ~mod.linlin(0,127,0.07,2),\r\n\t\t\\detune, 0.025,\r\n\t\t\\dens, 12,\r\n\t\t\\out, ~revBus\r\n\t],\r\n\t~src\r\n\t);\r\n});\r\n\r\nMIDIdef.noteOff(\\off, {\r\n\targ val, num, chan, src;\r\n\t~notes[num].set(\\gate, 0);\r\n\t~notes[num] = nil; //probably optional, just to be on the safe side\r\n});\r\n\r\nMIDIdef.bend(\\bendTest, {\r\n\targ val, chan, src;\r\n\t~bend = val;\r\n\t~notes.do{arg synth; synth.set(\\bend, val.linlin(0,16383,-2,2))};\r\n}, chan:0); //chan:0 makes the def only listen to events on chan 0\r\n\r\nMIDIdef.cc(\\modWheelTest, {\r\n\targ val, chan, num;\r\n\t~mod = val;\r\n\t~notes.do{arg synth; synth.set(\\basedur, val.linlin(0, 127, 0.07, 2))};\r\n}, 1);\r\n\r\n// register functions\r\nServerBoot.add(~makeBuses);\r\nServerBoot.add(~makeBuffers);\r\nServerQuit.add(~cleanup);\r\n\r\ns.waitForBoot({\r\n\ts.sync;\r\n\r\n\tSynthDef.new(\\voxgrains, {\r\n\t\tvar sig, env, rate, dur, pan;\r\n\t\tenv = Env.asr(\\atk.ir(0.12), 1, \\rel.ir(1)).kr(2, \\gate.kr(1));\r\n\t\trate = \\baserate.kr(1) * LFNoise1.kr(\\dens.kr(36) * 1.01).exprange(\\baserate.kr(1) * 4).round(\\baserate.kr(1)) + LFNoise1.kr(100).bipolar(\\detune.kr(0.025)) * \\bend.kr(~bend).midiratio; // randomly generated overtones \r\n\t\tdur = \\basedur.kr(0.5) + LFNoise1.ar(50).bipolar(0.02);\r\n\t\tpan = \\basepan.kr(0) + LFNoise1.ar(100).bipolar(0.25); // don't assign values greater than +- 0.7 to basepan!\r\n\t\tsig = GrainBuf.ar(\r\n\t\t\t2, // 2-channel output\r\n\t\t\tDust.kr(\\dens.kr(36)), // using Dust as trigger\r\n\t\t\tdur,\r\n\t\t\t\\buf.ir(~buf[5]), // choose buffer from which grains are taken\r\n\t\t\trate,\r\n\t\t\t\\pos.kr(0.1),\r\n\t\t\t2, // linear interpolation\r\n\t\t\tpan,\r\n\t\t\t\\grainenv.ir(-1), // using built-in envelope\r\n\t\t\t\\maxgrains.kr(128)\r\n\t\t);\r\n\t\tsig = sig * env * \\amp.kr(0.12);\r\n\t\tsig = Splay.ar(sig);\r\n\t\tOut.ar(\\out.ir(~out), sig); // possibly add dry/wet control via additional out here\r\n\t}).add;\r\n\r\n\tSynthDef.new(\\reverb, {\r\n\t\tvar sig;\r\n\t\tsig = JPverb.ar(\r\n\t\t\tIn.ar(\\in.ir(~revBus), 2),\r\n\t\t\t\\rtime.kr(4),\r\n\t\t\t\\damp.kr(0.75),\r\n\t\t\t\\size.kr(4.5),\r\n\t\t\t\\earlyDiff.kr(0.8),\r\n\t\t\t\\modDepth.kr(0.12),\r\n\t\t\t\\modFreq.kr(2),\r\n\t\t\t\\low.kr(1),\r\n\t\t\t\\mid.kr(0.9),\r\n\t\t\t\\high.kr(0.8)\r\n\t\t);\r\n\t\tsig = sig * \\revAmp.kr(0.5);\r\n\t\tOut.ar(\\out.ir(~out), sig);\r\n\t}).add;\r\n\r\n\ts.sync;\r\n\tServerTree.add(~makeNodes);\r\n\ts.freeAll;\r\n\r\n\ts.sync;\r\n\t\"done\".postln;\r\n});\r\n)\r\n\r\ns.quit; // quit performance",
   "id" : "1-5eG",
   "is_private" : null
}
