Hi listAfter making the MIDIKeyboard the other day I started thinking about how to represent various microtuning systems graphically, but that's not easy. It's amazing how the visual structure of the keyboard is imprinted in one's consciousness.
I made various sketches, one of them being the typical grid. The grid is of course useful for many things, so I finished that idea and made a helpfile. The helpfile contains some sketches from the development of the class. I'd be interested in how other people suggest representing microtonality or polyrhythms, so all
comments would be of great interest. Oh, and it's OS X only, sorry. How is the SwingOSC doing now? Could this code be ported to SwingOSC just by defining which GUI platform the code should run? cheers thor
Attachment:
Grid.sc
Description: Binary data
{\rtf1\mac\ansicpg10000\cocoartf824\cocoasubrtf330
{\fonttbl\f0\fswiss\fcharset77 Helvetica-Bold;\f1\fnil\fcharset77 Monaco;\f2\fswiss\fcharset77 Helvetica;
}
{\colortbl;\red255\green255\blue255;\red0\green0\blue191;\red96\green96\blue96;\red191\green0\blue0;
\red0\green0\blue0;\red191\green0\blue0;\red96\green96\blue96;\red0\green115\blue0;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural
\f0\b\fs36 \cf0 Grid a simple GUI grid\
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
\f1\b0\fs18 \cf0 \
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural
\f2\fs24 \cf0 This GUI widget can be useful in various situations such as representing scales,\
step sequencers, etc. \
\
The grid is basically a graphical representation of 2 dimensional array of\
columns and rows. \
\
For example, here we have 2 columns with 3 rows: [[0, 1, 0], [1, 1 0]]\
(1 and 0 indicating the state of the node in that location)\
\
The getState and setState_ methods function according to the convention of \
passing (x, y), i.e. the horizontal first and then the vertical.\
\
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural
\f0\b \cf0 *new(window, bounds, columns, rows, border);\
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural
\f2\b0 \cf0 \
\f0\b window
\f2\b0 - A SCWindow in which the Grid will appear
\f0\b \
bounds
\f2\b0 - The Rect of the Grid
\f0\b \
columns
\f2\b0 - The number of columns in the grid (vertical lines)
\f0\b \
rows
\f2\b0 - The number of rows in the grid (horizontal lines)
\f0\b \
border
\f2\b0 - Boolean. Border around the grid.\
\f0\b \
\f2\b0 \
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural
\f0\b \cf0 nodeDownAction_
\f2\b0 - The function that mouse down on a node will trigger.\
\f0\b nodeTrackAction_
\f2\b0 - The function that dragging the cursor will trigger.\
\f0\b nodeUpAction_
\f2\b0 - The function that mouse up on a node will trigger.\
\f0\b \
clearGrid
\f2\b0 - sets all nodes on the grid to inactive.\
\
\f0\b setNodeShape_(string)
\f2\b0 - The shape of the node ("circle" or "square").\
\f0\b setBorder_(boolean)
\f2\b0 - border around the Grid.
\f1\fs18 \
\f0\b\fs24 setFillMode_(boolean)
\f2\b0 - Whether the nodes are filled with color or not.\
\f0\b setFillColor_(color)
\f2\b0 - The color of the filled nodes.\
\f0\b setTrackDrag_(boolean)
\f2\b0 - Returns the state of an individual node.\
\
\
\f0\b setNodeStates_(array)
\f2\b0 - Updates the grid from a 2 dimensional array of columns and rows.\
\f0\b getNodeStates
\f2\b0 - Returns 2 dimensional array representing the columns and rows.
\f0\b \
setNodeSize_(int)
\f2\b0 - The size of the nodes\
\f0\b setState_(row, column, state)
\f2\b0 - Sets the state of an individual node.\
\f0\b getState(row, column)
\f2\b0 - Returns the state of an individual node.\
\f0\b setBackgrDrawFunc_(func)
\f2\b0 - Allows Pen to draw behind the grid.\
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
\f1\fs18 \cf0 \
\
\
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
\cf2 Grid\cf0 .new\
\
\cf2 Grid\cf0 .new(bounds: \cf2 Rect\cf0 (20, 20, 600, 400), columns: 24, rows: 12, border:\cf2 false\cf0 )\
\
\cf2 Grid\cf0 .new(bounds: \cf2 Rect\cf0 (20, 20, 600, 20), columns: 24, rows: 1, border:\cf2 false\cf0 )\
\
\cf2 Grid\cf0 .new( bounds: \cf2 Rect\cf0 (20, 20, 460, 140), columns: 24, rows: 4, border:\cf2 true\cf0 );\
\
(\
\
w = \cf2 SCWindow\cf0 (\cf3 "Testing Grid"\cf0 , \cf2 Rect\cf0 (10, 500, 500, 212));\
\cf2 Grid\cf0 .new(w, bounds: \cf2 Rect\cf0 (20, 20, 460, 20), columns: 24, rows: 1, border:\cf2 false\cf0 );\
\cf2 Grid\cf0 .new(w, bounds: \cf2 Rect\cf0 (20, 60, 460, 20), columns: 24, rows: 1, border:\cf2 false\cf0 );\
\cf2 Grid\cf0 .new(w, bounds: \cf2 Rect\cf0 (20, 100, 460, 20), columns: 24, rows: 1, border:\cf2 false\cf0 );\
\cf2 Grid\cf0 .new(w, bounds: \cf2 Rect\cf0 (20, 140, 460, 20), columns: 24, rows: 1, border:\cf2 false\cf0 );\
\
w.front;\
)\
\
\
(\
\
w = \cf2 SCWindow\cf0 (\cf3 "Testing Grid"\cf0 , \cf2 Rect\cf0 (10, 500, 500, 212));\
a = \cf2 Grid\cf0 .new(w, bounds: \cf2 Rect\cf0 (20, 20, 460, 140), columns: 24, rows: 4, border:\cf2 false\cf0 );\
\
)\
\
a.setBackground_(\cf2 Color\cf0 .white);\
a.setBorder_(\cf2 true\cf0 )\
\
\cf4 // you can use either boolean\cf0 \
a.setState_(1,1,\cf2 true\cf0 )\
\cf4 // or integer (1 = true, 0 = false)\cf0 \
a.setState_(2,3,1)\
\
\cf4 // get state will return only integers\cf0 \
a.getState(1,3)\
a.getState(2,3)\
\
\cf4 // get the states of all nodes\cf0 \
b = a.getNodeStates\
\cf4 // or just the first row:\cf0 \
c = a.getNodeStates[0]\
\
\cf4 // clear the grid\cf0 \
a.clearGrid\
\cf4 // set it back to previous state\cf0 \
a.setNodeStates_(b)\
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
\cf5 \
\cf6 // the shape of the nodes\cf5 \
a.setNodeShape_(\cf7 "circle"\cf5 );\
a.setNodeShape_(\cf7 "square"\cf5 );\
\
a.setNodeSize_(8);\
\cf0 a.setNodeSize_(14);\
\
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
\cf4 // fillmode and color\cf0 \
a.setFillMode_(\cf2 true\cf0 );\
a.setFillColor_(\cf2 Color\cf0 .blue);\
a.setFillColor_(\cf2 Color\cf0 .white);\
\
a.setFillMode_(\cf2 false\cf0 );\
a.setBackground_(\cf2 Color\cf0 .clear);\
\
a.setTrackDrag_(\cf2 false\cf0 ); \cf4 // leaves a trail of active nodes when moved around the grid\cf0 \
\
\
\cf4 // or\cf0 \
a.gridNodes[0][3].setState_(\cf2 true\cf0 )\
a.refresh\
\
\
\
\
\
(\
\
w = \cf2 SCWindow\cf0 (\cf3 "Testing Grid"\cf0 , \cf2 Rect\cf0 (100, 500, 500, 212));\
a = \cf2 Grid\cf0 .new(w, bounds: \cf2 Rect\cf0 (20, 20, 460, 140), columns: 24, rows: 8, border:\cf2 true\cf0 )\
.setBackground_(\cf2 Color\cf0 .white)\
.setBorder_(\cf2 true\cf0 )\
.setNodeShape_(\cf3 "circle"\cf0 )\
.setNodeSize_(8)\
.setFillMode_(\cf2 true\cf0 )\
.setFillColor_(\cf2 Color\cf0 .new255(103, 148, 103))\
.nodeDownAction_(\{\cf2 arg\cf0 nodeloc; nodeloc.postln;\})\
.nodeTrackAction_(\{\cf2 arg\cf0 nodeloc; nodeloc.postln;\})\
.nodeUpAction_(\{\cf2 arg\cf0 nodeloc; nodeloc.postln\}); \
\
30.do(\{\
a.setState_(24.rand, 8.rand, 1); \cf4 // column, row, state\cf0 \
\});\
\
)\
\
\
b = a.getNodeStates\
\cf4 // clear the grid\cf0 \
a.clearGrid\
\cf4 // set it back to previous state\cf0 \
a.setNodeStates_(b)\
\
\
\
\
\
\cf4 /////////////////// some examples from the development of Grid\cf0 \
\
\cf4 // the synthdef used in the examples\cf0 \
(\
\cf2 SynthDef\cf0 (\cf8 \\pure\cf0 , \{\cf2 arg\cf0 freq=440, pan=0.0, vol=0.3, envdur=0.8;\
\cf2 var\cf0 signal, envArray, env;\
env = \cf2 EnvGen\cf0 .kr(\cf2 Env\cf0 .perc(0.01, envdur), doneAction:2); signal = \cf2 Pan2\cf0 .ar(\cf2 SinOsc\cf0 .ar(freq, 0, vol), pan) * env;\
\cf2 Out\cf0 .ar(0, signal);\
\}).load(s);\
)\
\
\cf4 ///////////// scales\cf0 \
\cf4 // here we have one octave of 19 equal tempered scale (19-TET)\
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
\cf0 \
(\
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
\cf2 var\cf0 clock, scale;\
\cf2 var\cf0 nTET = 19; \cf4 // change this variable to something else (5, 12, 24, etc.)\cf0 \
\
w = \cf2 SCWindow\cf0 (\cf3 "Testing Grid"\cf0 , \cf2 Rect\cf0 (10, 500, 800, 100+ (20*nTET)));\
\
k = \cf2 Array\cf0 .fill(nTET, \{\cf2 arg\cf0 i; 2.pow(i/nTET) * 440;\}).reverse; \cf4 // 19-TET scale in A\cf0 \
\
a = \cf2 Grid\cf0 .new(w, bounds: \cf2 Rect\cf0 (20, 20, 760, 20), columns: 32, rows: 1, border: \cf2 false\cf0 )\
.setFillMode_(\cf2 true\cf0 )\
.setFillColor_(\cf2 Color\cf0 .white);\
b = \cf2 Grid\cf0 .new(w, bounds: \cf2 Rect\cf0 (20, 50, 760, 20*nTET), columns: 32, rows: nTET, border:\cf2 true\cf0 )\
.setBackground_(\cf2 Color\cf0 .white)\
.setBorder_(\cf2 true\cf0 )\
.setNodeShape_(\cf3 "circle"\cf0 )\
.setNodeSize_(8)\
.setFillMode_(\cf2 true\cf0 )\
.setFillColor_(\cf2 Color\cf0 .new255(103, 148, 103))\
.nodeDownAction_(\{\cf2 arg\cf0 nodeloc; nodeloc.postln; \cf2 Synth\cf0 (\cf8 \\
\f2\fs24 pure
\f1\fs18 \cf0 , [\cf8 \\
\f2\fs24 freq
\f1\fs18 \cf0 , k[nodeloc[1]], \cf8 \\envType\cf0 , 1])\})\
.nodeUpAction_(\{\cf2 arg\cf0 nodeloc; nodeloc.postln; b.setState_(nodeloc[0], nodeloc[1], 0)\}); \
\
\cf4 // fill it with random nodes\cf0 \
(nTET*3).do(\{\
b.setState_(32.rand, nTET.rand, 1); \cf4 // column, row, state\cf0 \
\});\
\
)\
\
\
\cf4 // and we can play the grid from above with a clock\cf0 \
(\
\cf2 var\cf0 nTET = 19;\
t = \cf2 TempoClock\cf0 (4);\
t.schedAbs(t.beats.ceil, \{ \cf2 arg\cf0 beat, sec;\
\
\{a.setState_( beat%32, 0, 1)\}.defer;\
\{a.setState_( beat%32, 0, 0)\}.defer(t.beatDur);\
\
nTET.do(\{\cf2 arg\cf0 i;\
if(b.getState( beat%32, i) == 1, \{\
\cf2 Synth\cf0 (\cf8 \\
\f2\fs24 pure
\f1\fs18 \cf0 , [\cf8 \\
\f2\fs24 freq
\f1\fs18 \cf0 , k[i], \cf8 \\envType\cf0 , 1]);\
\})\
\});\
1;\
\});\
b.nodeUpAction_(\{\cf2 nil\cf0 ;\}); \cf4 // remove the mouseUp function (so the nodes stay)\cf0 \
\
)\
\
b.clearGrid\
\
\
\cf4 // ----------------------------------------------------------------------\cf0 \
\
\cf4 // the same as above but with some differences:\cf0 \
\cf4 // horizontal location is the pitch in one octave\cf0 \
\cf4 // vertical location is octave higher/lower\cf0 \
\
\
(\
\cf2 var\cf0 clock, scale;\
\cf2 var\cf0 nTET = 19; \cf4 // change this variable to something else (5, 12, 24, etc.)\cf0 \
\cf2 var\cf0 octaves = 8;\
\
w = \cf2 SCWindow\cf0 (\cf3 "Testing Grid"\cf0 , \cf2 Rect\cf0 (10, 500, 800, 100+ (20*nTET)));\
\
k = \cf2 Array\cf0 .fill(nTET, \{\cf2 arg\cf0 i; 2.pow(i/nTET);\}); \cf4 // 19-TET scale in A\cf0 \
n = \cf2 Array\cf0 .fill(octaves, \{\cf2 arg\cf0 i; k * (2.pow(i)*110)\}).reverse;\
\
a = \cf2 Grid\cf0 .new(w, bounds: \cf2 Rect\cf0 (20, 20, 760, 20), columns: nTET, rows: 1, border: \cf2 false\cf0 )\
.setFillMode_(\cf2 true\cf0 )\
.setFillColor_(\cf2 Color\cf0 .white);\
b = \cf2 Grid\cf0 .new(w, bounds: \cf2 Rect\cf0 (20, 50, 760, 20*nTET), columns: nTET, rows: octaves, border:\cf2 true\cf0 )\
.setBackground_(\cf2 Color\cf0 .new255(203, 248, 203))\
.setBorder_(\cf2 true\cf0 )\
.setNodeShape_(\cf3 "square"\cf0 )\
.setNodeSize_(12)\
.setFillMode_(\cf2 true\cf0 )\
.setFillColor_(\cf2 Color\cf0 .new255(103, 148, 103))\
.nodeDownAction_(\{\cf2 arg\cf0 nodeloc; nodeloc.postln; \cf2 Synth\cf0 (\cf8 \\
\f2\fs24 pure
\f1\fs18 \cf0 , [\cf8 \\
\f2\fs24 freq
\f1\fs18 \cf0 , n[nodeloc[1]][nodeloc[0]], \cf8 \\envType\cf0 , 1])\})\
.nodeUpAction_(\{\cf2 arg\cf0 nodeloc; nodeloc.postln; b.setState_(nodeloc[0], nodeloc[1], 0)\}); \
\
)\
\
\cf4 // and we can play the grid from above with a clock\cf0 \
(\
\cf2 var\cf0 nTET = 19;\
\cf2 var\cf0 octaves = 8;\
\
t = \cf2 TempoClock\cf0 (4);\
t.schedAbs(t.beats.ceil, \{ \cf2 arg\cf0 beat, sec;\
\
\{a.setState_(beat%nTET, 0, 1)\}.defer;\
\{a.setState_(beat%nTET, 0, 0)\}.defer(t.beatDur);\
\
octaves.do(\{\cf2 arg\cf0 i;\
if(b.getState(beat%nTET, i) == 1, \{\
\cf2 Synth\cf0 (\cf8 \\
\f2\fs24 pure
\f1\fs18 \cf0 , [\cf8 \\
\f2\fs24 freq
\f1\fs18 \cf0 , n[i][beat%nTET], \cf8 \\envType\cf0 , 1]);\
\})\
\});\
1;\
\});\
b.nodeUpAction_(\{\cf2 nil\cf0 ;\}); \cf4 // remove the mouseUp function (so the nodes stay)\cf0 \
\
)\
\
t.tempo_(6)\
\
\cf4 // -------------------------------------------\cf0 \
\
\
\cf4 // I'm not sure what this is... but it's fun!\cf0 \
\cf4 // note the pitch algorithm in the array n is different from the above (where we have octaves)\cf0 \
(\
\cf2 var\cf0 clock, scale;\
\cf2 var\cf0 nTET = 19; \cf4 // change this variable to something else (5, 12, 24, etc.)\cf0 \
\cf2 var\cf0 octaves = 8;\
\
z = [ [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0 ], [ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0 ], [ 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0 ], [ 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0 ], [ 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1 ], [ 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1 ], [ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] ];\
\
w = \cf2 SCWindow\cf0 (\cf3 "Testing Grid"\cf0 , \cf2 Rect\cf0 (10, 500, 800, 100+ (20*nTET)));\
\
k = \cf2 Array\cf0 .fill(nTET, \{\cf2 arg\cf0 i; 2.pow(i/nTET);\}); \cf4 // 19-TET scale in A\cf0 \
n = \cf2 Array\cf0 .fill(octaves, \{\cf2 arg\cf0 i; k * (1.5.pow(i)*110)\}).reverse;\
\
a = \cf2 Grid\cf0 .new(w, bounds: \cf2 Rect\cf0 (20, 20, 760, 20), columns: nTET, rows: 1, border: \cf2 false\cf0 )\
.setFillMode_(\cf2 true\cf0 )\
.setFillColor_(\cf2 Color\cf0 .white);\
b = \cf2 Grid\cf0 .new(w, bounds: \cf2 Rect\cf0 (20, 50, 760, 20*nTET), columns: nTET, rows: octaves, border:\cf2 true\cf0 )\
.setBackground_(\cf2 Color\cf0 .new255(203, 248, 203))\
.setBorder_(\cf2 true\cf0 )\
.setNodeShape_(\cf3 "square"\cf0 )\
.setNodeSize_(12)\
.setFillMode_(\cf2 true\cf0 )\
.setNodeStates_(z)\
.setFillColor_(\cf2 Color\cf0 .new255(103, 148, 103))\
.nodeDownAction_(\{\cf2 arg\cf0 nodeloc; nodeloc.postln; \cf2 Synth\cf0 (\cf8 \\
\f2\fs24 pure
\f1\fs18 \cf0 , [\cf8 \\
\f2\fs24 freq
\f1\fs18 \cf0 , n[nodeloc[1]][nodeloc[0]], \cf8 \\envType\cf0 , 1])\});\
\
t = \cf2 TempoClock\cf0 (6);\
t.schedAbs(t.beats.ceil, \{ \cf2 arg\cf0 beat, sec;\
\
\{a.setState_(beat%nTET, 0, 1)\}.defer;\
\{a.setState_(beat%nTET, 0, 0)\}.defer(t.beatDur);\
\
octaves.do(\{\cf2 arg\cf0 i;\
if(b.getState(beat%nTET, i) == 1, \{\
\cf2 Synth\cf0 (\cf8 \\
\f2\fs24 pure
\f1\fs18 \cf0 , [\cf8 \\
\f2\fs24 freq
\f1\fs18 \cf0 , n[i][beat%nTET], \cf8 \\envType\cf0 , 1]);\
\})\
\});\
1;\
\});\
)\
\
\
\cf4 //////\cf0 \
\
\
\cf4 // same as above but without a TempoClock and here you can use the\cf0 \
\cf4 // top grid to scroll through the chords, like strumming strings or.... err...\cf0 \
(\
\cf2 var\cf0 clock, scale;\
\cf2 var\cf0 nTET = 19; \cf4 // change this variable to something else (5, 12, 24, etc.)\cf0 \
\cf2 var\cf0 octaves = 8;\
\
z = [ [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0 ], [ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0 ], [ 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0 ], [ 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0 ], [ 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1 ], [ 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1 ], [ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] ];\
\
w = \cf2 SCWindow\cf0 (\cf3 "Testing Grid"\cf0 , \cf2 Rect\cf0 (10, 500, 420, 100+ (10*nTET)));\
\
k = \cf2 Array\cf0 .fill(nTET, \{\cf2 arg\cf0 i; 2.pow(i/nTET);\}); \cf4 // 19-TET scale in A\cf0 \
n = \cf2 Array\cf0 .fill(octaves, \{\cf2 arg\cf0 i; k * (1.5.pow(i)*110)\}).reverse;\
\
a = \cf2 Grid\cf0 .new(w, bounds: \cf2 Rect\cf0 (20, 20, 380, 20), columns: nTET, rows: 1, border: \cf2 false\cf0 )\
.setFillMode_(\cf2 true\cf0 )\
.setFillColor_(\cf2 Color\cf0 .white)\
.nodeDownAction_(\{\cf2 arg\cf0 nodeloc; \
octaves.do(\{\cf2 arg\cf0 i;\
if(b.getState(nodeloc[0], i) == 1, \{\
\cf2 Synth\cf0 (\cf8 \\
\f2\fs24 pure
\f1\fs18 \cf0 , [\cf8 \\
\f2\fs24 freq
\f1\fs18 \cf0 , n[i][nodeloc[0]], \cf8 \\envType\cf0 , 1]);\
\})\
\});\
\})\
.nodeUpAction_(\{\cf2 arg\cf0 nodeloc; \
a.setState_(nodeloc[0], nodeloc[1], 0)\
\})\
.nodeTrackAction_(\{\cf2 arg\cf0 nodeloc; \
octaves.do(\{\cf2 arg\cf0 i;\
if(b.getState(nodeloc[0], i) == 1, \{\
\cf2 Synth\cf0 (\cf8 \\
\f2\fs24 pure
\f1\fs18 \cf0 , [\cf8 \\
\f2\fs24 freq
\f1\fs18 \cf0 , n[i][nodeloc[0]], \cf8 \\envType\cf0 , 1]);\
\})\
\});\
\});\
\
b = \cf2 Grid\cf0 .new(w, bounds: \cf2 Rect\cf0 (20, 50, 380, 10*nTET), columns: nTET, rows: octaves, border:\cf2 true\cf0 )\
.setBackground_(\cf2 Color\cf0 .new255(203, 248, 203))\
.setBorder_(\cf2 true\cf0 )\
.setNodeShape_(\cf3 "square"\cf0 )\
.setNodeSize_(8)\
.setFillMode_(\cf2 true\cf0 )\
.setNodeStates_(z)\
.setFillColor_(\cf2 Color\cf0 .new255(103, 148, 103))\
.nodeDownAction_(\{\cf2 arg\cf0 nodeloc; nodeloc.postln; \cf2 Synth\cf0 (\cf8 \\
\f2\fs24 pure
\f1\fs18 \cf0 , [\cf8 \\
\f2\fs24 freq
\f1\fs18 \cf0 , n[nodeloc[1]][nodeloc[0]], \cf8 \\envType\cf0 , 1])\});\
)\
\
\
\
\cf4 //----------------------------------------------------\cf0 \
\
\
\cf4 ///////// a step sequencer\cf0 \
(\
\cf2 var\cf0 gridArray, clockArray, meter, freq;\
\
w = \cf2 SCWindow\cf0 (\cf3 "Step sequencer"\cf0 , \cf2 Rect\cf0 (10, 500, 400, 250));\
\
a = \cf2 Grid\cf0 .new(w, bounds: \cf2 Rect\cf0 (20, 20, 360, 20), columns: 16, rows: 1, border: \cf2 false\cf0 )\
.setFillMode_(\cf2 true\cf0 )\
.setFillColor_(\cf2 Color\cf0 .gray);\
\
gridArray = \cf2 Array\cf0 .fill(4, \{\cf2 arg\cf0 i;\
\cf2 Grid\cf0 .new(w, bounds: \cf2 Rect\cf0 (20, 80+(i*30), 360, 20), columns: 16, rows: 1)\
.setFillMode_(\cf2 true\cf0 )\
.setFillColor_(\cf2 Color\cf0 .white);\
\});\
\
t = \cf2 TempoClock\cf0 (4);\
t.schedAbs(t.beats.ceil, \{ \cf2 arg\cf0 beat, sec;\
\
\{a.setState_( beat%16, 0, 1)\}.defer;\
\{a.setState_( beat%16, 0, 0)\}.defer(t.beatDur);\
\
4.do(\{\cf2 arg\cf0 i;\
if(gridArray[i].getState(beat%16, 0) == 1, \{\
\cf2 Synth\cf0 (\cf8 \\
\f2\fs24 pure
\f1\fs18 \cf0 , [\cf8 \\
\f2\fs24 freq
\f1\fs18 \cf0 , 440*(i+1), \cf8 \\envType\cf0 , 1]);\
\});\
\});\
\
1;\
\});\
)\
\
\
\
\
\
\cf4 //////////////// polyrhythm\cf0 \
\
(\
\cf2 var\cf0 gridArray, clockArray, meter, freq;\
meter = [4,3,5];\
\
w = \cf2 SCWindow\cf0 (\cf3 "polyrhythm"\cf0 , \cf2 Rect\cf0 (10, 500, 400, 150));\
\
gridArray = \cf2 Array\cf0 .fill(3, \{\cf2 arg\cf0 i;\
\cf2 Grid\cf0 .new(w, bounds: \cf2 Rect\cf0 (20, 20+(i*30), 360, 20), columns: meter[i], rows: 1)\
.setFillMode_(\cf2 true\cf0 )\
.setFillColor_(\cf2 Color\cf0 .white);\
\});\
\
clockArray = \cf2 Array\cf0 .fill(3, \{\cf2 arg\cf0 i;\
t = \cf2 TempoClock\cf0 (4);\
t.schedAbs(t.beats.ceil, \{ \cf2 arg\cf0 beat, sec;\
if(gridArray[i].getState(beat%meter[i], 0) == 1, \{\
\cf2 Synth\cf0 (\cf8 \\
\f2\fs24 pure
\f1\fs18 \cf0 , [\cf8 \\
\f2\fs24 freq
\f1\fs18 \cf0 , 440*(i+1), \cf8 \\envType\cf0 , 1]);\
\});\
1;\
\});\
\});\
)\
\
\
\
\
\
\
\cf4 // in case you want to draw behind the Grid then use the setBackgrDrawFunc\
// here is a typical keyboard for 12-TET. How would you draw a keyboard for a 19-TET tuning?\
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
\cf0 \
(\
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
\cf2 var\cf0 clock, scale;\
\cf2 var\cf0 nTET = 12;\
var octaves = 8;\
w = \cf2 SCWindow\cf0 (\cf3 "Keyboard"\cf0 , \cf2 Rect\cf0 (10, 500, 800, 100+ (20*nTET)));\
\
k = \cf2 Array\cf0 .fill(nTET, \{\cf2 arg\cf0 i; 2.pow(i/nTET);\}); \cf4 // 19-TET scale in A\cf0 \
n = \cf2 Array\cf0 .fill(octaves, \{\cf2 arg\cf0 i; k * (2.pow(i)*110)\}).reverse;\
\
a = \cf2 Grid\cf0 .new(w, bounds: \cf2 Rect\cf0 (20, 20, 760, 20), columns: nTET, rows: 1, border: \cf2 false\cf0 )\
.setFillMode_(\cf2 true\cf0 )\
.setFillColor_(\cf2 Color\cf0 .white);\
b = \cf2 Grid\cf0 .new(w, bounds: \cf2 Rect\cf0 (20, 50, 760, 20*nTET), columns: nTET, rows: 8, border:\cf2 true\cf0 )\
.setBorder_(\cf2 true\cf0 )\
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
\cf5 \cf6 //.setBackground_(Color.white)\cf5 \
\cf0 .setNodeShape_(\cf3 "square"\cf0 )\
.setNodeSize_(10)\
.setFillMode_(\cf2 true\cf0 )\
.setFillColor_(\cf2 Color\cf0 .white)\
.nodeDownAction_(\{\cf2 arg\cf0 nodeloc; nodeloc.postln; \cf2 Synth\cf0 (\cf8 \\
\f2\fs24 pure
\f1\fs18 \cf0 , [\cf8 \\
\f2\fs24 freq
\f1\fs18 \cf0 , n[nodeloc[1]][nodeloc[0]], \cf8 \\envType\cf0 , 1])\})\
.setBackgrDrawFunc_(\{\
12.do(\{\cf2 arg\cf0 i;\
if((i==1)||(i==3)||(i==6)||(i==8)||(i==10),\{\
\cf2 Color\cf0 .new255(0, 0, 0, 80).set;\
\},\{\
\cf2 Color\cf0 .new255(255, 255, 255, 80).set;\
\});\
\cf2 Pen\cf0 .fillRect(\cf2 Rect\cf0 (55+(i*(700/12)), 50, 600/12, 240));\
\})\
\});\
\
)\
\
\
\
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
\cf4 // - eh? \cf0 \
(\
\cf2 var\cf0 clock, scale;\
\cf2 var\cf0 nTET = 19;\
var octaves = 8;\
w = \cf2 SCWindow\cf0 (\cf3 "Keyboard"\cf0 , \cf2 Rect\cf0 (10, 500, 800, 100+ (20*nTET)));\
\
k = \cf2 Array\cf0 .fill(nTET, \{\cf2 arg\cf0 i; 2.pow(i/nTET);\}); \cf4 // 19-TET scale in A\cf0 \
n = \cf2 Array\cf0 .fill(octaves, \{\cf2 arg\cf0 i; k * (2.pow(i)*110)\}).reverse;\
\
a = \cf2 Grid\cf0 .new(w, bounds: \cf2 Rect\cf0 (20, 20, 760, 20), columns: nTET, rows: 1, border: \cf2 false\cf0 )\
.setFillMode_(\cf2 true\cf0 )\
.setFillColor_(\cf2 Color\cf0 .white)\
.nodeTrackAction_(\{\cf2 arg\cf0 nodeloc; \
octaves.do(\{\cf2 arg\cf0 i;\
if(b.getState(i, nodeloc[1]) == 1, \{\
\cf2 Synth\cf0 (\cf8 \\
\f2\fs24 pure
\f1\fs18 \cf0 , [\cf8 \\
\f2\fs24 freq
\f1\fs18 \cf0 , n[i][nodeloc[1]], \cf8 \\envType\cf0 , 1]);\
\})\
\});\
\});\
\
b = \cf2 Grid\cf0 .new(w, bounds: \cf2 Rect\cf0 (20, 50, 760, 20*nTET), columns: nTET, rows: 8, border:\cf2 true\cf0 )\
.setBorder_(\cf2 true\cf0 )\
//.setBackground_(\cf2 Color\cf0 .white)\
.setNodeShape_(\cf3 "square"\cf0 )\
.setNodeSize_(8)\
.setFillMode_(\cf2 true\cf0 )\
.setFillColor_(\cf2 Color\cf0 .white)\
.nodeDownAction_(\{\cf2 arg\cf0 nodeloc; nodeloc.postln; \cf2 Synth\cf0 (\cf8 \\
\f2\fs24 pure
\f1\fs18 \cf0 , [\cf8 \\
\f2\fs24 freq
\f1\fs18 \cf0 , n[nodeloc[0]][nodeloc[1]], \cf8 \\envType\cf0 , 1])\})\
.setBackgrDrawFunc_(\{\
19.do(\{\cf2 arg\cf0 i;\
if((i==1)||(i==3)||(i==6)||(i==8)||(i==10)||(i==13)||(i==15)||(i==17),\{\
\cf2 Color\cf0 .new255(0, 0, 0, 80).set;\
\},\{\
\cf2 Color\cf0 .new255(255, 255, 255, 80).set;\
\});\
\cf2 Pen\cf0 .fillRect(\cf2 Rect\cf0 (43+(i*(723/nTET)), 50, 580/nTET, 380));\
\})\
\});\
\
)}