Team Members: Dan Armendariz, Jordan Arnesen, Cindy Bayley, Pin-i Chen, Seema Hari, and Wook Lee
Description:
The goal of this lab was to invent a new musical instrument. Our inspiration for this instrument was to manipulate water to create sound. The structure of our musical instrument is a miniature version of a water fountain. Our instrument is collaborative and invites many people to 'play' it at once. The concept is for people to gather around it and to manipulate the water and ping pong balls to make sound.
How it works: A user pours water onto the top of the dome. There are six photocells attached under the base which depending on their values generate sound. Ping pong balls float in the water and block the light reaching the photocells, which contributes to sound being generated.
In total there are six input devices (photocells) and one output device (sound generated from the laptop). We are using serial communication to create sound through the laptop sound system.
The code, "synthesize", plays a C-major dominant ninth chord (with an additional high-octave C since we have 6 photocells) at all times. The value from the photocells determine the amplitude of individual notes within the chord.
Components Used:
1 - Arduino Uno
1 - Breadboard
6 - Photocells
6 - 10k Resistors
1 - Laptop
Processing
wires
Materials used to create the fountain:
Two plastic domes, plastic base, tubing, toilet paper rolls (splash guard for the wiring), cups (as a stand for the base), tub (to collect the falling water), ping pong balls
Note: The base was spray painted to focus the light reaching the photocells. Also, we cut slots into the base to let the water more easily drain into the tub. We used tape as a form of waterproofing.
Code:
Arduino:
* Data output for instrument. This just outputs
* raw data from all 6 photocells into a single line.
*/
void setup() {
Serial.begin(9600);
}
// output raw values from every input pin separated by spaces
void loop() {
for(int i = 0; i < 6; i++) {
Serial.print(analogRead(i));
Serial.print(" ");
}
Serial.println();
delay(50);
}
Synthesize:
*
* Chord generation. Plays a C-major dominant 9th with an additional higher-octave C
* to make 6 notes playing at a time. Each note's amplitude is affected by
* input from the photocells.
* Enable MOUSE_DEBUG mode and press the 1-6 keys while moving your mouse
* around the visualization to get a sense of what would happen.
*/
import ddf.minim.*;
import ddf.minim.ugens.*;
import processing.serial.*;
// audio
Minim minim;
AudioOutput out;
Oscil note[] = new Oscil[6];
// data
int input[] = new int[6];
int max[] = new int[6];
int min[] = new int[6];
// visualization
float r = 250;
float theta, theta_v = .005;
// Serial port
String portname = "/dev/tty.usbserial-AM01QP9F";
Serial port;
String buf="";
int offset=0; // used to iterate over the input array when inputting data
int cr = 13; // ASCII return == 13
int lf = 10; // ASCII linefeed == 10
int sp = 32; // ASCII space == 32
// debug mode?
boolean MOUSE_DEBUG = true;
// setup is run once at the beginning
void setup()
{
// initialize the drawing window
size( 1024, 700, P3D );
// initialize the minim and out objects
minim = new Minim( this );
out = minim.getLineOut();
// C major chord (change to Eb4 and Bb4 for minor)
String[] chord = { "C4", "E4", "G4", "B4", "D5", "C5" };
for(int i = 0; i < 6; i++) {
println(Frequency.ofPitch(chord[i]).asHz());
// generate a new wave for our note
note[i] = new Oscil( Frequency.ofPitch(chord[i]), 0.01f, Waves.SINE );
// add the note to the output
note[i].patch( out );
}
stroke(253, 0, 6);
fill(255, 171, 0);
port = new Serial(this, portname, 9600);
// reset min and max
for(int i = 0; i < min.length; i++) {
min[i] = 1023;
max[i] = 0;
}
}
// draw is run many times
void draw()
{
background(0, 153, 153);
// draw the waveforms; this waveform visualization code from the Minim package examples
for( int i = 0; i < out.bufferSize() - 1; i++ )
{
// find the x position of each buffer value
float x1 = map( i, 0, out.bufferSize(), 0, width );
float x2 = map( i+1, 0, out.bufferSize(), 0, width );
// draw a line from one buffer position to the next for both channels
line( x1, height/2+out.left.get(i)*50, x2, height/2+(out.left.get(i+1)+out.left.get(i+1))*25);
}
// draw 6 evenly-spaced circles rotating around a radius
translate(width/2, height/2);
for( int i = 0; i < 6; i++ ) {
float theta_x = theta + 6.283*i/6;
//ellipse(r*cos(theta_x), r*sin(theta_x), 256-(input[i]>>2), 256-(input[i]>>2));
ellipse(r*cos(theta_x), r*sin(theta_x), abs(max[i]-input[i])>>2, abs(max[i]-input[i])>>2);
}
theta += theta_v;
}
// remodulate the tones with the input data
void remodulate()
{
println("remodulating");
float modAmplitude;
for(int i=0; i < 6; i++) {
modAmplitude = map( input[i], min[i], max[i], 0.16, 0);
note[i].setAmplitude(modAmplitude);
}
}
void mouseMoved()
{
float modAmplitude = map( mouseY, 0, height, 0.16, 0.0);
if (keyPressed && MOUSE_DEBUG && key >= '1' && key <= '6')
note[key-'1'].setAmplitude(modAmplitude);
}
// called whenever serial data arrives
void serialEvent(Serial p) {
int c = port.read();
// read data into the buffer until line break or space
if (c != lf && c != cr && c != sp)
{
buf += char(c);
} else if (c == lf || c == cr || offset >= input.length) {
if (offset != input.length && offset != 0)
println("ERROR: Received " + offset + " vals, expected " + input.length);
remodulate();
offset = 0;
} else {
int val = int(buf);
// at a break, dump the buffer into the input
input[offset] = val;
if(val > max[offset]) max[offset] = val;
if(val < min[offset]) min[offset] = val;
println("i[" + offset + "]: " + input[offset]);
buf = "";
offset++;
}
}
Modulation:
* Frequency modulation. Plays a C-major triad Chord (C4, E4, G4, below concert A)
* and each note's sine wave is modulated by a pair of inputs; one of the pair
* will modulate the frequency, the other the amplitude.
* Enable MOUSE_DEBUG mode and press the 1, 2, or 3 keys while moving your mouse
* around the visualization to get a sense of what would happen.
*/
import ddf.minim.*;
import ddf.minim.ugens.*;
import processing.serial.*;
// audio
Minim minim;
AudioOutput out;
Oscil modulator[] = new Oscil[3];
// data
int input[] = new int[6];
// visualization
float r = 250;
float theta, theta_v = .005;
// Serial port
String portname = "/dev/tty.usbmodem1421";
Serial port;
String buf="";
int offset=0; // used to iterate over the input array when inputting data
int cr = 13; // ASCII return == 13
int lf = 10; // ASCII linefeed == 10
int sp = 32; // ASCII space == 32
// debug mode?
boolean MOUSE_DEBUG = false;
// setup is run once at the beginning
void setup()
{
// initialize the drawing window
size( 1024, 700, P3D );
// initialize the minim and out objects
minim = new Minim( this );
out = minim.getLineOut();
// C major triad chord frequencies
String[] chord = { "C4", "E4", "G4" };
Oscil wave;
for(int i = 0; i < 3; i++) {
Frequency f = Frequency.ofPitch(chord[i]);
// generate a new wave for our note
wave = new Oscil( f, 0.3, Waves.SINE );
// add a modulator for that wave
modulator[i] = new Oscil( 10, 2, Waves.SINE );
modulator[i].offset.setLastValue( f.asHz() );
modulator[i].patch( wave.frequency );
// add the modulated wave to the output
wave.patch( out );
}
stroke(253, 0, 6);
fill(255, 171, 0);
}
// draw is run many times
void draw()
{
background(0, 153, 153);
// draw the waveforms; this waveform visualization code from the Minim package examples
for( int i = 0; i < out.bufferSize() - 1; i++ )
{
// find the x position of each buffer value
float x1 = map( i, 0, out.bufferSize(), 0, width );
float x2 = map( i+1, 0, out.bufferSize(), 0, width );
// draw a line from one buffer position to the next for both channels
line( x1, height/2+out.left.get(i)*50, x2, height/2+(out.left.get(i+1)+out.left.get(i+1))*25);
}
// draw 6 evenly-spaced circles rotating around a radius
translate(width/2, height/2);
for( int i = 0; i < 6; i++ ) {
float theta_x = theta + 6.283*i/6;
ellipse(r*cos(theta_x), r*sin(theta_x), 256-(input[i]>>2), 256-(input[i]>>2));
}
theta += theta_v;
}
// remodulate the tones with the input data
void remodulate()
{
float modAmount, modFrequency;
for(int i=0; i < 3; i++) {
modFrequency = map( input[2*i], 0, 1023, 100, 0.1 );
modAmount = map( input[2*i+1], 0, 1023, 1, 220 );
modulator[i].setFrequency( modFrequency );
modulator[i].setAmplitude( modAmount );
}
}
void mouseMoved()
{
float modulateAmount = map( mouseY, 0, height, 220, 1 );
float modulateFrequency = map( mouseX, 0, width, 0.1, 100 );
int i = -1;
if(keyPressed && MOUSE_DEBUG) {
if(key == '1') i = 0;
else if(key == '2') i = 1;
else if(key == '3') i = 2;
}
if (!MOUSE_DEBUG || i < 0) return;
modulator[i].setFrequency( modulateFrequency );
modulator[i].setAmplitude( modulateAmount );
}
// called whenever serial data arrives
void serialEvent(Serial p) {
int c = port.read();
// read data into the buffer until line break or space
if (c != lf && c != cr && c != sp)
{
buf += char(c);
} else {
// at a break, dump the buffer into the input
input[offset] = int(buf);
println("i[" + offset + "]: " + input[offset]);
buf = "";
offset++;
// reset the input counter on line break
if (c == lf) {
if (offset != input.length)
println("ERROR: Received " + offset + " vals, expected " + input.length);
remodulate();
offset = 0;
}
}
}
- Login to post comments