Bobbing Melody

So finally, I present to you my magical, whimsical, and bobbing art piece that will let you make your own melody.  So here’s the Bobbing melody in action.

 

To explain the implementation, here is the Arduino circuit.

The potentiometer values are mapped to create a color wheel (the red knob) that will control the color of visualization in real-time. The values of the ultrasonic sensor fitted in the mysterious black box are mapped into the x-axis for visualization. So the hand position in front of the screen is the position of the new balls dropping from the top of the screen. In visualization, these balls bounce at the bottom of the screen and upon hitting the bottom, the color of the just hit ball changes to yellow to eventually later fade back into its original color.  The whole screen is divided into twenty-one different notes of a piano and the x position of the hit ball determines the corresponding note to play. The music for piano is implemented using MIDI library and using tools like sforzando and loopMIDI.

While this project was very challenging and frustrating at times to finish, I surely had a great time working on it.  I am really glad how it turned out. A lot of the parts, especially the visualization was a trial and error process. I played around with a lot of color schemes and the speed and frame rate, took a lot of time, but finalized on my current settings and I love it. I hope you enjoyed this too.  If time permits, I would love to work on this project more to have various different instruments and visualizations to create a whole tune using different musical instruments and different visualization. Surely, this project and the class have inspired me a lot creatively.

Below is the code for Processing and Arduino.

//PROCESSING
//Serial connection and sound
import processing.serial.*;
import themidibus.*;
MidiBus myBus;
Serial myPort;

//variables for color mapping
int count = 400;
float radToDeg = 180.0 / PI;
float countToRad = TWO_PI / float(count);
float currentcolor;
float Xslider;

boolean fade = false;

//array list for balls
ArrayList<Ball> balls;

//sounds 21
int[] pitches = {48,52,53,55,59,60,64,65,67,71,72,76,77,79,83,84,88,89,91,95,96};


void setup() {
  fullScreen();
  //size(800,600);
  noStroke();
  frameRate(54);
  
  //port communication
  String portname=Serial.list()[0];
  myPort = new Serial(this,portname,9600);
  
  //midibus 
  myBus = new MidiBus(this,-1,"loopMIDI Port");
  
  // Create an empty ArrayList (will store Ball objects)
  balls = new ArrayList<Ball>();
  
  
  //color mode
  colorMode(HSB,360,99,99);
    
}

void draw() {
  background(0,0,0);
  
  //adding new balls on screen
  if(fade == false){
    addBalls(1);
  }
  
  
  //display all balls in action
  for (int i = balls.size()-1; i >= 0; i--) { 
    Ball ball = balls.get(i);
    if(ball.colorhue != 0){
      ball.move();
    }
    if(ball.colorhue != 0){
      ball.display();
    }
    
    //if no motion or very away from slider, reduce their life
    if(ball.x <= Xslider-width/6|| ball.x >= Xslider+width/6 || fade==true){
      if(ball.life > 100){
        ball.life = 100;
      }
    }
    
    //sound and colorshift on bouncing back from bottom
    if(ball.y >= height){
      ball.colorhue = 60;
      int numsound = int(map(ball.x,0,width,0,19));
      if(numsound<0 || numsound>20){
        numsound = abs(numsound%21);
      }
      if(ball.life<100){
        myBus.sendNoteOn(0,pitches[numsound],50);
      }
      else{
        myBus.sendNoteOn(0,pitches[numsound],127);
      }
    }
    
    //set the color back to orignal
    if(ball.y < height-height/9){
      ball.colorhue = ball.originalhue;
    }
    
    //remove the balls from array with finished lives
    if (ball.finished()) {
      balls.remove(i);
    } 
  }
}


// A new set of x ball objects are added to the ArrayList
void addBalls(int count) {
  for(int i=0; i<count; i++){
    float randomS = random(0,10);
    float ballWidth = random(4,30);
    float xPos = random(Xslider-width/20, Xslider+width/20);
    float yPos = random(-20,0);
    float angle = currentcolor * countToRad;
    int hue = int(angle * radToDeg);
    balls.add(new Ball(xPos, yPos, ballWidth,randomS,hue));
  }
}

//Serial Communication
void serialEvent(Serial myPort){
  String s=myPort.readStringUntil('\n');
  s=trim(s);
  if (s!=null){
    
    int values[]=int(split(s,','));
    if (values.length==2){
      currentcolor = map(values[0],19,925,380,290);
      print(values[0]);
      print(",");
      println(values[1]);
      if(values[1]>150 && values[1]<3000){
        fade= false;
        Xslider= map(values[1],150,3000,width, 0);
      }
      else{
        fade = true;
      }
    }
  }
}


// Simple bouncing ball class

class Ball {
  
  float x;
  float y;
  float speed;
  float acceleration;
  float w;
  float life = 255;
  int colorhue;
  int originalhue;
  float drag;
  boolean bounced= false;
  
  Ball(float tempX, float tempY, float tempW, float tempS, int hue) {
    x = tempX;
    y = tempY;
    w = tempW;
    speed = tempS;
    colorhue = hue;
    originalhue = hue;
    acceleration = 0.3;
    drag = 0.99;
  }
  
  void move() {
    //add speed
    speed = speed + acceleration;
    speed = speed * drag;
    
    // Add speed to y location
    y = y + speed;
    
    // If ball reaches the bottom
    // Reverse speed
    if (y > height) {
      // Dampening
      speed = speed * -0.9;
      y = height;
      bounced = true;
    }
    
    if(bounced==true && y<=10){
      // Dampening
      speed = speed * -0.9;
      y = 10;
      bounced = false;
    }
  }
  
  boolean finished() {
    // Balls fade out
    life--;
    if (life < 0) {
      return true;
    } else {
      return false;
    }
  }
  
  void display() {
    // Display the circle
    if(colorhue == 0){
      fill(color(colorhue,0,99),life);
    }
    else{
      fill(color(colorhue,99,99),life);
    }
    ellipse(x,y,w,w);
  }
}  

//ARDUINO

int trigger_pin = 2;
int echo_pin = 3;
long distance, pulse_duration;
int starttime;

void setup() {
  Serial.begin(9600);
  pinMode(trigger_pin, OUTPUT);
  pinMode(echo_pin, INPUT);
  digitalWrite(trigger_pin, LOW);
  Serial.println("0,0");
}

void loop() {
  int sensor = analogRead(A0);
  digitalWrite(trigger_pin, HIGH);
  digitalWrite(trigger_pin, LOW);
  pulse_duration = pulseIn(echo_pin, HIGH);
  Serial.print(sensor);
  Serial.print(',');
  Serial.println(pulse_duration);
}

 

Leave a Reply