Dream Box

 

This final project was inspired by La Monte Young’s Dream House (hence the name), however, not only I wanted to play with how sound travels through space like La Monte Young did, I also want whoever enters the space of my project to be able to control sound in the whole space. The only way I could think of for making this project come true was building a room (Dream Box), sound isolating it as much as I could, or at least blocking as many of the external sounds as was possible in the given circumstances, and then finding a way for the visitor of the box to be able to move sound through space. The main purpose of sound isolating the room was to make sure it was easy to hear how the sound played in the Dream Box moves within the space by swiping the wall. Swiping the wall is not quite real though, so I was faking it by having an ultrasonic rangefinder in one part of the wall, so that depending of different values(distance) the sound would travel from speaker to speaker.

Making this final come to life was a long journey that consisted of two parts:

1) Making a wooden box (a.k.a. the Dream Box)

2) Programming processing so that it knows what speaker to send the mp3 file to depending on the value from the ultrasonic rangefinder

While making the room (or the box) of wood was time consuming and did not required a lot of planning and calculating for the right dimensions of the foam and food I had to cut, it did not cause me much of a problem in a sense that it all went as planned.

The initial idea for making the walls was taking 25mm plywood and simply attaching a couple of pieces together, however, this proved to be very unsafe and extremely heavy. Knowing that, the plan then was to do the walls in the similar way theater flats are done. I took 1×4 inch stick lumber and made three frames  (I used the Arts Center wall as the 4th wall) for the walls that I later simply skinned with 6mm luan plywood. The dimensions of the room, which turned out to be a cube, were 244x244x244cm, which seemed quite small on paper; however, it felt really large once I saw it in life. At some point I even doubted myself whether I need to keep working on it, or whether making something that big would be a waste of time. But, as the saying goes, go big or go home, and I’m not home yet J.

Those are the flats I’ve built.

I thought that the coding part of this project would be a lot simpler and would not cause me any problems. So first I made a working prototype with Arduino. I have soldered four speakers and had the Arduino tell which speaker to make noise depending on the value I was getting from the analog sensor. However, after spending hours making this prototype to work, I was pointed out that it is nearly worthless to have a nice big room be filled with annoying buzz. And that was totally right, why would I want the visitor of the Dream Box experience any annoying sound when one is supposed to enjoy their time in the room playing with the directionality of sound, rather than get tired of a random tone that is being played?

Unfortunately for me, I could not play anything but tone using Arduino only, unless I was to use an MP3 shield, but I could not do that since I had 4 outputs (speakers), and an MP3 shield is only capable of playing through 2.

So I had to use processing for this, which was fine, I never expected anything to go wrong. However, it felt like everything that could go wrong went wrong. The first problem was that I did not have enough outputs for four speakers in my computer either, so I had to find an audio interface with 4 or more outputs. Once I had that, I did not know how to tell processing to play it through this exact device. And once there was a way, processing thought that the audio interface was merely a speaker. I did not know a way to tell processing that this device that it thinks is a speaker actually has 14 different outputs, so I had to teach it how to see all of those separate outputs. If not Aaron, I would never figure this out. Aaron provided me with the code that would let processing know which specific output to play the sound from using the audio interface. I thought the struggle would end, however, it has just begun, because the Beads library was a little confusing, and once I figured out how to make it all work the way I want to with one sound, I did not stop there. I though one sound might be too boring, so I decided to add more sounds so that the visitor of the Dream Box had more freedom when choosing the direction and the type of sound. Making it work with four different sounds was really hard, but eventually the battle was won.

There was another problem along the way as I was going to use infra-red rangefinder at first, but the values there were too inaccurate and unpredictable art times, so I had to change to the ultrasonic rangefinder, which is a bit harder to program.

This is the Arduino code

#define trigPin 13
#define echoPin 12
#define led 11
#define led2 10
int buttonPressPin = 8;
int buttonPress = 0;

boolean isAvailable = true;
void setup() {
Serial.begin (9600);
Serial.println('0');
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
pinMode(buttonPressPin, INPUT);
pinMode(led, OUTPUT);
pinMode(led2, OUTPUT);

}

void loop() {
buttonPress = digitalRead(buttonPressPin);
long duration, distance;

digitalWrite(trigPin, LOW); // Added this line
delayMicroseconds(2); // Added this line
digitalWrite(trigPin, HIGH);
// delayMicroseconds(1000); - Removed this line
delayMicroseconds(10); // Added this line
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH);
distance = (duration / 2) / 29.1;
if (distance < 4) { // This is where the LED On/Off happens
digitalWrite(led, HIGH); // When the Red condition is met, the Green LED should turn off
digitalWrite(led2, LOW);
}
else {
digitalWrite(led, LOW);
digitalWrite(led2, HIGH);
}
if (distance >= 200 || distance <= 0) {
//Serial.println("Out of range");
}
else {
Serial.print(distance);
Serial.print(",");
Serial.println(buttonPress);
//Serial.println(" cm");
}
delay(10);
if (digitalRead(buttonPressPin) == 1) {
if (isAvailable == true) {
buttonPress = 1;
isAvailable = false;
}
} else {
isAvailable = true;
}

}

Processing code

  • import beads.*;
    import org.jaudiolibs.beads.AudioServerIO;
    import java.util.Arrays; 
    
    AudioContext audioContext;
    IOAudioFormat audioFormat;
    float sampleRate = 44100;
    int buffer = 512;
    int bitDepth = 16;
    int inputs = 2;
    int outputs = 14; //set for soundflower now
    float speaker1Gain, speaker2Gain, speaker3Gain, speaker4Gain;
    float soundPos;
    int distance, previousDistance;
    int buttonPress;
    import processing.serial.*;
    String sourceFile1;
    String sourceFile2;
    String sourceFile3;
    String sourceFile4;
    boolean isPressed;
    int spCounter;
    SamplePlayer sp1;
    SamplePlayer sp2;
    SamplePlayer sp3;
    SamplePlayer sp4;
    Serial myPort;
    
    WavePlayer wp;
    
    Gain g1;
    Gain g2;
    Gain g3;
    Gain g4;
    
    Glide gainGlide1;
    Glide gainGlide2;
    Glide gainGlide3;
    Glide gainGlide4;
    
    Glide rateValue1;
    Glide rateValue2;
    
    boolean initializeSound = true;
    
    void setup() {
    size(640, 640);
    
    buttonPress = 0;
    spCounter = 0;
    isPressed = false;
    
    sourceFile1 = sketchPath("") + "fire.wav";
    sourceFile2 = sketchPath("") + "waves.mp3";
    sourceFile3 = sketchPath("") + "thunder.mp3";
    sourceFile4 = sketchPath("") + "birds.wav";
    audioFormat = new IOAudioFormat(sampleRate, bitDepth, inputs, outputs);
    audioContext = new AudioContext(new AudioServerIO.JavaSound(), buffer, audioFormat);
    println("no. of inputs: " + audioContext.getAudioInput().getOuts()); 
    println("no of outputs: " + audioContext.out.getIns()); 
    try {
    // initialize our SamplePlayer, loading the file
    // indicated by the sourceFile string
    sp1 = new SamplePlayer(audioContext, new Sample(sourceFile1));
    sp2 = new SamplePlayer(audioContext, new Sample(sourceFile2));
    sp3 = new SamplePlayer(audioContext, new Sample(sourceFile3));
    sp4 = new SamplePlayer(audioContext, new Sample(sourceFile4));
    }
    catch(Exception e)
    {
    // If there is an error, show an error message
    // at the bottom of the processing window.
    println("Exception while attempting to load sample!");
    e.printStackTrace(); // print description of the error
    exit(); // and exit the program
    }
    
    rateValue1 = new Glide(audioContext, 1, 50);
    rateValue2 = new Glide(audioContext, 1, 50);
    
    wp = new WavePlayer(audioContext, 400, Buffer.SINE);
    
    gainGlide1 = new Glide(audioContext, 0.0, 50);
    gainGlide2 = new Glide(audioContext, 0.0, 50);
    gainGlide3= new Glide(audioContext, 0.0, 50);
    gainGlide4= new Glide(audioContext, 0.0, 50);
    
    g1 = new Gain(audioContext, 2, gainGlide1);
    g2 = new Gain(audioContext, 2, gainGlide2);
    g3 = new Gain(audioContext, 2, gainGlide3);
    g4 = new Gain(audioContext, 2, gainGlide4);
    
    g1.addInput(sp1);
    g2.addInput(sp1);
    g3.addInput(sp1);
    g4.addInput(sp1);
    g1.addInput(sp2);
    g2.addInput(sp2);
    g3.addInput(sp2);
    g4.addInput(sp2);
    g1.addInput(sp3);
    g2.addInput(sp3);
    g3.addInput(sp3);
    g4.addInput(sp3);
    g1.addInput(sp4);
    g2.addInput(sp4);
    g3.addInput(sp4);
    g4.addInput(sp4);
    
    audioContext.out.addInput(0, g1, 0); // OUT 1
    audioContext.out.addInput(1, g2, 0); // OUT 2
    audioContext.out.addInput(2, g3, 0); // OUT 3
    audioContext.out.addInput(3, g4, 0); // OUT 4
    
    audioContext.start();
    
    // for IR rangefinder
    printArray(Serial.list());
    String portname=Serial.list()[2];
    println(portname);
    myPort = new Serial(this, portname, 9600);
    myPort.clear();
    myPort.bufferUntil('\n');
    
    color fore = color(255);
    color back = color(0);
    
    
    
    // SamplePlayer can be set to be destroyed when
    // it is done playing
    // this is useful when you want to load a number of
    // different samples, but only play each one once
    // in this case, we would like to play the sample multiple
    // times, so we set KillOnEnd to false
    sp1.setKillOnEnd(false);
    sp1.setToLoopStart();
    sp2.setKillOnEnd(false);
    sp2.setToLoopStart();
    sp3.setKillOnEnd(false);
    sp3.setToLoopStart();
    sp4.setKillOnEnd(false);
    sp4.setToLoopStart();
    sp1.start(); // play the audio file
    }
    void draw() {
    
    if (buttonPress==1) {
    spCounter++;
    
    if (spCounter==5) {
    spCounter=1;
    }
    
    if (spCounter==1) {
    sp1.setToEnd();
    sp2.setToEnd();
    sp3.setToEnd();
    sp4.setToEnd();
  •  
    
    
    sp1.setToLoopStart();
    sp1.start();
    //println(“————————–ending——————“);
    } else if (spCounter==2) {
    sp1.setToEnd();
    sp2.setToEnd();
    sp3.setToEnd();
    sp4.setToEnd();
    sp2.setToLoopStart();
    sp2.start();
    } else if (spCounter==3) {
    sp1.setToEnd();
    sp2.setToEnd();
    sp3.setToEnd();
    sp4.setToEnd();
    sp3.setToLoopStart();
    sp3.start();
    } else if (spCounter==4) {
    sp1.setToEnd();
    sp2.setToEnd();
    sp3.setToEnd();
    sp4.setToEnd();
    sp4.setToLoopStart();
    sp4.start();
    }
    }

    //if (initializeSound == true) {
    // spCounter = 1;
    // sp1.start();
    // initializeSound = false;
    //}

    //if (buttonPress==1) {
    // spCounter++;

    // if (spCounter==5) {
    // spCounter=1;
    // }

    // if (spCounter==1) {
    // sp1.setToEnd();
    // sp2.setToEnd();
    // sp3.setToEnd();
    // sp4.setToEnd();
    // sp1.setToLoopStart();
    // sp1.start();
    // //println(“————————–ending——————“);
    // } else if (spCounter==2) {
    // sp1.setToEnd();
    // sp2.setToEnd();
    // sp3.setToEnd();
    // sp4.setToEnd();
    // sp2.setToLoopStart();
    // sp2.start();
    // } else if (spCounter==3) {
    // sp1.setToEnd();
    // sp2.setToEnd();
    // sp3.setToEnd();
    // sp4.setToEnd();
    // sp3.setToLoopStart();
    // sp3.start();
    // } else if (spCounter==4) {
    // sp1.setToEnd();
    // sp2.setToEnd();
    // sp3.setToEnd();
    // sp4.setToEnd();
    // sp4.setToLoopStart();
    // sp4.start();
    // }
    //}

    if (distance>200 || distance<=5) {
    distance = previousDistance;
    } else if (distance>6 && distance<50) {
    soundPos=map(distance, 1, 50, 0, 1);
    speaker1Gain= sin((1-soundPos) * PI/2);
    speaker2Gain= sin(soundPos * PI/2);
    speaker3Gain = 0;
    speaker4Gain = 0;
    } else if (distance>51 && distance<100) {
    soundPos =map(distance, 51, 100, 0, 1);
    speaker2Gain= sin((1-soundPos) * PI/2);
    speaker3Gain= sin(soundPos * PI/2);
    speaker4Gain = 0;
    speaker1Gain = 0;
    } else if (distance>101 && distance<150) {
    soundPos = map(distance, 101, 150, 0, 1);
    speaker3Gain= sin((1-soundPos) * PI/2);
    speaker4Gain= sin(soundPos * PI/2);
    speaker1Gain = 0;
    speaker2Gain = 0;
    } else if (distance>151 && distance<200) {
    soundPos = map(distance, 151, 200, 0, 1);
    speaker4Gain= sin((1-soundPos) * PI/2);
    speaker1Gain= sin(soundPos * PI/2);
    speaker2Gain = 0;
    speaker3Gain = 0;
    }

    println(speaker1Gain, speaker2Gain, speaker3Gain, speaker4Gain);
    println(“spCounter: ” + spCounter);
    //if(distance>0||distance<640){
    //soundPos = map(distance,0,640,0,1);
    // speaker4Gain = 1;
    // speaker1Gain = 1;
    // speaker2Gain = 1;
    // speaker3Gain = 1;
    //}

    //gainGlide1.setValue(distance / (float)width);
    //gainGlide2.setValue(distance / (float)width);
    //gainGlide3.setValue(distance / (float)width);
    //gainGlide4.setValue(mouseY / (float)height);

    //println(distance,mouseY);

    gainGlide1.setValue(speaker1Gain);
    gainGlide2.setValue(speaker2Gain);
    gainGlide3.setValue(speaker3Gain);
    gainGlide4.setValue(speaker4Gain);

    //println(mouseX);
    //loadPixels();
    ////set the background
    //Arrays.fill(pixels, back);
    ////scan across the pixels
    //for (int j = 0; j<4; j++) {
    // for (int i = 0; i < width; i++) {
    // //for each pixel work out where in the current audio buffer we are
    // int buffIndex = i * audioContext.getBufferSize() / width;
    // //then work out the pixel height of the audio data at that point
    // int vOffset = (int)((1 + audioContext.out.getValue(j, buffIndex)) * (height/2 ));
    // //draw into Processing’s convenient 1-D array of pixels
    // vOffset = min(vOffset, height);
    // vOffset+=(int)map(j, 0, 3, -250, 250);
    // pixels[vOffset* height + i] = fore;
    // }
    //}
    //updatePixels();

    previousDistance = distance;
    }

    void serialEvent(Serial myPort) {
    String s=myPort.readStringUntil(‘\n’);
    s=trim(s);
    if (s!=null) {
    int values[]=int(split(s, ‘,’));
    if (values.length==2) {
    distance=(int)values[0];
    buttonPress = (int)values[1];
    }
    println(“dist: ” + distance + ” button: ” + buttonPress);
    myPort.write(‘0’);
    }
    }

    void mousePressed() {
    //if

  • 
    
    (buttonPress==1) {
    // spCounter++;
    
    // if (spCounter==5) {
    // spCounter=1;
    // }
    
    // if (spCounter==1) {
    // sp1.setToEnd();
    // sp2.setToEnd();
    // sp3.setToEnd();
    // sp4.setToEnd();
    // sp1.setToLoopStart();
    // sp1.start();
    // //println("--------------------------ending------------------");
    // } else if (spCounter==2) {
    // sp1.setToEnd();
    // sp2.setToEnd();
    // sp3.setToEnd();
    // sp4.setToEnd();
    // sp2.setToLoopStart();
    // sp2.start();
    // } else if (spCounter==3) {
    // sp1.setToEnd();
    // sp2.setToEnd();
    // sp3.setToEnd();
    // sp4.setToEnd();
    // sp3.setToLoopStart();
    // sp3.start();
    // } else if (spCounter==4) {
    // sp1.setToEnd();
    // sp2.setToEnd();
    // sp3.setToEnd();
    // sp4.setToEnd();
    // sp4.setToLoopStart();
    // sp4.start();
    // } 
    //} 
    
    
    
    //spCounter++;
    //println("--------------------------------------------" + spCounter);
    //if (spCounter==1) {
    // turn on 1
    //sp1.start();
    //// turn off 2
    //sp1.setToEnd();
    //sp2.setToEnd();
    //sp3.setToEnd();
    //sp4.setToEnd();
    //sp1.setToLoopStart();
    //sp1.start();
    
    //} else if (spCounter==2) {
    // // turn on 2
    // rateValue2.setValue(1);
    // sp2.setRate(rateValue2);
    // // turn off 1
    // //rateValue1.setValue(0);
    // //sp1.setRate(rateValue1);
    // //sp1.setToEnd();
    //}
    /*else if (spCounter==3) {
    sp3.start();
    } else if (spCounter==4) {
    sp4.start();
    } */
    //if (spCounter==5) {
    //if (spCounter==3) { // was 5
    // spCounter=1;
    //}
    }

Another thing why I decided to do the framing this way is so that I could place the rangefinder flat on the inside of the wall, and still have some space between the 6mm luan and the layer of Styrofoam I am going to place in on top of it.

Last step was to sound-isolate the room, after I made sure everything worked the way it should. I used 10cm thick Styrofoam to go along all walls to provide sound isolation. The top two pieces of Styrofoam were covered with a layer of 15mm plywood to keep the whole Dream Box in place. The plywood also had 4 round holes on every corner of the roof, which was to let me put the speakers through it. To make the sound come from the ceiling once one is inside the box, I carved out slots for the speakers to go to, so that the speakers would face downwards. I must say that cutting Styrofoam with a handsaw is not as easy as it looks.

This is the top view of the way the speakers were stuffed into the ceiling

And this is half of the roof top view to see how they are connected. I had to solder the extensions for the speakers as well.

So that the Styrofoam does not look ugly, and also to create a feeling of a Dream Box, I coated all of the interior of the box with red fabric, which made it even more similar to the Dream House.

This is how it looks on the inside:

The only comment I had from the people who went through the experience in the Dream Box was that the button was more attractive to them than the arrow, probably the swipe indication was not that clear, or maybe it is in the human nature that pressing buttons feels so pleasing. I think working a little more on the button design and the swipe signifier design would fix this issue. Other than that, I’ve seen and heard only positive feedback and I ended up being really proud of what I’ve built in a relatively short period of time as well as very happy with how this piece was accepted by the people who walked into it.

Here is the exterior of the Dream Box