Lan Wei-Final Project-MUSICAL UNIVERSE

sketch

//Lan Wei
//lanw@andrew.cmu.edu
//Section // D
//Final Project-Musical Universe

var snd = []; //this array will hold the sounds
var amplitude = [];
var ampTotal; //Total amplitude to change background color
var mouseXArray = []; //the array of X values
var mouseYArray = []; //the array of y values
var balls = []; //the array of ball objects
var np = 26; //the number of background lines
var pArray = []; //the array of particles

//preload the sounds
function preload() {
    var PREFIX = "https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/";
    for (var i = 1; i < 10; i++){
        var sound = loadSound(PREFIX + "sound" + i + ".wav"); //load sounds
        sound.setVolume(0.5);
        snd.push(sound); //push the sounds in snd. var sound will never be used again
    }
}

//make ball objects
function makeBalls(bx, by, br){
    var ball = {x: bx,
                y: by,
                r: br, //radius of the ball
                update: updateBall, //update the size of the balls
                sound: whichSound, //which amplitude is the ball related to
                draw: drawBall,
                play: playSound};
    return ball; //return the new object
}

function drawBall(){
    stroke(255);
    //ellipse(this.x, this.y, this.r, this.r);
    var total = 20; //control the density of the points on the balls
    for (var i = 0; i < total; i++){
        var longtitude = map(i, 0, total, -PI, PI);
        for (var j = 0; j < total; j++){
            var latitude = map(j, 0, total, -HALF_PI, HALF_PI);
            //fold the longtitude-latitude panel into a sphere
            var ptX = this.r * sin(longtitude) * cos(latitude);
            var ptY = this.r * sin(longtitude) * sin(latitude);
            var ptZ = this.r * cos(longtitude);
            push();
            stroke(255);
            translate(ptX, ptY, ptZ);
            sphere(0.1);
            pop();
        }
    }
}

function whichSound(){
    if (zone(this.x, this.y) == 1){
        return snd[0];
    }
    if (zone(this.x, this.y) == 2){
        return snd[1];
    }
    if (zone(this.x, this.y) == 3){
        return snd[2];
    }
    if (zone(this.x, this.y) == 4){
        return snd[3];
    }
    if (zone(this.x, this.y) == 5){
        return snd[4];
    }
    if (zone(this.x, this.y) == 6){
        return snd[5];
    }
    if (zone(this.x, this.y) == 7){
        return snd[6];
    }
    if (zone(this.x, this.y) == 8){
        return snd[7];
    }
    if (zone(this.x, this.y) == 9){
        return snd[8];
    }
}

function playSound(){
    var soundLocal = this.sound();
    soundLocal.play();
}

function updateBall(){
    var amp = amplitude[zone(this.x, this.y) - 1];
    var level = amp.getLevel(); //get the level
    this.r = 30 + 300 * level; //the size of balls
}

//particles to make background lines
function makeParticle(px, py, pdx, pdy){
    p = {x: px,
         y: py,
         dx: pdx,
         dy: pdy,
         update: pUpdate,
         draw: pDraw}
    return p;
}

function pUpdate(){
    this.x += this.dx;
    this.y += this.dy;

    //limit the lines in a certain area of 500 * 500 using 'bouncing'
    if (this.x < -250){ //left boundary
        this.x = -this.x - 250;
        this.dx = -this.dx - 250;
    }
    else if (this.x > 250){ //right boundary
        this.x = 300 - this.x;
        this.dx = 300 - this.dx;
    }
    if (this.y < -250){ //downward boundary
        this.y = -this.y - 250;
        this.dy = -this.dy - 250;
    }
    else if (this.y > 250){ //upward boundary
        this.y = 300 - this.y;
        this.dy = 300 - this.dy;
    }
}

function pDraw(){
    ellipse(this.x, this.y, 5, 5);
}
/////////////////////////////////////////////////////////////////////////////////////
function setup() {
    createCanvas(450, 450, WEBGL); // 3D mode. (0, 0)locates in the center of the canvas
    background(0);
    stroke(255);
    perspective(); //perspective view

    for (var i = 0; i < 9; i++){
        amplitude[i] = new p5.Amplitude();
        amplitude[i].setInput(snd[i]); //get independent amplitude
    }
    ampTotal = new p5.Amplitude(); //total amplitude

    for (var i = 0; i < np; i++){
        //the boundary of particles is a little bigger than the canvas
        var p = makeParticle(random(-300, 300), random(-300, 300), random(-2, 2), random(-2, 2));
        pArray.push(p);
    }
}

function draw() {
    var levelTotal = ampTotal.getLevel();
    var col = map(levelTotal, 0, 1, 0, 100);//background color
    background(col, 0, 2 * col);
    //draw background lines
    for (var i = 0; i < np; i++){
        pArray[i].update();
        pArray[i].draw();
        //lines between particles
        for (var j = 0; j < np/2; j++){
            stroke(random(0, 150));
            strokeWeight(0.2);
            line(pArray[j].x, pArray[j].y, pArray[j + np/2].x, pArray[j + np/2].y);
        }
    }
    //the canvas is divided by a 3*3 grid
    strokeWeight(1);
    stroke(random(20, 70));
    line(-75, -225, -75, 225);
    line(75, -225, 75, 225);
    line(-225, -75, 225, -75);
    line(-225, 75, 225, 75);

    if (mouseXArray.length != 0){ //after the 1st mouse press
        stroke(255);
        fill(255);
        // draw all the balls
        for (i = 0; i < balls.length; i ++){
            balls[i].update(); //update the radius of the balls
            push();
            translate(balls[i].x, balls[i].y, 0);
            //rotate with randomness
            rotateX(frameCount * 0.05 + i);
            rotateY(frameCount * 0.05 + i * 5);
            rotateZ(frameCount * 0.05 + i * 5);
            balls[i].draw(); //draw the balls
            pop();
        }
    }
}

//To identify which zone is the ball in
//translate the coordinate to normal mode
function zone(x, y){
    if ((y > -225 )& (y < height/3 - 225)){
        if ((x > -225) && (x < width/3 - 225)){ //position 1
            return 1;
        }
        if ((x > width/3 - 225) & (x < 2 * width/3 - 225)){ //position 2
            return 2;
        }
        if ((x > 2 * width/3 - 225) & (x < width - 225)){ //position 3
            return 3;
        }
    }

    if ((y > height/3 - 225) & (y < 2 * height/3 - 225)){
        if ((x > -225) && (x < width/3 - 225)){ //position 4
            return 4;
        }
        if ((x > width/3 - 225) & (x < 2 * width/3 - 225)){ //position 5
            return 5;
        }
        if ((x > 2 * width/3 - 225) & (x < width - 225)){ //position 6
            return 6;
        }
    }

    if ((y > 2 * height/3 - 225) & (y < height - 225)){
        if ((x > -225) && (x < width/3 - 225)){ //position 7
            return 7;
        }
        if ((x > width/3 - 225) & (x < 2 * width/3 - 225)){ //position 8
            return 8;
        }
        if ((x > 2 * width/3 - 225) & (x < width - 225)){ //position 9
            return 9;
        }
    }
}

//when mouse is clicked, a sound will be played and a ball will be drawn
function mouseClicked(){
    var clickOn; //to check whether click on an existing ball
    var newBalls = [];
    var newMouseXArray = [];
    var newMouseYArray = [];

    if (mouseXArray.length == 0){ //when there is no existing balls
        clickOn = 0;
        newBalls.push(balls[0]);
        newMouseXArray.push(mouseXArray[0]);
        newMouseYArray.push(mouseYArray[0]);

    }

    // a ball will be removed when clicked again
    else{ //after the 1st click
        for (i = 0; i < balls.length; i++){
            var distance = dist(mouseX - 225, mouseY - 225, balls[i].x, balls[i].y);
            //is clicked
            if (distance <= 20){ //minimum distance
                var soundBall = balls[i].sound();
                soundBall.setVolume(0); //stop the sound of the ball that's clicked
                // balls.splice(i, 1); //remove the ball
                // mouseXArray.splice(i, 1);
                // mouseYArray.splice(i, 1);
                clickOn = 1;
            }
            //is not clicked
            else{
                clickOn = 0;
                newBalls.push(balls[i]);
                newMouseXArray.push(mouseXArray[i]);
                newMouseYArray.push(mouseYArray[i]);
            }
        }
        balls = newBalls;
        mouseXArray = newMouseXArray;
        mouseYArray = newMouseYArray;
    }
    if (clickOn == 0){
        mouseXArray.push(mouseX - 225);//translate to WEBGL coordinates
        mouseYArray.push(mouseY - 225);
        // initial radius: 30
        //make ball objects
        var newBall = makeBalls(mouseX - 225, mouseY - 225, 30);
        balls.push(newBall);

    }
    //play sound using the object
    for (i = 0; i < balls.length; i++){
        var soundLocal = balls[i].sound();
        balls[i].play();
        soundLocal.loop();
    }
}

//YOU'VE GOT A MUSICAL UNIVERSE!!!

[How to play]: Click on different positions of the canvas to get different mixtures of ‘UNIVERSE MUSIC’,click the balls again to remove them as well as the sound (in the opposite order you created the balls). Play slowly! (Sometimes it doesn’t work well if played fast, maybe it’s my browser’s problem.)

My initial idea of the project is to create a simple instrument that enables users to mix several pieces of music. In the proposal stage, I have imagined many sound effects and interaction methods, but have only achieved part of them. The actual coding process is much harder than I thought, and the final result becomes more visual than auditory. But actually, I’m very happy about the project because it is really a reviewing as well as a learning process. I’m not very good at using objects and particles so I tried to include these contents in my project. And I also watched some online tutorials to create the 3D effect of the balls. The final result is very satisfying for me, especially the 3D ball effect!

Have Fun!

 

Leave a Reply