JS:Web Audio APIを利用した簡単なサンプルと解説(参考文献MDN)

JavaScript

これはMDNにあるWeb Audio APIの使用で扱っているサンプルを少し簡単にしたものです。
Web Audio APIの理解や使い方の参考にしてください。
もっと簡単なやつが見たい方は 『Web Audio APIの超簡単なサンプル』をどうぞ!

以下のサンプルでは、Web Audio APIを使って音源の再生と一時停止ボリュームコントロールを行っています。

Web Audio APIを使う利点は?

単純な再生だけならaudioタグで問題ないのです。
Web Audio APIを使う利点は、ゲームなどで同時に複数の音声を再生したり、あるいは音声にエフェクトを加えたり音量調整をしたりと高度に利用できる点にあります。

Web Audio API は <audio> メディア要素を置き換えるものではなく、むしろその機能を補完するものであり、 <canvas> が <img> 要素と並行して存在するのと似ています。

Web Audio APIの使用 – MDN より引用

Web Audio APIを使ってみる上での注意点

今回紹介するサンプルを自分で作ってPC上で index.html をダブルクリックしても動きません

たぶんコンソール上に以下のようなメッセージが出力されます。

MediaElementAudioSource outputs zeroes due to CORS access restrictions for ...

今回のように音声ソースを扱うサイトの場合、セキュリティの関係上ブラウザ側でアクセス制限が掛かって動作しません。

サンプルソースを自分のPCで実行するには、VsCodeLive Server などの機能拡張を利用したり、ローカルサーバ上で動作確認したり、あるいは直接レンタルサーバーなどにアップロードして動作確認する必要があります。

ソースコード

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="main.js"></script>
    <title>Web Audio APIの利用</title>
</head>
<body>
    <p>Web Audio APIを利用した音源の再生と停止・音量調整</p>

    <!-- 利用する音声ファイル -->
    <audio src="maou_bgm_8bit29.mp3"></audio>

    <!-- 再生/一時停止ボタン -->
    <button data-playing="false" role="switch" aria-checked="false">
        <span>Play/Pause</span>
    </button>
    
    <!-- ボリューム調整バー -->
    <p>Volume<input type="range" name="" id="volume" min="0" max="2" value="1" step="0.01" data-action="volume"></p>
</body>
</html>

main.js

// Web Audio APIの簡単なサンプル

/*
	参考
	
	Web Audio API入門 | MDN
	https://developer.mozilla.org/ja/docs/Web/API/Web_Audio_API

	Web Audio API の使用 | MDN
	https://developer.mozilla.org/ja/docs/Web/API/Web_Audio_API/Using_Web_Audio_API
*/

window.addEventListener("load", ()=>{
	// 古いブラウザ向けの設定
	const AudioContext = window.AudioContext || window.webkitAudioContext;

	// 音声コンテキスト作成
	const audioContext = new AudioContext();

	// 音声ファイル(入力元)を取得
	const audio = document.querySelector("audio");

	// 音声コンテキストに音声ファイルを設定する
	const track = audioContext.createMediaElementSource(audio);

	// PLAY/PAUSEボタン取得
	const playButton = document.querySelector("button");

	// PLAY/PAUSEボタンを押した時の処理
	playButton.addEventListener(
		"click",
		function(){
			// 音声(audioContext)が中断状態かどうかチェックする(中断であれば再開する)
			if(audioContext.state === "suspended"){
				audioContext.resume();
			}

			// data-set: playingの状態により再生と一時停止を制御
			if(this.dataset.playing === "false"){
				audio.play();	// 再生
				this.dataset.playing = "true";
			}
			else if(this.dataset.playing === "true"){
				audio.pause();	// 一時停止
				this.dataset.playing = "false";
			}
		},
		false
	);

	// 再生が終了したときの処理
	audio.addEventListener(
		"ended",
		()=>{
			playButton.dataset.playing = "false";
		},
		false
	);


	// 音量調整コンテキスト作成
	const gainNode = audioContext.createGain();

	// 音量調整バーを取得
	const volumeControl = document.querySelector("[data-action='volume']");

	// 音量調整をしたときの処理
	volumeControl.addEventListener(
		"input",
		function(){
			gainNode.gain.value = this.value;
		},
		false
	);

	// 設定済みの音声ファイルの音声コンテキストを音量コントロールと出力先(スピーカー)に接続
	track.connect(gainNode).connect(audioContext.destination);
});

簡単な解説

HTML部分(index.html)

再生する音声ファイルはaudioタグとして記述しています。

<audio src="maou_bgm_8bit29.mp3"></audio>

このタグはmaou_bgm_8bit29.mp3という音声ファイルを読み込むためにだけ使っています。よって画面上には表示されない要素です。

今回使ったこのかわいい音声ファイルは、以下の魔王魂のサイトから自由に利用できます。

8bit29 | 魔王魂
魔王魂(森田交一)の音楽を無料ダウンロード。全曲フリーBGMとして使用可能です。

ボタンは再生と一時停止に利用するため、表示文字列をPlay/Pauseとしています。このPlay/Pauseの文字列はあくまでボタンの見た目です。

<button data-playing="false" role="switch" aria-checked="false">
<span>Play/Pause</span>
</button>

buttonタグに、再生と一時停止の状態を表す要素としてdata-playing=”false”を設定しています。
data-playingがtrueの場合を再生中falseの場合を一時停止中として後述するJavaScript側で実装するようにします。

ボリュームコントロールはinputタグのrangeタイプとしてスライダー表示にしています。

<p>Volume<input type="range" name="" id="volume" min="0" max="2" value="1" step="0.01" data-action="volume"></p>

max=”2” となっていますが、Web Audio API自体のボリューム要素の数値は-3.4~3.4の範囲とされています。とは言え、0でミュートとなるので実際にはボリュームは0~3.4の範囲になるかと思います。

ここでいうmax=”2″の意味は、MDNの説明によると元の音量の2倍という意味になります。
この辺りの数値は動作確認後に適当に変更してみてください。

data-action=”volume”の項目は、JavaScript側からのDOM取得のために利用しているだけですので、id=”volume”などとしてDOM取得しても問題はありません。

JavaScript部分(main.js)

最初の記述は旧いブラウザに対応するための記述です。
最近のブラウザ(2024年時点)であれば必要ないと思われます。

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

実際に上記の行をコメントアウトしても、わたしの利用しているChrome(バージョン: 124.0.6367.62)では動作しました。

Web Audio APIを使って音を鳴らすには、まずこのAudioContextのインスタンスを作成する必要があります。

// 音声コンテキスト作成
const audioContext = new AudioContext();

AudioContextのインスタンスに対して、音声ファイルを設定し、音量を設定し、出力先を設定することでWeb Audio APIを使った再生の準備が整います。Web Audio APIの重要な考え方は、AudioContextのインスタンスを通して全て操作を行うということです

まず音声ファイルの設定です。

// 音声ファイル(入力元)を取得
const audio = document.querySelector("audio");

// 音声コンテキストに音声ファイルを設定する
const track = audioContext.createMediaElementSource(audio);

index.htmlに記述されたaudioタグのDOM取得をしてから、AudioContextのメソッドであるcreateMediaElementSourceを使ってaudioContextの音声ファイルに設定しています。

今回のサンプルでは、ボタンイベントにより再生と一時停止を制御しています。
しかしこの部分の処理はWeb Audio APIとはほとんど関係がありません。従来のaudioタグを再生させるものとほぼ同じですが、1か所Web Audio APIと関係がありますので解説します。

再生用のボタンタグのDOM取得をします。

// PLAY/PAUSEボタン取得
const playButton = document.querySelector("button");

PLAY/PAUSEボタンを押した時の処理です。

// PLAY/PAUSEボタンを押した時の処理
playButton.addEventListener(
"click",
function(){
// 音声(audioContext)が中断状態かどうかチェックする(中断であれば再開する)
if(audioContext.state === "suspended"){
audioContext.resume();
}

// data-set: playingの状態により再生と一時停止を制御
if(this.dataset.playing === "false"){
audio.play(); // 再生
this.dataset.playing = "true";
}
else if(this.dataset.playing === "true"){
audio.pause(); // 一時停止
this.dataset.playing = "false";
}
},
false
);

最初のif文にある audioContext.state === “suspended” の意味は、audioContextが中断された状態にあるかどうかのチェックです。

audioContext.stateの状態が running でないと音を再生できません。
これは自動再生ブロックの問題があるため記述されています。

自動再生ブロックの問題とは?

昔のブラウザではページ読み込みと同時に音声や動画が再生できました。
現在はできなくなりました。ユーザがなんらかのアクションを起こすか無音で再生するかなど色々と条件がつくようになりました。

参考

メディアおよびウェブ音声 API の自動再生ガイド - ウェブメディア技術 | MDN
ページが読み込まれるとすぐに音声(または音声トラックを含む動画)の再生を自動的に開始することは、ユーザーにとって歓迎されない驚きです。 メディアの自動再生は便利な目的に役立ちますが、注意して必要なとき...

再生と一時停止の制御は、buttonタグのdata-playingに設定した値を使って切り替えています。

data-playingtrueなら再生中、falseなら一時停止中を表すようにしてボタンを押すたびに切り替えます。

// data-set: playingの状態により再生と一時停止を制御
if(this.dataset.playing === "false"){
audio.play(); // 再生
this.dataset.playing = "true";
}
else if(this.dataset.playing === "true"){
audio.pause(); // 一時停止
this.dataset.playing = "false";
}

再生と一時停止はあくまでaudio要素としてaudio.play()、audio.pause()などと行っている点に注目してください。audioContextの状態はあくまで変化せずaudioの状態を変化させています。
例えば、audioContext.play() などとはできないのです。

音声が再生終了したときは、ボタンを押す押さないにかかわらず一時停止状態とするようにしなければ挙動がおかしくなってしまいます。
これはaudioオブジェクトの再生が終了したというイベントであるendedイベントに設定することで制御します。

// 再生が終了したときの処理
audio.addEventListener(
"ended",
()=>{
playButton.dataset.playing = "false";
},
false
);

endedイベント発生時にdata-playingfalseとして一時停止状態に変化させています。

次に音量の設定をaudioContextに対して行います。

AudioContextのcreateGainメソッドを使うと音量調整が出来る様になります。

// 音量調整コンテキスト作成
const gainNode = audioContext.createGain();

inputタグに指定した[data-action=’volume’]を使って音量調整用のスライダーのDOM取得をしています。

// 音量調整バーを取得
const volumeControl = document.querySelector("[data-action='volume']");

スライダーを変化させたときのイベントはinputイベントです。

// 音量調整をしたときの処理
volumeControl.addEventListener(
"input",
function(){
gainNode.gain.value = this.value;
},
false
);

スライダーの現在値をgainNode.gain.valueに設定することでAudioContextに対して音量調整が出来ます。

最後にこれまでAudioContextインスタンスであるaudioContext変数に対して、音量設定と出力先(基本的にはスピーカー)を設定して完了です。

// 設定済みの音声ファイルの音声コンテキストを音量コントロールと出力先(スピーカー)に接続
track.connect(gainNode).connect(audioContext.destination);

上記の記述を見ると分かりますが、connect メソッドはつなげて記述ができます。
もし、音量コントロールが不要なら以下のように直接スピーカーに接続するように記述してもいいわけです。

track.connect(audioContext.destination);

参考

この記事はMDNの以下を参考に記述しました。

ウェブオーディオ API - Web API | MDN
ウェブオーディオ API はウェブ上で音声を扱うための強力で多機能なシステムを提供します。これにより開発者は音源を選択したり、エフェクトを加えたり、視覚効果を加えたり、パンニングなどの特殊効果を適用し...
Web Audio API の使用 - Web API | MDN
Web Audio API の入門を見てみましょう。ここではいくつかの概念を短く確認してから、簡単な boombox の例で、音声トラックの読み込み、再生と一時停止、音量やステレオ位置の変更の方法を学...

今回のサンプルをもっとシンプルにしたものが以下にあります。

コメント

タイトルとURLをコピーしました