/*
Processing sketch for interactive artwork "Ascending/Descending", located at the US check-in area in the Halifax International Airport.
The sketch plays the video of an elavator door that remains closed until someone approaches the video and stands on a custom-made sensor.
The sketch listens to the serial port for a signal from an Arduino. The signal is sent when someone stands on a custom-made normally-open
switch that is concealed under a mat in front of the projection screen.
Considerations are being made to substitute the Arduino with a direct connection from the normally open switch to a key on a keyboard,
thus changing the video a keyPressed function.
This sketch is possible because of the existing resources
http://processing.org/learning/topics/sequential.html: for playing the image of the elevator door opening as a sequence of images.
This was a necessary alternative to loading the elevator door as a video. Loading it as a video required too much processing power
and resulted on a vert slow performance of the sketch.
http://code.compartmental.net/tools/minim/: for
*/
// import the minim library for playing sounds
// and define objects for playing sounds
import ddf.minim.*;
AudioSnippet openingSound;
AudioSnippet closingSound;
AudioSnippet openSound;
AudioSnippet closedSound;
Minim minim;
//the following libraries and variables control position of the mouse
import java.awt.Robot;
import java.awt.AWTException;
int xx = 10, yy = 10;
Robot robby;
//Import serial library to communicate with Arduino
import processing.serial.*;
// The following variables are for uploading the series of .jpgs
// needed to play the videos.
int numFrames_opening = 187; // The number of frames in the animation
int numFrames_closed = 188; // The number of frames in the animation
int numFrames_closing = 169; // The number of frames in the animation
int numFrames_open = 47; // The number of frames in the animation
int frame_opening = 0;
int frame_closed = 0;
int frame_closing = 0;
int frame_open = 0;
PImage[] images_opening = new PImage[numFrames_opening];
PImage[] images_closed = new PImage[numFrames_closed];
PImage[] images_closing = new PImage[numFrames_closing];
PImage[] images_open = new PImage[numFrames_open];
int fr = 30;// set framerate for reading the images
// width and height should be set here
int xWidth = 1280;
int yHeight = 800;
// these variables are for the serial port connection object
boolean output = true;
Serial port;
// String portname = "/dev/tty.usbserial-00002006"; This is not necessary anymore note later how the name of the usb is assigned
int baudrate = 9600; // set baudrate here
int value; // variables used to store value from serial port
String buf=""; // String buffer to store serial values
int value1; // value1 is the read value
// the serial event function takes the value of the event
// and store it in the corresponding variable
void serialEvent(int serial) {
if(serial!=10) {
buf += char(serial);
}
else {
//extract the value from the string 'buf'
// buf = buf.substring(1,buf.length());
//cast the value to an integer
value1 = int(buf);
buf="";
}
}
// this is the threshold for activating the animations; not needed
// here because this value was only useful for distance sensors
//int threshold = 800;
// The role of these booleans will be explained later
boolean openingSwitch = true;
boolean closingSwitch = true;
void setup() {
//setup size and framerate
size(xWidth, yHeight);
frameRate(fr);
//Initialize robot that contros the mouse
try
{
robby = new Robot();
}
catch (AWTException e)
{
println("Robot class not supported by your system!");
exit();
}
// Begin communication with USB port
println("Available serial ports:") // Begin by showing all available ports
println(Serial.list()); // See which USB port is used by Arduino
port = new Serial(this, Serial.list()[0], baudrate); //Communicate with that port
println(port);
// Initialize Minim Library and load sounds as Snippets
minim = new Minim(this);
openingSound = minim.loadSnippet("opening.aif");
openSound = minim.loadSnippet("open.aif");
closingSound = minim.loadSnippet("closing.aif");
closedSound = minim.loadSnippet("closed.aif");
// The following series of 'for' statements load all of the .jpgs used for
// the videos. Not sure how everything works. Code appropriated from Processing
// Reference library.
// the nomenclature of the files is (title of action) + (number) + .jpg ... For example Elevator_opening003.jpg
for(int i=0; i<numFrames_opening; i++) {
String imageName_opening = "Elevator_opening" + nf(i, 3) + ".jpg"; // nf(i,3) determines somehow the amount of numbers in the image number on the file
// for example, all "Elevator_opening" files have 3 numbers that go from 000 to 187
// therefore the number "3"
images_opening[i] = loadImage(imageName_opening);
}
for(int i=0; i<numFrames_closed; i++) {
String imageName_closed = "Elevator_closed" + nf(i, 3) + ".jpg";
images_closed[i] = loadImage(imageName_closed);
}
for(int i=0; i<numFrames_closing; i++) {
String imageName_closing = "Elevator_closing" + nf(i, 3) + ".jpg";
images_closing[i] = loadImage(imageName_closing);
}
for(int i=0; i<numFrames_open; i++) {
String imageName_open = "Elevator_open" + nf(i, 2) + ".jpg"; // all "Elevator_open" files only use 2 numbers, therefore the number in nf(i,2) is "2"
images_open[i] = loadImage(imageName_open);
}
}
void draw() {
// Use the robot to snap the mouse on location
robby.mouseMove(xx, yy);
// draw listens to serial port, and if there is no response from an Arduino
// it remains trapped in a while loop
while(port.available() > 0) {
value = port.read();
serialEvent(value);
}
// Display the values received from the Arduino as stored in the variable value1
if(output) println(value1);
//The following 'if' statements pay attention to value1 to decide which videos to play/////////
if (value1==1) { // if someone enters...
closingSwitch = true; // Prepare this boolean for the closing part.
// This boolean as well as "openingSwitch" allows for the
// closing and opening animations to occur.
// By stablishing it as "true" you're preparing it when someone exits or enter the sensor.
// The program will look for this boolean when someone enters after it checks
// whether the animation is halfway closing or halfway opening.
// After it finishes the incomplete animation (if it has to) it begins playing the closing
// or opening action - depending on whether the person is off or on the mat.
// The boolean functions as a switch that turns to false when the animation finishes closing
// or opening. This then triggers the following action: while someone remains either on top or off
// the mat, the false boolean indicates that all animations are finished and that the loop of
// the door closed or open should come into effect.
if (frame_closing>0) { // If the string of images if above 0, this means that the animation was halfway closing when someone entered the mat
frame_closing = (frame_closing+1); // continue loading string of images
image(images_closing[frame_closing], 0,0,1280,800); // place the image
if (frame_closing>167) { // when the animation reaches its last frame...
closingSound.pause();//pause sound...
closingSound.rewind(); //...rewind sound
frame_closing = 0; //... reset to 0 and make this if statement invalid
}
}
// Begin opening sequence
else if (frame_closing == 0 && openingSwitch == true) { //notice how it make sures that the closing sequence is at 0 and that the boolean is TRUE
closedSound.pause(); //shuts down this sound
openingSound.play(); // plays sound of opening door
frame_opening = (frame_opening+1); // starts adding number to the string of images that make up this animation
image(images_opening[frame_opening], 0,0,1280,800);// places the jpg
if (frame_opening > 185) {// if it reaches this number of images, it means the animation is coming to an end.
openingSound.pause(); // stop the sount
openingSound.rewind(); // rewind it
frame_opening = 0; // rewind the opening frame back to 0
openingSwitch = false; // declare this boolean False to make if statement invalid
}
// if the person remains within range
}
else if (frame_opening == 0 && openingSwitch == false) { // note how it's looking for the boolean to be False
if (openSound.isPlaying() == false)
{
openSound.loop();// loop sound
}
frame_open = (frame_open+1) % numFrames_open; // this line of code loops the images that compose the loop the door open, use % to cycle
image(images_open[frame_open], 0,0,1280,800); // place the jpgs that make up this animation
}
}
//do the same for when value from the switch reads 0 (=no one is activating the sensor)
else if (value1==0) {
openingSwitch = true;
if (frame_opening>0) {// check if the door is in the middle of opening, and finish doing that if necessary
frame_opening = (frame_opening+1);
image(images_opening[frame_opening], 0,0,1280,800);
if (frame_opening > 185) {
openingSound.pause();
openingSound.rewind();
frame_opening = 0;
}
}
else if (frame_opening == 0 && closingSwitch == true) { //if door is open, start closing the door
openSound.pause();
closingSound.play();
frame_closing = (frame_closing+1);
image(images_closing[frame_closing], 0,0,1280,800);
if (frame_closing>167) {// if door closes: stop, rewind, and go to loop
closingSound.pause();
closingSound.rewind();
frame_closing = 0;
closingSwitch = false;
}
}
else if (frame_closing == 0 && closingSwitch == false) {// no one is within range, so play closed door loop.
if (closedSound.isPlaying() == false)
{
closedSound.loop();
}
frame_closed = (frame_closed+1) % numFrames_closed; // Use % to cycle through frames
image(images_closed[frame_closed], 0,0,1280,800);
}
}
}
void stop()
{
// always close Minim audio classes when you are done with them
openingSound.close();
openSound.close();
closingSound.close();
closedSound.close();
minim.stop();
super.stop();
}