«wavetable interpolation drone» by eli.fieldsteel
on 28 Feb'19 07:06 inA lush drone using a waveshaping algorithm for interpolating between multiple wavetables. This code is based off of a similar code example by totalgee:
http://sccode.org/1-4V1
It was only after coding this example that I realized the UGens VOsc and VOsc3 are specifically designed for this wavetable interpolating/morphing function. But this code was still very fun to build.
EF
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 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
( /* A lush drone using a waveshaping algorithm for interpolating between multiple wavetables. This code is based off of a similar code example by totalgee: http://sccode.org/1-4V1 It was only after coding this example that I realized the UGens VOsc and VOsc3 are specifically designed for this wavetable interpolating/morphing function. But this code was still very fun to build. EF */ s.freeAll; Window.closeAll; Buffer.freeAll; /* Function for interpolating between two values based on an interpolation value: 0 ==> old, 1 ==> new, 0.5 ==> halfway between old/new, etc */ ~interpFn = { arg old=20, new=10, bal=0.5; old + ((new - old) * bal); }; /* three wacky envelopes, each converted to Signal format. each has 12 level points. the first and last value are always zero. the inner 10 points are random between -1 and +1. the internal functions normalize the levels so the highest value is always +/-1.0 */ ~wt0 = Env( [0]++ { var levs, peak; levs = {rrand(-1.0,1.0)}!10; peak = levs.abs.maxItem; levs = levs * peak.reciprocal; }.value ++ [0], {exprand(0.01,1)}!11, {exprand(0.1,4)}!11 ).asSignal(512); ~wt1 = Env( [0]++ { var levs, peak; levs = {rrand(-1.0,1.0)}!10; peak = levs.abs.maxItem; levs = levs * peak.reciprocal; }.value ++ [0], {exprand(0.01,1)}!11, {exprand(0.1,4)}!11 ).asSignal(512); ~wt2 = Env( [0]++ { var levs, peak; levs = {rrand(-1.0,1.0)}!10; peak = levs.abs.maxItem; levs = levs * peak.reciprocal; }.value ++ [0], {exprand(0.01,1)}!11, {exprand(0.1,4)}!11 ).asSignal(512); /* Signals that contain values representing interpolations between two wavetables. ~i0 interpolates between ~wt0 <==> ~wt1 ~i1 interpolates between ~wt1 <==> ~wt2 */ ~i0 = ~wt0.copy; ~i1 = ~wt2.copy; s.waitForBoot({ //load signals to buffers in wavetable format ~wt0Buf = Buffer.loadCollection(s, ~wt0.asWavetable); ~wt1Buf = Buffer.loadCollection(s, ~wt1.asWavetable); ~wt2Buf = Buffer.loadCollection(s, ~wt2.asWavetable); ~i0Buf = Buffer.alloc(s, 1024, 1); ~i1Buf = Buffer.alloc(s, 1024, 1); s.sync; ~i0Buf.setn(0, ~i0.asWavetable); ~i1Buf.setn(0, ~i1.asWavetable); s.sync; /* Create two Osc Synths, reading their wavetable data from ~i0Buf and ~i1Buf */ ~synths = [~i0Buf, ~i1Buf].collect{ arg buf, i; { /* detune=0 makes a much more boring sound, but also makes the wavetable interpolation more observable (evaluate s.scope and adjust the horizontal slider to watch the waveform) */ arg detune=0.15, freq=40, amp=0.1; var sig; sig = Osc.ar( buf.bufnum, //frequency with random moving detune value (in semitones) freq * LFNoise1.kr({Rand(0.08,0.15)}!8).bipolar(detune).midiratio, {Rand(0,2pi)}!8 ); //spread 8-channel detuned Osc texture across two channels sig = Splay.ar(sig); //avoid funky DC bias sig = LeakDC.ar(sig); sig = sig * amp; }.play(fadeTime:4); }; { arg min=0.2, max=1; var sig; sig = LFDNoise1.kr( LFNoise1.kr(8!2).exprange(min,max) ).unipolar(1); /* uncomment this line to control wavetable interpolation with horizontal mouse position instead of a noise generator */ //sig = MouseX.kr!2; SendReply.kr(Impulse.kr(40), '/mouse', sig); 0 }.play(target:h); s.sync; OSCdef(\mouse, { arg msg; /* when SendReply sends a value to the language, use that value as an interpolation parameter and update the wavetables from which the Osc UGen is reading */ //modify values ~i0.waveFill({ arg x, val, i; ~interpFn.(~wt0[i], ~wt1[i], msg[3]) }); ~i1.waveFill({ arg x, val, i; ~interpFn.(~wt1[i], ~wt2[i], msg[4]) }); //dynamically update Buffers ~i0Buf.setn(0, ~i0.asWavetable); ~i1Buf.setn(0, ~i1.asWavetable); }, '/mouse').permanent_(true); }) ) //watch: s.scope; /*(adjust horizontal slider on scope window as needed to stabilize waveform view) */ //change frequency ~synths.do(_.set(\freq, 50)); //the two Synths can be changed independently ( ~synths[0].set(\freq, 60); ~synths[1].set(\freq, 106, \amp, 0.05); ) //fade out ~synths.do(_.set(\gate, 0));
descendants
full graph
«Re: wavetable interpolation drone» by Nandor Devai (private)
reception
comments