TECH BLOG

アートボード 1

Audio APIでねこふんじゃったを奏でてみる

2020.08.16

FRONT-END

AudioAPIについて

AudioAPIについては詳しいことは以下をご参照ください。
因みに記事公開時点では草案の段階です。
Web Audio API

前提

こちらの記事はJavaScriptと音階の仕組みが理解出来ていることを前提としています。
ギター等の弦楽器を触ったことがあるとイメージしやすいかもしれません。

デモページ
「Start」ボタンを押すと再生します。

実際のjsコード

window.AudioContext = window.AudioContext || window.webkitAudioContext;

var audioContext = new AudioContext();

var note = {};
var rythm = {};

var $btnStart = $('#js-btn-start');
var isPlaying =  false;

// 音階
var scale = ['lowDb','lowE','lowEb','lowE','lowF','lowGb','lowG','lowAb','lowA','lowBb','lowB','C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A'];
scale.forEach(function(v, i) {
  note[v] = i;
});

// 音の長さ 16分音符、8分音符、4分音符、2分音符、全音符
var lengthGroup = ['sixteenth', 'eighteenth', 'quater', 'half', 'whole'];
lengthGroup.forEach(function(v, i) {
  rythm[v] = i;
});

var play = function(rythmId, noteId) {
  var oscillatorNode = audioContext.createOscillator();
  oscillatorNode.start = oscillatorNode.start || oscillatorNode.noteOn;

  // 音程
  var frequency = parseInt(440 * Math.pow(Math.pow(2, 1/12), (-8-12) + note[noteId]), 10);
  oscillatorNode.frequency.value = frequency;

  // 音の長さ作成 4分音符基準
  var soundLength = parseFloat(1/4 * Math.pow(Math.pow(4, 1/2), rythm[rythmId]), 10);

  // 音量を少しずつ下げる
  var gainNode = audioContext.createGain();
  gainNode.gain.setValueAtTime(1.0, play.count + 0.01);
  gainNode.gain.linearRampToValueAtTime(1.0, play.count + 0.01);
  gainNode.gain.linearRampToValueAtTime(0.7, play.count + 0.20);
  gainNode.gain.linearRampToValueAtTime(0.4, play.count + 0.40);
  gainNode.gain.linearRampToValueAtTime(0.0, play.count + 0.80);

  // 接続 発生源→加工
  oscillatorNode.connect(gainNode);

  // 接続 加工→出力
  gainNode.connect(audioContext.destination);

  // 再生開始時間を指定
  oscillatorNode.start(play.count || 0);

  play.count = play.count + soundLength;
  return play;
}

play.count = 0;

function playMusic() {
  // 猫踏んじゃった
  play
  ('sixteenth', 'Eb') //ね
  ('sixteenth', 'Db') //こ
  ('eighteenth', 'lowGb') //ふん
  ('eighteenth', 'Gb') //じゃっ
  ('eighteenth', 'Gb') //た

  ('sixteenth', 'Eb') //ね
  ('sixteenth', 'Db') //こ
  ('eighteenth', 'lowGb') //ふん
  ('eighteenth', 'Gb') //じゃっ
  ('eighteenth', 'Gb') //た

  ('sixteenth', 'Eb') //ね
  ('sixteenth', 'Db') //こ
  ('eighteenth', 'lowGb') //ふん
  ('eighteenth', 'Gb') //ずけ
  ('eighteenth', 'lowEb') //ちゃっ
  ('eighteenth', 'Gb') //たら
  ('eighteenth', 'lowDb') //ひっ
  ('eighteenth', 'F') //かい
  ('eighteenth', 'F') //た

  ('sixteenth', 'Eb') //ね
  ('sixteenth', 'Db') //こ
  ('eighteenth', 'lowDb') //ひっ
  ('eighteenth', 'F') //かい
  ('eighteenth', 'F') //た

  ('sixteenth', 'Eb') //ね
  ('sixteenth', 'Db') //こ
  ('eighteenth', 'lowDb') //ひっ
  ('eighteenth', 'F') //かい
  ('eighteenth', 'F') //た

  ('sixteenth', 'Eb') //ね
  ('sixteenth', 'Db') //こ
  ('eighteenth', 'lowDb') //びっ
  ('eighteenth', 'F') //くり
  ('eighteenth', 'lowEb') //し
  ('eighteenth', 'F') //た
  ('eighteenth', 'lowGb') //ひっ
  ('eighteenth', 'Gb') //かい
  ('eighteenth', 'Gb'); //た
}

$btnStart.on('click', function() {
  if ( isPlaying ) return false;
  isPlaying = true;
  playMusic();
  $(this).addClass('is-playing');
});