/** * Press h-key to get hints on how to use the applet. */ import java.awt.Rectangle; import processing.opengl.*; import ddf.minim.*; import ddf.minim.signals.*; import ddf.minim.effects.*; import net.silentlycrashing.gestures.*; import net.silentlycrashing.gestures.preset.*; AudioOutput out; Wave wave; BandPass bpf; Rectangle ampRegion; Rectangle schemeRegion; Rectangle rotationRegion; Rectangle freqRegion; Rectangle marksRegion; Rectangle bgRegion; Rectangle resetRegion; GestureAnalyzer brain; ConcurrentGestureListener cwTwirlEar; ConcurrentGestureListener ccwTwirlEar; PostGestureListener incSchemeEar; PostGestureListener decSchemeEar; ConcurrentGestureListener resetEar; ConcurrentGestureListener bpfEar; ConcurrentGestureListener hShakeEar; ConcurrentGestureListener vShakeEar; //mouse variables int mouseDownX, mouseDownY; float mouseDeltaX, mouseDeltaY; // time variable float t; // mouse accessible parameters float DEFAULT_ROTATION_INC = 0.001; float ROTATION_INC = 0.001; float sx = 0f; // freq float sy = 0f; // amp float newsx = 0.02f; // freq float newsy = 0.5f; // amp int bgAlpha = 255; boolean hints = false; boolean ack = true; boolean bg = true; int scheme = 0; int schemes = 5; int xmark = 4; int ymark = 4; int MIN_MARK = 4; int SCALE_INC = 1; //float currScale; boolean scaleUpX = false; boolean scaleUpY = false; PFont myFont; void setup() { size(600,480); //frameRate(30); myFont = createFont("monospaced", 32, true); textFont(myFont, 12f); cursor(CROSS); smooth(); fill(0); // Minim.start(); Minim Minim = new Minim(this); // get a line out from Minim, default sample rate is 44100, bit depth is 16 out = Minim.getLineOut(Minim.STEREO, 1024, 44100, 16); wave = new Wave(); out.addSignal(wave); bpf = new BandPass(440, 20, out.sampleRate()); out.addEffect(bpf); ampRegion = new Rectangle(0,0,width/4, height*2/3); schemeRegion = new Rectangle(width/4, 0, width/2, height/3); rotationRegion = new Rectangle(width/4,height/3 ,width/2, height/3); freqRegion = new Rectangle(width*3/4,0,width/4, height*2/3); marksRegion = new Rectangle(0,height*2/3,width/4, height*2/3); bgRegion = new Rectangle(width/4,height*2/3,width/2, height/3); resetRegion = new Rectangle(width*3/4,height*2/3,width/4, height*2/3); //currRotation = DEFAULT_ROTATION; // init the gesture listeners brain = new MouseGestureAnalyzer(this); cwTwirlEar = new ConcurrentCWTwirlListener(this, brain, rotationRegion); cwTwirlEar.registerOnAction("incRotation", this); //cwTwirlEar.registerOffAction("setRotationDefault", this); ccwTwirlEar = new ConcurrentCCWTwirlListener(this, brain, rotationRegion); ccwTwirlEar.registerOnAction("decRotation", this); incSchemeEar = new PostGestureListener(this, brain, "^(R)$", schemeRegion); incSchemeEar.registerOnAction("incScheme", this); decSchemeEar = new PostGestureListener(this, brain, "^(L)$", schemeRegion); decSchemeEar.registerOnAction("decScheme", this); resetEar = new ConcurrentGestureListener(this, brain, "^(RDLU)$", resetRegion); resetEar.registerOnAction("reset", this); bpfEar = new ConcurrentGestureListener(this, brain, "^(LDRU)$", resetRegion); bpfEar.registerOnAction("toggleBPF", this); hShakeEar = new ConcurrentHShakeListener(this, brain); hShakeEar.registerOnAction("toggleScaleUpX", this); hShakeEar.registerOffAction("toggleScaleUpX", this); vShakeEar = new ConcurrentVShakeListener(this, brain); vShakeEar.registerOnAction("toggleScaleUpY", this); vShakeEar.registerOffAction("toggleScaleUpY", this); } void draw () { if (bg){ background(255); } else { noStroke(); fill(255, bgAlpha); rect(0, 0,width,height); } if (hints) { // draw areas stroke(0); fill(0); noFill(); strokeWeight(4); // amp rect(ampRegion.x,ampRegion.y,ampRegion.width, ampRegion.height); text("To adjust the AMPLITUDE, drag in this space. Drag UP to make it larger. Drag DOWN to make it smaller.", ampRegion.x+height/30,ampRegion.y+height/30,ampRegion.width-height/15, ampRegion.height-height/15); // scheme mode rect(schemeRegion.x,schemeRegion.y,schemeRegion.width, schemeRegion.height); text("To change the COLOR SCHEME, drag LEFT and RIGHT in this space.", schemeRegion.x+height/30,schemeRegion.y+height/30,schemeRegion.width-height/15, schemeRegion.height-height/15); // rotation rect(rotationRegion.x,rotationRegion.y,rotationRegion.width, rotationRegion.height); text("To adjust the ROTATION, draw a circle CLOCKWISE or COUNTERCLOCKWISE.", rotationRegion.x+height/30,rotationRegion.y+height/30,rotationRegion.width-height/15, rotationRegion.height-height/15); // freq rect(freqRegion.x,freqRegion.y,freqRegion.width, freqRegion.height); text("To adjust the FREQUENCY, drag in this space. Drag UP to make it increase. Drag DOWN to make it decrease.", freqRegion.x+height/30,freqRegion.y+height/30,freqRegion.width-height/15, freqRegion.height-height/15); // marks rect(marksRegion.x,marksRegion.y,marksRegion.width, marksRegion.height); text("To stretch the marks SHAKE HORIZONTALLY or VERTICALLY in this space.", marksRegion.x+height/30,marksRegion.y+height/30,marksRegion.width-height/15, marksRegion.height-height/15); // bg rect(bgRegion.x,bgRegion.y,bgRegion.width, bgRegion.height); text("To change the PERSISTENCE, drag LEFT and RIGHT in this space.", bgRegion.x+height/30,bgRegion.y+height/30,bgRegion.width-height/15, bgRegion.height-height/15); // reset rect(resetRegion.x,resetRegion.y,resetRegion.width, resetRegion.height); text("To RESET circle CLOCKWISE. To toggle BANDPASS COUNTERCLOCKWISE.", resetRegion.x+height/30,resetRegion.y+height/30,resetRegion.width-height/15, resetRegion.height-height/15); strokeWeight(1); // keystrokes rect(schemeRegion.x+height/30,schemeRegion.y+schemeRegion.height/2+height/30 ,schemeRegion.width-height/15, schemeRegion.height/2-height/15); text("Keystrokes", schemeRegion.x+height/15,schemeRegion.y+schemeRegion.height/2,schemeRegion.width-height/30, schemeRegion.height-height/30); textSize(8f); text("H: toggle hints (this!)\nG: toggle background", schemeRegion.x+height/15,schemeRegion.y+schemeRegion.height/2+height/20,schemeRegion.width-height/30, schemeRegion.height-height/30); text("A: toggle ack (red dot)\nB: toggle bandpass", schemeRegion.x+schemeRegion.width/2,schemeRegion.y+schemeRegion.height/2+height/20,schemeRegion.width-height/30, schemeRegion.height-height/30); // draw a rectangle to represent the pass band if (out.hasEffect(bpf)) { noStroke(); fill(204, 60); rect(mouseX - bpf.getBandWidth()/20, 0, bpf.getBandWidth()/10, height); } } // increment time (for pan and rotate) t += ROTATION_INC; translate(width/2,height/2); rotate(PI*t); translate(-width/2,-height/2); out.setPan(sin(PI*t)); for (int x = 0; x 0.001) { sx += 0.05 * delta; } delta = newsy-sy; if (abs(delta) > 0.001) { sy += 0.1 * delta; } } float fx (float xx, float tt) { return sin(tt+PI)*sin(sin(tt+PI/2.0)*xx+sin(tt/3.0)) + sin(tt+PI/4.0)*sin(sin(tt+PI/5.0)*xx+sin(tt/6.0)) + 0.1*sin(tt+PI/4.0)*tan(sin(tt+PI/5.0)*xx+sin(tt/6.0)); } class Wave implements AudioSignal { Wave() { } void generate(float[] samp) { for ( int i = 0; i < samp.length; i ++ ) { float temp = (sy*fx(i*sx, t))/2.0; if (temp > 1.0) temp=1.0; if (temp < -1.0) temp=-1.0; samp[i] = temp; } } void generate(float[] left, float[] right) { generate(left); generate(right); } } // keyboard stuff void keyPressed() { if (key == 'h' || key == 'H') { hints = !hints; } if (key == 'g' || key == 'G') { bg = !bg; } if (key == 'a' || key == 'A') { ack = !ack; } if (key == 'b' || key == 'B') { toggleBPF(); } } // mouse stuff void mousePressed() { mouseDownX = mouseX; mouseDownY = mouseY; } void mouseReleased() { mouseDeltaX = (mouseX - mouseDownX)/(float)width; mouseDeltaY = (mouseY - mouseDownY)/(float)height; if (ampRegion.contains(mouseDownX, mouseDownY)) changeAmp(); if (freqRegion.contains(mouseDownX, mouseDownY)) changeFreq(); if (bgRegion.contains(mouseDownX, mouseDownY)) changeBG(); } void mouseMoved() { // map the mouse position to the range [100, 10000], an arbitrary range of passBand frequencies float passBand = map(mouseX, 0, width, 50, 5000); bpf.setFreq(passBand); float bandWidth = map(mouseY, 0, height, 1, 7500); bpf.setBandWidth(bandWidth); } void incRotation() { ROTATION_INC += DEFAULT_ROTATION_INC; ack(); } void decRotation() { ROTATION_INC -= DEFAULT_ROTATION_INC; ack(); } void toggleScaleUpX() { scaleUpX = !scaleUpX; } void incScaleX() { xmark += SCALE_INC; } void resetScaleX() { if (xmark > MIN_MARK) xmark -= SCALE_INC; } void toggleScaleUpY() { scaleUpY = !scaleUpY; } void incScaleY() { ymark += SCALE_INC; } void resetScaleY() { if (ymark > MIN_MARK) ymark -= SCALE_INC; } void changeAmp() { float factor; if (newsy > 1f) factor= 1f; else if (newsy > 0.5 ) factor = 0.5f; else if (newsy > 0.05 ) factor = 0.25f; else factor =0.25f; newsy += mouseDeltaY*factor; if (newsy > 2) { newsy = 2f; ack(false); } else if (newsy < 0.001) { newsy = 0.001f; ack(false); } else { ack(); } } void changeFreq() { float factor; if (newsx > 0.5f) factor= 0.5f; else if (newsx > 0.25 ) factor = 0.25f; else if (newsx > 0.025 ) factor = 0.125f; else factor =0.0625f; newsx += mouseDeltaY*factor; if (newsx > 1) { newsx = 1f; ack(false); } else if (newsx < 0.001) { newsx = 0.001f; ack(false); } else { ack(); } } void incScheme() { scheme ++; if (scheme >= schemes) scheme = 0; ack(); } void decScheme() { scheme --; if (scheme < 0) scheme = schemes - 1; ack(); } void changeBG() { bgAlpha += (int)(mouseDeltaX*150); if (bgAlpha > 255) { bgAlpha = 255; ack(false); } else if (bgAlpha < 0) { bgAlpha = 0; ack(false); } else { ack(); } if (bgAlpha == 255) { bg = true; } else { bg = false; } } void toggleBPF() { if (out.hasEffect(bpf)) { out.removeEffect(bpf); } else { out.addEffect(bpf); } ack(); } void reset() { ROTATION_INC = DEFAULT_ROTATION_INC; newsx = 0.02f; // freq newsy = 0.5f; // amp bgAlpha = 255; hints = false; ack = true; bg = true; scheme = 0; xmark = 4; ymark = 4; if (!out.hasEffect(bpf)) { out.addEffect(bpf); } ack(); } void ack() { ack(true); } void ack(boolean OK) { if (ack) { if (OK) { stroke(255,0,0); fill(255,0,0); } else { stroke(255,0,0); noFill(); } ellipse(width/2,height/2 ,width/16, width/16); } }