今回は、その第一歩として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.creteOscillatorでOscillatorNodeを作成する。
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 件のコメント :
コメントを投稿