«Vocal Clouds (work in progress)» by emergent
on 08 Mar'21 14:16 inA sample-based granular synthesizer to make a cloud-like texture from short vocal samples, which can then be played with a MIDI controller
ToDo:
- add MIDI controls for more parameters (e.g. density, detune, pan, grain position)
- add mechanism to choose different vocal samples
- add mechanism to balance amplitude variations brought about by different grain durations and densities
- possibly add more controls for rate manipulation/overtone generation & grain position
- add mechanism to choose between Dust and Impulse as grain triggers (Dust for a slightly more glitchy texture, Impulse for smoother sound)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
// Vocal clouds - Proof of concept ( // configure server s = Server.local; s.options.sampleRate_(44100); s.options.memSize_(65536 * 4); s.newBusAllocators; // global variables ~out = 0; ~path = PathName(thisProcess.nowExecutingPath).parentPath++"/vocal-samples/"; // expects folder "vocal-samples" with audio samples in the same folder as the project file // make buffers ~makeBuffers = { ~buf = Array.new; PathName(~path).entries.do({ arg path; ~buf = ~buf.add(Buffer.read(s, path.fullPath)); }); }; // make buses ~makeBuses = { ~revBus = Bus.audio(s, 2); // effects bus }; // make groups ~makeNodes = { s.bind({ ~src = Group.new(s); ~efx = Group.after(~src); ~reverb = Synth.new(\reverb, [\in, ~revBus, \out, ~out], ~efx); }); }; // clean up on ServerQuit ~cleanup = { s.freeAll; MIDIdef.freeAll; s.newBusAllocators; ServerBoot.removeAll; ServerTree.removeAll; ServerQuit.removeAll; }; // MIDI stuff! ~bend = 8192; // initializing global variable for pitch bend wheel ~mod = 0; // global variable MIDIIn.connectAll; ~notes = Array.fill(128, {nil}); MIDIdef.noteOn(\on, { arg val, num, chan, src; ~notes[num] = Synth.new(\voxgrains, [ \baserate, (num-60).midiratio, \amp, val.linexp(1,127,0.02,0.3), //velocity detection \gate, 1, \bend, ~bend.linlin(0,16383,-2,2), \basedur, ~mod.linlin(0,127,0.07,2), \detune, 0.025, \dens, 12, \out, ~revBus ], ~src ); }); MIDIdef.noteOff(\off, { arg val, num, chan, src; ~notes[num].set(\gate, 0); ~notes[num] = nil; //probably optional, just to be on the safe side }); MIDIdef.bend(\bendTest, { arg val, chan, src; ~bend = val; ~notes.do{arg synth; synth.set(\bend, val.linlin(0,16383,-2,2))}; }, chan:0); //chan:0 makes the def only listen to events on chan 0 MIDIdef.cc(\modWheelTest, { arg val, chan, num; ~mod = val; ~notes.do{arg synth; synth.set(\basedur, val.linlin(0, 127, 0.07, 2))}; }, 1); // register functions ServerBoot.add(~makeBuses); ServerBoot.add(~makeBuffers); ServerQuit.add(~cleanup); s.waitForBoot({ s.sync; SynthDef.new(\voxgrains, { var sig, env, rate, dur, pan; env = Env.asr(\atk.ir(0.12), 1, \rel.ir(1)).kr(2, \gate.kr(1)); rate = \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 dur = \basedur.kr(0.5) + LFNoise1.ar(50).bipolar(0.02); pan = \basepan.kr(0) + LFNoise1.ar(100).bipolar(0.25); // don't assign values greater than +- 0.7 to basepan! sig = GrainBuf.ar( 2, // 2-channel output Dust.kr(\dens.kr(36)), // using Dust as trigger dur, \buf.ir(~buf[5]), // choose buffer from which grains are taken rate, \pos.kr(0.1), 2, // linear interpolation pan, \grainenv.ir(-1), // using built-in envelope \maxgrains.kr(128) ); sig = sig * env * \amp.kr(0.12); sig = Splay.ar(sig); Out.ar(\out.ir(~out), sig); // possibly add dry/wet control via additional out here }).add; SynthDef.new(\reverb, { var sig; sig = JPverb.ar( In.ar(\in.ir(~revBus), 2), \rtime.kr(4), \damp.kr(0.75), \size.kr(4.5), \earlyDiff.kr(0.8), \modDepth.kr(0.12), \modFreq.kr(2), \low.kr(1), \mid.kr(0.9), \high.kr(0.8) ); sig = sig * \revAmp.kr(0.5); Out.ar(\out.ir(~out), sig); }).add; s.sync; ServerTree.add(~makeNodes); s.freeAll; s.sync; "done".postln; }); ) s.quit; // quit performance
reception
comments