Web Audio API使うことで、音声ファイルを再生したり、音量を調整したり、さまざまなエフェクトを適用したりできます。
Web Audio APIはウェブページで音を「プログラムで操作」するためのものです。読み込んだ音楽に対しても様々な効果を適用することが可能です。また、音をプログラムで生成することもできます
基本操作
- オーディオコンテキストの作成:最初に、Web Audio APIを使うための「舞台」を作ります。
- オーディオソースの設定:音声ファイルやマイクなど、音の「出どころ」を設定します。
- オーディオノードの接続:オーディオソースから出力される音声データを処理するノード(エフェクト、フィルターなど)を接続します。
- 出力先の設定:最終的にどこで音を再生するのかを設定します(通常はコンピュータのスピーカー)
- 音の制御: 再生、一時停止などの操作をします。
音の制御
audioタグでの音楽の制御
<audio id="audioElement" controls>
<source src="your_audio_file.mp3" type="audio/mp3">
</audio>
<script>
// audio要素を取得
const audio = document.getElementById("audioElement");
// 再生
audio.play();
// 一時停止
audio.pause();
</script>
Web Audio APIとHTMLのaudioを組み合わせて音楽の制御
*Web Audio APIのcreateMediaElementSourceメソッドで生成された「MediaElementAudioSourceNodeオブジェクト」は、HTMLのaudioまたはvideoタグからオーディオデータを取得し、それをWeb Audio APIのオーディオグラフに接続するために使用されます
<!DOCTYPE html>
<html>
<body>
<!-- 音楽を再生するaudioタグ -->
<audio src="your_audio_file.mp3" id="audio"></audio>
<!-- 音楽を再生・一時停止するボタン -->
<button id="play">再生</button>
<button id="pause">一時停止</button>
<script>
// Web Audio APIのエントリーポイントであるAudioContextを生成
const ctx = new AudioContext();
// HTMLのaudioタグ要素を取得
const audioElement = document.querySelector("audio");
// audioタグをWeb Audio APIで扱える形に変換(MediaElementAudioSourceNodeオブジェクト)
const track = ctx.createMediaElementSource(audioElement);
// 「再生」ボタンがクリックされたときの処理
document.querySelector("#play").addEventListener("click", () => {
// AudioContextが一時停止状態であれば再開
if (ctx.state === "suspended") {
ctx.resume();
}
// audio要素をAudioContextの出力に接続
track.connect(ctx.destination);
// audio要素の音楽を再生
audioElement.play();
});
// 「一時停止」ボタンがクリックされたときの処理
document.querySelector("#pause").addEventListener("click", () => {
// audio要素の音楽を一時停止
audioElement.pause();
});
</script>
</body>
</html>
余談:resume()メソッド
Web Audio APIのAudioContextオブジェクトで使用され、AudioContextの状態を「suspended」(一時停止)から「running」(動作中)に変更します。
*多くのモダンなウェブブラウザでは、ページが読み込まれた際にAudioContextは自動的に「suspended」状態になります。これは、ユーザーが予期せず音が再生されるのを防ぐための仕様です
fetchやXMLHttpRequestで音楽ファイルをバイナリデータ(ArrayBuffer)として読み込む場合
「AudioBuffer」について
「AudioBuffer」はサーバーから読み込まれた音声データ、またはJavaScriptで生成された音声データを保存するための容器となるオブジェクトです
*ArrayBufferオブジェクトをAudioBufferにデコードします。
<!DOCTYPE html>
<html>
<body>
<!-- 音楽を再生・一時停止するボタン -->
<button id="play">再生</button>
<button id="pause">一時停止</button>
<script>
//AudioContextを生成
const ctx = new AudioContext();
// AudioBufferSourceNodeを格納するための変数
let source;
// 音楽ファイルをArrayBufferとして非同期で取得
fetch('./sound.mp3')
.then(response => response.arrayBuffer())
//ArrayBufferオブジェクトをAudioBufferにデコードします
.then(arrayBuffer => ctx.decodeAudioData(arrayBuffer))
.then(audioBuffer => {
// AudioBufferをAudioBufferSourceNodeにセット
source = ctx.createBufferSource();
source.buffer = audioBuffer;
// AudioBufferSourceNodeをAudioContextの出力に接続
source.connect(ctx.destination);
});
// 「再生」ボタンがクリックされたときの処理
document.querySelector("#play").addEventListener("click", () => {
// AudioContextが一時停止状態であれば再開
if (ctx.state === "suspended") {
ctx.resume();
}
// 音楽を再生
if (source) {
source.start(0);
}
});
// 「一時停止」ボタンがクリックされたときの処理
document.querySelector("#pause").addEventListener("click", () => {
// 音楽を一時停止
if (source) {
source.stop();
source = null; // 一度停止した後は再度startできないのでnullにする
}
});
</script>
</body>
</html>
「AudioBufferSourceNode」について
「AudioBufferSourceNode」は、メモリに保存されたオーディオデータ(AudioBuffer)を再生するためのノードです。
このノードは、一度start()が呼び出されると、そのオーディオデータを再生し始めます。また、stop()を呼び出すと再生が停止します。
再度同じオーディオを再生する場合は、新しいAudioBufferSourceNodeインスタンスを生成する必要があります。
オーディオデータの解析
「AnalyserNode」について
「AnalyserNode」は、周波数スペクトル、波形データ、信号の平均振幅などを取得することが可能です
- getByteFrequencyData(): 周波数データをバイト配列(Uint8Array)として取得。
- getFloatFrequencyData(): 周波数データを浮動小数点数の配列として取得。
- getByteTimeDomainData(): 時間領域データ(波形)をバイト配列(Uint8Array)として取得。
AnalyserNodeを使った簡単な例
const audioContext = new AudioContext();
const analyser = audioContext.createAnalyser();
const dataArray = new Uint8Array(analyser.frequencyBinCount);
// オーディオソース(例えば、audioタグやAudioBuffer)を接続
audioSource.connect(analyser);
// 音声データの解析
function analyze() {
requestAnimationFrame(analyze);
// 周波数データを取得
analyser.getByteFrequencyData(dataArray);
// dataArrayを使用して何らかの処理(例えば、ビジュアライゼーション)を行う
}
analyze();
audioタグから取得したオーディオレベルに応じて円が拡大・縮小するアニメーション
<!DOCTYPE html>
<html>
<body>
<!-- 音楽を再生するaudioタグ -->
<audio src="./sound.mp3" id="audio"></audio>
<!-- 音楽を再生・一時停止するボタン -->
<button id="play">再生</button>
<button id="pause">一時停止</button>
<!-- canvas要素で円を描画 -->
<canvas id="canvas" width="400" height="400"></canvas>
<script>
// Web Audio APIのエントリーポイントであるAudioContextを生成
const ctx = new AudioContext();
// HTMLのaudioタグ要素を取得
const audioElement = document.querySelector("audio");
// audioタグをWeb Audio APIで扱える形に変換
const track = ctx.createMediaElementSource(audioElement);
// AnalyserNodeを作成して音声データの解析を準備
const analyser = ctx.createAnalyser();
// FFTのサイズを256に設定。これにより周波数解析の精度とパフォーマンスが調整される。
// 設定することで、解析データ配列のサイズ(frequencyBinCount)がfftSizeの半分の128になる。
analyser.fftSize = 256;
// canvas要素と2D描画コンテキストを取得
const canvas = document.getElementById('canvas');
const canvasCtx = canvas.getContext('2d');
// trackをAnalyserNodeと出力に接続
track.connect(analyser).connect(ctx.destination);
function draw() {
// analyserのfrequencyBinCountプロパティからバッファの長さ(FFTのサイズの半分)を取得
const bufferLength = analyser.frequencyBinCount;
// Uint8Arrayを生成し、各頻度バンドのデータを格納するための配列を作成
const dataArray = new Uint8Array(bufferLength);
// AnalyserNodeから頻度データを取得してdataArrayに格納
analyser.getByteFrequencyData(dataArray);
// dataArrayの各要素(頻度バンドの強度)を合計する
let sum = 0;
for(let i = 0; i < bufferLength; i++) {
sum += dataArray[i];
}
// 頻度データの平均値を計算
const average = sum / bufferLength;
// canvasをクリア
canvasCtx.clearRect(0, 0, canvas.width, canvas.height);
// 円を描画
canvasCtx.beginPath();
canvasCtx.arc(200, 200, average, 0, 2 * Math.PI);
canvasCtx.fill();
// アニメーションループ
requestAnimationFrame(draw);
}
document.querySelector("#play").addEventListener("click", () => {
if (ctx.state === "suspended") {
ctx.resume();
}
audioElement.play();
draw();
});
document.querySelector("#pause").addEventListener("click", () => {
audioElement.pause();
});
</script>
</body>
</html>
AudioBuffer(バイナリデータとして読み込む)から取得して、オーディオレベルに応じて円が拡大・縮小するアニメーション
*AudioBufferSourceNodeは一度stopすると再度startできないため、再生ボタンを押すたびに新しいAudioBufferSourceNodeを生成
<!DOCTYPE html>
<html>
<body>
<!-- 音楽を再生・一時停止するボタン -->
<button id="play">再生</button>
<button id="pause">一時停止</button>
<!-- canvas要素で円を描画 -->
<canvas id="canvas" width="400" height="400"></canvas>
<script>
// AudioContextを生成
const ctx = new AudioContext();
// AnalyserNodeを作成して音声データの解析を準備
const analyser = ctx.createAnalyser();
analyser.fftSize = 256;
// canvas要素と2D描画コンテキストを取得
const canvas = document.getElementById('canvas');
const canvasCtx = canvas.getContext('2d');
let source;
let buffer;
// MP3ファイルをfetchで取得
fetch('./sound.mp3')
.then(response => response.arrayBuffer())
.then(data => ctx.decodeAudioData(data))
.then(decodedBuffer => {
buffer = decodedBuffer;
});
function draw() {
const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
analyser.getByteFrequencyData(dataArray);
let sum = 0;
for(let i = 0; i < bufferLength; i++) {
sum += dataArray[i];
}
const average = sum / bufferLength;
// canvasをクリア
canvasCtx.clearRect(0, 0, canvas.width, canvas.height);
// 円を描画
canvasCtx.beginPath();
canvasCtx.arc(200, 200, average, 0, 2 * Math.PI);
canvasCtx.fill();
// アニメーションループ
requestAnimationFrame(draw);
}
document.querySelector("#play").addEventListener("click", () => {
if (ctx.state === "suspended") {
ctx.resume();
}
source = ctx.createBufferSource();
source.buffer = buffer;
// バッファをAnalyserNodeと出力に接続
source.connect(analyser).connect(ctx.destination);
source.start(0);
draw();
});
document.querySelector("#pause").addEventListener("click", () => {
if (source) {
source.stop();
}
});
</script>
</body>
</html>
音を作る
「OscillatorNode」について
「OscillatorNode」は、短い周期的な波形(sin波、square波、triangle波、sawtooth波など)を生成する役割があります。このノードを利用することで、合成音や短い音をプログラムから直接生成することが可能です。短い通知音やアラート音を生成する際に便利です
- type: 生成される波形のタイプ(”sine”, “square”, “triangle”, “sawtooth” など)
- frequency: 発生する音の周波数(デフォルトは440Hz)
- start(): オシレーターを開始するメソッド
- stop(): オシレーターを停止するメソッド
- connect(): 他のAudioNodeに接続するメソッド
「GainNode」について
「GainNode」は、音量(ゲイン)を制御するためのものです。これを使用することで、オーディオストリームの音量を上げたり下げたり、あるいは完全にミュートにしたりすることができます。
<!DOCTYPE html>
<html>
<body>
<!-- 効果音を再生するボタン -->
<button id="play">効果音を再生</button>
<script>
// AudioContextを生成
const audioCtx = new AudioContext();
// 効果音を再生するボタンのクリックイベントを設定
document.querySelector("#play").addEventListener("click", () => {
// OscillatorNodeを生成
const oscillator = audioCtx.createOscillator();
// GainNodeを生成(音量調整用)
const gainNode = audioCtx.createGain();
// Oscillatorの設定
oscillator.type = 'sine'; // sin波を生成
oscillator.frequency.setValueAtTime(300, audioCtx.currentTime); // 初期周波数を300Hzに設定
// GainNodeの設定(初期音量を1に設定)
gainNode.gain.setValueAtTime(1, audioCtx.currentTime);
// OscillatorNodeをGainNodeに、GainNodeを出力(スピーカー)に接続
oscillator.connect(gainNode).connect(audioCtx.destination);
// 効果音の変化(周波数と音量を変更)
oscillator.frequency.exponentialRampToValueAtTime(100, audioCtx.currentTime + 0.5); // 0.5秒後に周波数を100Hzに下げる
gainNode.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.5); // 0.5秒後に音量をほぼ0に下げる
// オシレーターを開始(すぐに)
oscillator.start();
// 0.5秒後にオシレーターを停止(音が終わるタイミングと合わせる)
oscillator.stop(audioCtx.currentTime + 0.5);
});
</script>
</body>
</html>