Web Audio APIを使ってみた

<!-- Web Audio APIを使ってみた -->

※この記事は[UEC Advent Calendar 2024 - Adventar](https://adventar.org/calendars/10127)の2日目の記事です。

## はじめに

こんにちは！Udonです。

去年はUEC2になってしまいましたが、今年は無印が埋まる前に参加することができました。

今年もUEC Advent Calendarをやっていきたいと思います。前回は映画の話、前々回はソシャゲの話と、技術系の話ができていなかったので今回はさすがに技術的なことを書こうと思います。

## 背景

### iCCD2にて

我らが電気通信大学には「イノベイティブ総合コミュニケーションデザイン」という授業があります。

チームを組み、そのチームの中で独自にやりたいプロジェクトを立ち上げ、様々なアプローチでそのプロジェクトを進めていく、というものです。

なんやかんやあって、Udonは[music-stream](https://github.com/music-stream)というプロジェクトに飛び込むこととなりました。知っている人がいたことと、このプロジェクトに興味があったからです。

このプロジェクトは簡単に言うと「個人が購入したCDのデータを個人専用のサーバに保存し、様々な機器でストリーミングにより利用できるようにする」というものでした。

プロジェクトのメンバーは4人で、自分の他には[Yちゃん](https://github.com/y-chan)、[へるくん](https://github.com/hel-kun)、[すしくん](https://github.com/sushichan044)がいます。それぞれ名前は以前から知っていました。技術系サークルに所属する仲間として、一緒に頑張っていきたいと思います。3人とも強い人なので、足手まといにならないかちょっと不安です。

### 自分の役割

このプロジェクトは0から始まったわけではなく、すでに自分以外の人たちが行ってきていたプロジェクトを母体として進めていくという形になっていました。なので、すでに決まっている要件や仕様の実装などは既存のメンバーが進めていくという形になります。

というわけで、自分には「Web Audio APIを調査する」とう役割が振られました。なので、とりあえずこれを紹介している記事(参考文献にあります)を読んだり、公式ドキュメントを読んだりしました。

## Web Audio APIとは？

簡単に言うと、「Webページ上で音楽ファイルを操作するためのAPI」です。Webページ上での音楽の再生といえば``audio``タグがありますが、これは再生しかできないらしいです。このAPIをJavaScriptを用いて呼び出すことにより、再生と停止だけでなく、音量調整や一時停止など、音楽プレイヤーに求められる一連の操作を実装することができる、ということです。

### 使ってみた

まずは自分で使えないとどうにもならない、というわけで最小限の構築をしてみました。リポジトリは以下の通りです。

https://github.com/Gitudon/WebAudio-API/

合わせて、GitHub Pagesを用いて公開までしてみました。以下から見ることが出来ます。

https://gitudon.github.io/WebAudio-API/

時間がなかったのでCSSとかはまったくこだわっていません。非常に簡素です。

「Play Sound」を押すと音楽が再生され、「Stop Sound」を押すと音楽が停止されます。音楽は一曲しかありません。[魔王魂さんの『シャイニングスター』](https://maou.audio/14_shining_star/)を使用させていただきました。選曲は適当です。

### 実装内容

リポジトリを見てもらうとわかりますが、以下のようなディレクトリ構成をとっています。

```
.
├── index.html
├── statics
│   ├── script.js
│   └── style.css
└── music
    └── shining_star.mp3
```

``index.html``のは以下のような内容です。

```html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="./statics/style.css">
<title>Web Audio API</title>
</head>
<body>
<h1>Web Audio API</h1>
<p>Web Audio APIのサンプルです。</p>
<button id="startButton">Play Sound</button>
<button id="stopButton">Stop Sound</button>
<script src="./statics/script.js"></script>
</body>
</html>
```

非常に簡素な作りですが、重要なのは``button``要素です。これらのボタンを実装することで、以下のJavaScriptファイルで定義された関数が実行されるようになります。`

``script.js``は以下のような内容です。

```javascript
// AudioContextの生成
const context = new AudioContext();

// サウンドの読み込み
const loadSound = (url) => {
    return new Promise((resolve) => {
        const request = new XMLHttpRequest();
        request.open('GET', url, true);
        request.responseType = 'arraybuffer';
        request.onload = () => {
            context.decodeAudioData(request.response, (buffer) => {
                resolve(buffer);
            });
        };
        request.send();
    });
};

// サウンドの再生用の変数を定義
let source = null;

// サウンドの再生
const playSound = (buffer) => {
    // Source
    source = context.createBufferSource();
    source.buffer = buffer;
    // Destination
    source.connect(context.destination);
    // Sourceの再生
    source.start(0);
};

// サウンドの停止
const stopSound = () => {
    if (source) {
        source.stop();
        source = null; // ソースをクリアして次回再生可能にする
    }
};

// 再生ボタンの処理
const startButton = document.getElementById('startButton');
startButton.addEventListener('click', async () => {
    if (context.state === 'suspended') {
        await context.resume();
    }
    try {
        const buffer = await loadSound('./music/shining_star.mp3');
        playSound(buffer);
    } catch (err) {
        console.error(err);
    }
});

// 停止ボタンの処理
const stopButton = document.getElementById('stopButton');
stopButton.addEventListener('click', () => {
    stopSound();
});
```

基本的にコメントアウトに書いてある通りで、``AudioContext``を生成し、音楽ファイルを読み込んで再生する、という流れです。ボタンのクリックをトリガーとし、再生と停止をつかさどる関数が実行されるようになっています。

### 詰まったこと

テストの際、``html``ファイルをそのまま開いても音は再生できませんでした。ブラウザ側が音声ファイルを読み込むことができないかららしいです。そのため、テストをする際は自前でローカルサーバを立てる必要があるようです。例えば、pythonを用いた方法があります。``index.html``があるディレクトリで以下のコマンドを実行すると、ローカルhttpサーバが立ち上がります。

```bash
$ python -m http.server
```

これで、``http://localhost:8000``にアクセスすると、作ったページが表示されます。そのページでは音が再生されるようになっているはずです。

また、別の問題もあります。ブラウザのポリシーとやらで、音はページを読み込んだだけでは自動再生されず、ユーザの操作を待ってから再生されるよう設定する必要がありました。なので、「Play Sound」というボタンを用意しました。これを押すことによって音楽が再生されるようになっています。普通は再生ボタンを押すことで音楽が再生されるので、この仕様は別に問題のあるものではないと思います。

また、音楽を止めたい場合は「Stop Sound」というボタンを押すことによって、音楽を停止することができるようになっています。一時停止ではないので、一回停止してしまうと、もう一度再生したとしても最初から音楽が始まります。

調べた限り、一時停止の実装はかなり難しいようなので、とりあえず放置することとします。今は「Webブラウザ上でサーバに保存した音楽ファイルを任意に再生することができる」ということが分かったことが重要と考えています。

今は試していないですが、音量調整やシークバーの実装も可能であり、プレイリストを作ったりすることもできそうです。実装はおそらくJavaScriptが中心となりそうです。

## おわりに

お疲れさまでした。ここまで読んでいただきありがとうございました。

今後もiCCD2のプロジェクトは進んでいくと思うので、他の人たちと協力して、なんとか頑張っていきたいと思います。今後はバックエンドの開発とかを少しでも手伝えたらいいかなと思ってます(フロントエンドもやってみたい気持ちはあるけど、比較的バックエンドの方が経験があるため)。

**Yちゃん**、**へるくん**、**すしくん**、一緒に頑張りましょう！

明日は**fjdjさん**の記事です。がんばってください！

### 宣伝

[バカなこと](https://adventar.org/calendars/10764)もしてます。興味があればこちらもぜひ。

## 参考文献

[Web Audio API 入門｜npaka](https://note.com/npaka/n/n2252e1c983f7)