2017/11/17

Web Audio APIとVue.jsでシンプルなシンセサイザーをつくる

ブラウザで使えるMinimoogとかいいじゃん!そんなことを思い立ち、Web Audio APIを勉強しはじめた。

今回は、その第一歩として88鍵のキーボードを表示し、クリックしたら音がなるものをつくろうと思う。Vue.jsを使っているのは、要素の生成やイベントハンドリングを楽にするためだ。


オシレーターをつくる


発音するための機構をつくる。

今回はシンプルなシンセサイザーなので、打鍵されたキーの周波数を正弦波(サイン波)で発音するシンセサイザー(というよりキーボード)をつくる。
class VOscillator {
  constructor() {
    this.ctx = new AudioContext();
    this.osc;
  }

  // 音を鳴らす
  sound(note) {
    // Failed to execute 'start' on 'AudioScheduledSourceNode': cannot call start more than once.
    // というエラーがでるので、音を鳴らす度にオシレーターを再生成する
    this.osc = this.ctx.createOscillator();
    this.osc.frequency.value = note;
    this.osc.connect(this.ctx.destination);

    this.osc.start();
  }

  // 音を止める
  stop() {
    this.osc.stop();
  }
}

Web Audio APIのAudioContextを使って、音声処理を行う。
Web Audio APIのオシレーターは使い捨てなので、音を鳴らすたびにAudioContext.creteOscillatorOscillatorNodeを作成する。

soundメソッドの引数に周波数(Hz)が渡ってくるので、それをOscillator.frequencyにセットし、AudioContext.destinationに接続する。
AudioContext.destinationは音の出口(PCのスピーカー)のようなイメージ。

最後にOscillator.startで発音させることで音がなる。
音を止めるときは、Oscillator.stopで止められる。



鍵盤を表示する


今回は88鍵の鍵盤を表示する。
<div id="app">
  <div class="keyboard">
    <ul class="key-list">
      <li
        v-for="(note, i) in notes"
        :key="i"
        class="key"
        :class="{ '-sharp': isSharp(i) }"  
        @mousedown="stroke(note)"
        @mouseup="release"
        @mouseleave="release"
        ></li>
    </ul>
  </div>
</div>
new Vue({
  el: '#app',
  data () {
    return {
      KEY_A: 440,  // 基音: 440Hz
      osc: null    // オシレーター
    }
  },
  created () {
    this.osc = new VOscillator();
  },
  mounted () {
    // 基音付近にスクロール
    const keyList = document.querySelector('.key-list');
    keyList.scrollLeft = (keyList.scrollWidth / 2) - (keyList.offsetWidth / 2);
  },
  computed: {
    notes () {
      // 88鍵 -48は基音A4→A0までの距離
      return [...Array(88)].map((_, i) => this.KEY_A * Math.pow(2, (1 / 12) * (-48 + i)));
    }
  },
  methods: {
    isSharp (i) {
      // A#, C#, D#, F#, G#
      return [1, 4, 6, 9, 11].includes(i % 12)
    },
    stroke (note) {
      this.osc.sound(note);
    },
    release () {
      this.osc.stop();
    }
  }
});
88鍵の鍵盤なので、最低音はA0(27.5Hz)で最高音はC8(4186Hz)になる。それを1オクターブを12で割った十二平均律を使って各鍵盤の周波数を決定する
鍵盤は1音(短2度)上がることで2の1/12乗周波数が上がる。そのため各鍵盤の周波数は基音 * 2のn/12乗の式によって求められる

たとえば最低音A0は、基音A4から48鍵分低い位置にあるので440 * 2^(-48/12)で求められる。
同様にC4(中央のド)は基音A4の長6度(9鍵分)下なので、440 * 2^(-9/12)で求められる。

黒鍵の判定は単純に基音Aのインデックスを0とした場合、1, 4, 6, 9, 11がシャープするので黒鍵だと判定している。

あとはマウスイベントでmousedownされたら発音、mouseupされたら停止している。


ちなみにCSSはこんな感じ。
.keyboard {
  padding: 10px 0;
  background-color: rgba(6,6,6,.3);
}

.key-list {
  height: 250px;
  background-color: #666666;
  
  /* 改行させないため */
  overflow-x: scroll;
  overflow-y: hidden;
  white-space:nowrap;

  > .key {
    cursor: pointer;
    display: inline-block;
    
    width: 60px;
    height: calc(100% - 6px);  /* scrollbar分 */
    background-color: #ffffff;
    margin: 0 3px;

    border-radius: 0 0 5px 5px;

    &.-sharp {
      position: relative;
      width: 0;
      margin: 0;

      &::after {
        display: block;
        content: '';

        position: absolute;
        top: 0;
        left: calc(-50px / 2);
        width: 50px;
        height: 50%;

        border-radius: 0 0 5px 5px;
        background-color: #000000;
      }
    }
  }
}


完成





今度は正弦波だけでなく三角波、ノコギリ波、矩形波を出したり、その他のWeb Audio APIについて触れようと思う。



以上

written by @bc_rikko

0 件のコメント :

コメントを投稿