連打しても同期的に一度ずつしか鳴らない再生ボタンをjsで作る
2023-05-12
概要
- 非同期で要求された処理(キュー)を、1つずつ順番に同期っぽく消化していく
- 全て消化し終える前に、追加でキューが送られてくるケースも想定
そんな感じのことをやってみたかった
処理例・イメージ
今回は「サウンド再生ボタン」を例にとってみました。
"押すと数秒の音源ファイルを再生するボタン" があったとして、これを3連打したら
押したぶんだけサウンドが一度ずつ順番に再生処理される感じ。
全ての再生処理を終える前に追加でボタンを押せば、そのぶんキューが積まれるようにもした。
ただし今回はキュー数に上限を設け、幾ら連打しても所定数を超えた再生処理は積まない仕様も追加。
コード例
- ※ 🔊 同階層に
file.wav
を用意してください。
( .mp3 とかでも良いと思う )
const CueList = []; // キューの保持
const CueLimit = 5; // 上限
window.onload = ()=>{
const _btn = document.getElementById("play_button");
_btn.addEventListener("click",async()=>{
const _count = CueList.length;
console.log("未完了のキュー数" , _count);
if(_count > 0){
console.log("未完了のキューがまだあるので、キューに追加しておくだけ");
if(_count >= CueLimit){
console.warn("上限到達により、このキューを破棄します");
return;
}
CueList.push("キューに関する任意の付随情報");
}else{
console.log("予約なし。キューに入れつつ即時消化のために再生を開始");
CueList.push("キューに関する任意の付随情報");
CuePlay();
}
});
};
// サウンドファイルを取得・再生する。
// ここは原則、手動で一度だけ呼ばれるが、
// 再生終了後に CueList をチェックし、要素が存在していれば自身を再帰的に呼び出す
async function CuePlay(){
// 先頭のキュー情報を取り出し( 本例では特に使っていない )
const _someCueInfo = CueList[0];
console.log( _someCueInfo , "を実行" );
// ファイルの通信取得
const _res = await fetch( "file.wav" , { method : "GET" , responseType: 'arraybuffer' })
.then(_res=> _res);
// 取得した音源の再生準備
{
const _audioCtx = new window.AudioContext();
const _arrBuf = await _res.arrayBuffer();
const _srcNode = _audioCtx.createBufferSource();
_srcNode.buffer = await _audioCtx.decodeAudioData( _arrBuf );
_srcNode.connect( _audioCtx.destination );
_srcNode.addEventListener("ended",(_e)=>{
// 再生終了時に呼ばれる。「このキューを完了した」とみなし、キュー情報を削除
CueList.shift();
const _count = CueList.length;
if(_count > 0){
// 未消化のキューがあれば、再帰で同じ処理を掘る。
CuePlay();
}
});
// 再生
_srcNode.start(0);
}
}
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>再生テスト</title>
<script src="./main.js"></script>
<style>
#play_button{
padding:10px;
}
</style>
</head>
<body>
<button id="play_button">キュー追加 兼 再生ボタン</button>
</body>
</html>
その他補足
- 今回、
_someCueInfo
部分の情報は活用していないものの、
本来は「キューごとに独自の付随情報を入れる」みたいな使い方を想定して書いた。 - ブラウザの制約で
file:///
スキーム &fetch
を用いた音源ファイル通信取得はできないので、
適当にローカルサーバー立てたのちhttp://localhost/
経由で触るなどしてください。