Piano – OOP

Control a “you” and release your “touch” to play some notes.
For this week’s assignment,  I used Object Oriented Programming to create a simple representation of a piano.

I was inspired by a similar concept I saw on Pinterest and I wanted to include the picture for inspiration credits but I couldn’t find it again!

Nevertheless, to do this, I  started off by making 3 classes in three different tabs. One for the piano, one for the person moving around (or “you”), and lastly one for the “touch” released by the person.

General Information

The Piano class is quite simple. It has a constructor and two functions.
The first function is just to draw the piano on the screen. While the second one is used when a piano key is pressed to make it grey and give it the illusion to have actually been pressed.
Here is the code for this:

class Piano{
  float pianoTop, pianoSide, keyWidth;
  
  //constructor
  Piano(){ 
  pianoTop = height/1.7;
  pianoSide = width/7;
  keyWidth = (width-(2*pianoSide))/13; //13 is the number of keys
  }
  
  //displaying the piano
  void drawPiano(){
    for (int i=0; i<13; i++){
      if (i==1 || i==3 || i==6 || i==8 || i==10){
        fill(0);
        stroke(0);
        strokeWeight(2);
        rect(pianoSide+i*keyWidth, pianoTop, keyWidth, (height-pianoTop)/1.4);
      }
      else{
        fill(255);
        stroke(0);
        strokeWeight(2);
        rect(pianoSide+ i*keyWidth, pianoTop, keyWidth, height-pianoTop);
      }
    }
  }
  //function that makes a pressed key grey 
  void pianoKeyPressed(int a){
    if (a==1 || a==3 || a==6 || a==8 || a==10){
      stroke(0);
      strokeWeight(2);
      fill(100);
      rect(pianoSide+a*keyWidth, pianoTop, keyWidth, (height-pianoTop)/1.4);
    }
    else{
      stroke(0);
      strokeWeight(2);
      fill(100);
      rect(pianoSide+ a*keyWidth, pianoTop, keyWidth, height-pianoTop);
    }
  
  }
  
}

Next, there is the You class. Here as well, there is a constructor along with three functions. The first one just displays an image/draws the “you”. The second one is to move it with the Left and Right arrow keys. And the last one is to return the x position of the You at a specific moment. This last function is needed for the touch. Since the touch is represented by a small circle that appears at the position of the person.
Here is the code for this class:

class You{
  float x, y, speed;
  int youWidth;
  float r, g, b;
  PImage img;
  
  //constructor
  You(){
    speed= 5;
    x= width/2;
    y= height/9;
    youWidth = 100;
    img = loadImage("square.png");
  }
  
  void displayYou(){
    image(img, x-youWidth/2, y, youWidth, youWidth);
    //rect(x-youWidth/2,y,youWidth,youWidth);
  }
  
  void controlYou(){
    if (keyPressed && keyCode == RIGHT){
      if(x < width - (piano.pianoSide)){
        x += speed;
      }
    }
    else if (keyPressed && keyCode == LEFT){
      if (x > piano.pianoSide){
        x -= speed;
      }
    }
  }
  
  float getYouPos(){
    return x;
  }


}

The final class is the Touch class. It has a constructor and two functions. The first function is used to display the touch at the position of the person when it is between the bottom of the person and the top border of the piano. And it also makes it disappear into the piano when it reaches the top border along with a small visual effect representing a touch.
(Note: each “touch” gets a random color.)
The second function is used to update the y-position of the touch.
This is the code for this class:

class Touch{
  float y, speed;
  int touchWidth;
  float r, g, b;
  boolean touchArrived;
  
  //constructor
  Touch(){
    speed = 5;
    y = height/9 + you.youWidth + touchWidth/2;
    touchWidth = 15;
    r = random(255);
    g = random(255);
    b = random(255);
    touchArrived = false;
    
  }
  
  void displayTouch(float pos){
    if (y <= piano.pianoTop){
      fill(r,g,b); //fill the touch with a random color
      noStroke();
      ellipse(pos,y,touchWidth,touchWidth);
    }
    //visual effect when the touch reaches the pressed key
    if (touchArrived){
      stroke(0);
      strokeWeight(3);
      line(pos, piano.pianoTop - 5, pos, piano.pianoTop-17);
      line(pos - 10, piano.pianoTop - 5, pos-20, piano.pianoTop-15);
      line(pos + 10, piano.pianoTop - 5, pos+20, piano.pianoTop-15);
    }
  }
  
  void moveTouch(){
    y += speed;
    touchArrived = y>= piano.pianoTop && y<(height-piano.pianoTop/2);
  }
  
}

In the main tab, instances of all of these classes were created. One for the piano, one for “you” and an ArrayList for all the “touches” that will be created throughout the program.
In draw() there is an if statement that will either display the Start Screen or the Piano Screen (the latter will be triggered when the mouse is clicked. This is done by a mouseClicked() function and a boolean variable.)
A keyPressed() function is also found in this main tab and it is responsible for creating a new Touch object every time the spacebar is pressed.
Finally, there is a function that checks no sound files overlap.
Here is the code for that:

//declaring global variables
import processing.sound.*;
SoundFile[] notes; //an array contaning all the sound files
boolean start;

Piano piano;
You you;
int touchCounter; 
ArrayList<Touch> touches; //an array for all the touches created
FloatList touchPositions; //an array for the x-position of each touch
float pos, temp1, temp2;



void setup(){
  size(1000,700);
  start=false;
  notes = new SoundFile[13];
  for(int i=0; i<13; i++){
    notes[i]= new SoundFile(this,"note"+str(i)+".mp3"); //adding all sound files to an array
  }
  piano= new Piano();
  you= new You();
  touches = new ArrayList<Touch>();
  touchPositions = new FloatList();
}


void draw(){
  background(255,243,249);
   
  //start screen
  if (!start){
    fill(0);
    textSize(20);
    textAlign(CENTER);
    text("Use the left and right arrow keys to move.",width/2 ,height/3);
    text("Press space to release your \"touch\" :)",width/2 ,height/2.7);
    text("Click to start playing!", width/2, height/1.8);
    textAlign(LEFT);
    textSize(15);
    text("(the piano.)", width/2, height/1.7);  
  }
  //when the mouse is clicked, the "game"(?) starts
  else{
    //draw piano
    piano.drawPiano();
    
    //drawing "you" and controlling it
    you.displayYou();
    you.controlYou();
    
    //drawing the touch and controlling it when the spacebar is pressed
    for (int i=0; i<touchCounter; i++){
      touches.get(i).displayTouch(touchPositions.get(i));
      touches.get(i).moveTouch();
      //adding the visual and sound effects when a note is played
      for (int j=0; j<13; j++){
          temp1 = piano.pianoSide+j*piano.keyWidth; //temporary value to find the start border of the pressed key
          temp2 = temp1 + piano.keyWidth; //temporary value to find the end border of the pressed key
          if (touchPositions.get(i)>temp1 && touchPositions.get(i)<temp2 && touches.get(i).touchArrived){
            piano.pianoKeyPressed(j);
            checkSilence(j);
            break;
          }
      }
    }
  }
}

//This is to check when the spacebar is pressed
//and add a new touch to the touches-array everytime
void keyPressed(){
  if (key==' '){
    touches.add(new Touch());
    pos = you.getYouPos(); //SAVE the value of the you position at the time the key is pressed
    //and add it to the positions FloatList
    touchPositions.append(pos);
    touchCounter+=1; //increasing the touchCounter to be ready for next value
  }
}

void mouseClicked(){
  start = true;
}

//function that makes sure no sounds are playing before playing the pressed one
void checkSilence(int index){
  for (int i=0; i<13; i++){
    if (notes[i].isPlaying()){
      notes[i].stop();
    }
  }
  notes[index].play();
}

Difficulties

While trying to get the program to work. I ran into three main difficulties.

  • The first one was that every time I press the space bar to create a new Touch, the previous one would disappear. To fix this, I used an ArrayList and stored every created Touch object there.
  • The second one was that, after pressing the spacebar and making a touch appear, moving the person moves the Touch objects along with it (because they use the x-position of the person as the parameter). So to fix this, every time the spacebar is pressed, I stored the x-position of the person in a temporary value and appended it to a FloatList (It ends up containing all Touch objects’ positions that can easily be accessed by indices.)
  • The third and final one was that the sounds made by pressing a piano key were overlapping, thus creating a lot of noise. To stop this, I included a function that lets a note be played only after it has stopped the previous one from playing.

All of these failed attempts can be visualized, respectively, in this video:

Final Outcome

Here is how it turned out in the end 🙂

One thought on “Piano – OOP”

Leave a Reply