{
   "labels" : [
      "midi",
      "step sequencer",
      "sequencer"
   ],
   "code" : "(\r\n// ................ init\r\n\r\nvar numRows = 7*2; // <-- change size here!!!\r\nvar numCols = 3*16; // <-- and here!!!\r\n\r\nvar window, decorator, matrix, buttonMatrix, leds;\r\nvar synthName, scale, octave, root, transp, sustain, amp;\r\nvar player, playButton, control, controlView, sendType;\r\nvar midiDevice, popMidiDevice, midiChannel;\r\nvar synthList = [\\ping, \\sine];\r\n\r\n\r\nvar screen = ();\r\nvar layout = ();\r\nvar pointSize = 20;\r\n\r\ns = Server.default;\r\ns.boot;\r\n\r\nscreen.width = Window.screenBounds.width;\r\nscreen.height = Window.screenBounds.height;\r\n\r\n\r\n// layout stuff\r\nlayout.width = numCols * pointSize;\r\nlayout.height = numRows * pointSize;\r\nlayout.centerPoint = (\r\n\t( (screen.width/2) - (layout.width/2) ) @\t\r\n\t( (screen.height/2) - (layout.height/2))\r\n);\r\nlayout.button = ((layout.width/numCols)@(layout.height/numRows));\r\nlayout.center = Rect( 0, 200, layout.width, layout.height + (layout.button.y+10));\r\n\r\n// window\r\nwindow = Window(\"MATRIX\", layout.center, resizable:false, border:true).front;\r\nwindow.view.background_(Color.gray(0.5));\r\ndecorator = window.addFlowLayout(0@0, 0@0);\r\n\r\n// make arrays\r\nmatrix = Array2D.new(numRows, numCols);\r\nbuttonMatrix = Array2D.new(numRows, numCols);\r\n\r\n// makeLEDS\r\n\r\nleds = numCols.collect{ |i|\r\n\t\tButton(window, Rect(0, 0, layout.button.x, layout.button.y))\r\n\t\t\t.states_([\r\n\t\t\t\t[\"\", Color.black, Color.grey],\r\n\t\t\t\t[\"\", Color.black, Color.green(0.7)]\r\n\t\t\t])\r\n};\r\n\r\nStaticText(window, layout.width@10); // separator...\r\n\r\n// makebuttons\r\nbuttonMatrix.rowsDo{ |row, rowCount|\r\n\trow.do{ |item, i|\r\n\t\tmatrix.put(rowCount, i, 0);\t// init matrix with 0s\r\n\t\tbuttonMatrix.put(rowCount, i, Button(window, Rect(0, 0, layout.button.x, layout.button.y))\r\n\t\t\t.states_([\r\n\t\t\t\t[\"\", Color.black, Color.white],\r\n\t\t\t\t[\"\", Color.white, Color.black]\r\n\t\t\t])\r\n\t\t\t.action_({ |v|\r\n\t\t\t\tmatrix.put(rowCount, i, v.value);\r\n\t\t\t});\r\n\t\t)\r\n\t};\r\n};\r\n\r\n/************************* PLAYER **************************/\r\n\r\nsynthName = \\ping;\r\nscale = nil;\r\noctave = 4;\r\nroot = 0;\r\ntransp = 0;\r\nsustain = 0.2;\r\namp = 0.1;\r\n\r\nplayer = Task({\r\n\tinf.do{ |counter|\r\n\t\tvar col = counter % numCols;\r\n\t\t\r\n\t\t// main func\r\n\t\tmatrix.colAt(col).do{ |item, pos|\r\n\t\t\tvar ipos = (pos - (numRows-1)).abs;\r\n\r\n\t\t\tif(item != 0){ \r\n\t\t\t\tif(sendType == 0){\r\n\t\t\t\t\t(\r\n\t\t\t\t\t\tinstrument:\tsynthName,\r\n\t\t\t\t\t\tscale:\t\tscale,\r\n\t\t\t\t\t\tamp:\t\t\tamp,\r\n\t\t\t\t\t\tsustain:\t\tsustain,\r\n\t\t\t\t\t\toctave:\t\toctave,\r\n\t\t\t\t\t\troot:\t\troot,\r\n\t\t\t\t\t\tctranspose:\ttransp,\r\n\t\t\t\t\t\tdegree:\t\tipos\r\n\t\t\t\t\t).play;\r\n\t\t\t\t};\r\n\t\t\t\tif(sendType == 1){\r\n\t\t\t\t\t(\r\n\t\t\t\t\t\ttype:\t\t\\midi,\r\n\t\t\t\t\t\tmidiout:\t\tmidiDevice,\r\n\t\t\t\t\t\tscale:\t\tscale,\r\n\t\t\t\t\t\tamp:\t\t\tamp,\r\n\t\t\t\t\t\tsustain:\t\tsustain,\r\n\t\t\t\t\t\toctave:\t\toctave,\r\n\t\t\t\t\t\troot:\t\troot,\r\n\t\t\t\t\t\tctranspose:\ttransp,\r\n\t\t\t\t\t\tdegree:\t\tipos\r\n\t\t\t\t\t).play;\r\n\t\t\t\t};\r\n\t\t\t\tif(sendType == 2){\r\n\t\t\t\t\t(\r\n\t\t\t\t\t\tinstrument:\tsynthName,\r\n\t\t\t\t\t\tscale:\t\tscale,\r\n\t\t\t\t\t\tamp:\t\t\tamp,\r\n\t\t\t\t\t\tsustain:\t\tsustain,\r\n\t\t\t\t\t\toctave:\t\toctave,\r\n\t\t\t\t\t\troot:\t\troot,\r\n\t\t\t\t\t\tctranspose:\ttransp,\r\n\t\t\t\t\t\tdegree:\t\tipos\r\n\t\t\t\t\t).play;\r\n\r\n\t\t\t\t\t(\r\n\t\t\t\t\t\ttype:\t\t\\midi,\r\n\t\t\t\t\t\tmidiout:\t\tmidiDevice,\r\n\t\t\t\t\t\tscale:\t\tscale,\r\n\t\t\t\t\t\tamp:\t\t\tamp,\r\n\t\t\t\t\t\tsustain:\t\tsustain,\r\n\t\t\t\t\t\toctave:\t\toctave,\r\n\t\t\t\t\t\troot:\t\troot,\r\n\t\t\t\t\t\tctranspose:\ttransp,\r\n\t\t\t\t\t\tdegree:\t\tipos\r\n\t\t\t\t\t).play;\r\n\t\t\t\t\t\r\n\t\t\t\t};\r\n\t\t\t\t\r\n\t\t\t};\r\n\t\t};\r\n\r\n\t\t// LED func\r\n\t\t\r\n\t\tif(col != 0){\r\n\t\t\t{\r\n\t\t\t\tleds[col].value_(1);\t// turn on actual\r\n\t\t\t\tleds[col-1].value_(0);\t// turn off last\r\n\t\t\t}.defer;\r\n\t\t\t\r\n\t\t}{\r\n\t\t\t{\r\n\t\t\t\tleds[col].value_(1);\r\n\t\t\t\tleds.last.value_(0);\r\n\t\t\t}.defer;\r\n\t\t};\r\n\r\n\t\t0.25.wait;\t\t// quarter notes\r\n\t}\r\n});\r\n\r\n\r\n/********************* CONTROL WINDOW ***************************/\r\n\r\ncontrol = ();\r\ncontrol.width = 1024;\r\ncontrol.height = 200;\r\n\r\ncontrol.window = Window(\"CONTROLS\", Rect(\r\n\t0,\r\n\t(screen.height - control.height - 22),\r\n\tcontrol.width,\r\n\tcontrol.height),\r\n\tresizable:false,\r\n\tborder:true\r\n).front;\r\n// control.window.userCanClose_(false);\r\n\r\ncontrolView = control.window.addFlowLayout(10@10, 10@0);\r\n\r\ncontrol.button = ();\r\ncontrol.button.width = 110;\r\ncontrol.button.height = 20;\r\ncontrol.button.dim = (control.button.width@control.button.height);\r\n\r\n// LABELS\r\n[\"SCALE\", \"TUNING\", \"SYNTH\", \"ROOT\", \"OCTAVE\", \"TRANSP\"].do{ |label|\r\n\tStaticText(control.window, control.button.dim)\r\n\t\t.align_(\\center)\r\n\t\t.string_(label);\r\n};\r\n\r\nStaticText(control.window, (control.button.dim.x*2.2)@(control.button.dim.y))\r\n\t.align_(\\center)\r\n\t.string_(\"TEMPO\");\r\n\r\n\r\n\r\n// scale\r\ncontrol.chooseScale = PopUpMenu(control.window, control.button.dim)\r\n\t.items_(ScaleInfo.scales.keys.asArray.sort)\r\n\t.action_({|menu|\r\n\t\tscale = ScaleInfo.at(menu.item);\r\n\t\tpostf(\"\\t*** Using scale: ---> %\\n\", menu.item.asString);\r\n\t});\r\ncontrol.chooseScale.valueAction_(0);\r\n\r\n// tuning\r\ncontrol.chooseTuning = PopUpMenu(control.window, control.button.dim)\r\n\t.items_(TuningInfo.tunings.keys.asArray.sort)\r\n\t.action_({|menu|\r\n\t\tscale.tuning_(menu.item);\r\n\t\tpostf(\"\\t*** Using tuning: ---> %\\n\", menu.item.asString);\r\n\t});\r\ncontrol.chooseScale.valueAction_(0);\r\n\r\n// synth\r\ncontrol.chooseSynth = PopUpMenu(control.window, control.button.dim)\r\n\t.items_(synthList)\r\n\t.action_({|menu|\r\n\t\tsynthName = menu.item;\r\n\t\tpostf(\"\\t*** Using synth: ---> %\\n\", menu.item.asString);\r\n\t});\r\n\r\n// root\r\nEZSlider(control.window, control.button.dim, controlSpec: [0,24, \\linear, 1].asSpec, initVal: root, numberWidth:18, gap:0@0)\r\n\t.action_({ |v|\r\n\t\troot = v.value;\r\n\t});\r\n\r\n\r\n// octave\r\nEZSlider(control.window, control.button.dim, controlSpec: [1,8, \\linear, 1].asSpec, initVal: octave, numberWidth:12, gap:0@0)\r\n\t.action_({ |v|\r\n\t\toctave = v.value;\r\n\t});\r\n\r\n// transpose\r\nEZSlider(control.window, control.button.dim, controlSpec: [-24,24, \\linear, 1].asSpec, initVal: transp, numberWidth:22, gap:0@0)\r\n\t.action_({ |v|\r\n\t\ttransp = v.value;\r\n\t});\r\n\r\n\r\n// tempo\r\nEZSlider(control.window,\r\n\tcontrol.button.dim.x * 2.3 @ control.button.dim.y,\r\n\tcontrolSpec: [40, 240, \\linear, 0.1].asSpec, initVal: 60, numberWidth:35, gap:0@0)\r\n\t.action_({ |v|\r\n\t\tTempoClock.default.tempo_( v.value / 60);\r\n\t})\r\n\t.valueAction_(60);\r\n\r\n/**********************Separator************************/\r\nStaticText(control.window, control.width@10);\r\n/*******************************************************/\r\n\r\n// random scale\r\nButton(control.window, control.button.dim)\r\n\t.states_([[\"rand scale\", Color.white, Color.magenta(0.5)]])\r\n\t.action_({\r\n\t\tcontrol.chooseScale.valueAction_(\r\n\t\t\tcontrol.chooseScale.items.indexOf(control.chooseScale.items.choose)\r\n\t\t);\r\n\t});\r\n\r\n// random tuning\r\nButton(control.window, control.button.dim)\r\n\t.states_([[\"rand tuning\", Color.white, Color.magenta(0.5)]])\r\n\t.action_({\r\n\t\tcontrol.chooseTuning.valueAction_(\r\n\t\t\tcontrol.chooseTuning.items.indexOf(control.chooseTuning.items.choose)\r\n\t\t)\r\n\t});\r\n\r\n// random synth\r\nButton(control.window, control.button.dim)\r\n\t.states_([[\"rand synth\", Color.white, Color.magenta(0.5)]])\r\n\t.action_({\r\n\t\tcontrol.chooseSynth.valueAction_(\r\n\t\t\tcontrol.chooseSynth.items.indexOf(control.chooseSynth.items.choose)\r\n\t\t)\r\n\t});\r\n\r\n// randomize matrix\r\nButton(control.window,  control.button.dim )\r\n\t.states_([[\"rand matrix\", Color.white, Color.cyan(0.5)]])\r\n\t.action_({\r\n\t\tbuttonMatrix.colsDo{ |col, pos|\r\n\t\t\tcol.do{ |item, i|\r\n\t\t\t\tbuttonMatrix.at(i, pos).valueAction_([1,0].wchoose([1,10].normalizeSum));\r\n\t\t\t}\r\n\t\t}\r\n\t});\r\n\r\n// clear matrix\r\nButton(control.window,  control.button.dim )\r\n\t.states_([[\"clear matrix\", Color.white, Color.cyan(0.5)]])\r\n\t.action_({\r\n\t\tbuttonMatrix.colsDo{ |col, pos|\r\n\t\t\tcol.do{ |item, i|\r\n\t\t\t\tbuttonMatrix.at(i, pos).valueAction_(0);\r\n\t\t\t}\r\n\t\t}\r\n\t});\r\n\r\nEZSlider(control.window,\r\n\tcontrol.button.dim.x * 3.4 @ control.button.dim.y,\r\n\t\"legato (dur):   \",\r\n\tcontrolSpec: [0.001, 2, \\linear, 0.01, 0.8].asSpec,\r\n\tinitVal: 0.8, labelWidth: control.button.dim.x + 10, numberWidth:35, gap:0@0)\r\n\t.action_({ |v|\r\n\t\tsustain = v.value;\r\n\t})\r\n\t.valueAction_(0.8);\r\n\r\nStaticText(control.window, control.width@10); // separator...\r\n\r\n// play / pause\r\nplayButton = Button(control.window, (control.width / 2) - 10 @ control.button.dim.y)\r\n\t.states_([[\"PLAY\", Color.black, Color.green], [\"PAUSE\", Color.black, Color.yellow]])\r\n\t.action_({ |v|\r\n\t\tif(v.value == 1){\r\n\t\t\tplayer.play;\r\n\t\t}{\r\n\t\t\tplayer.pause;\r\n\t\t}\r\n\t\r\n\t});\r\n\r\n// stop\r\nButton(control.window, (control.width / 2) - 20 @ control.button.dim.y)\r\n\t.states_([[\"STOP\", Color.black, Color.red]])\r\n\t.action_({\r\n\t\tplayer.stop;\r\n\t\tplayer.reset;\r\n\t\tplayButton.value_(0);\r\n\t\tleds.do{ |led| led.value_(0) };\r\n\t});\r\n\r\nStaticText(control.window, control.width@20); // separator\r\n\r\n// sendmidi\r\nsendType = 0;\r\n\r\nStaticText(control.window, control.button.dim).string_(\"Send to:\").align_(\\center);\r\nPopUpMenu(control.window, control.button.dim)\r\n\t.items_([\"SC Synth\", \"MIDI\", \"Both\"])\r\n\t.action_({ |menu|\r\n\t\tsendType = menu.value;\r\n\t\tif(menu.value != 0){\r\n\t\t\tMIDIClient.init;\r\n\t\t\tpopMidiDevice.items_(\r\n\t\t\t\tMIDIClient.destinations.collect{ |aDevice|\r\n\t\t\t\t\taDevice.name.asString\r\n\t\t\t\t}\r\n\t\t\t);\r\n\t\t\tmidiDevice = MIDIOut(0);\r\n\t\t}{\r\n\t\t\tpopMidiDevice.items_([\"sending to SC only\"]);\r\n\t\t};\r\n\t});\r\n\r\n\r\n// midi device\r\nmidiDevice = nil;\r\n\r\nStaticText(control.window, control.button.dim).string_(\"MIDI Device:\").align_(\\center);\r\npopMidiDevice = PopUpMenu(control.window, (control.button.dim.x/3)*5 @ control.button.dim.y)\r\n\t.items_([\"sending to SC only\"])\r\n\t.action_({ |menu|\r\n\t\tmidiDevice = MIDIOut(menu.value);\r\n\t\tpostf(\"\\n\\t*** Usind MIDI Device: % ---> %\",\r\n\t\t\tMIDIClient.destinations[menu.value].device,\r\n\t\t\tMIDIClient.destinations[menu.value].name\r\n\t\t);\r\n\t});\r\n\r\n// midi channel\r\nmidiChannel = 0;\r\nStaticText(control.window, control.button.dim).string_(\"MIDI Channel:\").align_(\\center);\r\nPopUpMenu(control.window, control.button.dim.x/3 @ control.button.dim.y)\r\n\t.items_((0..15).collect{ |item| item.asString })\r\n\t.action_({ |menu|\r\n\t\tmidiChannel = menu.value;\r\n\t});\r\n\r\n// midi velocity\r\nStaticText(control.window, control.button.dim)\r\n\t.string_(\"Note Amplitude:\").align_(\\center);\r\nEZSlider(\r\n\t\tcontrol.window,\r\n\t\tcontrol.button.dim.x * 1.25 @ control.button.dim.y,\r\n\t\tnumberWidth:32,\r\n\t\tgap: 0@0\r\n\t)\r\n\t.action_({ |v|\r\n\t\tamp = v.value;\r\n\t})\r\n\t.valueAction_(0.2);\r\n\r\nStaticText(control.window, control.width@20); // separator ...\r\n\r\nButton(control.window, (control.width-20)@control.button.dim.y )\r\n\t.states_([[\"CLOSE ALL WINDOWS\", Color.black, Color.grey]])\r\n\t.action_({ window.close; control.window.close; player.stop; });\r\n\r\n// CmdPeriod.add({ window.close; control.window.close; });\r\n\r\n// ......................... SYNTHS\r\nSynthDef(synthList[0], { |out, freq=440, pan, amp=0.1, sustain=0.25|\r\n\tvar snd = SinOsc.ar(freq);\r\n\tsnd = snd * EnvGen.ar(Env.perc(0.01, sustain), doneAction:2);\r\n\tsnd = Pan2.ar(snd, pan);\r\n\tOffsetOut.ar(out, snd*amp);\r\n}).add;\r\n\r\nSynthDef(synthList[1], { |out, freq=440, pan, amp=0.1, sustain=0.25|\r\n\tvar snd = SinOsc.ar(freq);\r\n\tsnd = snd * EnvGen.ar(Env.sine(sustain), doneAction:2);\r\n\tsnd = Pan2.ar(snd, pan);\r\n\tOffsetOut.ar(out, snd*amp);\r\n}).add;\r\n\r\n)",
   "id" : "1-Up",
   "is_private" : null,
   "name" : "Step Sequencer with Midi/Scales/Tuning capabilities",
   "author" : "rukano",
   "description" : "Very old code I made in my first SuperCollider years. Changed some stuff to not use environment variables. Still works... but, yeah... spaghetti code ALL the way.\r\n\r\nDemo video @ Youtube: http://www.youtube.com/watch?v=PnfAOlA1dJA",
   "ancestor_list" : []
}
