Add beating to the pulley effect
[SugarCubes.git] / MarkSlee.pde
CommitLineData
0137237d
MS
1class Pulley extends SCPattern {
2
3 final int NUM_DIVISIONS = 16;
4 private final Accelerator[] gravity = new Accelerator[NUM_DIVISIONS];
5 private final Click[] delays = new Click[NUM_DIVISIONS];
6
7 private final Click reset = new Click(9000);
8 private boolean isRising = false;
9
eb0d5167
MS
10 private BasicParameter beatAmount = new BasicParameter("BEAT", 0.1);
11
0137237d
MS
12 Pulley(GLucose glucose) {
13 super(glucose);
14 for (int i = 0; i < NUM_DIVISIONS; ++i) {
15 addModulator(gravity[i] = new Accelerator(0, 0, 0));
16 addModulator(delays[i] = new Click(0));
17 }
18 addModulator(reset).start();
eb0d5167 19 addParameter(beatAmount);
0137237d
MS
20 trigger();
21 }
22
23 private void trigger() {
24 isRising = !isRising;
25 int i = 0;
26 for (Accelerator g : gravity) {
27 if (isRising) {
28 g.setSpeed(random(40, 50), 0).start();
29 } else {
30 g.setVelocity(0).setAcceleration(-420);
31 delays[i].setDuration(random(0, 500)).trigger();
32 }
33 ++i;
34 }
35 }
36
37 public void run(double deltaMs) {
38 if (reset.click()) {
39 trigger();
40 }
41 int j = 0;
42 for (Click d : delays) {
43 if (d.click()) {
44 gravity[j].start();
45 d.stop();
46 }
47 ++j;
48 }
49
50 for (Accelerator g : gravity) {
51 if (isRising) {
52 if (g.getValuef() > model.yMax) {
53 g.stop();
54 } else if (g.getValuef() > model.yMax*.55) {
55 if (g.getVelocityf() > 10) {
56 g.setAcceleration(-16);
57 } else {
58 g.setAcceleration(0);
59 }
60 }
61 } else {
62 if (g.getValuef() < 0) {
63 g.setValue(-g.getValuef());
64 g.setVelocity(-g.getVelocityf() * random(0.74, 0.84));
65 }
66 }
67 }
68
eb0d5167
MS
69 float fPos = 1 - lx.tempo.rampf();
70 if (fPos < .2) {
71 fPos = .2 + 4 * (.2 - fPos);
72 }
73 float falloff = 100. / (12 + fPos * beatAmount.getValuef()*48);
0137237d
MS
74 for (Point p : model.points) {
75 int i = (int) constrain((p.x - model.xMin) * NUM_DIVISIONS / (model.xMax - model.xMin), 0, NUM_DIVISIONS-1);
76 colors[p.index] = color(
77 (lx.getBaseHuef() + abs(p.x - model.cx)*.8 + p.y*.4) % 360,
78 constrain(130 - p.y*.8, 0, 100),
eb0d5167 79 max(0, 100 - abs(p.y - gravity[i].getValuef())*falloff)
0137237d
MS
80 );
81 }
82 }
83}
84
4760e696
MS
85class ViolinWave extends SCPattern {
86
87 BasicParameter level = new BasicParameter("LVL", 0.45);
88 BasicParameter range = new BasicParameter("RNG", 0.5);
89 BasicParameter edge = new BasicParameter("EDG", 0.5);
90 BasicParameter release = new BasicParameter("RLS", 0.5);
91 BasicParameter speed = new BasicParameter("SPD", 0.5);
92 BasicParameter amp = new BasicParameter("AMP", 0.25);
93 BasicParameter period = new BasicParameter("WAVE", 0.5);
94 BasicParameter pSize = new BasicParameter("PSIZE", 0.5);
95 BasicParameter pSpeed = new BasicParameter("PSPD", 0.5);
96 BasicParameter pDensity = new BasicParameter("PDENS", 0.25);
97
98 LinearEnvelope dbValue = new LinearEnvelope(0, 0, 10);
99
100 ViolinWave(GLucose glucose) {
101 super(glucose);
102 addParameter(level);
103 addParameter(edge);
104 addParameter(range);
105 addParameter(release);
106 addParameter(speed);
107 addParameter(amp);
108 addParameter(period);
109 addParameter(pSize);
110 addParameter(pSpeed);
111 addParameter(pDensity);
112
113 addModulator(dbValue);
114 }
115
116 final List<Particle> particles = new ArrayList<Particle>();
117
118 class Particle {
119
120 LinearEnvelope x = new LinearEnvelope(0, 0, 0);
121 LinearEnvelope y = new LinearEnvelope(0, 0, 0);
122
123 Particle() {
124 addModulator(x);
125 addModulator(y);
126 }
127
128 Particle trigger(boolean direction) {
129 float xInit = random(model.xMin, model.xMax);
130 float time = 3000 - 2500*pSpeed.getValuef();
131 x.setRange(xInit, xInit + random(-40, 40), time).trigger();
132 y.setRange(model.cy + 10, direction ? model.yMax + 50 : model.yMin - 50, time).trigger();
133 return this;
134 }
135
136 boolean isActive() {
137 return x.isRunning() || y.isRunning();
138 }
139
140 public void run(double deltaMs) {
141 if (!isActive()) {
142 return;
143 }
144
145 float pFalloff = (30 - 27*pSize.getValuef());
146 for (Point p : model.points) {
147 float b = 100 - pFalloff * (abs(p.x - x.getValuef()) + abs(p.y - y.getValuef()));
148 if (b > 0) {
149 colors[p.index] = blendColor(colors[p.index], color(
150 lx.getBaseHuef(), 20, b
151 ), ADD);
152 }
153 }
154 }
155 }
156
157 float[] centers = new float[30];
158 double accum = 0;
159 boolean rising = true;
160
161 void fireParticle(boolean direction) {
162 boolean gotOne = false;
163 for (Particle p : particles) {
164 if (!p.isActive()) {
165 p.trigger(direction);
166 return;
167 }
168 }
169 particles.add(new Particle().trigger(direction));
170 }
171
172 public void run(double deltaMs) {
173 accum += deltaMs / (1000. - 900.*speed.getValuef());
174 for (int i = 0; i < centers.length; ++i) {
175 centers[i] = model.cy + 30*amp.getValuef()*sin((float) (accum + (i-centers.length/2.)/(1. + 9.*period.getValuef())));
176 }
177
178 float zeroDBReference = pow(10, (50 - 190*level.getValuef())/20.);
179 float dB = 20*GraphicEQ.log10(lx.audioInput().mix.level() / zeroDBReference);
180 if (dB > dbValue.getValuef()) {
181 rising = true;
182 dbValue.setRangeFromHereTo(dB, 10).trigger();
183 } else {
184 if (rising) {
185 for (int j = 0; j < pDensity.getValuef()*3; ++j) {
186 fireParticle(true);
187 fireParticle(false);
188 }
189 }
190 rising = false;
191 dbValue.setRangeFromHereTo(max(dB, -96), 50 + 1000*release.getValuef()).trigger();
192 }
193 float edg = 1 + edge.getValuef() * 40;
194 float rng = (78 - 64 * range.getValuef()) / (model.yMax - model.cy);
195 float val = max(2, dbValue.getValuef());
196
197 for (Point p : model.points) {
198 int ci = (int) lerp(0, centers.length-1, (p.x - model.xMin) / (model.xMax - model.xMin));
199 float rFactor = 1.0 - 0.9 * abs(p.x - model.cx) / (model.xMax - model.cx);
200 colors[p.index] = color(
201 (lx.getBaseHuef() + abs(p.x - model.cx)) % 360,
202 min(100, 20 + 8*abs(p.y - centers[ci])),
203 constrain(edg*(val*rFactor - rng * abs(p.y-centers[ci])), 0, 100)
204 );
205 }
206
207 for (Particle p : particles) {
208 p.run(deltaMs);
209 }
210 }
211}
212
d1dcc4b5 213class BouncyBalls extends SCPattern {
cc8b3f82 214
d1dcc4b5 215 static final int NUM_BALLS = 6;
cc8b3f82 216
d1dcc4b5 217 class BouncyBall {
cc8b3f82
MS
218
219 Accelerator yPos;
220 TriangleLFO xPos = new TriangleLFO(0, model.xMax, random(8000, 19000));
d1dcc4b5 221 float zPos;
cc8b3f82 222
d1dcc4b5 223 BouncyBall(int i) {
cc8b3f82
MS
224 addModulator(xPos).setBasis(random(0, TWO_PI)).start();
225 addModulator(yPos = new Accelerator(0, 0, 0));
d1dcc4b5 226 zPos = lerp(model.zMin, model.zMax, (i+2.) / (NUM_BALLS + 4.));
cc8b3f82
MS
227 }
228
229 void bounce(float midiVel) {
230 float v = 100 + 8*midiVel;
231 yPos.setSpeed(v, getAccel(v, 60 / lx.tempo.bpmf())).start();
232 }
233
234 float getAccel(float v, float oneBeat) {
235 return -2*v / oneBeat;
236 }
237
238 void run(double deltaMs) {
239 float flrLevel = flr.getValuef() * model.xMax/2.;
240 if (yPos.getValuef() < flrLevel) {
241 if (yPos.getVelocity() < -50) {
242 yPos.setValue(2*flrLevel-yPos.getValuef());
243 float v = -yPos.getVelocityf() * bounce.getValuef();
244 yPos.setSpeed(v, getAccel(v, 60 / lx.tempo.bpmf()));
245 } else {
246 yPos.setValue(flrLevel).stop();
247 }
248 }
249 float falloff = 130.f / (12 + blobSize.getValuef() * 36);
d1dcc4b5
MS
250 float xv = xPos.getValuef();
251 float yv = yPos.getValuef();
252
cc8b3f82 253 for (Point p : model.points) {
d1dcc4b5 254 float d = sqrt((p.x-xv)*(p.x-xv) + (p.y-yv)*(p.y-yv) + .1*(p.z-zPos)*(p.z-zPos));
cc8b3f82
MS
255 float b = constrain(130 - falloff*d, 0, 100);
256 if (b > 0) {
257 colors[p.index] = blendColor(colors[p.index], color(
258 (lx.getBaseHuef() + p.y*.5 + abs(model.cx - p.x) * .5) % 360,
259 max(0, 100 - .45*(p.y - flrLevel)),
260 b
261 ), ADD);
262 }
263 }
264 }
265 }
266
d1dcc4b5 267 final BouncyBall[] balls = new BouncyBall[NUM_BALLS];
cc8b3f82
MS
268
269 final BasicParameter bounce = new BasicParameter("BNC", .8);
270 final BasicParameter flr = new BasicParameter("FLR", 0);
271 final BasicParameter blobSize = new BasicParameter("SIZE", 0.5);
272
d1dcc4b5 273 BouncyBalls(GLucose glucose) {
cc8b3f82 274 super(glucose);
d1dcc4b5
MS
275 for (int i = 0; i < balls.length; ++i) {
276 balls[i] = new BouncyBall(i);
cc8b3f82
MS
277 }
278 addParameter(bounce);
279 addParameter(flr);
280 addParameter(blobSize);
281 }
282
283 public void run(double deltaMs) {
284 setColors(#000000);
d1dcc4b5
MS
285 for (BouncyBall b : balls) {
286 b.run(deltaMs);
cc8b3f82
MS
287 }
288 }
289
290 public boolean noteOnReceived(Note note) {
d1dcc4b5
MS
291 int pitch = (note.getPitch() + note.getChannel()) % NUM_BALLS;
292 balls[pitch].bounce(note.getVelocity());
cc8b3f82
MS
293 return true;
294 }
295}
296
49815cc0
MS
297class SpaceTime extends SCPattern {
298
3e8e60d8 299 SinLFO pos = new SinLFO(0, 1, 3000);
49815cc0
MS
300 SinLFO rate = new SinLFO(1000, 9000, 13000);
301 SinLFO falloff = new SinLFO(10, 70, 5000);
3f8be614
MS
302 float angle = 0;
303
304 BasicParameter rateParameter = new BasicParameter("RATE", 0.5);
305 BasicParameter sizeParameter = new BasicParameter("SIZE", 0.5);
49815cc0 306
3e8e60d8 307
49815cc0
MS
308 public SpaceTime(GLucose glucose) {
309 super(glucose);
3e8e60d8 310
49815cc0
MS
311 addModulator(pos).trigger();
312 addModulator(rate).trigger();
313 addModulator(falloff).trigger();
314 pos.modulateDurationBy(rate);
3f8be614
MS
315 addParameter(rateParameter);
316 addParameter(sizeParameter);
49815cc0 317 }
3f8be614
MS
318
319 public void onParameterChanged(LXParameter parameter) {
320 if (parameter == rateParameter) {
321 rate.stop().setValue(9000 - 8000*parameter.getValuef());
322 } else if (parameter == sizeParameter) {
323 falloff.stop().setValue(70 - 60*parameter.getValuef());
49815cc0
MS
324 }
325 }
326
34327c96 327 void run(double deltaMs) {
3f8be614 328 angle += deltaMs * 0.0007;
87f6fa39
MS
329 float sVal1 = model.strips.size() * (0.5 + 0.5*sin(angle));
330 float sVal2 = model.strips.size() * (0.5 + 0.5*cos(angle));
49815cc0
MS
331
332 float pVal = pos.getValuef();
333 float fVal = falloff.getValuef();
334
335 int s = 0;
cfc57fca 336 for (Strip strip : model.strips) {
49815cc0
MS
337 int i = 0;
338 for (Point p : strip.points) {
339 colors[p.index] = color(
3e8e60d8
MS
340 (lx.getBaseHuef() + 360 - p.fx*.2 + p.fy * .3) % 360,
341 constrain(.4 * min(abs(s - sVal1), abs(s - sVal2)), 20, 100),
342 max(0, 100 - fVal*abs(i - pVal*(strip.metrics.numPoints - 1)))
343 );
49815cc0
MS
344 ++i;
345 }
3f8be614 346 ++s;
49815cc0
MS
347 }
348 }
349}
350
351class Swarm extends SCPattern {
352
3e8e60d8 353 SawLFO offset = new SawLFO(0, 1, 1000);
49815cc0
MS
354 SinLFO rate = new SinLFO(350, 1200, 63000);
355 SinLFO falloff = new SinLFO(15, 50, 17000);
c39f7a04
MS
356 SinLFO fX = new SinLFO(0, model.xMax, 19000);
357 SinLFO fY = new SinLFO(0, model.yMax, 11000);
358 SinLFO hOffX = new SinLFO(0, model.xMax, 13000);
49815cc0
MS
359
360 public Swarm(GLucose glucose) {
361 super(glucose);
3e8e60d8 362
49815cc0
MS
363 addModulator(offset).trigger();
364 addModulator(rate).trigger();
365 addModulator(falloff).trigger();
2b6688e8 366 addModulator(fX).trigger();
49815cc0 367 addModulator(fY).trigger();
2b6688e8 368 addModulator(hOffX).trigger();
49815cc0
MS
369 offset.modulateDurationBy(rate);
370 }
371
372 float modDist(float v1, float v2, float mod) {
373 v1 = v1 % mod;
374 v2 = v2 % mod;
375 if (v2 > v1) {
376 return min(v2-v1, v1+mod-v2);
377 }
378 else {
379 return min(v1-v2, v2+mod-v1);
380 }
381 }
382
34327c96 383 void run(double deltaMs) {
49815cc0 384 float s = 0;
cfc57fca 385 for (Strip strip : model.strips ) {
49815cc0
MS
386 int i = 0;
387 for (Point p : strip.points) {
2b6688e8 388 float fV = max(-1, 1 - dist(p.fx/2., p.fy, fX.getValuef()/2., fY.getValuef()) / 64.);
49815cc0 389 colors[p.index] = color(
2b6688e8 390 (lx.getBaseHuef() + 0.3 * abs(p.fx - hOffX.getValuef())) % 360,
49815cc0 391 constrain(80 + 40 * fV, 0, 100),
34327c96 392 constrain(100 - (30 - fV * falloff.getValuef()) * modDist(i + (s*63)%61, offset.getValuef() * strip.metrics.numPoints, strip.metrics.numPoints), 0, 100)
49815cc0
MS
393 );
394 ++i;
395 }
396 ++s;
397 }
398 }
399}
400
401class SwipeTransition extends SCTransition {
3f8be614
MS
402
403 final BasicParameter bleed = new BasicParameter("WIDTH", 0.5);
404
49815cc0
MS
405 SwipeTransition(GLucose glucose) {
406 super(glucose);
407 setDuration(5000);
3f8be614 408 addParameter(bleed);
49815cc0
MS
409 }
410
411 void computeBlend(int[] c1, int[] c2, double progress) {
3f8be614 412 float bleedf = 10 + bleed.getValuef() * 200.;
c39f7a04 413 float xPos = (float) (-bleedf + progress * (model.xMax + bleedf));
87f6fa39 414 for (Point p : model.points) {
2b6688e8 415 float d = (p.fx - xPos) / bleedf;
49815cc0
MS
416 if (d < 0) {
417 colors[p.index] = c2[p.index];
2b6688e8 418 } else if (d > 1) {
49815cc0 419 colors[p.index] = c1[p.index];
2b6688e8 420 } else {
49815cc0
MS
421 colors[p.index] = lerpColor(c2[p.index], c1[p.index], d, RGB);
422 }
423 }
424 }
425}
426
fe30b226 427abstract class BlendTransition extends SCTransition {
ed149627 428
fe30b226
MS
429 final int blendType;
430
431 BlendTransition(GLucose glucose, int blendType) {
ed149627 432 super(glucose);
fe30b226 433 this.blendType = blendType;
ed149627
MS
434 }
435
436 void computeBlend(int[] c1, int[] c2, double progress) {
437 if (progress < 0.5) {
438 for (int i = 0; i < c1.length; ++i) {
439 colors[i] = lerpColor(
440 c1[i],
fe30b226 441 blendColor(c1[i], c2[i], blendType),
ed149627
MS
442 (float) (2.*progress),
443 RGB);
444 }
445 } else {
446 for (int i = 0; i < c1.length; ++i) {
447 colors[i] = lerpColor(
448 c2[i],
fe30b226 449 blendColor(c1[i], c2[i], blendType),
ed149627
MS
450 (float) (2.*(1. - progress)),
451 RGB);
452 }
453 }
454 }
455}
456
fe30b226
MS
457class MultiplyTransition extends BlendTransition {
458 MultiplyTransition(GLucose glucose) {
459 super(glucose, MULTIPLY);
460 }
461}
462
463class ScreenTransition extends BlendTransition {
464 ScreenTransition(GLucose glucose) {
465 super(glucose, SCREEN);
466 }
467}
468
469class BurnTransition extends BlendTransition {
470 BurnTransition(GLucose glucose) {
471 super(glucose, BURN);
472 }
473}
474
475class DodgeTransition extends BlendTransition {
476 DodgeTransition(GLucose glucose) {
477 super(glucose, DODGE);
478 }
479}
480
481class OverlayTransition extends BlendTransition {
482 OverlayTransition(GLucose glucose) {
483 super(glucose, OVERLAY);
484 }
485}
486
487class AddTransition extends BlendTransition {
488 AddTransition(GLucose glucose) {
489 super(glucose, ADD);
490 }
491}
492
493class SubtractTransition extends BlendTransition {
494 SubtractTransition(GLucose glucose) {
495 super(glucose, SUBTRACT);
496 }
497}
498
499class SoftLightTransition extends BlendTransition {
500 SoftLightTransition(GLucose glucose) {
501 super(glucose, SOFT_LIGHT);
502 }
503}
ed149627 504
4c006f7b
MS
505class BassPod extends SCPattern {
506
507 private GraphicEQ eq = null;
508
509 public BassPod(GLucose glucose) {
510 super(glucose);
511 }
512
513 protected void onActive() {
514 if (eq == null) {
515 eq = new GraphicEQ(lx, 16);
516 eq.slope.setValue(0.6);
517 addParameter(eq.level);
518 addParameter(eq.range);
519 addParameter(eq.attack);
520 addParameter(eq.release);
521 addParameter(eq.slope);
522 }
523 }
524
34327c96 525 public void run(double deltaMs) {
4c006f7b
MS
526 eq.run(deltaMs);
527
528 float bassLevel = eq.getAverageLevel(0, 5);
529
530 for (Point p : model.points) {
531 int avgIndex = (int) constrain(1 + abs(p.fx-model.xMax/2.)/(model.xMax/2.)*(eq.numBands-5), 0, eq.numBands-5);
532 float value = 0;
533 for (int i = avgIndex; i < avgIndex + 5; ++i) {
534 value += eq.getLevel(i);
535 }
536 value /= 5.;
537
538 float b = constrain(8 * (value*model.yMax - abs(p.fy-model.yMax/2.)), 0, 100);
539 colors[p.index] = color(
540 (lx.getBaseHuef() + abs(p.fy - model.cy) + abs(p.fx - model.cx)) % 360,
541 constrain(bassLevel*240 - .6*dist(p.fx, p.fy, model.cx, model.cy), 0, 100),
542 b
543 );
544 }
545 }
546}
547
548
49815cc0
MS
549class CubeEQ extends SCPattern {
550
4c006f7b 551 private GraphicEQ eq = null;
49815cc0 552
3f8be614 553 private final BasicParameter edge = new BasicParameter("EDGE", 0.5);
3f8be614 554 private final BasicParameter clr = new BasicParameter("CLR", 0.5);
4c006f7b 555 private final BasicParameter blockiness = new BasicParameter("BLK", 0.5);
49815cc0
MS
556
557 public CubeEQ(GLucose glucose) {
558 super(glucose);
49815cc0 559 }
3f8be614 560
49815cc0 561 protected void onActive() {
4c006f7b
MS
562 if (eq == null) {
563 eq = new GraphicEQ(lx, 16);
564 addParameter(eq.level);
565 addParameter(eq.range);
566 addParameter(eq.attack);
567 addParameter(eq.release);
568 addParameter(eq.slope);
569 addParameter(edge);
570 addParameter(clr);
571 addParameter(blockiness);
49815cc0
MS
572 }
573 }
574
34327c96 575 public void run(double deltaMs) {
4c006f7b 576 eq.run(deltaMs);
3f8be614 577
4c006f7b 578 float edgeConst = 2 + 30*edge.getValuef();
49815cc0
MS
579 float clrConst = 1.1 + clr.getValuef();
580
87f6fa39 581 for (Point p : model.points) {
4c006f7b 582 float avgIndex = constrain(2 + p.fx / model.xMax * (eq.numBands-4), 0, eq.numBands-4);
49815cc0 583 int avgFloor = (int) avgIndex;
3f8be614 584
4c006f7b
MS
585 float leftVal = eq.getLevel(avgFloor);
586 float rightVal = eq.getLevel(avgFloor+1);
587 float smoothValue = lerp(leftVal, rightVal, avgIndex-avgFloor);
588
589 float chunkyValue = (
590 eq.getLevel(avgFloor/4*4) +
591 eq.getLevel(avgFloor/4*4 + 1) +
592 eq.getLevel(avgFloor/4*4 + 2) +
593 eq.getLevel(avgFloor/4*4 + 3)
594 ) / 4.;
595
596 float value = lerp(smoothValue, chunkyValue, blockiness.getValuef());
597
598 float b = constrain(edgeConst * (value*model.yMax - p.fy), 0, 100);
49815cc0 599 colors[p.index] = color(
4c006f7b
MS
600 (480 + lx.getBaseHuef() - min(clrConst*p.fy, 120)) % 360,
601 100,
602 b
603 );
3f8be614
MS
604 }
605 }
606}
607
608class BoomEffect extends SCEffect {
609
610 final BasicParameter falloff = new BasicParameter("WIDTH", 0.5);
611 final BasicParameter speed = new BasicParameter("SPD", 0.5);
612 final BasicParameter bright = new BasicParameter("BRT", 1.0);
613 final BasicParameter sat = new BasicParameter("SAT", 0.2);
614 List<Layer> layers = new ArrayList<Layer>();
c39f7a04 615 final float maxr = sqrt(model.xMax*model.xMax + model.yMax*model.yMax + model.zMax*model.zMax) + 10;
3f8be614
MS
616
617 class Layer {
618 LinearEnvelope boom = new LinearEnvelope(-40, 500, 1300);
619
620 Layer() {
621 addModulator(boom);
622 trigger();
623 }
624
625 void trigger() {
626 float falloffv = falloffv();
2b6688e8 627 boom.setRange(-100 / falloffv, maxr + 100/falloffv, 4000 - speed.getValuef() * 3300);
3f8be614
MS
628 boom.trigger();
629 }
630
631 void doApply(int[] colors) {
632 float brightv = 100 * bright.getValuef();
633 float falloffv = falloffv();
634 float satv = sat.getValuef() * 100;
635 float huev = lx.getBaseHuef();
87f6fa39 636 for (Point p : model.points) {
3f8be614
MS
637 colors[p.index] = blendColor(
638 colors[p.index],
c39f7a04 639 color(huev, satv, constrain(brightv - falloffv*abs(boom.getValuef() - dist(p.fx, 2*p.fy, 3*p.fz, model.xMax/2, model.yMax, model.zMax*1.5)), 0, 100)),
3f8be614
MS
640 ADD);
641 }
642 }
643 }
644
645 BoomEffect(GLucose glucose) {
646 super(glucose, true);
647 addParameter(falloff);
648 addParameter(speed);
649 addParameter(bright);
650 addParameter(sat);
651 }
652
653 public void onEnable() {
654 for (Layer l : layers) {
655 if (!l.boom.isRunning()) {
656 l.trigger();
657 return;
658 }
659 }
660 layers.add(new Layer());
661 }
662
663 private float falloffv() {
664 return 20 - 19 * falloff.getValuef();
665 }
666
667 public void onTrigger() {
668 onEnable();
669 }
670
671 public void doApply(int[] colors) {
672 for (Layer l : layers) {
673 if (l.boom.isRunning()) {
674 l.doApply(colors);
675 }
49815cc0
MS
676 }
677 }
678}
679
5d70e4d7
MS
680public class PianoKeyPattern extends SCPattern {
681
682 final LinearEnvelope[] cubeBrt;
683 final SinLFO base[];
684 final BasicParameter attack = new BasicParameter("ATK", 0.1);
685 final BasicParameter release = new BasicParameter("REL", 0.5);
686 final BasicParameter level = new BasicParameter("AMB", 0.6);
687
688 PianoKeyPattern(GLucose glucose) {
689 super(glucose);
a8d55ade 690
5d70e4d7
MS
691 addParameter(attack);
692 addParameter(release);
693 addParameter(level);
87f6fa39 694 cubeBrt = new LinearEnvelope[model.cubes.size() / 4];
5d70e4d7
MS
695 for (int i = 0; i < cubeBrt.length; ++i) {
696 addModulator(cubeBrt[i] = new LinearEnvelope(0, 0, 100));
697 }
87f6fa39 698 base = new SinLFO[model.cubes.size() / 12];
5d70e4d7
MS
699 for (int i = 0; i < base.length; ++i) {
700 addModulator(base[i] = new SinLFO(0, 1, 7000 + 1000*i)).trigger();
701 }
702 }
703
704 private float getAttackTime() {
705 return 15 + attack.getValuef()*attack.getValuef() * 2000;
706 }
707
708 private float getReleaseTime() {
709 return 15 + release.getValuef() * 3000;
710 }
711
712 private LinearEnvelope getEnvelope(int index) {
713 return cubeBrt[index % cubeBrt.length];
714 }
715
716 private SinLFO getBase(int index) {
717 return base[index % base.length];
718 }
719
d6ac1ee8 720 public boolean noteOnReceived(Note note) {
5d70e4d7
MS
721 LinearEnvelope env = getEnvelope(note.getPitch());
722 env.setEndVal(min(1, env.getValuef() + (note.getVelocity() / 127.)), getAttackTime()).start();
d6ac1ee8 723 return true;
5d70e4d7
MS
724 }
725
d6ac1ee8 726 public boolean noteOffReceived(Note note) {
5d70e4d7 727 getEnvelope(note.getPitch()).setEndVal(0, getReleaseTime()).start();
d6ac1ee8 728 return true;
5d70e4d7
MS
729 }
730
34327c96 731 public void run(double deltaMs) {
5d70e4d7
MS
732 int i = 0;
733 float huef = lx.getBaseHuef();
734 float levelf = level.getValuef();
87f6fa39 735 for (Cube c : model.cubes) {
5d70e4d7
MS
736 float v = max(getBase(i).getValuef() * levelf/4., getEnvelope(i++).getValuef());
737 setColor(c, color(
e0cea600 738 (huef + 20*v + abs(c.cx-model.xMax/2.)*.3 + c.cy) % 360,
5d70e4d7
MS
739 min(100, 120*v),
740 100*v
741 ));
742 }
743 }
744}
745
0106b29c
MS
746class CrossSections extends SCPattern {
747
c39f7a04
MS
748 final SinLFO x = new SinLFO(0, model.xMax, 5000);
749 final SinLFO y = new SinLFO(0, model.yMax, 6000);
750 final SinLFO z = new SinLFO(0, model.zMax, 7000);
0106b29c
MS
751
752 final BasicParameter xw = new BasicParameter("XWID", 0.3);
753 final BasicParameter yw = new BasicParameter("YWID", 0.3);
754 final BasicParameter zw = new BasicParameter("ZWID", 0.3);
2b6688e8 755 final BasicParameter xr = new BasicParameter("XRAT", 0.7);
0106b29c 756 final BasicParameter yr = new BasicParameter("YRAT", 0.6);
2b6688e8
MS
757 final BasicParameter zr = new BasicParameter("ZRAT", 0.5);
758 final BasicParameter xl = new BasicParameter("XLEV", 1);
0106b29c 759 final BasicParameter yl = new BasicParameter("YLEV", 1);
2b6688e8 760 final BasicParameter zl = new BasicParameter("ZLEV", 0.5);
0106b29c
MS
761
762
763 CrossSections(GLucose glucose) {
764 super(glucose);
765 addModulator(x).trigger();
766 addModulator(y).trigger();
767 addModulator(z).trigger();
b12bca62
MS
768 addParams();
769 }
770
771 protected void addParams() {
0106b29c
MS
772 addParameter(xr);
773 addParameter(yr);
774 addParameter(zr);
2b6688e8 775 addParameter(xw);
0106b29c
MS
776 addParameter(xl);
777 addParameter(yl);
778 addParameter(zl);
2b6688e8
MS
779 addParameter(yw);
780 addParameter(zw);
0106b29c 781 }
3e8e60d8
MS
782
783 void onParameterChanged(LXParameter p) {
0106b29c 784 if (p == xr) {
2b6688e8 785 x.setDuration(10000 - 8800*p.getValuef());
0106b29c 786 } else if (p == yr) {
2b6688e8 787 y.setDuration(10000 - 9000*p.getValuef());
0106b29c
MS
788 } else if (p == zr) {
789 z.setDuration(10000 - 9000*p.getValuef());
790 }
0106b29c 791 }
b12bca62
MS
792
793 float xv, yv, zv;
794
795 protected void updateXYZVals() {
796 xv = x.getValuef();
797 yv = y.getValuef();
798 zv = z.getValuef();
799 }
0106b29c 800
34327c96 801 public void run(double deltaMs) {
f1c74267
MS
802 updateXYZVals();
803
0106b29c
MS
804 float xlv = 100*xl.getValuef();
805 float ylv = 100*yl.getValuef();
806 float zlv = 100*zl.getValuef();
807
808 float xwv = 100. / (10 + 40*xw.getValuef());
809 float ywv = 100. / (10 + 40*yw.getValuef());
810 float zwv = 100. / (10 + 40*zw.getValuef());
811
87f6fa39 812 for (Point p : model.points) {
0106b29c
MS
813 color c = 0;
814 c = blendColor(c, color(
2b6688e8 815 (lx.getBaseHuef() + p.fx/10 + p.fy/3) % 360,
c39f7a04 816 constrain(140 - 1.1*abs(p.fx - model.xMax/2.), 0, 100),
2b6688e8 817 max(0, xlv - xwv*abs(p.fx - xv))
0106b29c
MS
818 ), ADD);
819 c = blendColor(c, color(
2b6688e8 820 (lx.getBaseHuef() + 80 + p.fy/10) % 360,
c39f7a04 821 constrain(140 - 2.2*abs(p.fy - model.yMax/2.), 0, 100),
2b6688e8 822 max(0, ylv - ywv*abs(p.fy - yv))
0106b29c
MS
823 ), ADD);
824 c = blendColor(c, color(
2b6688e8 825 (lx.getBaseHuef() + 160 + p.fz / 10 + p.fy/2) % 360,
c39f7a04 826 constrain(140 - 2.2*abs(p.fz - model.zMax/2.), 0, 100),
2b6688e8 827 max(0, zlv - zwv*abs(p.fz - zv))
0106b29c
MS
828 ), ADD);
829 colors[p.index] = c;
830 }
831 }
832}
833
834class Blinders extends SCPattern {
835
aa54c1ca 836 final SinLFO[] m;
0106b29c
MS
837 final TriangleLFO r;
838 final SinLFO s;
839 final TriangleLFO hs;
840
841 public Blinders(GLucose glucose) {
842 super(glucose);
aa54c1ca
MS
843 m = new SinLFO[12];
844 for (int i = 0; i < m.length; ++i) {
845 addModulator(m[i] = new SinLFO(0.5, 120, (120000. / (3+i)))).trigger();
846 }
847 addModulator(r = new TriangleLFO(9000, 15000, 29000)).trigger();
848 addModulator(s = new SinLFO(-20, 275, 11000)).trigger();
0106b29c 849 addModulator(hs = new TriangleLFO(0.1, 0.5, 15000)).trigger();
aa54c1ca 850 s.modulateDurationBy(r);
0106b29c
MS
851 }
852
34327c96 853 public void run(double deltaMs) {
0106b29c 854 float hv = lx.getBaseHuef();
aa54c1ca 855 int si = 0;
87f6fa39 856 for (Strip strip : model.strips) {
0106b29c 857 int i = 0;
aa54c1ca 858 float mv = m[si % m.length].getValuef();
0106b29c
MS
859 for (Point p : strip.points) {
860 colors[p.index] = color(
2b6688e8
MS
861 (hv + p.fz + p.fy*hs.getValuef()) % 360,
862 min(100, abs(p.fx - s.getValuef())/2.),
254fbb68 863 max(0, 100 - mv/2. - mv * abs(i - (strip.metrics.length-1)/2.))
0106b29c
MS
864 );
865 ++i;
866 }
aa54c1ca 867 ++si;
0106b29c
MS
868 }
869 }
870}
871
87f6fa39
MS
872class Psychedelia extends SCPattern {
873
874 final int NUM = 3;
875 SinLFO m = new SinLFO(-0.5, NUM-0.5, 9000);
876 SinLFO s = new SinLFO(-20, 147, 11000);
877 TriangleLFO h = new TriangleLFO(0, 240, 19000);
878 SinLFO c = new SinLFO(-.2, .8, 31000);
879
880 Psychedelia(GLucose glucose) {
881 super(glucose);
882 addModulator(m).trigger();
883 addModulator(s).trigger();
884 addModulator(h).trigger();
885 addModulator(c).trigger();
886 }
0106b29c 887
34327c96 888 void run(double deltaMs) {
87f6fa39
MS
889 float huev = h.getValuef();
890 float cv = c.getValuef();
891 float sv = s.getValuef();
892 float mv = m.getValuef();
893 int i = 0;
894 for (Strip strip : model.strips) {
895 for (Point p : strip.points) {
896 colors[p.index] = color(
897 (huev + i*constrain(cv, 0, 2) + p.fz/2. + p.fx/4.) % 360,
898 min(100, abs(p.fy-sv)),
899 max(0, 100 - 50*abs((i%NUM) - mv))
900 );
901 }
902 ++i;
903 }
904 }
905}
5129affb
MS
906
907class AskewPlanes extends SCPattern {
908
909 class Plane {
910 private final SinLFO a;
911 private final SinLFO b;
912 private final SinLFO c;
775d3394
MS
913 float av = 1;
914 float bv = 1;
915 float cv = 1;
916 float denom = 0.1;
5129affb
MS
917
918 Plane(int i) {
919 addModulator(a = new SinLFO(-1, 1, 4000 + 1029*i)).trigger();
920 addModulator(b = new SinLFO(-1, 1, 11000 - 1104*i)).trigger();
921 addModulator(c = new SinLFO(-50, 50, 4000 + 1000*i * ((i % 2 == 0) ? 1 : -1))).trigger();
922 }
923
34327c96 924 void run(double deltaMs) {
5129affb
MS
925 av = a.getValuef();
926 bv = b.getValuef();
927 cv = c.getValuef();
928 denom = sqrt(av*av + bv*bv);
929 }
930 }
931
932 final Plane[] planes;
933 final int NUM_PLANES = 3;
934
935 AskewPlanes(GLucose glucose) {
936 super(glucose);
937 planes = new Plane[NUM_PLANES];
938 for (int i = 0; i < planes.length; ++i) {
939 planes[i] = new Plane(i);
940 }
941 }
5129affb 942
34327c96 943 public void run(double deltaMs) {
5129affb 944 float huev = lx.getBaseHuef();
775d3394
MS
945
946 // This is super fucking bizarre. But if this is a for loop, the framerate
947 // tanks to like 30FPS, instead of 60. Call them manually and it works fine.
948 // Doesn't make ANY sense... there must be some weird side effect going on
949 // with the Processing internals perhaps?
950// for (Plane plane : planes) {
951// plane.run(deltaMs);
952// }
953 planes[0].run(deltaMs);
954 planes[1].run(deltaMs);
955 planes[2].run(deltaMs);
956
5129affb 957 for (Point p : model.points) {
775d3394
MS
958 float d = MAX_FLOAT;
959 for (Plane plane : planes) {
960 if (plane.denom != 0) {
961 d = min(d, abs(plane.av*(p.fx-model.cx) + plane.bv*(p.fy-model.cy) + plane.cv) / plane.denom);
962 }
963 }
964 colors[p.index] = color(
965 (huev + abs(p.fx-model.cx)*.3 + p.fy*.8) % 360,
966 max(0, 100 - .8*abs(p.fx - model.cx)),
967 constrain(140 - 10.*d, 0, 100)
968 );
5129affb
MS
969 }
970 }
971}
972
973class ShiftingPlane extends SCPattern {
974
975 final SinLFO a = new SinLFO(-.2, .2, 5300);
976 final SinLFO b = new SinLFO(1, -1, 13300);
977 final SinLFO c = new SinLFO(-1.4, 1.4, 5700);
978 final SinLFO d = new SinLFO(-10, 10, 9500);
979
980 ShiftingPlane(GLucose glucose) {
981 super(glucose);
982 addModulator(a).trigger();
983 addModulator(b).trigger();
984 addModulator(c).trigger();
985 addModulator(d).trigger();
986 }
987
34327c96 988 public void run(double deltaMs) {
5129affb
MS
989 float hv = lx.getBaseHuef();
990 float av = a.getValuef();
991 float bv = b.getValuef();
992 float cv = c.getValuef();
993 float dv = d.getValuef();
994 float denom = sqrt(av*av + bv*bv + cv*cv);
995 for (Point p : model.points) {
775d3394 996 float d = abs(av*(p.fx-model.cx) + bv*(p.fy-model.cy) + cv*(p.fz-model.cz) + dv) / denom;
5129affb 997 colors[p.index] = color(
775d3394 998 (hv + abs(p.fx-model.cx)*.6 + abs(p.fy-model.cy)*.9 + abs(p.fz - model.cz)) % 360,
5129affb
MS
999 constrain(110 - d*6, 0, 100),
1000 constrain(130 - 7*d, 0, 100)
1001 );
1002 }
1003 }
1004}
f3f5a876 1005
4c006f7b
MS
1006class Traktor extends SCPattern {
1007
1008 final int FRAME_WIDTH = 60;
1009
1010 final BasicParameter speed = new BasicParameter("SPD", 0.5);
1011
1012 private float[] bass = new float[FRAME_WIDTH];
1013 private float[] treble = new float[FRAME_WIDTH];
1014
1015 private int index = 0;
1016 private GraphicEQ eq = null;
1017
1018 public Traktor(GLucose glucose) {
1019 super(glucose);
1020 for (int i = 0; i < FRAME_WIDTH; ++i) {
1021 bass[i] = 0;
1022 treble[i] = 0;
1023 }
1024 addParameter(speed);
1025 }
1026
1027 public void onActive() {
1028 if (eq == null) {
1029 eq = new GraphicEQ(lx, 16);
1030 eq.slope.setValue(0.6);
1031 eq.level.setValue(0.65);
1032 eq.range.setValue(0.35);
1033 eq.release.setValue(0.4);
1034 addParameter(eq.level);
1035 addParameter(eq.range);
1036 addParameter(eq.attack);
1037 addParameter(eq.release);
1038 addParameter(eq.slope);
1039 }
1040 }
1041
1042 int counter = 0;
1043
34327c96 1044 public void run(double deltaMs) {
4c006f7b
MS
1045 eq.run(deltaMs);
1046
1047 int stepThresh = (int) (40 - 39*speed.getValuef());
1048 counter += deltaMs;
1049 if (counter < stepThresh) {
1050 return;
1051 }
1052 counter = counter % stepThresh;
1053
1054 index = (index + 1) % FRAME_WIDTH;
1055
1056 float rawBass = eq.getAverageLevel(0, 4);
1057 float rawTreble = eq.getAverageLevel(eq.numBands-7, 7);
1058
1059 bass[index] = rawBass * rawBass * rawBass * rawBass;
1060 treble[index] = rawTreble * rawTreble;
1061
1062 for (Point p : model.points) {
1063 int i = (int) constrain((model.xMax - p.x) / model.xMax * FRAME_WIDTH, 0, FRAME_WIDTH-1);
1064 int pos = (index + FRAME_WIDTH - i) % FRAME_WIDTH;
1065
1066 colors[p.index] = color(
1067 (360 + lx.getBaseHuef() + .8*abs(p.x-model.cx)) % 360,
1068 100,
1069 constrain(9 * (bass[pos]*model.cy - abs(p.fy - model.cy)), 0, 100)
1070 );
1071 colors[p.index] = blendColor(colors[p.index], color(
1072 (400 + lx.getBaseHuef() + .5*abs(p.x-model.cx)) % 360,
1073 60,
1074 constrain(5 * (treble[pos]*.6*model.cy - abs(p.fy - model.cy)), 0, 100)
1075
1076 ), ADD);
1077 }
1078 }
1079}
6702151a
MS
1080
1081class ColorFuckerEffect extends SCEffect {
1082
1083 BasicParameter hueShift = new BasicParameter("HSHFT", 0);
1084 BasicParameter sat = new BasicParameter("SAT", 1);
1085 BasicParameter bright = new BasicParameter("BRT", 1);
1086
1087 ColorFuckerEffect(GLucose glucose) {
1088 super(glucose);
1089 addParameter(hueShift);
1090 addParameter(bright);
1091 addParameter(sat);
1092 }
1093
1094 public void doApply(int[] colors) {
d626bc9b
MS
1095 if (!enabled) {
1096 return;
1097 }
6702151a
MS
1098 float bMod = bright.getValuef();
1099 float sMod = sat.getValuef();
1100 float hMod = hueShift.getValuef();
1101 if (bMod < 1 || sMod < 1 || hMod > 0) {
1102 for (int i = 0; i < colors.length; ++i) {
1103 colors[i] = color(
1104 (hue(colors[i]) + hueShift.getValuef()*360.) % 360,
1105 saturation(colors[i]) * sat.getValuef(),
1106 brightness(colors[i]) * bright.getValuef()
1107 );
1108 }
1109 }
1110 }
1111}
348fed66
MS
1112
1113class BlurEffect extends SCEffect {
1114
1115 final LXParameter amount = new BasicParameter("AMT", 0);
1116 final int[] frame;
1117 final LinearEnvelope env = new LinearEnvelope(0, 1, 100);
1118
1119 BlurEffect(GLucose glucose) {
1120 super(glucose);
1121 addParameter(amount);
1122 addModulator(env);
1123 frame = new int[lx.total];
1124 for (int i = 0; i < frame.length; ++i) {
1125 frame[i] = #000000;
1126 }
1127 }
1128
1129 public void onEnable() {
1130 env.setRangeFromHereTo(1, 400).start();
1131 for (int i = 0; i < frame.length; ++i) {
1132 frame[i] = #000000;
1133 }
1134 }
1135
1136 public void onDisable() {
1137 env.setRangeFromHereTo(0, 1000).start();
1138 }
1139
1140 public void doApply(int[] colors) {
1141 float amt = env.getValuef() * amount.getValuef();
1142 if (amt > 0) {
1143 amt = (1 - amt);
1144 amt = 1 - (amt*amt*amt);
1145 for (int i = 0; i < colors.length; ++i) {
1146 // frame[i] = colors[i] = blendColor(colors[i], lerpColor(#000000, frame[i], amt, RGB), SCREEN);
1147 frame[i] = colors[i] = lerpColor(colors[i], blendColor(colors[i], frame[i], SCREEN), amt, RGB);
1148 }
1149 }
1150
1151 }
1152}