I'm trying to make a theremin using a Leap Motion controller through Processing (Java). So far, I've come very close. I am able to make the pitch and amplitude change by keeping one hand very still over the controller, while the other moves. I was hoping to make it so that either one hand is able to do that, or the left hand controls amplitude while the right controls pitch.
As it is, you have to keep one hand completely still while the other moves. If you only use one hand, it will only update the tone when the hand fully leaves and reenters the controller's view. I'm wondering if anyone can tell me why this is happening, and what I can do to make it update with the hand's position consistently.
Here's the code:
import processing.sound.*;
import de.voidplus.leapmotion.*;
LeapMotion leap;
ArrayList<PVector> points;
PVector fp;
float freq;
float amp;
TriOsc tri;
SinOsc sin;
SqrOsc sqr;
SawOsc saw;
void setup () {
size(640,800);
leap = new LeapMotion(this);
points = new ArrayList<PVector>();
tri = new TriOsc(this);
sin = new SinOsc(this);
sqr = new SqrOsc(this);
saw = new SawOsc(this);
}
void leapOnInit() {
// println("Leap Motion Init");
}
void leapOnConnect() {
// println("Leap Motion Connect");
}
void leapOnFrame() {
// println("Leap Motion Frame");
}
void leapOnDisconnect() {
// println("Leap Motion Disconnect");
}
void leapOnExit() {
// println("Leap Motion Exit");
}
void draw() {
tri.freq(freq);
tri.amp(amp);
tri.play();
for (Hand hand : leap.getHands()) {
fp = hand.getPosition();
if (fp.z <= 30) {
points = new ArrayList<PVector>();
}
else if (fp.z > 30) {
points.add(new PVector(fp.x, fp.y));
}
}
for (int i = points.size()-1; i >= 0; i--) {
PVector p = points.get(i);
amp = map(width-p.x, 0, width, 1, .01); //Volume based on x-axis
freq = map(height-p.y, 0, height, 40, 880); //Pitch based on y-axis
}
}
Okay, I got it. Let it be known, by the way, that the documentation on Leap Motion's SDK uses a COMPLETELY DIFFERENT LIBRARY from the one that is actually imported when you select "Sketch>Import Library...> Leap Motion for Processing" on Processing. I spent half a day trying to figure out why their coding examples used classes without telling you what were in them...
The documentation was frustrating to use, so I didn't use it or make my own interaction box. Instead, I toyed around with the code I had and found out that there was an "else" statement messing up my readings. Once I got rid of that, the darn thing worked like a charm. I even got it to separate "amp" and "freq" to my left and right hands!
Here's what it looks like:
import processing.sound.*;
import de.voidplus.leapmotion.*;
LeapMotion leap;
Theremin leapTheremin;
ArrayList<PVector> points;
PVector handPos;
TriOsc tri;
void setup () {
size(640,800);
leap = new LeapMotion(this);
points = new ArrayList<PVector>();
tri = new TriOsc(this);
leapTheremin = new Theremin(tri);
}
void draw() {
leapTheremin.renderSound();
for (Hand hand : leap.getHands()) {
handPos = hand.getPosition();
boolean handIsLeft = hand.isLeft();
boolean handIsRight = hand.isRight();
if (handPos.z <= 75) {
points = new ArrayList<PVector>();
points.add(new PVector(handPos.x, handPos.y));
background((handPos.x)/2.5,(width-handPos.x)/3,handPos.y-(height/3.5));
}
if (hand.isRight()) {
leapTheremin.setPitch();
}
if (hand.isLeft()) {
leapTheremin.setVolume();
}
}
}
class Theremin {
float freq;
float amp;
int sound;
Theremin (TriOsc tri_g) {
setPitch();
sound = 1;
tri = tri_g;
}
void setPitch () {
for (int i = points.size()-1; i >= 0; i--) {
PVector p = points.get(i);
freq = map((height-handPos.y)+10, 0, height, 40, 880); //"upright antenna", aka "the right one that controls pitch"
// To match the colors with the moaods of the pitches
}
}
void setVolume() {
for (int i = points.size()-1; i >= 0; i--) {
PVector p = points.get(i);
amp = map(width-p.x, 0, width, 1, .01); //"loop antenna", aka "the left one that controls volume"
}
}
void renderSound() {
tri.freq(freq);
tri.amp(amp);
tri.play();
}
}