IMA Midterm Documentation: Taiko

Taiko: An immersive drumming experience.

Author: Andrew Huang

Instructor: Professor Dan Mikesell

Description: A drum pad  experience made with Arduino and processing for visualization. I created this because of inspiration of midi instruments, and games I have played such as DDR or other rhythm games in arcades. I wanted to build off that and simply make a processing / Arduino version. In some sense, this isn’t an original idea, but the idea of creating an immersive drumming experience resonated with me. Remembering the vibration sensor from previous projects, I remembered what kind of meaningful interfaces I could make with Arduino.

Demo:

taiko1

Conceptual Development: 

I originally wanted to build a type of midi like instrument such as a wind instrument, but that proved to be too ambitious as I found out from the previous assignment that the noise sensors prove not to be very accurate regarding it’s surroundings. So I settled for drums. Making this proved to be very intuitive, but I did not have the time before to plan and gather the materials, so I settled for cardboard boxes. I took most of my inspiration from games like Guitar Hero or Rock band, where the goal of the game is to maximize your score while keeping a sense of rhythm at hand. Now that I had a concrete idea of what to make for the game, I decided to get started with making my project on the Arduino side first.

Technical Development:

I had a good idea in mind already, so I did not make a mock-up and went straight to trying to create a presentable project. Most of the heavy lifting of the project is Processing code, since the actual game has several complex components such as scoring, and the types of gameplay augmentation that I was looking for. I tried to get a 3d implementation of the program working in the end with P3D but the assignment due date was nearing. So I settled with a 2d version of the program.

The hardware portion of the project proved simple, as I simply just did simple analogRead and sent the values I got from Arduino into Processing, where I detected during the hit, and applied my scoring function.

Lessons learned

I’ve learned that sometimes, presentation matters more than the hidden features that does not really affect the final product. The cardboard boxes I used for my midterm projects did not really demonstrate the feasibility of the project, and it was a stretch to even call them as “drums”. Next project, I will seek to gather the appropriate materials in order to deliver the true immersive experience. Overall, the user experience using the vibration sensors is kind of buggy, as the box continues to vibrate after a hit, leading to multiple hits of the scoring function.

Perhaps next time I will make a more intuitive user interface, as the score itself was hard to see against the backdrop. Additionally, I hope to use more fonts and art to make a look that makes a difference between my project and that stock Processing shape look. Overall, I would consider my project to be somewhat of a success, the vision was clear, however, it lacked creativity and originality.

Attached is the source code I used in the project

//Processing main class:

import de.looksgood.ani.*;
import de.looksgood.ani.easing.*;

/*
*DDR MAIN CLASS FOR THE MIDTERM PROJECT
* AUTHOR: ANDREW HUANG
* DATE: MAR 24 2017
*
*/ 
final int COLS = 2;
final int wid = 1200;
final int ht = 700;
final int tolerance = 10; // no one is perfect
final int AR = 7; //ms of approach rate

import processing.serial.*;
Serial myPort;
int val, value;

void setup(){
   size(1200,700, P2D); // manually set COLS and wid,  weird bug
   printArray(Serial.list());
   myPort = new Serial(this, Serial.list()[2], 9600);
   Ani.init(this);
}

// GRID( color, alpha=-1, columns, width, height, approach rate);

Grid g = new Grid(100, -1, COLS, wid, 0, AR); // starts at top.

Grid hz = new Grid(255, 0, COLS, wid, ht-35, 0); // hitzone on bottom.

int score = 0;
int valueFromArduino;

void draw(){
  //background(0);
  noStroke();
  gradientRect(0, 0, wid, ht, #AEC6CF, #B39EB5);
  //fill(100);
  textSize(32);
  text("Score: " + score, 10, 30); 
  g.drawGrid();
  g.moverandom();
  stroke(255);
  strokeWeight(5);
  drumhit();
  hz.drawGrid();
}
void keyPressed(){
   score += g.scoring(ht - 35, tolerance);
   println(score);
}

void drumhit(){
  while ( myPort.available() > 0) {
    valueFromArduino = myPort.read();
    
    valueFromArduino = valueFromArduino / 10 / 2;
    println(valueFromArduino);
    
  }
  if (valueFromArduino > 20){
    score += g.scoring(ht - 35, tolerance);
     println(score);
    delay(5);
  }
}

void gradientRect(int x, int y, int w, int h, color c1, color c2) {
  beginShape();
  fill(c1);
  vertex(x,y);
  vertex(x+w,y);
 
  fill(c2);
  vertex(x+w,y+h);
  vertex(x,y+h);
  endShape();
}

/////////////////////////////////////////////////////////////////////////////

/*
*CELL/GRID CLASS FOR THE MIDTERM PROJECT
 * AUTHOR: ANDREW HUANG
 * DATE: MAR 24 2017
 *
 */
class Cell {
  int speed, rad, posx, posy;
  
  Cell(int rad, int posx, int posy, int speed) {
    this.rad = rad;
    this.posx = posx;
    this.posy = posy;
    this.speed = speed;
    
  }
  void draw() {
    ellipse(posx, posy, rad, rad);
  }
  void animate(int r, int g, int b){
    //radiusAni.start();
    //fill(r,g,b);
    //Ani.to( this, 0.1,  "rad", rad+30);
    
    //Ani.to( this, 0.1,  "rad", rad-30);
    
    ////if (rad >= 50){
    
    ////}
    //ellipse(posx, posy, rad, rad);
    
    
    for (int i = rad; i < rad +30; i++){
      fill(r,g,b);
      ellipse(posx, posy, i, i);
    }
    for (int i = rad+30; i < rad; i--){
      fill(r,g,b);
      ellipse(posx, posy, i, i);
    }  
  }
  void move(){
    posy += speed;
  }
  int getposx(){
    return posx;
  }
  int getposy(){
    return posy;
  }
  int getspeed(){
    return speed;
  }
  void setspeed(int spd){
    speed = spd;
  }
  void setpos(int x, int y){
    posx = x;
    posy = y;
  }
}

class Grid{
  // both the pieces api and the hitzone api will be coded from this class
  
  Cell[] cells;
  int cols;
  int permute = -1;
  int fillc=0;
  int alph=-1;
  
  Grid(int fillc, int alph, int cols, int w, int h, int AR){
    this.alph = alph;
    this.cols = cols;
    this.fillc = fillc;
    cells = new Cell[cols];
    for (int i =0; i < cols; i++){
      cells[i] = new Cell(50, (int )map(i, 0, cols, 0 , w) + w/cols/2, h, AR);
    }
  }
  void drawGrid(){
    for (int i=0; i < cols; i++){
      if (alph >= 0){
         fill(fillc, alph);
      } else {
        fill(fillc, cells[i].getposy()-20);
      }
      cells[i].draw();
    }
  }
  void moveGrid(){
    for (int i=0; i < cols; i++){
      cells[i].move();
    }
  }
  void moverandom(){
    //moves one at a time.
    if (permute == -1){
      permute = (int) random(0, cols);
    }
    if (cells[permute].getposy() >= 0 && cells[permute].getposy() < height){
      cells[permute].move();
    } else {
      //cells[permute].setspeed(-cells[permute].getspeed());
      cells[permute].setpos(cells[permute].getposx(), 0);
      permute = (int) random(0, cols);
    }
  }
  void bounce(){
    for (int i=0; i < cols; i++){
      if (cells[i].getposx() < 0 || cells[i].getposy()< 0 || cells[i].getposy()> height){
        cells[i].setspeed(-cells[i].getspeed());
      }
    }
  }
  int scoring(int ht, int tolerance){
    // scoring for the balls position, relative to the screen.
    Cell ball = cells[permute]; //current moving ball
    if (ball.getposy() <= ht + tolerance && ball.getposy() >=  ht - tolerance){
      ball.animate(51, 252, 255);
      return 300;
    }
    else if (ball.getposy() <= ht + tolerance*2 && ball.getposy() >= ht - tolerance*2){
      ball.animate(51, 255, 60);
      return 200;
    }
    else if (ball.getposy() <= ht + tolerance*3 && ball.getposy() >= ht - tolerance*5){
      ball.animate(255, 94, 51);
      return 100;
    }
    return -100;
  }
}

////////////////////////////////////////////////////////////////////////////////////////////

// Arduino Code

int analogPin = 0;
int analogPin2 = 1;// potentiometer wiper (middle terminal) connected to analog pin 3
                       // outside leads to ground and +5V
int val = 0;           // variable to store the value read
int val2 = 0;

void setup() 
{
  Serial.begin(9600);          //  setup serial
}
void loop()
{
  val = analogRead(analogPin);    // read the input pin
  Serial.println(val); 
//  Serial.write(val); 
  delay(10); // debug value 
}

//////////////////////////////////////////////////////////////////////////////////

Leave a Reply