Howler.jsで何かいい感じにループするBGMを実装する

ふとHTML5ゲーで細かくてシームレスなループをさせるのってどうするんや?と思い、試してみたら一応出来たので、後々使うだろうときのために書き残してみる。

howler.js(v2.0系)を使用。またwebaudioAPIで再生を行っているという前提。

要件

  • 再生開始時は普通に曲頭から
  • ループ終点までいったら今度は曲頭ではなく、ループ開始地点にシークして2週目開始
  • 以後、ル開始~終点で無限ループさせる

実装

html部

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>howler.js ループテスト</title>
</head>
<body>

<script src="howler.jsへのパス"></script>
<script src="./main.js"></script>
</body>
</html>

特にHTML側で変わったことはせず。

スクリプト部(main.js)

Howlクラスを拡張。

class Music extends Howl {
  constructor (data, debugStart=0) {
    const params = {
      src: [data.src],
      preload: false,
      // オーデイオスプライト設定
      sprite: {
        start: [debugStart, data.loopEnd-debugStart],
        loop: [data.loopStart, data.loopEnd - data.loopStart, true],
      },
    };
    super(params);
    this.load();
  }

  playMusic () {
    this.play("start");

    // 一周目の再生終了を検知したらループ部に入る
    this.once('end', ()=> {
      this.play("loop");
    });
  }
}

howler.jsにはオーデイオスプライトなる機能がある。 いわばサウンドのスプライトシートで、複数の音源を一つの音源ファイルにまとめ上げ、範囲を指定することでそれぞれの音を鳴らすことができる。

通常は効果音を一つのファイルにまとめて通信量を減らす目的で使うそうだけど、この機能はシームレスなループ再生にもイケる。
範囲はそれぞれ最初~ル終点を「start」、2週目以降の無限ループ範囲を「loop」と命名

sprite設定は見ての通り配列で[ 開始位置(msec), 範囲長(duration), ループするかどうか ]のように指定。 durationは(ル終 - ル開)で動的に計算する。

debugStart変数については後述。

流す

// loopStart, loopEndはそれぞれミリ秒単位でのル開始、終点
const musicData = {
  src: "./music.mp3",
  loopStart: 17950,
  loopEnd: 126936,
};

const music = new Music(musicData);
music.onload = function() {
  this.playMusic();
};

後はシンプルにロード&再生。

デバッグ再生

ル終点までわざわざ再生して接続を確認するのはかったるい。 ということで最初から終点間際にして再生確認するため、 第二引数で開始位置を渡せるようにすると便利。

// ル終点2秒前にシークした状態で再生
var music = new Music(musicData, musicData.loopEnd - 2000);
music.onload = function() {
  this.playMusic(); // すぐループに入る
};

おまけ:ループポイント探しの旅

自分で作曲とかしない限り、ロイヤリティフリー音源を使うことも多いが、大体の場合、ループポイントは実際に音源を聞いて探すしかない。(RPGツクール用などで、ループポイントを付記してくれている音源もたまにあるけど)

基本的には以下のような感じで探ってます。

f:id:pentamania:20180725131101p:plain

(メニューの「編集」 -> 「設定」で開いたウィンドウの「トラック」を選び、Auto-scroll if head unpinnedのチェックをはずす)

  • ループが綺麗につながりそうな範囲を指定(大雑把で良い)
  • ループ終点辺りでShiftを押しながら上部のタイムライン部を押してループ再生、つながり具合を確かめる

f:id:pentamania:20180725132642p:plain

  • 拡大して接続が自然になるまで範囲を微調整 選択範囲の端(マウスアイコンが手に変わる)をドラッグする
  • いい感じになったら下の範囲開始秒と終点秒を記録しておく f:id:pentamania:20180725130358p:plain