JavaScriptでキーボードを作る(Tone.js)

`Tone.js`というJavaScriptのライブラリがあることを知り、使ってみることにしました。  
これを使うことで、ブラウザ上で音声の処理をする`Web Audio API`を簡単に活用できるようになるらしいです。  

https://tonejs.github.io/  

https://developer.mozilla.org/ja/docs/Web/API/Web_Audio_API  

めちゃくちゃ簡単にですが、キーボードのようなものをブラウザで描画するところまで書きます。  

## ソースコード

とりあえず以下のようなコードを書いてみました。  

```html:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Tone.js</title>
  <link rel="stylesheet" href="./index.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
  <script src="./index.js"></script>
</head>
<body>
  <p>キーボード</p>
  <!-- 鍵盤表示エリア -->
  <div id="keyboard" class="keyboard"></div>
</body>
</html>
```

```css:index.css
.keyboard div {
  position: relative;
  width: 40px;
  height: 200px;
  float: left;
}

.keyboard button {
  position: absolute;
}

.white {
  background-color: #ffffff;
  border: 1px solid #c0c0c0;
  width: 40px;
  height:200px;
}

.black {
  background-color: #000000;
  border: 1px solid #c0c0c0;
  width: 30px;
  height: 120px;
  left: 25px;
  z-index: 1;
}
```

```js:index.js
const synth = new Tone.Synth().toDestination();

window.onload = () => {
  drawKeyboard();
};

// 鍵盤の描画メソッド
const drawKeyboard = () => {
  // 鍵盤の生成(音階を表す文字列を配列に入れておきます)
  const musicalScaleArray = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
  let baseKey;
  // 1つ1つ鍵盤を作っていく作業
  for (let i = 0; i < 25; i++) {
    // 鍵盤をボタンとして作成する
    const key = document.createElement("button");
    // idに音階の情報を付与(スタートはC3になるようにしています)
    key.id = `key_${musicalScaleArray[i % musicalScaleArray.length]}${Math.floor(i / 12) + 3}`;

    // クリックしている間に音が出るという仕様にする
    key.onmousedown = play;
    key.onmouseup = stop;
    key.onmouseleave = stop;

    // 黒鍵が白鍵かによってデザインを変えるので、そのためのclassをそれぞれ付与
    if (musicalScaleArray[i % 12].indexOf("#") > -1) {
      // 黒鍵(#がついている)
      key.classList.add("black");
    } else {
      // 白鍵
      key.classList.add("white");
      baseKey = document.createElement("div");
    }
    baseKey.appendChild(key);
    document.getElementById("keyboard").appendChild(baseKey);
  }
};

// 音の再生
const play = async (e) => {
  await Tone.start();
  // 鍵盤のidから音階を取得
  const scale = e.target.id.split("_")[1];
  // 指定した音を再生する
  synth.triggerAttack(scale);
};

// 音の停止
const stop = async (e) => {
  synth.triggerRelease();
};
```

できた画面はこんな感じ  

![スクリーンショット 2023-04-15 094140.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2780581/f354dc8b-1555-4418-cdec-f75f21a9cb05.jpeg)


上で書いたコードについて簡単に解説していきます。  

### 読み込み

CDNを利用してTone.jsを読み込んでいます。  

```html
<script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
```

### 音源の作成

`Synth`を使って音源に接続します。  

```js
const synth = new Tone.Synth().toDestination();
```

### 音の再生・停止

作成したsynthについて、`triggerAttack`で音の再生の処理を、`triggerRelease`で音の停止の処理を表すことができます。  
(triggerReleaseは「停止」というよりは音の振幅を0にするようなイメージみたいですね。)  

```js
// C4など音階を指定してあげます
synth.triggerAttack("C4");
synth.triggerRelease();
```

今回は鍵盤をクリックすることでtriggerAttackを呼び出し音を再生、クリック終了時にtriggerReleaseが呼ばれて音が停止…というようにしています。  

## おわり

以上、Tone.jsを使ってみる記事でした。  
実際に音の出ているところを動画にしてアップ…はできなかったので(Twitterを使用しなきゃいけないとかなんとか)、本当に音が出るか気になる方は今回のコードを参考に作ってみてください。  
音は出るんです。本当です。