游星通信

作ったものの紹介など

Howler.jsで簡単に自然にループするBGMを実装する

※メモ書きに近いです

やるきっかけ

HTML5ゲーで細かくてシームレスなループをさせるのってどうするの?と思い、何とか自己流でやってみた。

howler.js(v2.0系)使用。

要件

  • 再生開始時は普通に曲頭から
  • ループ終点までいったら今度は曲頭ではなく、ループ開始地点にシークして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にはオーデイオスプライト機能があります。 スプライトシートのいわばサウンド版で、複数の音源を一つの音源ファイルにまとめ上げ、範囲を指定することでそれぞれの音を鳴らすことができます。

通常は効果音を一つのファイルにまとめて通信量を減らす目的で使うそうだけど、この機能はシームレスなループにも使えます。
範囲はそれぞれ最初は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