{
   "name" : "wavetable interpolation drone",
   "author" : "eli.fieldsteel",
   "ancestor_list" : [],
   "description" : "A lush drone using a waveshaping algorithm for interpolating between multiple wavetables. This code is based off of a similar code example by totalgee:\r\n\r\nhttp://sccode.org/1-4V1\r\n\r\nIt 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.\r\n\r\nEF",
   "labels" : [
      "bass",
      "complex",
      "drone",
      "rich",
      "waveshaping",
      "wavetable"
   ],
   "code" : "(\r\n/*\r\nA lush drone using a waveshaping algorithm for interpolating between multiple wavetables. This code is based off of a similar code example by totalgee:\r\n\r\nhttp://sccode.org/1-4V1\r\n\r\nIt 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.\r\n\r\nEF\r\n*/\r\n\r\ns.freeAll;\r\nWindow.closeAll;\r\nBuffer.freeAll;\r\n\r\n/*\r\nFunction for interpolating between two\r\nvalues based on an interpolation value:\r\n\r\n0 ==> old, 1 ==> new,\r\n0.5 ==> halfway between old/new, etc\r\n*/\r\n~interpFn = {\r\n\targ old=20, new=10, bal=0.5;\r\n\told + ((new - old) * bal);\r\n};\r\n\r\n/*\r\nthree wacky envelopes, each converted to Signal format.\r\neach has 12 level points. the first and last value are\r\nalways zero. the inner 10 points are random between -1\r\nand +1. the internal functions normalize the levels so\r\nthe highest value is always +/-1.0\r\n*/\r\n~wt0 = Env(\r\n\t[0]++\r\n\t{\r\n\t\tvar levs, peak;\r\n\t\tlevs = {rrand(-1.0,1.0)}!10;\r\n\t\tpeak = levs.abs.maxItem;\r\n\t\tlevs = levs * peak.reciprocal;\r\n\t}.value ++\r\n\t[0],\r\n\t{exprand(0.01,1)}!11,\r\n\t{exprand(0.1,4)}!11\r\n).asSignal(512);\r\n\r\n~wt1 = Env(\r\n\t[0]++\r\n\t{\r\n\t\tvar levs, peak;\r\n\t\tlevs = {rrand(-1.0,1.0)}!10;\r\n\t\tpeak = levs.abs.maxItem;\r\n\t\tlevs = levs * peak.reciprocal;\r\n\t}.value ++\r\n\t[0],\r\n\t{exprand(0.01,1)}!11,\r\n\t{exprand(0.1,4)}!11\r\n).asSignal(512);\r\n\r\n~wt2 = Env(\r\n\t[0]++\r\n\t{\r\n\t\tvar levs, peak;\r\n\t\tlevs = {rrand(-1.0,1.0)}!10;\r\n\t\tpeak = levs.abs.maxItem;\r\n\t\tlevs = levs * peak.reciprocal;\r\n\t}.value ++\r\n\t[0],\r\n\t{exprand(0.01,1)}!11,\r\n\t{exprand(0.1,4)}!11\r\n).asSignal(512);\r\n\r\n/*\r\nSignals that contain values representing interpolations between two wavetables.\r\n~i0 interpolates between ~wt0 <==> ~wt1\r\n~i1 interpolates between ~wt1 <==> ~wt2\r\n*/\r\n~i0 = ~wt0.copy;\r\n~i1 = ~wt2.copy;\r\n\r\ns.waitForBoot({\r\n\r\n\t//load signals to buffers in wavetable format\r\n\t~wt0Buf = Buffer.loadCollection(s, ~wt0.asWavetable);\r\n\t~wt1Buf = Buffer.loadCollection(s, ~wt1.asWavetable);\r\n\t~wt2Buf = Buffer.loadCollection(s, ~wt2.asWavetable);\r\n\t~i0Buf = Buffer.alloc(s, 1024, 1);\r\n\t~i1Buf = Buffer.alloc(s, 1024, 1);\r\n\ts.sync;\r\n\t~i0Buf.setn(0, ~i0.asWavetable);\r\n\t~i1Buf.setn(0, ~i1.asWavetable);\r\n\r\n\ts.sync;\r\n\r\n\t/*\r\n\tCreate two Osc Synths, reading their wavetable\r\n\tdata from ~i0Buf and ~i1Buf\r\n\t*/\r\n\t~synths = [~i0Buf, ~i1Buf].collect{\r\n\t\targ buf, i;\r\n\t\t{\r\n\t\t\t/*\r\n\t\t\tdetune=0 makes a much more boring sound, but also\r\n\t\t\tmakes the wavetable interpolation more\r\n\t\t\tobservable (evaluate s.scope and adjust the\r\n\t\t\thorizontal slider to watch the waveform)\r\n\t\t\t*/\r\n\t\t\targ detune=0.15, freq=40, amp=0.1;\r\n\t\t\tvar sig;\r\n\t\t\tsig = Osc.ar(\r\n\t\t\t\tbuf.bufnum,\r\n\r\n\t\t\t\t//frequency with random moving detune value (in semitones)\r\n\t\t\t\tfreq * LFNoise1.kr({Rand(0.08,0.15)}!8).bipolar(detune).midiratio,\r\n\r\n\t\t\t\t{Rand(0,2pi)}!8\r\n\t\t\t);\r\n\r\n\t\t\t//spread 8-channel detuned Osc texture across two channels\r\n\t\t\tsig = Splay.ar(sig);\r\n\r\n\t\t\t//avoid funky DC bias\r\n\t\t\tsig = LeakDC.ar(sig);\r\n\r\n\t\t\tsig = sig * amp;\r\n\t\t}.play(fadeTime:4);\r\n\t};\r\n\r\n\t{\r\n\t\targ min=0.2, max=1;\r\n\t\tvar sig;\r\n\t\tsig = LFDNoise1.kr(\r\n\t\t\tLFNoise1.kr(8!2).exprange(min,max)\r\n\t\t).unipolar(1);\r\n\r\n\t\t/*\r\n\t\tuncomment this line to control wavetable\r\n\t\tinterpolation with horizontal mouse position\r\n\t\tinstead of a noise generator\r\n\t\t*/\r\n\t\t//sig = MouseX.kr!2;\r\n\r\n\t\tSendReply.kr(Impulse.kr(40), '/mouse', sig);\r\n\t\t0\r\n\t}.play(target:h);\r\n\r\n\ts.sync;\r\n\r\n\tOSCdef(\\mouse, {\r\n\t\targ msg;\r\n\r\n\t\t/*\r\n\t\twhen SendReply sends a value to the language,\r\n\t\tuse that value as an interpolation parameter\r\n\t\tand update the wavetables from which the Osc\r\n\t\tUGen is reading\r\n\t\t*/\r\n\r\n\t\t//modify values\r\n\t\t~i0.waveFill({\r\n\t\t\targ x, val, i;\r\n\t\t\t~interpFn.(~wt0[i], ~wt1[i], msg[3])\r\n\t\t});\r\n\t\t~i1.waveFill({\r\n\t\t\targ x, val, i;\r\n\t\t\t~interpFn.(~wt1[i], ~wt2[i], msg[4])\r\n\t\t});\r\n\r\n\t\t//dynamically update Buffers\r\n\t\t~i0Buf.setn(0, ~i0.asWavetable);\r\n\t\t~i1Buf.setn(0, ~i1.asWavetable);\r\n\t}, '/mouse').permanent_(true);\r\n})\r\n)\r\n\r\n//watch:\r\ns.scope;\r\n/*(adjust horizontal slider on scope window\r\nas needed to stabilize waveform view)\r\n*/\r\n\r\n\r\n//change frequency\r\n~synths.do(_.set(\\freq, 50));\r\n\r\n//the two Synths can be changed independently\r\n(\r\n~synths[0].set(\\freq, 60);\r\n~synths[1].set(\\freq, 106, \\amp, 0.05);\r\n)\r\n\r\n//fade out\r\n~synths.do(_.set(\\gate, 0));",
   "is_private" : null,
   "id" : "1-5bu"
}
