Skip to content

Instantly share code, notes, and snippets.

@rioleo
Created November 4, 2012 21:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rioleo/4013771 to your computer and use it in GitHub Desktop.
Save rioleo/4013771 to your computer and use it in GitHub Desktop.
Wrist-based input
import processing.serial.*;
import ddf.minim.*;
Minim minim;
AudioOutput out;
PluckedString string;
// The fundamental frequency of the string (in Hz)
// Setting this very low (e.g. 2.0) results in
// visible traveling waves.
float f0 = 440.0;
int thresholds = 25000;
import cc.arduino.*;
int wait = 3;
int sum;
Arduino arduino;
int time;
int testExecuted = 0;
int traintime = 400;
int downdelay = 500;
int numsensors = 4;
int numtrials = 5;
int forceRun = -1;
int rows = 200;
int thickness = 25;
int [][][] signal = new int [5][traintime][4];
int [][] tempsignal = new int [traintime][numsensors];
int [][] tempsignalshift = new int [traintime][numsensors];
int tempsignallength = 0;
color off = color(4, 79, 111);
color on = color(84, 145, 158);
void setup() {
minim = new Minim(this);
// get a line out from Minim, default bufferSize is 1024, default sample rate is 44100, bit depth is 16
out = minim.getLineOut(Minim.STEREO);
// initialize the string (but it won't ring until you pluck it)
string = new PluckedString(f0, 1.0);
int[] seqA = {
0, 0, 0, 3, 6, 13, 25, 22, 7, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
int[] seqB = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 12, 24, 23, 8, 3, 1, 0, 0, 0, 0, 0
};
println(dynamicTimeWarp(seqA, seqB));
size(700, 500);
arduino = new Arduino(this, Arduino.list()[0], 57600);
for (int i = 0; i <= 13; i++) {
arduino.pinMode(i, Arduino.INPUT);
}
}
void sampleTest2() {
int [] train0 = new int[traintime];
int [] train1 = new int[traintime];
int [] train2 = new int[traintime];
int [] train3 = new int[traintime];
int [] test0 = new int[traintime];
int [] test1 = new int[traintime];
int [] test2 = new int[traintime];
int [] test3 = new int[traintime];
for (int x = 0;x <traintime;x++) {
// Compare sensor 1
if (forceRun == -1) {
train0[x] = signal[0][x][0];
train1[x] = signal[0][x][1];
train2[x] = signal[0][x][2];
train3[x] = signal[0][x][3];
test0[x] = tempsignal[x][0];
test1[x] = tempsignal[x][1];
test2[x] = tempsignal[x][2];
test3[x] = tempsignal[x][3];
}
else {
train0[x] = signal[0][x][forceRun];
test0[x] = tempsignal[x][forceRun];
}
}
if (forceRun == -1) {
sum = (dynamicTimeWarp(train0, test0) + dynamicTimeWarp(train1, test1) + dynamicTimeWarp(train2, test2) + dynamicTimeWarp(train3, test3));
}
else {
sum = dynamicTimeWarp(train0, test0);
}
if (sum < thresholds) {
play();
println("Signal: perceived match with score: " + sum);
}
else {
println("Signal: perceived nonmatch with score: " + sum);
}
}
void play() {
float pluck_point = map(0, 0, 100, 0, 1);
float sharpness = map(0, 0, 100, 0, 1);
string.pluck(pluck_point, sharpness);
string.release();
}
void testAgainstFirst(int id) {
int [] train0 = new int[traintime];
int [] train1 = new int[traintime];
int [] train2 = new int[traintime];
int [] train3 = new int[traintime];
int [] test0 = new int[traintime];
int [] test1 = new int[traintime];
int [] test2 = new int[traintime];
int [] test3 = new int[traintime];
for (int x = 0;x <traintime;x++) {
// Compare sensor 1
if (forceRun == -1) {
train0[x] = signal[0][x][0];
train1[x] = signal[0][x][1];
train2[x] = signal[0][x][2];
train3[x] = signal[0][x][3];
test0[x] = signal[id][x][0];
test1[x] = signal[id][x][1];
test2[x] = signal[id][x][2];
test3[x] = signal[id][x][3];
}
else {
train0[x] = signal[0][x][forceRun];
test0[x] = signal[id][x][forceRun];
}
}
if (forceRun == -1) {
sum = (dynamicTimeWarp(train0, test0) + dynamicTimeWarp(train1, test1) + dynamicTimeWarp(train2, test2) + dynamicTimeWarp(train3, test3));
}
else {
sum = dynamicTimeWarp(train0, test0);
}
if (sum < thresholds) {
println("Signal: perceived match with score: " + sum);
play();
if (id == 4) {
testExecuted = 1;
}
}
else {
testExecuted = 0;
println("Perceived nonmatch with score: " + sum);
}
}
void runTrial(int id) {
testExecuted = 0;
while (wait > 0) {
println("Starting training session " + id + " in " + wait);
delay(downdelay);
wait = wait - 1;
}
println("Go!");
for (int x = 0;x <traintime;x++) {
for (int y = 0;y < numsensors;y++) {
int z = arduino.analogRead(y);
signal[id-1][x][y] = z;
//subsignal = append(subsignal, z);
delay(1);
}
//signal[id-1][y] = subsignal;
}
println("Training done.");
drawOnce();
wait = 3;
testAgainstFirst(id-1);
}
int abso(int x, int y) {
return abs(x-y);
}
int dynamicTimeWarp(int [] seqA, int [] seqB) {
int numRows = seqA.length;
int numCols = seqB.length;
int[][] cost = new int[numCols][numRows];
// Initialize 2D array values
for (int i = 0; i < numCols; i++ ) {
for (int j = 0; j < numRows; j++ ) {
cost[i][j] = 0;
}
}
cost[0][0] = abso(seqA[0], seqB[0]);
for (int i = 1; i < numRows; i++) {
cost[i][0] = cost[i-1][0] + abso(seqA[i], seqB[0]);
}
for (int j = 1; j < numCols; j++) {
cost[0][j] = cost[0][j-1] + abso(seqA[0], seqB[j]);
}
for (int i = 1; i < numRows; i++) {
for (int j = 1; j < numCols; j++) {
int choices1 = cost[i-1][j];
int choices2 = cost[i][j-1];
int choices3 = cost[i-1][j-1];
cost[i][j] = min(choices1, choices2, choices3) + abso(seqA[i], seqB[j]);
}
}
return cost[numCols-1][numRows-1];
}
void keyPressed() {
switch (key) {
case 'r':
println("Resetting");
wait = 3;
testExecuted = 0;
forceRun = -1;
// do something if the key pressed was 'r'
break;
case 'a':
forceRun = 0;
break;
case 'b':
forceRun = 1;
break;
case 'c':
forceRun = 2;
break;
case 'd':
forceRun = 3;
break;
case 'f':
thresholds = sum;
println("Set threshold to: " + thresholds);
break;
case '1':
runTrial(1);
break;
case '2':
runTrial(2);
break;
case '3':
runTrial(3);
break;
case '4':
runTrial(4);
break;
case '5':
runTrial(5);
break;
default:
break;
}
}
void sampleTest() {
int [] train = new int[traintime];
int [] test = new int[traintime];
for (int x = 1;x <traintime;x++) {
// Training for x is signal at trial 0, sensor 0
train[x] = signal[0][x][0];
test[x] = tempsignal[x][0];
}
println(dynamicTimeWarp(train, test));
}
void drawOnce() {
colorMode(RGB, 1023);
// Trial 0
for (int l = 0; l < numtrials; l++) {
for (int j = 0; j < rows; j++) {
for (int i = 0; i < numsensors; i++) {
stroke(signal[l][j][i]);
for (int k = 0; k < thickness; k++) {
point(i+(thickness*i) + k+(l*numsensors*thickness), j);
}
}
}
}
}
void draw() {
if (testExecuted == 1) {
if (tempsignallength < traintime) {
//println("Entering");
for (int x = 0;x <traintime;x++) {
//tempsignallength++;
for (int y = 0;y < numsensors;y++) {
int z = arduino.analogRead(y);
tempsignal[x][y] = z;
delay(1);
}
}
}
// else {
// //println("Shifting");
// for (int x = 1;x <traintime;x++) {
// for (int y = 0;y < numsensors;y++) {
// tempsignalshift[x-1][y] = tempsignal[x][y];
// }
// }
//
// for (int y = 0;y < numsensors;y++) {
// int z = arduino.analogRead(y);
// tempsignalshift[traintime-1][y] = z;
// delay(1);
// }
// tempsignal = tempsignalshift;
// }
sampleTest2();
}
else {
for (int i = 0; i < 4; i++) {
ellipse(280 + i * 30, 240, arduino.analogRead(i) / 16, arduino.analogRead(i) / 16);
}
}
}
class PluckedString implements AudioSignal
{
int wg_length; // the length of the string in samples
float[] y_plus; // buffer to hold positive-moving wave
float[] y_minus; // buffer to hold negative-moving wave
float[] y; // temporary buffer to calculate resultant shape of wave, used during display
float[] reflec_filt = {
.25, .5, .25
}; // the dispersion filter at the "bridge" end reflection
int ref_hlen;
boolean active; // flag to stop the wave from propagating while string is being plucked
// variables to control interactive damping (experimental)
float damp_point;
float damp_r;
PluckedString(float freq, float ampl)
{
// Waveguide length is a delay equivalent to half the desired period
wg_length = Math.round(out.sampleRate()/freq/2);
// how much extra to allow for reflection filter
ref_hlen = (reflec_filt.length-1)/2;
y_plus = new float[wg_length+ref_hlen]; // extra for symmetric ref_filt
y_minus = new float[wg_length+ref_hlen]; // just convenience
// combined wave used only for display
y = new float[wg_length];
// initialize damping point to 0.0, no effect
damp_point = 0.0;
damp_r = 1.0;
// connect it to output for sound output
out.addSignal(this);
}
// PluckedString::pluck is called to setup an initial displacement of the string
// It stops the string from ringing, call PluckedString::release to start it again.
void pluck(float pluck_point, float sharpness)
{
// stop wave propagation for a moment
active = false;
// remove damping
damp_point = 0.0;
// initialize traveling wave values in response to pluck
// equal and triangular, for string displaced at pluck_point of its length
// raise triangular waveshape to a positive exponent to allow more sharper plucks
float exponent = (float)Math.pow(2.0, 8*sharpness);
float pluck_amp = 1.0;
for (int i = 0; i < wg_length; ++i) {
if (i < Math.round(pluck_point * (float)wg_length)) {
y_minus[i] = y_plus[i] = (float)Math.pow(pluck_amp*(float)i/(pluck_point*(float)wg_length), exponent);
}
else {
y_minus[i] = y_plus[i] = (float)Math.pow(pluck_amp*(float)(wg_length-i)/((1-pluck_point)*(float)wg_length), exponent);
}
}
}
// called after setting up waveshape with pluck() to start oscillation
void release() {
active = true;
}
// external control for the experimental damping function
void setDamp(float position, float ratio)
{
damp_point = position;
// ratio comes in as -1..1
//damp_r = ratio;
damp_r = (float)Math.pow(2.0, 5*(-1.0-ratio)); // 0.001 .. 1.0
println("damp_r="+damp_r);
}
// helper function draws a waveshape in a specific box and color
void drawWave(float [] vals, int len, int x, int y, int w, int h, int r, int g, int b)
{
stroke(r, g, b);
int mid = y + h/2;
int vscale = h/2;
for (int i = 0; i < len-1; i++)
{
float x1 = x+map(i, 0, len, 0, w);
float x2 = x+map(i+1, 0, len, 0, w);
line(x1, mid - vscale*vals[i],
x2, mid - vscale*vals[i+1]);
}
}
// display the current state of the string
void draw()
{
background(0);
stroke(255);
// draw the two traveling waves and their sum = string displacement
// forward wave in red, top half of pane
drawWave(y_plus, wg_length, 0, height/2, width, height/2, 255, 0, 0);
// backward wave in blue, bottom half of pane
drawWave(y_minus, wg_length, 0, 0, width, height/2, 0, 0, 255);
// combined wave - white, middle of pane
// sum up the two traveling waves
for (int i = 0; i < wg_length; ++i)
{
y[i] = y_plus[i] + y_minus[i];
}
drawWave(y, wg_length, 0, height/4, width, height/2, 255, 255, 255);
if (damp_point > 0) {
stroke(255);
int damp_x = Math.round(map(damp_point, 0, 1, 0, width));
line(damp_x, 0, damp_x, height);
}
}
// called by sound output to calculate samples, drives the basic
// computation loop.
void generate(float[] samps)
{
// each sample of the output corresponds to one time step
// of the basic physics simulaiton
for (int i = 0; i < samps.length; ++i)
{
float y_reflec = 0;
// only do anything if the string has been released
if (active == true)
{
// move the traveling waves one point forward
for (int j = y_plus.length-1; j > 0; --j) {
// copy forward starting at end
y_plus[j] = y_plus[j-1];
}
for (int j = 0; j < y_plus.length-1; ++j)
{
// copy backwards starting at beginning
y_minus[j] = y_minus[j+1];
}
// simple reflection at one end
y_plus[0] = -y_minus[0];
// filtered reflection at far end
for (int k=0; k < reflec_filt.length; ++k)
{
// y_plus has a few points "off the end" to allow
// a symmetric reflec_filter
y_reflec += reflec_filt[k] * y_plus[wg_length - 1 - ref_hlen + k];
}
// reflected wave is the new point in the backward-traveling wave
y_minus[wg_length - 1] = -y_reflec;
// apply experimental damping/scattering
int damp_index = Math.round(damp_point * wg_length);
if (damp_index > 0)
{
// additional offsets to traveling wave both ways
float y0 = y_plus[damp_index] + y_minus[damp_index];
if (y0 > damp_r) {
float delta = y0 - damp_r;
// add to ongoing waves
y_plus[damp_index] -= delta;
y_minus[damp_index] -= delta;
}
}
}
// output is given by amount of reflection (~ energy being passed through bridge)
samps[i] = y_reflec;
}
}
// AudioSignal requires both mono and stereo generate functions
void generate(float [] sampL, float [] sampR)
{
generate(sampL);
// copy left to right
System.arraycopy(sampL, 0, sampR, 0, sampR.length);
}
}
//void draw() {
//
// for (int i = 0; i < 4; i++) {
// ellipse(280 + i * 30, 240, arduino.analogRead(i) / 16, arduino.analogRead(i) / 16);
// }
//}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment