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