JSでシームレスにループするサウンド再生を実現する
はじめに
先日、Scrapboxで環境音を流せるChrome拡張であるScrapboxRelaxExtensionを公開した。
その際、環境音をループ再生する際にシームレスなループになるようにひと工夫必要だったので残しておく。
実装方法
通常、JSで音楽を再生するにはHTMLAudioElement
で足りる。自分も最初は以下のような実装をした。
const audioElement = new Audio("sound.wav");
audioElement.loop = true;
audioElement.play();
audioElement.pause();
ただ、これだとループの繋ぎ目で音が少し途切れてしまった。音楽自体の再生時間の長さを大きくして繋ぎ目の頻度を減らすという手段も試してみたが、ファイルサイズが大きくなるし根本的な解決ではないため断念した。
他の手段を検討していた際に、AudioBufferSourceNode
というAPIを見つけた。
音楽データを事前にメモリ内に読み込んでおくことで、ループ時の繋ぎ目をシームレスにすることができる。自分はネットワーク経由からファイルを取得したが、Buffer
であれば良いのでローカルファイルからデータを持ってくる場合はFileReader
などでも実現できると思われる。
const audioContext = new AudioContext();
const response = await fetch("https://sample.com/sound.wav");
const arrayBuffer = await response.arrayBuffer();
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
const audio = audioContext.createBufferSource();
audio.buffer = audioBuffer;
audio.loop = true;
audio.connect(audioContext.destination);
audio.start();
audio.stop();
必要に応じてloopStart
とloopEnd
を設定することで、さらに細かく繋ぎ目の調整もできる。
注意点としては音楽データをメモリに読み込むため、ファイルサイズが大きいとメモリ消費も大きくなってしまうので注意が必要。
おわり
Audio関連のAPIは今回初めて触ったので新鮮だった。
ループ再生以外にも、ブラウザの制限によってWebページにおける音楽の再生はユーザーのアクションが必要となっていたりなど、ハマりどころがいくつかあり勉強になった。