どんなブラウザでも安定して効果音を鳴らす

3 min read読了の目安(約1900字

ブラウザで効果音を再生する時にはaudioタグを使いますが、効果音を重ねたりすると、音が途切れたり遅延が発生することがあります。

特にiOSのsafariでは顕著で、audioタグを複数読み込んでマルチチャンネル化しても他の処理に遅延などの影響が出たりします。

以下のクラスでは、音源のマルチチャンネル化と再利用手順を組み合わせて、連続再生の問題を解決することができます。

class Se
  {
    constructor(jparent,src,channels=1)
    {    
      const setags=[];
      for(var i=0;i<channels;i++){
        jparent.append('<audio id="sound" preload="none" src="'+src+'" type="audio/mp3"></audio>');
        const a=jparent.children('audio:last-child')[0];
        a.load();
        setags.push(a);
      }
      this._tags=setags;
      this._channel=0;
    }
    play(){
      this._channel=(this._channel+1)%this._tags.length;
      const a=this._tags[this._channel];
      if(a.currentTime!=0){
        if(!a.ended){
          //終わってない場合は再生位置リセット
          a.currentTime=0;
        }else{
          //終わってたら再生再開
          a.play();
        }
      }else{
        a.play();
      }
    }
    /** 全てのチャンネルのSEを停止 */
    stop()
    {
      for(var a of this._tags){
        if(!a.ended){
          a.pause();
          a.currentTime=0;
        }
      }
    }
  }

使い方

jqueryが必要です。

Seクラスの第一引数にaudioタグ、第二引数にaudioファイルのURL、第三引数に同時再生のチャンネル数を指定します。

const se=new Se($("body"),"./audio/boyon1.mp3",5),

再生はplay関数を呼び出します。

se.play();

途中で再生を停止するには、stop関数を使います。stop関数はその音源のすべてのチャンネルを停止します。

se.stop();

複数の音源を使う

3種類の音源を使うには音源ごとにインスタンスを作ります。

const se_parent=$("body");
const se={
  touch1:new Se(se_parent,"./audio/boyon1.mp3",5),
  touch2:new Se(se_parent,"./audio/boyoyon1.mp3",2),
  water:new Se(se_parent,"./audio/toilet1.mp3",1),
};
se.touch1.play();
se.touch2.play();
se.touch3.play();

動作デモ

あまり上品ではありませんがこちらで試せます。

https://unko.shop/

動作原理

Seクラスは同一音源の複数のaudioタグを管理します。
play関数は最も過去に再生を開始したチャンネルを調べます。
そのチャンネルが停止中なら再生し、再生中なら再生位置を戻します。

再生位置を戻すと、(何故か)スタックせずに重ね合わせができるようになります。

この記事は以下の日記の焼き直しです。

https://nyatla.hatenadiary.jp/entry/20200731/1596153856