Changes to make DPat able to run multiple instances
[SugarCubes.git] / DanUtil.pde
1 //----------------------------------------------------------------------------------------------------------------------------------
2 float xdMax,ydMax,zdMax;
3 int NumApcRows = 5, NumApcCols = 8;
4
5 boolean btwn (int a,int b,int c) { return a >= b && a <= c; }
6 boolean btwn (double a,double b,double c) { return a >= b && a <= c; }
7
8 public class Pick {
9 Pick (String label, int _Def, int _Max, String d[]) { NumPicks=_Max; Default = _Def; tag=label; Desc = d; }
10 int Cur() { return (CurRow-StartRow)*NumApcCols + CurCol; }
11 int NumPicks, Default, CurRow, CurCol, StartRow, EndRow;
12 String Desc[] ;
13 String tag ;
14 }
15 //----------------------------------------------------------------------------------------------------------------------------------
16 public class _DhP extends BasicParameter {
17 double dflt;
18 _DhP (String label, double value) { super(label,value); dflt=value; }
19 void Set (double value) { super.setValue(value); }
20 void reset () { super.setValue(dflt); }
21 float Val () { return getValuef(); }
22 boolean ZeroOrOne () { return Val()==0 || Val() == 1; }
23 }
24 //----------------------------------------------------------------------------------------------------------------------------------
25 public class xyz { float x,y,z;
26 xyz() {x=y=z=0;}
27 xyz(Point p ) {x=p.fx ; y=p.fy; z=p.fz;}
28 xyz(float _x,float _y,float _z) {x=_x ; y=_y ; z=_z ;}
29 void set(Point p ) {x=p.fx ; y=p.fy; z=p.fz;}
30 void set(float _x,float _y,float _z) {x=_x ; y=_y ; z=_z ;}
31 float distance(xyz b) {return dist(x,y,z,b.x,b.y,b.z); }
32 float dot (xyz b) {return x*b.x + y*b.y + z*b.z; }
33 xyz minus (xyz b) {return new xyz(x-b.x,y-b.y,z-b.z); }
34 xyz plus (xyz b) {return new xyz(x+b.x,y+b.y,z+b.z); }
35 xyz plus (float b) {return new xyz(x+b ,y+b ,z+b ); }
36 xyz over (xyz b) {return new xyz(x/b.x,y/b.y,z/b.z); }
37 xyz times (float b) {return new xyz(x*b ,y*b ,z*b ); }
38 boolean isZero () {return x==0 && y==0 && z==0; }
39
40 void RotateZ (xyz o, float nSin, float nCos) {
41 float nX = nCos*(x-o.x) - nSin*(y-o.y) + o.x;
42 float nY = nSin*(x-o.x) + nCos*(y-o.y) + o.y;
43 x = nX; y = nY;
44 }
45
46 void RotateX (xyz o, float nSin, float nCos) {
47 float nY = nCos*(y-o.y) - nSin*(z-o.z) + o.y;
48 float nZ = nSin*(y-o.y) + nCos*(z-o.z) + o.z;
49 y = nY; z = nZ;
50 }
51
52 void RotateY (xyz o, float nSin, float nCos) {
53 float nZ = nCos*(z-o.z) - nSin*(x-o.x) + o.z;
54 float nX = nSin*(z-o.z) + nCos*(x-o.x) + o.x;
55 z = nZ; x = nX;
56 }
57
58 xyz setRand () { return new xyz ( random(xdMax), random(ydMax), random(zdMax)); }
59 xyz setNorm () { return new xyz ( x / xdMax, y / ydMax, z / zdMax); }
60
61 float interp (float a, float b, float c) { return (1-a)*b + a*c; }
62 xyz interpolate(float i, xyz d) { return new xyz ( interp(i,x,d.x), interp(i,y,d.y), interp(i,z,d.z)); }
63 }
64 //----------------------------------------------------------------------------------------------------------------------------------
65 public class DGlobals {
66
67 DGlobals() {
68 midiEngine.addListener(new MidiEngineListener() {
69 public void onFocusedDeck(int deckIndex) {
70 if (isFocused()) {
71 UpdateLights();
72 SetText();
73 }
74 }
75 });
76 }
77
78 boolean bInit = false;
79 MidiOutput APCOut = null;
80 MidiInput APCIn = null, OxygenIn = null;
81 DPat CurPat = null, NextPat = null;
82 boolean _XSym = false, _YSym = false,
83 _ZSym = false, _RSym = false;
84 String Text1 = "", Text2 = "";
85
86 float Sliders[] = new float[] {0,0,0,0,0,0,0,0};
87 String SliderText[] = new String[] {"Trails", "Dim", "Saturate", "SpinHue", "Hue", "NoiseHue", "Spark", "Wiggle"};
88
89 int mapRow (int a) { return btwn(a,53,57) ? a-53 : a; }
90 int unmapRow (int a) { return btwn(a,0 , 4) ? a+53 : a; }
91
92 void SetLight (int row, int col, int clr){ if (APCOut != null) APCOut.sendNoteOn(col, unmapRow(row), clr); }
93 void SetKnob (int cc , int chan,int val){ if (APCOut != null) APCOut.sendController(cc , chan , val); }
94
95 float _Trails () { return Sliders[0]; }
96 float _Dim () { return Sliders[1]; }
97 float _Saturate () { return Sliders[2]; }
98 float _SpinHue () { return Sliders[3]; }
99 float _ModHue () { return Sliders[4]; }
100 float _NoiseHue () { return Sliders[5]; }
101 float _Spark () { return Sliders[6]; }
102 float _Wiggle () { return Sliders[7]; }
103
104 boolean isFocused () {
105 return CurPat == midiEngine.getFocusedDeck().getActivePattern();
106 }
107
108 void Init () {
109 if (bInit) return; bInit=true;
110 for (MidiOutputDevice output : RWMidi.getOutputDevices()) {
111 if (APCOut == null && output.toString().contains("APC")) APCOut = output.createOutput();
112 }
113 }
114
115 void SetText()
116 {
117 if (!isFocused()) return;
118
119 Text1 = ""; Text2 = "";
120 Text1 += " XSym: " + (_XSym ? "ON" : "OFF") + " ";
121 Text1 += " YSym: " + (_YSym ? "ON" : "OFF") + " ";
122 Text1 += " ZSym: " + (_ZSym ? "ON" : "OFF") + " ";
123 Text1 += " RSym: " + (_RSym ? "ON" : "OFF") + " ";
124 for (int i=0; i<CurPat.picks.size(); i++) {
125 Pick P = (Pick)CurPat.picks.get(i); Text1 += P.tag + ": " + P.Desc[P.Cur()] + " ";
126 }
127
128 Text2 = "SLIDERS: ";
129 for (int i=0; i<8; i++) if (SliderText[i] != "") {
130 Text2 += SliderText[i] + ": " + int(100*Sliders[i]) + " "; }
131
132 uiDebugText.setText(Text1, Text2);
133 }
134
135 public boolean controllerChangeReceived(rwmidi.Controller cc) {
136 if (cc.getCC() == 7 && btwn(cc.getChannel(),0,7)) {
137 Sliders[cc.getChannel()] = 1.*cc.getValue()/127.;
138 return true;
139 }
140 //else { println(cc.getCC() + " " + cc.getChannel() + " " + cc.getValue()); }
141 return false;
142 }
143
144 void Deactivate (DPat p) { if (p == CurPat) { uiDebugText.setText(""); CurPat = NextPat; } NextPat = null; }
145 void Activate (DPat p) {
146 NextPat = CurPat; CurPat = p;
147 while (lx.tempo.bpm() > 40) lx.tempo.setBpm(lx.tempo.bpm()/2);
148 for (int i=0; i<p.paramlist.size(); i++) ((_DhP)p.paramlist.get(i)).reset();
149 UpdateLights();
150 }
151
152 void UpdateLights() {
153 if (!isFocused()) return;
154
155 for (int i=0; i<NumApcRows ; i++) for (int j=0; j<NumApcCols; j++) SetLight(i, j, 0);
156 for (int i=48;i< 56 ; i++) SetKnob(0, i, 0);
157 for (int i=16;i< 20 ; i++) SetKnob(0, i, 0);
158 for (int i=0; i<CurPat.picks.size() ; i++) {
159 Pick P = (Pick)CurPat.picks.get(i); SetLight(P.CurRow, P.CurCol, 3);
160 }
161 SetLight(82, 0, _XSym ? 3 : 0);
162 SetLight(83, 0, _YSym ? 3 : 0);
163 SetLight(84, 0, _ZSym ? 3 : 0);
164 SetLight(85, 0, _RSym ? 3 : 0);
165
166 for (int i=0; i<CurPat.paramlist.size(); i++) {
167 _DhP Param = (_DhP)CurPat.paramlist.get(i);
168 SetKnob ( 0, i<=55 ? 48+i : 16 + i - 8, int(Param.Val()*127) );
169 }
170 }
171
172 double Tap1 = 0;
173 double getNow() { return millis() + 1000*second() + 60*1000*minute() + 3600*1000*hour(); }
174 public boolean noteOffReceived(Note note) {
175 if (CurPat == null) return false;
176 int row = mapRow(note.getPitch()), col = note.getChannel();
177 boolean consumed = false;
178
179 if (row == 50 && col == 0 && btwn(getNow() - Tap1,5000,300*1000)) { // hackish tapping mechanism
180 double bpm = 32.*60000./(getNow()-Tap1);
181 while (bpm < 20) bpm*=2;
182 while (bpm > 40) bpm/=2;
183 lx.tempo.setBpm(bpm); lx.tempo.trigger(); Tap1=0; println("Tap Set - " + bpm + " bpm");
184 consumed = true;
185 }
186
187 UpdateLights();
188 return consumed;
189 }
190
191 public boolean noteOnReceived (Note note) {
192 if (CurPat == null) return false;
193 int row = mapRow(note.getPitch()), col = note.getChannel();
194
195 if (row == 50 && col == 0) { lx.tempo.trigger(); Tap1 = getNow(); return true; }
196 else if (row == 82 && col == 0) { _XSym = !_XSym ; return true; }
197 else if (row == 83 && col == 0) { _YSym = !_YSym ; return true; }
198 else if (row == 84 && col == 0) { _ZSym = !_ZSym ; return true; }
199 else if (row == 85 && col == 0) { _RSym = !_RSym ; return true; }
200 else {
201 for (int i=0; i<CurPat.picks.size(); i++) { Pick P = (Pick)CurPat.picks.get(i);
202 if (!btwn(row,P.StartRow,P.EndRow) ) continue;
203 if (!btwn(col,0,NumApcCols-1) ) continue;
204 if (!btwn((row-P.StartRow)*NumApcCols + col,0,P.NumPicks-1) ) continue;
205 P.CurRow=row; P.CurCol=col; return true;
206 }
207 //println(row + " " + col);
208 }
209 return false;
210 }
211 }
212 //----------------------------------------------------------------------------------------------------------------------------------
213 public class DPat extends SCPattern
214 {
215 DGlobals DG = new DGlobals();
216
217 ArrayList picks = new ArrayList();
218 ArrayList paramlist = new ArrayList();
219 int nMaxRow = 0;
220 float zSpinHue = 0;
221 int nPoint , nPoints;
222 xyz xyzHalf = new xyz(.5,.5,.5),
223 xyzdMax = new xyz(),
224 xyzMid = new xyz();
225
226 float NoiseMove = random(10000);
227 _DhP pSharp, pRotX, pRotY, pRotZ;
228 float Dist (xyz a, xyz b) { return dist(a.x,a.y,a.z,b.x,b.y,b.z); }
229 int c1c (float a) { return int(100*constrain(a,0,1)); }
230 float CalcCone (xyz v1, xyz v2, xyz c) { return degrees( acos ( v1.minus(c).dot(v2.minus(c)) /
231 (sqrt(v1.minus(c).dot(v1.minus(c))) * sqrt(v2.minus(c).dot(v2.minus(c))) ) )); }
232 void StartRun(double deltaMs) { }
233 color CalcPoint(xyz p) { return color(0,0,0); }
234 boolean IsActive() { return this == DG.CurPat; }
235 void onInactive() { DG.Deactivate(this); }
236 void onActive () { DG.Activate(this); }
237
238 _DhP addParam(String label, double value) {
239 _DhP P = new _DhP(label, value);
240 super.addParameter(P);
241 paramlist.add(P); return P;
242 }
243
244 Pick addPick(String name, int def, int nmax, String[] desc) {
245 Pick P = new Pick(name, def, nmax, desc);
246 P.StartRow = nMaxRow;
247 P.EndRow = P.StartRow + int((nmax-1) / NumApcCols);
248 nMaxRow = P.EndRow + 1;
249 P.CurCol = def % NumApcCols;
250 P.CurRow = P.StartRow + def / NumApcCols;
251 picks.add(P);
252 return P;
253 }
254
255 DPat(GLucose glucose) {
256 super(glucose);
257 DG.Init();
258 pSharp = addParam("Shrp" , 0);
259 nPoints = model.points.size();
260 xdMax = model.xMax;
261 ydMax = model.yMax;
262 zdMax = model.zMax;
263 xyzdMax = new xyz(xdMax,ydMax,zdMax);
264 xyzMid = new xyz(xdMax/2, ydMax/2, zdMax/2);
265 }
266
267 public boolean noteOnReceived(Note note) {
268 return DG.noteOnReceived(note);
269 }
270
271 public boolean noteOffReceived(Note note) {
272 return DG.noteOffReceived(note);
273 }
274
275 public boolean controllerChangeReceived(rwmidi.Controller cc) {
276 return DG.controllerChangeReceived(cc);
277 }
278
279 void run(double deltaMs)
280 {
281 NoiseMove += deltaMs;
282 StartRun (deltaMs);
283 zSpinHue += DG._SpinHue ()*deltaMs*.05;
284 xyz P = new xyz();
285 float modhue = DG._ModHue ()==0 ? 0 : DG._ModHue ()*360;
286 float fSharp = 1/(1.01-pSharp.Val());
287
288 DG.SetText();
289 nPoint = 0;
290 for (Point p : model.points) { nPoint++;
291 if (!IsActive()) { colors[p.index] = color(0,0,0); continue; }
292
293 P.set(p);
294
295 if (DG._Spark () > 0) P.y += DG._Spark () * (noise(P.x,P.y+NoiseMove/30 ,P.z)*ydMax - ydMax/2.);
296 if (DG._Wiggle() > 0) P.y += DG._Wiggle() * (noise(P.x/(xdMax*.3)-NoiseMove/1500.) - .5) * (ydMax/2.);
297
298 color cOld = colors[p.index];
299
300 color cNew = CalcPoint(P);
301 if (DG._XSym) cNew = blendColor(cNew, CalcPoint(new xyz(xdMax-P.x,P.y,P.z)), ADD);
302 if (DG._YSym) cNew = blendColor(cNew, CalcPoint(new xyz(P.x,ydMax-P.y,P.z)), ADD);
303 if (DG._ZSym) cNew = blendColor(cNew, CalcPoint(new xyz(P.x,P.y,zdMax-P.z)), ADD);
304
305 float b = brightness(cNew)/100.;
306 b = b < .5 ? pow(b,fSharp) : 1-pow(1-b,fSharp);
307
308 float noizhue = DG._NoiseHue()==0 ? 0 : DG._NoiseHue()*360*noise(
309 P.x/(xdMax*.3)+NoiseMove*.0003,
310 P.y/(ydMax*.3)+NoiseMove*.00025,
311 P.z/(zdMax*.3)+NoiseMove*.0002 );
312
313 cNew = color( (hue(cNew) + modhue + zSpinHue - noizhue) % 360,
314 saturation(cNew) + 100*DG._Saturate(),
315 100 * (DG._Trails()==0 ? b : max(b, (float) (brightness(cOld)/100. - (1-DG._Trails()) * deltaMs/200.)))
316 * (DG._Dim ()==0 ? 1 : 1-DG._Dim())
317 );
318
319 colors[p.index] = cNew;
320 }
321 }
322 }
323 //----------------------------------------------------------------------------------------------------------------------------------