{
   "description" : "ComparFeedback implements an FM feedback matrix based on the ComplexRes UGen. The number of nodes can be set when defining the synthesis engine. ComparFeedback implements a superset of the Compar system (see Compar.scd in this folder).\r\nThis code example accompanies the paper \"Dynamic FM synthesis using a network of complex resonator filters\" by Julian Parker and Till Bovermann. \r\nSee http://tai-studio.org/index.php/projects/complexres/ for details.",
   "ancestor_list" : [],
   "name" : "ComparFeedback",
   "author" : "LFSaw",
   "id" : "1-4V7",
   "is_private" : null,
   "code" : "(\r\nq = q ? ();\r\nq.numOscs = 10;\r\nq.numChans = 2;\r\n)\r\n\r\n(\r\n// careful! the system uses AudioIn as input, feedback might cause serious trouble, possibly use headphones unless you know what you're doing.\r\n\r\n\r\nNdef(\\comparMatrix, {\r\n\tvar numChans = q.numChans;\r\n\t\r\n\tvar in = LeakDC.ar(SoundIn.ar(\\in.kr));\r\n\tvar filterIn, feedbacks, filterOut, dst;\r\n\tvar ctlLag = \\ctlLag.kr(0.1);\r\n\tvar numOscs = q.numOscs;\r\n\t\r\n\r\n\tvar preAmp = \\preAmp.kr(0.1, ctlLag);\r\n\tvar postAmp = \\postAmp.kr(0.1, ctlLag);\r\n\tvar dryAmp = \\dryAmp.kr(1, ctlLag);\r\n\tvar filterWet = \\filterWet.kr(0.9, ctlLag);\r\n\tvar reverbWet = \\reverbWet.kr(0.9, ctlLag);\r\n\r\n\tvar freqs = \\freqs.kr({|i| 500}!numOscs);\r\n\tvar modParams = \\modParams.kr({|i|   0}!(numOscs**2)).clump(numOscs).postln;\r\n\tvar amps     = \\amps    .kr(0!numOscs);\r\n\tvar decays = \\decays.kr(0.01!numOscs);\r\n\r\n\tvar oscs;\r\n\tvar tmpOsc;\r\n\r\n\r\n\t\r\n\t// FM network\r\n\tfeedbacks = LocalIn.ar(numOscs);\r\n\r\n\tfilterIn = preAmp * in;\r\n\toscs = freqs.inject([], {|oscArray, freq, i|\r\n\t\ttmpOsc = ComplexRes.ar(filterIn, \r\n\t\t\tfreq\r\n\t\t\t+ oscArray.inject(0, {|sum, osc, j| \r\n\t\t\t\t// modulators from already instantiated oscs\r\n\t\t\t\tsum + (feedbacks[j] * modParams[i][j]) })\r\n\t\t\t+ (numOscs - 1 - Array.iota(numOscs - (i))).inject(0, {|sum, g| \r\n\t\t\t\t// modulators from to be instantiated oscs\r\n\t\t\t\tsum + (feedbacks[g] * modParams[i][g]) }),\r\n\t\t\tdecays[i]\r\n\t\t);\r\n\t\toscArray ++ tmpOsc;\r\n\t}); // end inject\r\n\r\n\tLocalOut.ar(oscs); // feedback is pre-\"fader\"\r\n\tfilterOut = oscs * amps * postAmp;\r\n\t// end filter\r\n\r\n\t// dryWet\r\n\tdst = SelectX.ar(filterWet, [\r\n\t\tOnePole.ar(dryAmp * in, \\lpCoeff.kr(0.3, 0.1)), \r\n\t\tpostAmp * LeakDC.ar(filterOut)\r\n\t]);\r\n\r\n\t// Compressor\r\n\tdst = Compander.ar(dst,dst,\r\n\t\tthresh: \\compThresh.kr(0.5,0.1),\r\n\t\tslopeBelow: 1 ,\r\n\t\tslopeAbove: \\compRatio.kr(0.3, 0.1),\r\n\t\tclampTime: 0.0001,\r\n\t\trelaxTime: 0.1\r\n\t);\r\n\r\n\t// reverb, channel spreading + gp limiting\r\n\tLimiter.ar(\r\n\t\tSelectX.ar(reverbWet, [\r\n\t\t\tSplayAz.ar(numChans, dst), \r\n\t\t\tAdCVerb.ar(0.1 * dst, 10, nOuts: numChans)\r\n\t\t])\r\n\t);\r\n})\r\n)\r\n\r\n(\r\n// control range specifications\r\nSpec.add(\\freq0, [1, 20000, \\exp]);\r\nSpec.add(\\freq1, \\freq0);\r\nSpec.add(\\freq2, \\freq0);\r\nSpec.add(\\in, [0, 2, \\lin, 1, 1]);\r\nSpec.add(\\filterWet, [0, 1]);\r\nSpec.add(\\reverbWet, [0, 1]);\r\nSpec.add(\\preAmp, [0.5, 5, \\exp]);\r\nSpec.add(\\dryAmp, [0.5, 5, \\exp]);\r\nSpec.add(\\postAmp, [0.5, 50, \\exp]);\r\nSpec.add(\\lpCoeff, [0, 1]);\r\n)\r\n\r\n\r\n(\r\n// the standard Ndef gui and an example preset. \r\n// to hear something, press play.\r\n\r\nNdef('comparMatrix').set('preAmp', 5.0, 'reverbWet', 0.036082474226804, 'postAmp', 7.7229139013412, 'filterWet', 1.0, 'compThresh', 0.11354325669618, 'compRatio', 1.0642438515127);\r\nNdef('comparMatrix').setn('amps', [ 0.0, 0.0, 0.0625, 0.8875, 0.0, 0.0, 0.2125, 0.275, 0.05, 0.0625 ], 'modParams', [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8697.0, 0, 0, 0, 2275.0, 728.0, 6045.0, 0, 0, 0.0, 169.0, 104.0, 0, 156.0, 104.0, 13.0, 39.0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5863.0, 0, 0, 0, 0, 741.0, 0, 0, 0, 9997.0, 3900.0, 0, 0, 9997.0, 0.0, 9997.0, 0.0, 0, 0, 6097.0, 6097.0, 0, 0, 7163.0, 0.0, 0.0, 0, 0, 0, 9997.0, 9100.0, 0, 0, 117.0, 611.0, 13.0, 3887.0 ], 'decays', [ 0.01, 0.01, 0.01, 0.55, 0.65, 1.8, 0.72, 2.97, 0.18, 0.26 ], 'freqs', [ 500, 500, 1537.5, 58.5, 500, 500, 179.5, 500.6, 365.7, 305.8 ]);\r\n\r\nNdef(\\comparMatrix).gui;\r\nNdef(\\comparMatrix).setn(\\modParams, 0!(q.numOscs**2));\r\n\r\n// GUI\r\n(\r\nvar specs = (\r\n\tfreqs: [0, 10000, \\lin, 0.1].asSpec,\r\n\tmodParams: [0, 10000, \\lin, 13].asSpec,\r\n\tin: [0, 3, \\lin, 1].asSpec,\r\n\tdecays: [0.01, 4, \\lin, 0.01].asSpec,\r\n);\r\nvar modParams     = Ndef(\\comparMatrix).get(\\modParams).clump(q.numOscs);\r\nvar freqState = Ndef(\\comparMatrix).get(\\freqs);\r\nvar ampState     = Ndef(\\comparMatrix).get(\\amps);\r\nvar decayState     = Ndef(\\comparMatrix).get(\\decays);\r\n\r\n\r\nvar colWidth = 40;\r\nvar knobHeight = 50;\r\nvar idxKnobColors   = [\r\n\t// upper right area\r\n\t[Color.gray(0.8), Color.blue, blend(Color.white, Color.blue, 0.5)],\r\n\t[Color.gray(0.8), Color.blue, blend(Color.white, Color.blue, 0.2)],\r\n\t// lower left area\r\n\t[Color.gray(0.8), Color.red, blend(Color.white, Color.red, 0.5)],\r\n\t[Color.gray(0.8), Color.red, blend(Color.white, Color.red, 0.2)]\r\n];\t\r\nvar bgColors = [\r\n\tColor.gray(0.8), Color.gray(1),\r\n\tColor.gray(0.6), Color.gray(0.8), \r\n];\r\nq.win = Window.new(\"ComparFeedback\", Rect(100, 100, (q.numOscs+5) * colWidth, 800)).decorate.front;\r\n\r\n/////////// INDEXES\r\n\r\nStaticText(q.win, Rect(10, 10, q.numOscs * (colWidth + 5) + 150, 20)).string_(\"-- modulation \");\r\nq.win.view.decorator.nextLine;\r\nq.higherAmpSliders = q.numOscs.collect{|i|\r\n\tvar slider;\r\n\t//(i+1).do{|j|\r\n\tq.numOscs.do{|j|\r\n\t\tvar ez;\r\n\t\t\r\n\t\tez = EZKnob(q.win, Rect(25, 25, colWidth, knobHeight), \r\n\t\t\tcontrolSpec: specs[\\modParams],\r\n\t\t\tinitAction: true,\r\n\t\t\tinitVal: modParams[i][j].postln\r\n\t\t)\r\n\t\t.action_{|knob| \r\n\t\t\tmodParams[i][j] = knob.value;\r\n\t\t\tNdef(\\comparMatrix).setn(\\modParams, modParams.flat);\r\n\t\t};\r\n\t\tez.knobView.mode_(\\vert);\r\n\t\t((j) > i).if({\r\n\t\t\tez.setColors(knobColors: idxKnobColors[j%2]);\r\n\t\t\tez.setColors(background: bgColors     [j%2]);\r\n\t\t}, {\r\n\t\t\tez.setColors(knobColors: idxKnobColors[j%2 + 2]);\r\n\t\t\tez.setColors(background: bgColors     [j%2 + 2]);\r\n\t\t});\r\n\r\n\t\t(i == j).if{\r\n\t\t\tez.knobView.color_([Color.gray, Color.blue, Color.green]);\r\n\t\t};\r\n\t};\r\n\tslider = EZSlider(q.win, Rect(0, 0, 150, knobHeight * 0.5), \r\n\t\tlabel: i, \r\n\t\tlayout: 'horz',\r\n\t\tnumberWidth: 0,\r\n\t\tlabelWidth: 10,\r\n\t\tinitVal: ampState[i]\r\n\t)\r\n\t.action_{|slider|\r\n\t\tampState[i] = slider.value;\r\n\t\tq.lowerAmpSliders[i].value = slider.value;\r\n\t\tNdef(\\comparMatrix).setn(\\amps, ampState);\r\n\t};\r\n\tslider.setColors(background: bgColors[i%2 + 2]);\r\n\r\n\tq.win.view.decorator.nextLine;\r\n\r\n\t// return\r\n\tslider\r\n};\r\nq.win.view.decorator.nextLine;\r\n\r\n\r\n/////////// FREQS\r\nStaticText(q.win, Rect(10, 10, q.numOscs * (colWidth + 5), 20)).string_(\"-- freqs ----------\");\r\n\r\nq.win.view.decorator.nextLine;\r\nq.numOscs.do{|i|\r\n\tvar ez;\r\n\tez = EZKnob(q.win, Rect(0, 0, colWidth, knobHeight), \r\n\t\tcontrolSpec: specs[\\freqs],\r\n\t\tinitAction: true,\r\n\t\tinitVal: freqState[i]\r\n\r\n\t)\r\n\t.action_{|knob|\r\n\t\tfreqState[i] = knob.value;\r\n\t\tNdef(\\comparMatrix).setn(\\freqs, freqState);\r\n\t};\r\n\tez.knobView.mode_(\\vert);\r\n\tez.setColors(background: bgColors[i%2 + 2]);\r\n\r\n};\r\n\r\n/////////// DECAYS\r\nq.win.view.decorator.nextLine;\r\nStaticText(q.win, Rect(10, 10, q.numOscs * (colWidth + 5), 20)).string_(\"-- decays ----------\");\r\nq.win.view.decorator.nextLine;\r\nq.decays = q.numOscs.collect{|i|\r\n\tvar ez; \r\n\tez = EZKnob(q.win, Rect(25, 25, colWidth, knobHeight), \r\n\t\tcontrolSpec: specs[\\decays],\r\n\t\tinitVal: decayState[i]\r\n\t)\r\n\t.action_{|knob|\r\n\t\tdecayState[i] = knob.value;\r\n\t\tNdef(\\comparMatrix).setn(\\decays, decayState);\r\n\t};\r\n\tez.knobView.mode_(\\vert);\r\n\tez.setColors(background: bgColors[i%2 + 2]);\r\n};\r\n\r\n\r\nq.win.view.decorator.nextLine;\r\n\r\n\r\nq.lowerAmpSliders = q.numOscs.collect{|i|\r\n\tvar ez; \r\n\t\r\n\tez = EZSlider(q.win, Rect(0, 0, colWidth, 150), \r\n\t\tlabel: i, \r\n\t\tlayout: 'vert',\r\n\t\tinitVal: ampState[i]\r\n\t)\r\n\t.action_{|slider|\r\n\t\tampState[i] = slider.value;\r\n\t\tNdef(\\comparMatrix).setn(\\amps, ampState);\r\n\t\tq.higherAmpSliders[i].value = slider.value;\r\n\t};\r\n\tez.setColors(background: bgColors[i%2 + 2]);\r\n}\r\n)\r\n\r\n\r\n///////////// set random values ///////////\r\nNdef(\\comparMatrix).setn(\\decays, {1.0.rand}!10)\r\nNdef(\\comparMatrix).setn(\\freqs, {200.rand}!10)\r\nNdef(\\comparMatrix).setn(\\amps, {1.0.rand}!10)\r\nNdef(\\comparMatrix).setn(\\modParams, {10000.0.rand}!100)",
   "labels" : [
      "ndef",
      "resonator",
      "complexres",
      "plugin",
      "recursive network"
   ]
}
