Final Project Lydia Jin – Piano Keys for Beginners

//Lydia Jin
//jialuj@andrew.cmu.edu
//Section D
//Final Project

//define necessary variables
var piano = [];
var alphabet = ['a', 's', 'd', 'f', 'g', 'h', 'j'];
var alphabet2 = ['A', 'S', 'D', 'F', 'G', 'H', 'J'];
var ASSETS_DIR = "assets/";
var POSTFIX = "-piano.wav"
var amp;
var hz;
var rec = [];
var w;
var buffer;
var button1, button2, button3, button4, button5, button6, button7;
var colorNum = 0;
var bgm1, bgm2, bgm3; //BGM stands for background metronome 
var mic, recorder, soundFile;
var state = 0;

var soundLinks = ["https://courses.ideate.cmu.edu/15-104/f2016/wp-content/uploads/2016/12/0-piano.wav",
                "https://courses.ideate.cmu.edu/15-104/f2016/wp-content/uploads/2016/12/1-piano.wav",
                "https://courses.ideate.cmu.edu/15-104/f2016/wp-content/uploads/2016/12/2-piano.wav",
                "https://courses.ideate.cmu.edu/15-104/f2016/wp-content/uploads/2016/12/3-piano.wav",
                "https://courses.ideate.cmu.edu/15-104/f2016/wp-content/uploads/2016/12/4-piano.wav",
                "https://courses.ideate.cmu.edu/15-104/f2016/wp-content/uploads/2016/12/5-piano.wav",
                "https://courses.ideate.cmu.edu/15-104/f2016/wp-content/uploads/2016/12/6-piano.wav"];



for (var i = 0; i < 7; i++){
    //push keys into the array 
    piano[i] = new KeyStroke(alphabet[i], alphabet2[i]);

}

function setup() {
    createCanvas(600, 200);
    amp = new p5.Amplitude();
    hz = new p5.FFT(0, 256);
    w = width/256;
    colorMode(HSB);
    angleMode(DEGREES);
    
    //create button for BGM 40
    button1 = createButton('40 BPM');
    button1.position(543,40);
    button1.mousePressed(PlayBGM1);

    //create button for BGM 50
    button2 = createButton('50 BPM');
    button2.position(543,64);
    button2.mousePressed(PlayBGM2);

    //create button for BGM 60
    button3 = createButton('60 BPM');
    button3.position(543,88);
    button3.mousePressed(PlayBGM3);

    //create button for no background metronome
    button4 = createButton('No BGM');
    button4.position(541,112);
    button4.mousePressed(ResetBGM);

    //create button for recording
    button5 = createButton('Record');
    button5.position(545,136);
    button5.mousePressed(RecordAudio);

    //create button for playing recording
    button6 = createButton('Play');
    button6.position(562,160);
    button6.mousePressed(PlayAduio);
    button6.hide();

    //create button for saving recording
    button7 = createButton('Save');
    button7.position(557,184);
    button7.mousePressed(SaveRecord);
    button7.hide();

    //create recorded soundfile
    recorder = new p5.SoundRecorder();
    recorder.setInput();
    soundFile = new p5.SoundFile();

    
}

//helper function for playing BGM1
function PlayBGM1(){
    bgm2.stop();
    bgm3.stop();
    bgm1.loop();
}

//helper function for playing BGM2
function PlayBGM2(){
    bgm1.stop();
    bgm3.stop();
    bgm2.loop();
}

//helper function for playing BMG3
function PlayBGM3(){
    bgm1.stop();
    bgm2.stop();
    bgm3.loop();
}

//helper function for no BGM
function ResetBGM(){
    bgm1.stop();
    bgm2.stop();
    bgm3.stop();
}

//helper function to record audio
function RecordAudio(){
    if (state == 0 ){
        recorder.record(soundFile);
        state ++;
    }
    else if (state = 1){
        recorder.stop();
        state++;
    }
  
}

//helper function to play recording
function PlayAduio(){
    soundFile.play();
}

//helper function to download recording
function SaveRecord(){
    saveSound(soundFile, 'mySound.wav');
}
    


function draw() {
    noStroke();
    background('black');

    //draw buttons
    fill('white');
    textSize(13);
    noStroke();
    text('Metronome', 535, 15);
    text('Rhythms', 543, 27);
    
    //hide buttons
    if (state === 0) {
        button6.hide();
        button7.hide();
        //state++;
    }

    else if (state === 1) {
        //display instructing text
        text('Recording! Click Record Again to stop.', 150, 20);
    }

    else if (state === 2) {
        
        if (soundFile.isPlaying() == true){
            button6.hide();
            bgm1.stop();
            bgm2.stop();
            bgm3.stop();
            //display text
            text('Playing...', 250, 20);

        }
        else{
            button5.hide();
            //show buttons after recording is done
            button6.show();
            button7.show(); 
            text('Recording stopped! Chose to play or save. Refresh the page to start a new recording.', 20, 20);
        }
    }


    //draw keyboard letters
    for (var i = 0; i < 7; i++){

        push();
        textSize(25);
        stroke('white');
        fill('white');
        text(alphabet2[i], i* (width - 70)/7 + (width - 70)/14, 195); 
        pop();


    }
    
    //draw displays
    if (state != 0){
        push();
        var level = amp.getLevel();
        rec.push(level);
        stroke('white');
        noFill();
        var cy = map(level, 0 , 1, height, 0);
        translate(0, height/2 - cy);
        beginShape();
        colorNum++;
        if (colorNum > 255){
                colorNum = 0;
            }
        for (var i = 0; i < rec.length; i++){
            var y = map(rec[i], 0 , 1, height, 0);
            stroke(colorNum, 255, 255);
            vertex(i, y);
        }
        endShape();

        if (rec.length > width - 70){
            rec.splice(0, 1);
        }
        pop();

        push();
        stroke('red');
        line(rec.length, 0, rec.length, height);
        pop();
    }
    //display amplitude with rectangles
    var spectrum = hz.analyze();
    noStroke();
    for (var i = 0; i < spectrum.length; i++){
        var amp1 = spectrum[i];
        var y = map(amp1, 0, 256, height, 0);
        fill(i, 255, 255);
        if (soundFile.isPlaying() == true){
            rect(i + (width - 90)/2, y , w - 2, height - y);
        }
        else{
            rect(i + buffer, y , w - 2, height - y);
        }
    }

}


function preload(){
    //preload sound files 
    for (var i = 0; i < piano.length; i++){
        piano[i].sound = loadSound(soundLinks[i]);

    }
    //store sound files to variables 
    bgm1 = loadSound("https://courses.ideate.cmu.edu/15-104/f2016/wp-content/uploads/2016/12/40-BPM.wav");
    bgm2 = loadSound("https://courses.ideate.cmu.edu/15-104/f2016/wp-content/uploads/2016/12/50-BPM.wav");
    bgm3 = loadSound("https://courses.ideate.cmu.edu/15-104/f2016/wp-content/uploads/2016/12/60-BPM.wav");
}



function KeyStroke(LKey, CKey){
    var sound;
    //lower case keys
    this.LKey = LKey;
    //upper case keys
    this.CKey = CKey;
}

function keyPressed(){
    
    if (state === 0 & key === 'space') {

        state++;
    }
    for (var i = 0; i < piano.length; i++){
        if ((key === piano[i].LKey) || (key === piano[i].CKey)){
            piano[i].sound.play();
            
            buffer = i*80;
            fill('orange');
            rect(i*width/7, 170, width/7, 30);
        }

    }
}

My program is designed for kids or beginners who don’t have any musical background in the goal of getting them to know the 7 basic keys in piano with the aid of a metronome. This program is simple to use and should build interest in users because they get to record what they played and replay or download the recordings. Users can follow instructions that are displayed in the program to know exactly how to use this program.

Instructions for use:
1. 4 background metronome options: 40 BPM, 50 BPM, 60 BPM or no BPM at all. Press the button to access each. (The buttons are named BGM, which stands for background metronome)
2. Keys A,S,D,F,G,H,J when pressed, will play piano keys A,B,C,D,E,F,G respectively. The code is written in a way that both capital and lower case keyboard keys will work.
3. Record your performance by hitting the record button, once done, hit the record button again to stop recording.
4. After you are finished recording, you have the option to either play your recording or save it. By pressing the save button, your audio recording file will be downloaded to your computer.
5. Refresh the page to start new recording.

Additional features displayed:
1. The line in the middle shows you the amplitude of the sound. The lines changes color for more fun 🙂
2. The rectangles that pop up when sound is played shows you the frequency of the sound.
* These are made possible by using P5js built in sound functions

Notes to be taken into consideration when grading:
1. This is modified from the final proposal. The reason I decided to change this is because I think this program is more practical for users and also has cool features. (Already talked to the professor about the change)
2. The sounds are downloaded from freesound.org
3. The buttons’ scales are off when sketch is embedded on WordPress. The original file did not have overlapping buttons.
4. When running locally, the program only runs when Firefox is used.

Leave a Reply