The assembled projector

DSC_6674

DSC_6651

The synthesizer tin- unfortunately I didn’t have time to finish it. LEDs and buttons were routed to the other Arduino inside the projector enclosure to trigger different lighting modes.

DSC_6653  DSC_6664

The inside of the projector. The Arduino is screwed onto the frame. The focal length of the lens is totally adjustable via the wingnuts. Infra red sensor is attached to the frame in the left of the picture.

DSC_6663 DSC_6661 DSC_6658

Unfortunately this setup didn’t work properly! The new frame seems to be too rigid to transmit vibrations from the speaker to the tray properly, where our simpler frame worked really well. It also seems like the weight of the DMX lights (despite not being especially heavy) had a dampening effect. The only way we could get the wave patterns with the lights was to have the lights on a totally different surface, which shows we probably had some way to go to create a successful self-contained wave projector. We made a good a start though. Footage of the projector in action:

Test sensors Ultrasonic- Sharp

By Ana

Test of ultrasonic sensor:

We decided to use the Sharp IR sensor. The first one burned because was attached to a 20v power. I designed and 3D-printed a holder for the sensor which can be screwed onto the frame with a 5v source for the 2nd one

photo-4

photo-5

LED Strip + DMX + Sharp Sensor

 

 

 

Gp physcomp Gp physcomp schematic

CODE

 

#include <DmxSimple.h>
#include <SharpIR.h>
#define ir A1
#define model 1080
#define REDPIN 5
#define GREENPIN 6
#define BLUEPIN 3
SharpIR sharp(ir, 25, 93, model);

int xPos; // finger position
int RED, GREEN, BLUE; // values for MODE_1
int section=255; //Tray sensor value devided by 3
int delay1= 80; // delay for MODE_1
int delay2= 30; // delay for MODE_2
float brightness; //other sensor valure to affect the brightness
//int random1,random2,random3;
boolean start;

const int buttonPin = 0; // the number of the pushbutton pin

const int BUTTON1 = 1;
const int BUTTON2 = 2;
const int BUTTON3 = 3;

const int BUTTON1LOW = 560;
const int BUTTON1HIGH = 650;
const int BUTTON2LOW = 680;
const int BUTTON2HIGH = 850;
const int BUTTON3LOW = 950;
const int BUTTON3HIGH = 1000;

const int ledPin1 = 8; // the number of the LED pins on the synthesizer tin
const int ledPin2 = 10;
const int ledPin3 = 12;

// Variables will change:
int ledState = HIGH; // the current state of the output pin
int buttonState; // the current reading from the input pin
int lastButtonState = LOW; // the previous reading from the input pin

// the following variables are long’s because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long lastDebounceTime = 0; // the last time the output pin was toggled
long debounceDelay = 50; // the debounce time; increase if the output flickers

int trigger;
int mode2_stage = 0; // 0 to 5 – which colour channel transition we’re on
int mode2_iter = 0; // 0 to 255 – which iteration of that transition

void setup() {
pinMode(REDPIN, OUTPUT);
pinMode(GREENPIN, OUTPUT);
pinMode(BLUEPIN, OUTPUT);

pinMode(buttonPin, INPUT);
pinMode(ledPin1, OUTPUT);
pinMode(ledPin2, OUTPUT);
pinMode(ledPin3, OUTPUT);

pinMode (ir, INPUT);
DmxSimple.usePin(2);
Serial.begin(9600);
//setting variables
brightness=1;
xPos=0;
start= true;

MODE_0();
delay(1000); //hold on a sec :)

}

void loop(){

// read the state of the switch into a local variable:
int reading = analogRead(buttonPin);
Serial.println(buttonState);

//Not needed while getting the values.

int tmpButtonState = LOW; // the current reading from the input pin

if(reading>BUTTON3LOW && reading<BUTTON3HIGH){
//Read switch 3
tmpButtonState = BUTTON3;
} else if(reading>BUTTON2LOW && reading<BUTTON2HIGH){
//Read switch 2
tmpButtonState = BUTTON2;
} else if(reading>BUTTON1LOW && reading<BUTTON1HIGH){
//Read switch 1
tmpButtonState = BUTTON1;
}else{
//No button is pressed;
tmpButtonState = LOW;
}

// check to see if you just pressed the button
// (i.e. the input went from LOW to a buttonState), and you’ve waited
// long enough since the last press to ignore any noise:

// If the switch changed, due to noise or pressing:
if (tmpButtonState != lastButtonState) {
// reset the debouncing timer
lastDebounceTime = millis();
}

if ((millis() – lastDebounceTime) > debounceDelay) {
// whatever the reading is at, it’s been there for longer
// than the debounce delay, so take it as the actual current state:
buttonState = tmpButtonState;

digitalWrite(ledPin1, LOW); //turn off all LEDS before new trigger
digitalWrite(ledPin2, LOW);
digitalWrite(ledPin3, LOW);
switch(buttonState){
case BUTTON1:
trigger=1;
digitalWrite(ledPin1, HIGH);
break;
case BUTTON2:
// If we’re switching to mode 2, set the blue channel on full
if(trigger != 2) {
DmxSimple.write(3, 255);
analogWrite(BLUEPIN, 255);
}
trigger=2;
digitalWrite(ledPin2, HIGH);
break;
case BUTTON3:
trigger=3;
digitalWrite(ledPin3, HIGH);
break;
}
// Serial.println(buttonState);
}

// save the reading. Next time through the loop,
// it’ll be the lastButtonState:
lastButtonState = tmpButtonState;

Serial.println(trigger);

if(trigger== 1){
MODE_1();
}
if(trigger== 2){
MODE_2();
}
if(trigger== 3){
MODE_3();
}

}

//——————————————————————

void MODE_0(){ //start white
DmxSimple.write(1, 255);
DmxSimple.write(2, 255);
DmxSimple.write(3, 255);

analogWrite(REDPIN, 255);
analogWrite(GREENPIN, 255);
analogWrite(BLUEPIN, 255);
}

//——————————————————————

void MODE_1(){

xPos=sharp.distance(); // this returns the distance to the object you’re measuring
// Serial.println(xPos);
xPos= constrain(xPos,7,20);
xPos = map(xPos, 7, 22, 0, 765);//100-200Hz
// Serial.println(xPos);

if (xPos<section ) {
RED=map(xPos, 0, section, 255, 0);
GREEN=map(xPos, 0, section, 0, 255);
BLUE=0;
}
else if (xPos>=section && xPos<(section*2) ) {
RED=0;
GREEN=map(xPos, section, (section*2), 255, 0);
BLUE=map(xPos, section, (section*2), 0, 255);
}
else if (xPos>=(2*section) ) {
RED=map(xPos,(section*2), (section*3), 0, 255);
GREEN=0;
BLUE=map(xPos, (section*2), (section*3), 255, 0);
}

DmxSimple.write(1,RED*brightness);
DmxSimple.write(2,GREEN*brightness);
DmxSimple.write(3,BLUE*brightness);

analogWrite(REDPIN,RED*brightness);
analogWrite(GREENPIN,GREEN*brightness);
analogWrite(BLUEPIN,BLUE*brightness);

// DmxSimple.write(6,DIM);
delay(delay1);
//xPos+=1;
//brightness+=0.1;

// Just for monitoring
// Serial.println(xPos);
// Serial.println(RED);
// Serial.println(GREEN);
// Serial.println(BLUE);
if(xPos>(section*3)){
xPos=0;
}
}

//——————————————————————

void MODE_2(){

/* transition sequence by channel:

R G B
start 0 0 255
blue -> violet 255 0 255 + red
violet -> red 255 0 0 – blue
red -> yellow 255 255 0 + green
yellow -> green 0 255 0 – red
green -> cyan 0 255 255 + blue
cyan -> blue 0 0 255 – green

*/

mode2_iter += 1;

// if we’ve reached
if (mode2_iter > 255) {
mode2_iter = 0;
mode2_stage += 1;

if (mode2_stage > 5) {
mode2_stage = 0;
}
}

// blue to violet
if (mode2_stage == 0) {
DmxSimple.write(1, mode2_iter);
analogWrite(REDPIN, mode2_iter);
}

// violet to red
else if (mode2_stage == 1) {
DmxSimple.write(3, 255-mode2_iter);
analogWrite(BLUEPIN, 255-mode2_iter);
}

// red to yellow
else if (mode2_stage == 2) {
DmxSimple.write(2, mode2_iter);
analogWrite(GREENPIN, mode2_iter);
}

// yellow to green
else if (mode2_stage == 3) {
DmxSimple.write(1, 255-mode2_iter);
analogWrite(REDPIN, 255-mode2_iter);
}

// green to cyan
else if (mode2_stage == 4) {
DmxSimple.write(3, mode2_iter);
analogWrite(BLUEPIN, mode2_iter);
}

// cyan to blue
else if (mode2_stage == 5) {
DmxSimple.write(2, 255-mode2_iter);
analogWrite(GREENPIN, 255-mode2_iter);
}

delay(delay2);
}

//——————————————————————

void MODE_3(){
DmxSimple.write(6, 255); // SoundMode_ON

//In case SoundMode doesn’t work
analogWrite(REDPIN, random(0,255));
analogWrite(GREENPIN, random(0,255));
analogWrite(BLUEPIN, random(0,255));
// DmxSimple.write(5, random(14,255)); //StrobeEffect_ON
delay(random(180,1200));

}

– Giampaolo

Making the synthesizer enclosure

by Matilda

Originally I was planning to 3D print a custom enclosure for the synthesizer but getting it just right would have taken a lot of iterations with the number of holes needed for buttons, audio jacks etc, so I bought a tin instead. I cut a piece of polystyrene foam to go in the base of the tin and hold the circuits in place as well as preventing ground loops.

Inside the tin will feature:

– Arduino Uno, with holes for USB and power sockets

– 3 x push buttons, attached to 3 resistors of different values which produce difference voltage readings and read by an analog pin

– 3 x LEDs, corresponding to the button most recently triggered

– Audio circuit, with op-amp to amplify the audio signal to line level. 3.5mm socket. Initially we bought a PCB mount socket, but replaced that with a chassis-mount one in the final circuit so it can be mounted securely to the tin and withstand the pressure of plugging in an audio cable.

After discussing what function the buttons and infrared sensors should have, we’ve decided they will affect the DMX light controls. Each button will trigger a lighting mode written by GP. This means routing the connections from the buttons and LEDs to the second arduino mounted inside the projector enclosure with long wires, so there’s a hole in the tin for this too, and my button and LED code will be added to GP and Ana’s lighting and sensor code. Soldering and testing went quite smoothly, however we did manage to fry the LM-386 amplifier chip by accidentally running 12v of power through it- whoops!

The circuit layout:

Schematic:

Note the LEDs, button and a ground wire are connected to the second Arduino. We considered reading the voltages from the buttons on this Arduino and then routing the data to the other one via PWM, but this seemed unneccessarily complicated given that we’d need a wire to connect the two circuits either way.

 

Planning the layout of the tin:

 

The finished tin:

DSC_6686

Rear of tin with holes for 9v power, arduino USB, and 3.5mm socket.

DSC_6689

DMX_Coding::Update();

Working with the code for the lights which are going to cast patterns on the ceiling through water.

I decided to develop 3 different presets triggered by the synth buttons:

  • MODE_1 will let the user choose with the finger positioned on the tray the color.
  • MODE_2 will set an auto-fading to colours loop to let the user focus just on the water with some nice slow colour shading.
  • MODE_3 will set a random colour trigged by the built-in microphone.

In this video you can see MODE_2 and MODE_3:

Code:

#include <DmxSimple.h>

int xPos; // finger position
int RED, GREEN, BLUE; // values for MODE_1
int section=255; //Tray sensor value devided by 3
int delay1= 80; // delay for MODE_1
int delay2= 30; // delay for MODE_2
float brightness; //other sensor valure to affect the brightness
//int random1,random2,random3;
boolean start;

void setup() {
DmxSimple.usePin(2);
Serial.begin(9600);
//setting variables
brightness=1;
xPos=0;
start= true;

MODE_0();

}

void loop(){
// this portion is just for triggering manually each mode to test it
if (Serial.available()) {
MODE_3();
//MODE_2();
//MODE_1();
Serial.println(“trigged”);
}

//IT WILL BE LIKE:
// switch(buttonState){
//
case BUTTON1:
//MODE_1();break;
//
case BUTTON2:
//MODE_2();break;
//
case BUTTON3:
//MODE_3();break;
//
// }

}
//——————————————————————

void MODE_0(){ //start white
DmxSimple.write(1, 255);
DmxSimple.write(2, 255);
DmxSimple.write(3, 255);
}
//——————————————————————

void MODE_1(){
if (xPos<section ) {
RED=map(xPos, 0, section, 255, 0);
GREEN=map(xPos, 0, section, 0, 255);
BLUE=0;
}
else if (xPos>=section && xPos<(section*2) ) {
RED=0;
GREEN=map(xPos, section, (section*2), 255, 0);
BLUE=map(xPos, section, (section*2), 0, 255);
}
else if (xPos>=(2*section) ) {
RED=map(xPos,(section*2), (section*3), 0, 255);
GREEN=0;
BLUE=map(xPos, (section*2), (section*3), 255, 0);
}

DmxSimple.write(1,RED*brightness);
DmxSimple.write(2,GREEN*brightness);
DmxSimple.write(3,BLUE*brightness);

// DmxSimple.write(6,DIM);
delay(delay1);
xPos+=1;
//brightness+=0.1;

// Just for monitoring
// Serial.println(xPos);
// Serial.println(RED);
// Serial.println(GREEN);
// Serial.println(BLUE);
if(xPos>(section*3)){
xPos=0;
}

}

//——————————————————————

void MODE_2(){

int r, g, b;

if(start){
// only run at start
DmxSimple.write(3, 255);
Serial.println(“Started”);
start=false;
}

// blue to violet
for (r = 0; r < 256; r++) {
DmxSimple.write(1, r);
delay(delay2);
}
// violet to red
for (b = 255; b > 0; b–) {
DmxSimple.write(3, b);
delay(delay2);
}
// red to yellow
for (g = 0; g < 256; g++) {
DmxSimple.write(2, g);
delay(delay2);
}
// yellow to green
for (r = 255; r > 0; r–) {
DmxSimple.write(1, r);
delay(delay2);
}
// green to cyan
for (b = 0; b < 256; b++) {
DmxSimple.write(3, b);
delay(delay2);
}
// cyan to blue
for (g = 255; g > 0; g–) {
DmxSimple.write(2, g);
delay(delay2);
}
}

//——————————————————————

void MODE_3(){
DmxSimple.write(6, 255); // SoundMode_ON

// In case SoundMode doesn’t work
// DmxSimple.write(1, random(0,255));
// DmxSimple.write(2, random(0,255));
// DmxSimple.write(3, random(0,255));
// DmxSimple.write(5, random(14,255)); //StrobeEffect_ON
// delay(random(180,1200));

}

 

 

 

– Giampaolo

Composing with Mozzi

by Matilda

Code is based on Mozzi’s Oscillator Wash example. It uses 9 cos wave oscillators playing at different harmonic frequencies, each modified by a volume oscillator at frequencies varying between relatively high/fast, producing a ‘wah-wah-wah’ sound, and much slower periods of 30-50 seconds. These oscillators are grouped in threes and have a further volume oscillator applied to each grouping. This has the effect of introducing a lot of variation in how many oscillators are playing at once, which I did with the intention of building wave patterns which start off simple and become more complex as more oscillations are added. Unfortunately I had to make the frequency quite a lot lower than it was composed at in order to generate wave patterns effectively. If I get time I’m hoping to rework it with a broader range of harmonic frequencies, so we can get the benefit of the low frequencies which generate wave patterns and high ones which sound good.

Mozzi offers two levels of audio fidelity: 8-bit and 16-bit. The latter requires a second PWM pin, and I tested both circuits on a breadboard before deciding which to use. The dual-PWM pin circuit looked like this, as detailed in the Mozzi documentation:

Unfortunately this circuit didn’t play so well with the amplifier circuit, and caused the potentiometer on the amp to have no effect and for the overall volume to be considerably lower than with the single-pin circuit. We think this might be a phase-cancellation issue between the two PWM signals, but never managed to work out exactly what the problem was, so I added the single PWM-version of the circuit to the final layout. The audio quality is not perfect, with some crackles. This is a combination of overdrive and perhaps clicks produced by the chip trying to perform more calculations than it is able. The script is very resource intensive because it has so many oscillators, though I managed to improve it quite a lot by using more low-level operations like bitshifting. In spite of the crackle the audio is quite successful in creating an intense experience in which to enjoy the wave patterns, and the crunchiness adds to this in some ways. It sounds a lot more like a drone piece pitched down.

Composition at the original pitch:

Composition pitched down:

 

Code:

/*
Author: Matilda Skelton Mace
Sources:
- https://github.com/sensorium/Mozzi/blob/master/examples/02.Control/Control_Oscil_Wash/Control_Oscil_Wash.ino
- https://github.com/sensorium/Mozzi/blob/master/examples/11.Communication/Teensy_USB_MIDI_Input/Teensy_USB_MIDI_Input.ino
*/
 
// Arduino MIDI library
#include 
MIDI_CREATE_DEFAULT_INSTANCE();
 
// Mozzi library
#include 
#include  // oscillator template
#include  // for envelope
#include <tables/waveshape_chebyshev_4th_256_int8.h>
#include <tables/cos8192_int8.h>
#include 
#include 
#include 
 
// How often mozzi should run updateControl()
#define CONTROL_RATE 128
// Tweak how fast the control oscillators pulse
#define K_FREQ_MULTIPLIER 0.5f
 
// Main harmonic oscillators.
Oscil<COS8192_NUM_CELLS, AUDIO_RATE> aCos1(COS8192_DATA);
Oscil<COS8192_NUM_CELLS, AUDIO_RATE> aCos2(COS8192_DATA);
Oscil<COS8192_NUM_CELLS, AUDIO_RATE> aCos3(COS8192_DATA);
Oscil<COS8192_NUM_CELLS, AUDIO_RATE> aCos4(COS8192_DATA);
Oscil<COS8192_NUM_CELLS, AUDIO_RATE> aCos5(COS8192_DATA);
Oscil<COS8192_NUM_CELLS, AUDIO_RATE> aCos6(COS8192_DATA);
Oscil<COS8192_NUM_CELLS, AUDIO_RATE> aCos7(COS8192_DATA);
Oscil<COS8192_NUM_CELLS, AUDIO_RATE> aCos8(COS8192_DATA);
Oscil<COS8192_NUM_CELLS, AUDIO_RATE> aCos9(COS8192_DATA);
 
// Individual volume controls 
Oscil<CHEBYSHEV_4TH_256_NUM_CELLS, CONTROL_RATE> kVol1(CHEBYSHEV_4TH_256_DATA);
Oscil<CHEBYSHEV_4TH_256_NUM_CELLS, CONTROL_RATE> kVol2(CHEBYSHEV_4TH_256_DATA);
Oscil<CHEBYSHEV_4TH_256_NUM_CELLS, CONTROL_RATE> kVol3(CHEBYSHEV_4TH_256_DATA);
Oscil<CHEBYSHEV_4TH_256_NUM_CELLS, CONTROL_RATE> kVol4(CHEBYSHEV_4TH_256_DATA);
Oscil<CHEBYSHEV_4TH_256_NUM_CELLS, CONTROL_RATE> kVol5(CHEBYSHEV_4TH_256_DATA);
Oscil<CHEBYSHEV_4TH_256_NUM_CELLS, CONTROL_RATE> kVol6(CHEBYSHEV_4TH_256_DATA);
Oscil<CHEBYSHEV_4TH_256_NUM_CELLS, CONTROL_RATE> kVol7(CHEBYSHEV_4TH_256_DATA);
Oscil<CHEBYSHEV_4TH_256_NUM_CELLS, CONTROL_RATE> kVol8(CHEBYSHEV_4TH_256_DATA);
Oscil<CHEBYSHEV_4TH_256_NUM_CELLS, CONTROL_RATE> kVol9(CHEBYSHEV_4TH_256_DATA);
 
// Layered volume controls, each controlling 3 harmonic oscillators
Oscil<COS8192_NUM_CELLS, CONTROL_RATE> lVol1(COS8192_DATA);
Oscil<COS8192_NUM_CELLS, CONTROL_RATE> lVol2(COS8192_DATA);
Oscil<COS8192_NUM_CELLS, CONTROL_RATE> lVol3(COS8192_DATA);
 
// Audio volumes updated each control interrupt and reused in audio till next control
char v1,v2,v3,v4,v5,v6,v7,v8,v9,l1,l2,l3;
 
// Handles an event from the MIDI keyboard, which is useful in debugging and tweaking
void HandleNoteOn(byte channel, byte note, byte velocity) {
  // Set the harmonic oscillators to frequencies based off the MIDI note
  aCos1.setFreq_Q16n16(Q16n16_mtof(Q8n0_to_Q16n16(note + 0)));
  aCos2.setFreq_Q16n16(Q16n16_mtof(Q8n0_to_Q16n16(note + 14)));
  aCos3.setFreq_Q16n16(Q16n16_mtof(Q8n0_to_Q16n16(note + 4)));
  aCos4.setFreq_Q16n16(Q16n16_mtof(Q8n0_to_Q16n16(note + 17)));
  aCos5.setFreq_Q16n16(Q16n16_mtof(Q8n0_to_Q16n16(note + 7))); 
  aCos6.setFreq_Q16n16(Q16n16_mtof(Q8n0_to_Q16n16(note + 21))); 
  aCos7.setFreq_Q16n16(Q16n16_mtof(Q8n0_to_Q16n16(note - 7))); 
  aCos9.setFreq_Q16n16(Q16n16_mtof(Q8n0_to_Q16n16(note + 24)));
  aCos8.setFreq_Q16n16(Q16n16_mtof(Q8n0_to_Q16n16(note - 12)));
}
 
void setup() {
  // Initiate MIDI communications, listen to all channels
  MIDI.begin(MIDI_CHANNEL_OMNI);   
  Serial.begin(57600); 
 
  // HandleNoteOn will be run when we receive MIDI data
  MIDI.setHandleNoteOn(HandleNoteOn);
 
  // Simulate a MIDI note to get things started
  HandleNoteOn(0, 47, 0); // 64 --> 47
 
  // Set volume control frequencies
  kVol1.setFreq(4.43f); // more of a pulse
  kVol2.setFreq(K_FREQ_MULTIPLIER * 0.0245f);
  kVol3.setFreq(K_FREQ_MULTIPLIER * 0.019f);
  kVol4.setFreq(K_FREQ_MULTIPLIER * 0.07f);
  kVol5.setFreq(K_FREQ_MULTIPLIER * 0.047f);
  kVol6.setFreq(K_FREQ_MULTIPLIER * 0.031f);
  kVol7.setFreq(K_FREQ_MULTIPLIER * 0.0717f);
  kVol9.setFreq(K_FREQ_MULTIPLIER * 0.041f);
  kVol8.setFreq(1.71f);
 
  // Set layered volume control frequences - slow!
  lVol1.setFreq(0.013f);
  lVol2.setFreq(0.007f);
  lVol3.setFreq(0.004f);
 
  // Give these variables a value so updateAudio has something to go on
  v1=v2=v3=v4=v5=v6=v7=v8=v9=0;
  l1=l2=l3=0;
  
  startMozzi(CONTROL_RATE); 
}
 
 
void updateControl(){
  MIDI.read();
 
  // Read values from layered volume control
  l1 = lVol1.next();
  l2 = lVol2.next();
  l3 = lVol3.next();
  
  // Mask out some bits identifying which transition we're in
  // The first two bitmasks are modified to make the piece more interesting
  char l1a = l1 & 0x7F; // 01111111
  char l2a = l2 & 0x1F; // 00011111
  char l3a = l3 & 0x3F; // 00111111
 
  // Stage 1: v1 turning on (0 -> 63), v2 and v3 at 0
  if(l1 < -64) {     v1 = (l1a          * kVol2.next()) >> 7;
    v2 = (0            * kVol1.next()) >> 7;
    v3 = (0            * kVol3.next()) >> 7;
  // Stage 2, v1 turning on (64 -> 127), v2 turning on (0 -> 63), v3 at 0
  } else if (l1 < 0) {     v1 = (l1a          * kVol2.next()) >> 7;
    v2 = ((l1a - 64  ) * kVol1.next()) >> 7;
    v3 = (0            * kVol3.next()) >> 7;
  // Stage 3, v1 at 255, v2 turning on (64 -> 127), v3 turning on (0 -> 63)
  } else if (l1 < 64) {     v1 = (128          * kVol2.next()) >> 7;
    v2 = ((l1a + 64  ) * kVol1.next()) >> 7;
    v3 = (l1a          * kVol3.next()) >> 7;
  } else {
  // Stage 4: v1 and v2 at 255, v3 turning on (64 -> 127)
    v1 = (128          * kVol2.next()) >> 7;
    v2 = (128          * kVol1.next()) >> 7;
    v3 = (l1a          * kVol3.next()) >> 7;
  }
 
  // Same thing for the other 2 groups of 3 oscillators
  if(l2 < -64) {     v4 = (l2a          * kVol4.next()) >> 8;
    v5 = (0            * kVol5.next()) >> 8;
    v6 = (0            * kVol6.next()) >> 8;
  } else if (l2 < 0) {     v4 = (l2a          * kVol4.next()) >> 8;
    v5 = ((l2a - 64  ) * kVol5.next()) >> 8;
    v6 = (0            * kVol6.next()) >> 8;
  } else if (l2 < 64) {     v4 = (128          * kVol4.next()) >> 8;
    v5 = ((l2a + 64  ) * kVol5.next()) >> 8;
    v6 = (l2a          * kVol6.next()) >> 8;
  } else {
    v4 = (128          * kVol4.next()) >> 8;
    v5 = (128          * kVol5.next()) >> 8;
    v6 = (l2a          * kVol6.next()) >> 8;
  }
 
  if(l3 < -64) {     v7 = (l3a          * kVol7.next()) >> 8;
    v8 = (0            * kVol8.next()) >> 8;
    v9 = (0            * kVol9.next()) >> 8;
  } else if (l3 < 0) {     v7 = (l3a          * kVol7.next()) >> 8;
    v8 = ((l3a - 64  ) * kVol8.next()) >> 8;
    v9 = (0            * kVol9.next()) >> 8;
  } else if (l3 < 64) {     v7 = (128          * kVol7.next()) >> 8;
    v8 = ((l3a + 64  ) * kVol8.next()) >> 8;
    v9 = (l3a          * kVol9.next()) >> 8;
  } else {
    v7 = (128          * kVol7.next()) >> 8;
    v8 = (128          * kVol8.next()) >> 8;
    v9 = (l3a          * kVol9.next()) >> 8;
  }
 
}
 
 
int updateAudio(){
  // for every place bitshifted to the right, number is divided by two
  // same as knocking a 0 off a decimal number
  
  // add up all the oscillators and apply the volume controls
  long acog = (long)
    (aCos1.next()*v1) +
    (aCos2.next()*v2) +
    (aCos3.next()*v3) +
    (aCos4.next()*v4) +
    (aCos5.next()*v5) +
    (aCos6.next()*v6) +
    (aCos7.next()*v7) +
    (aCos8.next()*v8) +
    (aCos9.next()*v9);
  acog >>= 9;
  return (int)acog;
}
 
 
void loop() {
  audioHook(); // required here
} 

Testing with the water tray:

Building the projector enclosure

by matilda

 

wave projector frame

wave projector frame2

Positioning the lights

DSC_6637

Adding struts to rest the lights on, which also turn out to be useful for cable routing.

DSC_6638 DSC_6643

We still have the fresnel lens to mount above the tray and focus the wave pattern image on the ceiling. Originally we were using a roughly A5 size lens, but the water tray is larger, so we recycled the lens from an old overhead projector, which luckily was just a bit larger than the tray- just enough for room to drill a hole either side to mount the lens.

It’s important to have the means to adjust the focal length by small increments to get the best quality image, which will also vary depending on the height of the ceiling, so I couldn’t just build a frame of fixed height to hold it. I found two 50cm threaded rods in Wickes, with wing nuts to sandwich the lens between and hold it firm, which is a pretty perfect solution. The lens is much more powerful than the original A5 lens, and so it sits much closer to the water tray.

 

We need to mount the IR sensor on the frame to detect when people put their hands in the water. Ana 3D printed a custom enclosure with screw holes to attach it with.

We’re now using just one infrared sensor.

Week 8

DMXLights vs LEDstrip

Today  we had to make our final decision in terms of lights.

DMX Lights

GOOD:

  • Narrow 30º cone for a deeper reachability
  • More coding features thanks to the 6channels communication protocol

BAD:

  • Bulky
  • Delicate
  • Need a Shield which covers the whole Arduino hardware

 

LED Light Strip

GOOD:

  • More raw,no external libraries or shield
  • The 3LEDs are so close to each other that you cant see the red, blue, green led alone
  • Really compact and waterproof

BAD:

  • Approx 160º of lighting angle

 

As you can notice, the LED strip is NOT able to make the waves visible on the ceiling, therefore we have one only way to go: DMX!

 

– Giampaolo

Week 7 experiments

This week our main success was realising all the wave patterns come from the transmission of the sound as vibration through the table and wooden frame the tray is on, none of it comes from the air vibration as we previously thought, so we don’t need to have the speaker cone underneath the water tray at all! This is great news as it means we can have the cone next to the frame, the lights underneath the tray and make a much more compact and neat projector setup.

We have broken the project into 3 main areas for each of us to work on- GP is going to concentrate on the lighting, Matilda on the synthesizer and Ana on the infrared sensor.

P1160006

Images by ana made using marbling inks on the water surface, following on from the ink and oil experiments. The inks are supposed to “capture” the water vibrations made by sound.P1160014 P1160015

Input #1- infrared sensor

We are going to use an infrared distance sensor mounted on the water tray to detect if people put their hand in the water- values generated from this will be fed into the synthesizer code to change some aspect of the sound.

Ana’s SHARP code:

#include <SharpIR.h>

#define ir A1
#define model 1080

boolean done=false;
SharpIR sharp(ir, 25, 93, model);

// ir: the pin where your sensor is attached
// 25: the number of readings the library will make before calculating a mean distance
// 93: the difference between two consecutive measurements to be taken as valid
// model: an int that determines your sensor: 1080 for GP2Y0A21Y
// 20150 for GP2Y0A02Y
// (working distance range according to the datasheets)
void setup(){

Serial.begin(9600);
pinMode (ir, INPUT);

}
void loop(){
delay(20); // it gives you time to open the serial monitor after you upload the sketch
int dis=sharp.distance(); // this returns the distance to the object you're measuring

Serial.println(dis);

dis = map(dis, 10, 35, 100, 200);//100-200Hz
tone(9, dis);

}

Testing the sensor: