+ (480 + lx.getBaseHuef() - min(clrConst*p.fz, 120)) % 360,
+ 100,
+ b);
+ }
+ }
+}
+
+class BoomEffect extends SCEffect {
+
+ final BasicParameter falloff = new BasicParameter("WIDTH", 0.5);
+ final BasicParameter speed = new BasicParameter("SPD", 0.5);
+ final BasicParameter bright = new BasicParameter("BRT", 1.0);
+ final BasicParameter sat = new BasicParameter("SAT", 0.2);
+ List<Layer> layers = new ArrayList<Layer>();
+
+ class Layer {
+ LinearEnvelope boom = new LinearEnvelope(-40, 500, 1300);
+
+ Layer() {
+ addModulator(boom);
+ trigger();
+ }
+
+ void trigger() {
+ float falloffv = falloffv();
+ boom.setRange(-100 / falloffv, 500 + 100/falloffv, 4000 - speed.getValuef() * 3300);
+ boom.trigger();
+ }
+
+ void doApply(int[] colors) {
+ float brightv = 100 * bright.getValuef();
+ float falloffv = falloffv();
+ float satv = sat.getValuef() * 100;
+ float huev = lx.getBaseHuef();
+ for (Point p : Point.list) {
+ colors[p.index] = blendColor(
+ colors[p.index],
+ color(huev, satv, constrain(brightv - falloffv*abs(boom.getValuef() - dist(2*p.fx, p.fy, 2*p.fz, 128, 128, 128)), 0, 100)),
+ ADD);
+ }
+ }
+ }
+
+ BoomEffect(GLucose glucose) {
+ super(glucose, true);
+ addParameter(falloff);
+ addParameter(speed);
+ addParameter(bright);
+ addParameter(sat);
+ }
+
+ public void onEnable() {
+ for (Layer l : layers) {
+ if (!l.boom.isRunning()) {
+ l.trigger();
+ return;
+ }
+ }
+ layers.add(new Layer());
+ }
+
+ private float falloffv() {
+ return 20 - 19 * falloff.getValuef();
+ }
+
+ public void onTrigger() {
+ onEnable();
+ }
+
+ public void doApply(int[] colors) {
+ for (Layer l : layers) {
+ if (l.boom.isRunning()) {
+ l.doApply(colors);
+ }
+ }
+ }
+}
+
+public class PianoKeyPattern extends SCPattern {
+
+ final LinearEnvelope[] cubeBrt;
+ final SinLFO base[];
+ final BasicParameter attack = new BasicParameter("ATK", 0.1);
+ final BasicParameter release = new BasicParameter("REL", 0.5);
+ final BasicParameter level = new BasicParameter("AMB", 0.6);
+
+ PianoKeyPattern(GLucose glucose) {
+ super(glucose);
+
+ for (MidiInputDevice input : RWMidi.getInputDevices()) {
+ input.createInput(this);
+ }
+
+ addParameter(attack);
+ addParameter(release);
+ addParameter(level);
+ cubeBrt = new LinearEnvelope[Cube.list.size() / 4];
+ for (int i = 0; i < cubeBrt.length; ++i) {
+ addModulator(cubeBrt[i] = new LinearEnvelope(0, 0, 100));
+ }
+ base = new SinLFO[Cube.list.size() / 12];
+ for (int i = 0; i < base.length; ++i) {
+ addModulator(base[i] = new SinLFO(0, 1, 7000 + 1000*i)).trigger();
+ }
+ }
+
+ private float getAttackTime() {
+ return 15 + attack.getValuef()*attack.getValuef() * 2000;
+ }
+
+ private float getReleaseTime() {
+ return 15 + release.getValuef() * 3000;
+ }
+
+ private LinearEnvelope getEnvelope(int index) {
+ return cubeBrt[index % cubeBrt.length];
+ }
+
+ private SinLFO getBase(int index) {
+ return base[index % base.length];
+ }
+
+ public void noteOnReceived(Note note) {
+ LinearEnvelope env = getEnvelope(note.getPitch());
+ env.setEndVal(min(1, env.getValuef() + (note.getVelocity() / 127.)), getAttackTime()).start();
+ }
+
+ public void noteOffReceived(Note note) {
+ getEnvelope(note.getPitch()).setEndVal(0, getReleaseTime()).start();
+ }
+
+ public void run(int deltaMs) {
+ int i = 0;
+ float huef = lx.getBaseHuef();
+ float levelf = level.getValuef();
+ for (Cube c : Cube.list) {
+ float v = max(getBase(i).getValuef() * levelf/4., getEnvelope(i++).getValuef());
+ setColor(c, color(
+ (huef + 20*v + abs(c.fy-128.)*.3 + c.fz) % 360,
+ min(100, 120*v),
+ 100*v
+ ));