All Articles

【100sites #010】Piano,用P5js的sound函式庫實作虛擬鋼琴

Piano,用P5js的sound函式庫實作虛擬鋼琴

點我玩線上Demo

點我查看程式碼

螢幕快照 2016-03-26 上午1.18.01.png

要營造出好氣氛,一定要有適合的音樂,於是今天我想要玩音樂,所以就研究了一下P5.jssound函式庫,做出了今天的Piano虛擬鋼琴。

這個函式庫有不少功能可以使用,播音錄音波型轉換都可以做到,今天的Piano使用了sine函數的oscillator製造聲音,再用FFT.analyze()來顯示波形。

本次的程式碼:

<!DOCTYPE html>
<html>
<head>
  <meta charset-"UTF-8">
  <title>Piano</title>
  <link rel="stylesheet" type="text/css" href="style.css">
  <script src="http://cdnjs.cloudflare.com/ajax/libs/p5.js/0.4.23/p5.js"></script>
  <script src="p5.sound.min.js"></script>
  <script src="piano.js"></script>
</head>

<body>
</body>
</html>
* {
  margin: 0;
  padding: 0;
}

body {
  overflow: hidden;
}
var attackLevel = 2.0;
var releaseLevel = 0;

var attackTime = 0.001;
var decayTime = 0.2;
var susPercent = 0.2;
var releaseTime = 0.5;

var env, triOsc;
var notePressed = [false, false, false, false, false, false, false, false, false, false, false, false];
var fft;

function setup() {
  createCanvas(windowWidth, windowHeight);

  textAlign(CENTER);
  textSize(40);

  env = new p5.Env();
  env.setADSR(attackTime, decayTime, susPercent, releaseTime);
  env.setRange(attackLevel, releaseLevel);

  triOsc = new p5.Oscillator('sine');
  triOsc.amp(env);
  triOsc.start();

  ellipseMode(CENTER);
  strokeWeight(2);
  rectMode(CENTER);
  
  fft = new p5.FFT();
}


function draw() {
  background(200);

  // title
  fill(255);  
  strokeWeight(5);
  textSize(120);
  textFont("Georgia");
  text("PIANO", width/2, 150);
  textFont("Normal");

  // spectrum 
  var spectrum = fft.analyze(); 
  noStroke();
  fill(230); 
  for (var i = 0; i< spectrum.length; i++){
    var x = map(i, 0, spectrum.length, 0, width/2);
    var h = -height/2 + map(spectrum[i], 0, 255, height/2, 0);
    rect(x, height/2, width / spectrum.length, h);
    rect(width-x, height/2, width / spectrum.length, h);
  }

  // black note
  drawNote(windowWidth/2-150, windowHeight/2-60, 'E', 'b', notePressed[0]);
  drawNote(windowWidth/2-90, windowHeight/2-60, 'R', 'b', notePressed[1]);
  drawNote(windowWidth/2+30, windowHeight/2-60, 'Y', 'b', notePressed[2]);
  drawNote(windowWidth/2+90, windowHeight/2-60, 'U', 'b', notePressed[3]);
  drawNote(windowWidth/2+150, windowHeight/2-60, 'I', 'b', notePressed[4]);
  
  // white note
  drawNote(windowWidth/2-180, windowHeight/2, 'S', 'w', notePressed[5]);
  drawNote(windowWidth/2-120, windowHeight/2, 'D', 'w', notePressed[6]);
  drawNote(windowWidth/2-60, windowHeight/2, 'F', 'w', notePressed[7]);
  drawNote(windowWidth/2, windowHeight/2, 'G', 'w', notePressed[8]);
  drawNote(windowWidth/2+60, windowHeight/2, 'H', 'w', notePressed[9]);
  drawNote(windowWidth/2+120, windowHeight/2, 'J', 'w', notePressed[10]);
  drawNote(windowWidth/2+180, windowHeight/2, 'K', 'w', notePressed[11]);
}


function playEnv(f){
  triOsc.freq(f);
  env.play();
}


function drawNote(xpos, ypos, key, color, pressed) {
  // black note
  var big = 0;
  if (pressed) {
    big = 8;
  }
  // shadow
  fill(100);
  strokeWeight(0);
  ellipse(xpos+3, ypos+3, 50+big, 50+big);
  // note
  if (color == 'b') {
    fill(0);
    stroke(255);  
  } else {
    fill(255);
    stroke(0);  
  }
  strokeWeight(2);
  ellipse(xpos, ypos, 50+big, 50+big);
  // text
  if (color == 'b') {
    fill(255);  
  } else {
    fill(0);
  }
  textSize(40);
  if (pressed) {
    textSize(44);
  }
  text(key, xpos, ypos+17);
}


function keyPressed() {
  switch (keyCode) {
  case 83: // S
  case 115: // s
    playEnv(261); // C note
    notePressed[5] = true;
    break;
  case 69: // E
  case 101: // e
    playEnv(277); // C# note
    notePressed[0] = true;
    break;
  case 68: // D
  case 110: // d
    playEnv(293); // D note
    notePressed[6] = true;
    break;
  case 82: // R
  case 114: // r
    playEnv(311); // D# note
    notePressed[1] = true;
    break;
  case 70: // F
  case 102: // f
    playEnv(329); // E note
    notePressed[7] = true;
    break;
  case 71: // G
  case 103: // g
    playEnv(349); // F note
    notePressed[8] = true;
    break;
  case 89: // Y
  case 121: // y
    playEnv(369); // F# note
    notePressed[2] = true;
    break;
  case 72: // H
  case 104: // h
    playEnv(392); // G note
    notePressed[9] = true;
    break;
  case 85: // U
  case 117: // u
    playEnv(415); // G# note
    notePressed[3] = true;
    break;
  case 74: // J
  case 106: // j
    playEnv(440); // A note
    notePressed[10] = true;
    break;
  case 73: // U
  case 105: // u
    playEnv(466); // A# note
    notePressed[4] = true;
    break;
  case 75: // K
  case 107: // k
    playEnv(493); // B note
    notePressed[11] = true;
    break;
  default:
    break;
  }
}

function keyReleased() {
  switch (keyCode) {
  case 83: // S
  case 115: // s
    // C note
    notePressed[5] = false;
    break;
  case 69: // E
  case 101: // e
    // C# note
    notePressed[0] = false;
    break;
  case 68: // D
  case 110: // d
    // D note
    notePressed[6] = false;
    break;
  case 82: // R
  case 114: // r
    // D# note
    notePressed[1] = false;
    break;
  case 70: // F
  case 102: // f
    // E note
    notePressed[7] = false;
    break;
  case 71: // G
  case 103: // g
    // F note
    notePressed[8] = false;
    break;
  case 89: // Y
  case 121: // y
    // F# note
    notePressed[2] = false;
    break;
  case 72: // H
  case 104: // h
    // G note
    notePressed[9] = false;
    break;
  case 85: // U
  case 117: // u
    // G# note
    notePressed[3] = false;
    break;
  case 74: // J
  case 106: // j
    // A note
    notePressed[10] = false;
    break;
  case 73: // U
  case 105: // u
    // A# note
    notePressed[4] = false;
    break;
  case 75: // K
  case 107: // k
    // B note
    notePressed[11] = false;
    break;
  default:
    break;
  }
}