[HTML5] 音声再生中に再生時間を表示する – howler.js編

WebAudioAPIを簡単に扱えるHowler.jsにはtimeupdateイベントがありません。もし再生時間に合わせて何らかの処理を行う場合には自分でがんばる必要があります。今回はsetInterval()を使ってかんたんな実装を行ってみます。

基本的な原理

原理は非常にシンプルで再生が開始されたらplayイベントが発行されるので、それに合わせsetInterval()で定期的に再生状況をチェックしてやるだけです。

const sound = new Howl({
    src: ['op.mp3']
});
let timerid;

// 再生開始直後に実行
sound.on("play", ()=>{
  // タイマースタート
  timerid = setInterval(()=>{
    const cur_time = sound.seek();  // 現在の再生時間を取得

    // ここにやりたいことを記述
  }
  , 200);
});

// 一時停止直後に実行
sound.on("pause", ()=>{
  clearInterval(timerid);  // タイマーを解除
});

// 再生終了直後に実行
sound.on("end", ()=>{
  clearInterval(timerid);  // タイマーを解除
});

// 再生開始
sound.play();

再生終了時にclearInterval()でタイマーを停止させるのはおわかりだと思いますが、忘れがちなのは一時停止時にもタイマーを止めてあげる部分でしょうか。一時停止状態から再びsound.play()で再生を再開した場合もplayイベントが発生します。

Howler.jsそのものの基本的な利用方法は過去の記事を参照ください。 blog.katsubemakito.net

再生時間を表示する

実行例

以下から実際のサンプルをお試しいただけます。前回同様にボタンを押す度に再生と一時停止を繰り返し、再生中はリアルタイムに再生時間が更新されます。 miku3.net

  • 音声ファイルの読み込みが完了するまでボタンは無効状態です。
  • ボタンを押す度に再生と一時停止を繰り返します。
  • シークバーをドラッグしても再生位置は変わりません。

今回も音声ファイルは「魔王魂」さん、アイコンは「FontAwesome」からお借りしました。

ソースコード

HTML

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Howler.js Sample2</title>
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
  <style>
    #btn-play{ width:100px; height:40px; padding:10px; font-size:18px; }
    #seekbar{ width:200px; }
  </style>
</head>
<body>

<h1>Howler.js Sample2</h1>

<!-- 再生ボタン (ロード完了まで利用不可) -->
<button id="btn-play" type="button" disabled><i class="fas fa-play"></i></button>

<!-- シークバーのような物 -->
<input id="seekbar" type="range" value="0" readonly>
<span id="curtime">--:--</span>/<span id="alltime">--:--</span>

<script src="howler.min.js"></script>
<script src="app.js"></script>

</body>
</html>

JavaScript

const btn = document.querySelector("#btn-play");     // <button>
const seekbar = document.querySelector("#seekbar");  // <input type="range">
const curtime = document.querySelector("#curtime");  // <span>
const alltime = document.querySelector("#alltime");  // <span>
let duration = 0;   // 曲の長さ
let timerid;        // setInterval()の戻り値

//--------------------------
// インスタンスを生成
//--------------------------
const bgm1 = new Howl({
    src: [
      'op.ogg',
      'op.mp3'  // Ogg非対応の場合にMP3を採用する
    ]
});

/**
 * [event] ボタンクリック時に実行
 */
btn.addEventListener("click", ()=>{
  // true=>再生中, false=>停止
  if( bgm1.playing() ){
    btn.innerHTML = '<i class="fas fa-play"></i>';  // 「再生ボタン」に変更
    bgm1.pause();
  }
  else{
    btn.innerHTML = '<i class="fas fa-pause"></i>';  // 「停止ボタン」に変更
    bgm1.play();
  }
});


/**
 * [event] 曲のロード完了時に実行
 */
bgm1.on("load", ()=>{
  // 曲の長さを表示
  duration = bgm1.duration();
  alltime.innerHTML = getMMSS( duration );

  // ボタンを使用可能にする
  btn.removeAttribute("disabled");
});

/**
 * [event] 曲の再生開始時に実行
 */
bgm1.on("play", ()=>{
  const len = duration;  //曲の長さを取得

  // タイマースタート
  timerid = setInterval(()=>{
    let cur = bgm1.seek();
    // シークバーに現在の再生位置をセット
    seekbar.value = Math.ceil( (cur / len) * 100 );  // 百分率(0〜100)に直す

    // 現在の再生時間を更新
    curtime.innerHTML = getMMSS(cur);
  }
  , 200);  // 0.2秒ごとに実行
});

/**
 * [event] 曲の一時停止時の実行
 */
bgm1.on("pause", ()=>{
  // タイマーを解除
  clearInterval(timerid);
});

/**
 * [event] 曲の再生終了時に実行
 */
bgm1.on("end", ()=>{
  // タイマーを解除
  clearInterval(timerid);

  // 「再生ボタン」に変更
  btn.innerHTML = '<i class="fas fa-play"></i>';
});


/**
 * 秒数をmm:ss形式で返却する
 *
 * @param {number} sec - 秒数
 */
function getMMSS(sec){
  const mm = Math.floor( sec / 60 );  // 分
  const ss = Math.floor( sec % 60 );  // 秒

  return(
    ("00" + mm).slice(-2) + ":" + ("00" + ss).slice(-2)
  );
}

参考ページ