{
   "description" : "FM Matrix Synth, now with 1to1 feedback!",
   "ancestor_list" : [],
   "author" : "LFSaw",
   "name" : "An FM Matrix Synth with 1to1 feedback.",
   "code" : "///////////////////////////////////////////\r\n// a basic FM Matrix Synth, now with 1to1 feedback.\r\ns.boot;\r\nq = q ? ();\r\nq.numOscs = 10;\r\n\r\n(\r\n\r\nNdef(\\fmMatrix, {\r\n\tvar numOscs = q.numOscs;\r\n\t\r\n\tvar oscs;\r\n\tvar freqs = \\freqs.kr({|i| 500}!numOscs);\r\n\tvar modIndex = \\modIndex.kr({|i|   0}!(numOscs**2)).clump(numOscs).postln;\r\n\tvar amps     = \\amps    .kr(0!numOscs);\r\n\tvar tmpOsc;\r\n\tvar feedbacks = LocalIn.ar(numOscs);\r\n\r\n\t\r\n\toscs = freqs.inject([], {|oscArray, freq, i|\r\n\t\ttmpOsc = SinOsc.ar(\r\n\t\t\tfreq\r\n\t\t\t+ oscArray.inject(0, {|sum, osc, j| \r\n\t\t\t\tsum \r\n\t\t\t\t// modulators from already instantiated oscs\r\n\t\t\t\t//+ (osc * modIndex[i][j] * freq)\r\n\t\t\t\t+ (feedbacks[j] * modIndex[i][j] * freq)\r\n\t\t\t})\r\n\t\t\t+ (numOscs - 1 - Array.iota(numOscs - (i))).postln.inject(0, {|sum, g| \r\n\t\t\t\tsum \r\n\t\t\t\t// modulators from to be instantiated oscs\r\n\t\t\t\t+ (feedbacks[g] * modIndex[i][g] * freq)\r\n\t\t\t})\r\n\t\t\t// self modulation\r\n\t\t\t//modIndex[i][i]\r\n\t\t);\r\n\t\t\r\n\t\toscArray ++ tmpOsc;\r\n\t}); // end inject\r\n\t\r\n\t\r\n\tLocalOut.ar(oscs);\r\n\t//SplayAz.ar(8, oscs * amps, center: LFSaw.kr(0.01));\r\n\tSplay.ar(oscs * amps);\r\n})\r\n)\r\n\r\n// the standard Ndef gui. to hear something, press play.\r\nNdef(\\fmMatrix).gui;\r\nNdef(\\fmMatrix).setn(\\modIndex, 0!(q.numOscs**2));\r\n\r\n\r\n// \r\n(\r\nvar specs = (\r\n\tfreqs: [0, 10000, \\lin, 0.1].asSpec,\r\n\tmodIndex: [0, 4, \\lin, 0].asSpec;\r\n);\r\nvar modIndex     = Ndef(\\fmMatrix).get(\\modIndex).clump(q.numOscs);\r\nvar freqState = Ndef(\\fmMatrix).get(\\freqs);\r\nvar ampState     = Ndef(\\fmMatrix).get(\\amps);\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(\"FM Matrix\", Rect(100, 100, (q.numOscs+5) * colWidth, 800)).front;\r\nq.win.addFlowLayout;\r\n/////////// INDEXES\r\n\r\nStaticText(q.win, Rect(10, 10, q.numOscs * (colWidth + 5) + 150, 20)).string_(\"-- modulation index \");\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[\\modIndex],\r\n\t\t\tinitAction: true,\r\n\t\t\tinitVal: modIndex[i][j]\r\n\t\t)\r\n\t\t.action_{|knob| \r\n\t\t\tmodIndex[i][j] = knob.value;\r\n\t\t\tNdef(\\fmMatrix).setn(\\modIndex, modIndex.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(\\fmMatrix).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(\\fmMatrix).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/////////// AMPS\r\nq.win.view.decorator.nextLine;\r\nStaticText(q.win, Rect(10, 10, q.numOscs * (colWidth + 5), 20)).string_(\"-- amps ----------\");\r\nq.win.view.decorator.nextLine;\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(\\fmMatrix).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)",
   "id" : "1-4SM",
   "is_private" : null,
   "labels" : [
      "fm",
      "synth",
      "matrix"
   ]
}
