Tuesday, December 16, 2008

Physical Computing Final: Sensor Dome

The Sensor Dome is an interface for controlling light and sound using photocells. Each of the 16 photocells sends a value based on how much it is covered by shade. These values are used to control both the volume of music tracks and the lights above the sensors. The dimming and animation of the lights above the sensors alter the values of the sensors.


Sensor Dome performs for YOU! from Matt Richard on Vimeo.


Sensor Dome comes together... almost from Matt Richard on Vimeo.


Building the Light Dome for the Sensor Dome! from Matt Richard on Vimeo.

Processing Code:
//Matt Richard(visual) and Eyal Ohana(audio)

import UDP.*; // import Light Tile library
UDPSend udp;

import processing.serial.*; // import the Processing serial library
Serial myPort;

import ddf.minim.signals.*; // import the Minim sound library
import ddf.minim.*;
Minim minim;
AudioOutput out;


//declare variables
int totalSquares = 16; // var for how many squares you want on the screen, !! must be a square itself
int colorMax = 255;
int tracks = 16;
float rectColorArray[][][][]; // create nested array that holds the rects alpha value
float sensorValue[] = new float[tracks]; // array to hold sensor values
AudioPlayer players[] = new AudioPlayer[tracks];
float gain[] = new float[tracks];
float val[] = new float[tracks];


void setup() {
//colorMax = totalSquares;
size(240, 240);
background(0);
colorMode(HSB, 255);
noStroke();
smooth();

udp = new UDPSend(this,"10.031.066.202",6038,"/Users/matt/Documents/Processing/libraries/UDP/colorkinetics_lib/header","/Users/matt/Documents/Processing/libraries/UDP/colorkinetics_lib/lookup",200,0);
myPort = new Serial(this, Serial.list()[0], 9600);
// read bytes into a buffer until you get a linefeed (ASCII 10):
myPort.bufferUntil('\n');

setup_audio(); // contains all of the setup code for audio(located below)
setup_visual(); // contains all of the setup code for visual(located below)
}

void draw() {
draw_audio();
draw_visual();
udp.send();
}


void serialEvent(Serial myPort) {
// serialEvent method is run automatically by the Processing applet
// whenever the buffer reaches the byte value set in the bufferUntil()
// method in the setup():


// read the serial buffer:
String myString = myPort.readStringUntil('\n');
// if you got any bytes other than the linefeed:
if (myString != null) {
myString = trim(myString);

// split the string at the commas
// and convert the sections into integers:
int sensors[] = int(split(myString, ","));

// print out the values you got:
for (int sensorNum = 0; sensorNum < sensors.length; sensorNum++) {
print(sensorNum + ": " + sensors[sensorNum] + " ");
}
// add a linefeed after all the sensor values are printed:
println();
if (sensors.length > 1) {
for(int i=0;i < tracks;i++){
val[i] = map(sensors[i], 100, 300, 7, -30); // map the sensor read to the audible range of the gain for the audio
sensorValue[i] = map(sensors[i], 20,200,0,colorMax); // map the sensor value for visual
}
}
}
}


/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
//////// AUDIO FUNCTIONS ////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
void setup_audio(){
minim = new Minim(this);
out = minim.getLineOut();

players[0] = minim.loadFile("01_drums.aif");
players[1] = minim.loadFile("02_filter.aif");
players[2] = minim.loadFile("03_flute.aif");
players[3] = minim.loadFile("04_hard.aif");
players[4] = minim.loadFile("05_horns.aif");
players[5] = minim.loadFile("06_pad.aif");
players[6] = minim.loadFile("07_pad_b.aif");
players[7] = minim.loadFile("08_bass.aif");
players[8] = minim.loadFile("09_didge.aif");
players[9] = minim.loadFile("10_beat_hi.aif");
players[10] = minim.loadFile("11_beat_low.aif");
players[11] = minim.loadFile("12_machine.aif");
players[12] = minim.loadFile("13_click.aif");
players[13] = minim.loadFile("14_scape.aif");
players[14] = minim.loadFile("15_strings.aif");
players[15] = minim.loadFile("16_bells.aif");
//players[.play();
for (int i=0; i< players.length; i++) {
players[i].loop();
}
}
void draw_audio() {

if ( out.hasControl(Controller.GAIN) )
{
for (int i = 0; i < players.length; i++) {
players[i].setGain(val[i]);
}
}
}
void stop() {

// the Audioplayers[ you got from Minim.loadFile()
for (int i=0; i< players.length; i++) {
players[i].close();
}
out.close();
// the AudioInput you got from Minim.getLineIn()
//input.close();
minim.stop();

// this calls the stop method that
// you are overriding by defining your own
// it must be called so that your application
// can do all the cleanup it would normally do
super.stop();
}



/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
//////// VISUAL FUNCTIONS ////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
void setup_visual(){
rectColorArray = new float[floor(sqrt(totalSquares))][floor(sqrt(totalSquares))][3][2];
for(int x=0; x < sqrt(totalSquares); x++){
for(int y=0; y < sqrt(totalSquares); y++){
rectColorArray[x][y][0][0] = x;
rectColorArray[x][y][1][0] = 1;
rectColorArray[x][y][2][0] = 1;

rectColorArray[x][y][0][1] = 10;
rectColorArray[x][y][1][1] = 10;
rectColorArray[x][y][2][1] = 10;
}
}
}

void draw_visual(){
for(int x = 0; x < sqrt(totalSquares); x++){
for(int y = 0; y < sqrt(totalSquares); y++){

fill(sensorValue[(x*4)+y]);
rect(floor((x * (width/(sqrt(totalSquares))))), floor((y * (height/(sqrt(totalSquares))))), floor((width/(sqrt(totalSquares)))), floor((height/(sqrt(totalSquares)))));
}
}
}

Arduino Code:
int sensorValue[16]; // an array to store the sensor values

// the address pins will go in order from the first one:
#define firstAddressPin 8

int analogInput = 0;

void setup() {
Serial.begin(9600);
// set the output pins:
for (int pinNumber = firstAddressPin; pinNumber < firstAddressPin + 4; pinNumber++) {
pinMode(pinNumber, OUTPUT);
}
}

void loop() {
for (int channelNum = 0; channelNum < 16; channelNum ++) {
// determine the four address pin values from the channelNum:
setChannel(channelNum);

// read the analog input and store it in the value array:
sensorValue[channelNum] = analogRead(analogInput);
//delay(10);
// print the values as a single tab-separated line:
Serial.print(sensorValue[channelNum], DEC);
if(channelNum < 15){
Serial.print(",");
}
}

// print a carriage return at the end of each read of the mux:
Serial.println();
}

void setChannel(int whichChannel) {
for (int bitPosition = 0; bitPosition < 4; bitPosition++) {
// shift value x bits to the right, and mask all but bit 0:
int bitValue = (whichChannel >> bitPosition) & 1;
// set the address pins:
int pinNumber = firstAddressPin + bitPosition;
digitalWrite(pinNumber, bitValue);
}
}

Saturday, December 6, 2008

Sensor Dome comes alive!!!!

I am proud to say that the Sensor Dome is up and running. Eyal Ohana and I were able to get both parts(audio and visual) to work seperately. Tomorrow we will get them to work together and build the structure that will house the light tile and the Sensor Dome. I uploaded two videos showing it working. I will add some pictures of the guts tomorrow.

Oh yeah, I almost forgot! We are using a multiplexer to allow for 16 photocells in analog input.


Sensor Dome - Light Tile Test from Matt Richard on Vimeo.


Sensor Dome - Sound Test from Matt Richard on Vimeo.