[HTML5] 音声ファイルをJSで再生/停止する - audioタグ編

HTML5で音声を再生するには単純にaudioタグを使うだけで実現できますが、今回はJavaScriptから再生や停止をコントロールしたいと思います。

音声ファイルを再生する

実行例

以下から実際のサンプルをお試しいただけます。ボタンを押す度に再生と一時停止を繰り返します。 miku3.net

音声ファイルは「魔王魂」さんからお借りしました。

ソース

ボタンの上に表示されるアイコンはFontAwesomeを利用しています。

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

<h1>HTML5 Audio Sample1</h1>

<!-- 音声ファイル -->
<audio id="bgm1" preload loop>
  <source src="op.ogg" type="audio/ogg">
  <source src="op.mp3" type="audio/mp3">
</audio>

<!-- 再生ボタン -->
<button id="btn-play" type="button"><i class="fas fa-play"></i></button>

<script>
  const bgm1 = document.querySelector("#bgm1");       // <audio>
  const btn  = document.querySelector("#btn-play");   // <button>

  btn.addEventListener("click", ()=>{
    // pausedがtrue=>停止, false=>再生中
    if( ! bgm1.paused ){
      btn.innerHTML = '<i class="fas fa-play"></i>';  // 「再生ボタン」に切り替え
      bgm1.pause();
    }
    else{
      btn.innerHTML = '<i class="fas fa-pause"></i>';  // 「一時停止ボタン」に切り替え
      bgm1.play();
    }
  });

  /**
   * [event] 再生終了時に実行
   */
   bgm1.addEventListener("ended", ()=>{
    bgm1.currentTime = 0;  // 再生位置を先頭に移動(こいつはなくても大丈夫です)
    btn.innerHTML = '<i class="fas fa-play"></i>';  // 「再生ボタン」に変更
  });
</script>

</body>
</html>

解説

JSで再生と一時停止

audioタグをJavaScriptで操作するのは非常に簡単です。例のようにdocument.querySelector()などでHTMLの要素を取得、play()メソッドで再生、pause()で一時停止します。

const bgm1 = document.querySelector("#bgm1");       // <audio>

if( ! bgm1.paused ){
  bgm1.pause();
}
else{
  bgm1.play();
}

ただ残念ながら最近のブラウザーはセキュリティの関係でこれだでは動作してくれません。

自動再生はできない

最近のブラウザはセキュリティの関係で自動的に音声を再生することができません。

ページが読み込まれるとすぐに音声(または音声トラックを含む動画)の再生を自動的に開始することは、ユーザーにとって歓迎されない驚きです。 (中略)ユーザーがこれを制御できるようにするために、ブラウザーは多くの場合、さまざまな形式の自動再生のブロックを提供します。

メディアおよびウェブオーディオ API の自動再生ガイド(MDN)

そのため音声を再生するためには、以下のいずれか1つ以上の条件をクリアする必要があります。

  1. 音量が0またはミュートに設定されている場合
  2. ユーザーがクリック、タップ、キーを押すなどの操作を行った場合
  3. サイトがブラウザの「ホワイトリスト」に登録されている場合
  4. サイトがブラウザの「自動再生機能ポリシー」により許可されている場合

初めて訪問したユーザーに音を再生してもらうためには現実的には2番の方法を取ることが多いでしょう。そこで今回のサンプルではユーザーがボタンをクリックしたイベントに紐付けて音声を再生しています。

const bgm1 = document.querySelector("#bgm1");       // <audio>
const btn  = document.querySelector("#btn-play");   // <button>

btn.addEventListener("click", ()=>{
  // 再生する
  bgm1.play();  
});

なおページが表示されたあと一度でも音声を再生しておけば、あとは自由に好きなタイミングで鳴らすことができます(ページを移動したり再読み込みした場合はもう一回クリックしてもらう必要があります)

複数のファイル形式を提供する

今回のサンプルではOgg形式の音声ファイルを利用していますが、ブラウザによっては対応していないため再生することができません。

以下、簡単な対応表示になります。詳細な対応バージョンなどが知りたい場合は「形式」のリンクをクリックすると参考にさせていただいた「Can I use」へ飛びます。

形式 IE Edge FireFox Chrome Safari Safari(iOS) Browser Chrome
Ogg
MP3
WAV
  • はAndroidの意
  • Android BrowserはAndroid6あたりまで標準で搭載されていたブラウザーです。

そこでブラウザがファイル形式に対応していない場合を考え、sourceタグを利用すると複数のファイル形式を同時に提供することが可能です。これでOgg形式に対応していないブラウザはMP3形式が採用されるというわけです(逆のパターンもあり)。

<audio id="bgm1" preload loop>
  <source src="op.ogg" type="audio/ogg">
  <source src="op.mp3" type="audio/mp3">
</audio>

audioタグのみで再生する

audioタグにcontrols属性を付けてあげると簡単な再生機能が表示されます。

<audio src="op.ogg" controls loop></audio>

以下が実行例です。例えば自分の作った楽曲をブログなどで公開する場合はこちらが手軽ですね。ただし前述の通りサイトを訪れた瞬間に自動的に音声を再生することができない点に注意が必要です。

audioタグで用意されている属性は以下の通りです。

autoplay
再生可能な状態になった時点で自動的に再生が始まります。現在では実質的に利用することができません。
controls
ブラウザが再生や一時停止、シークなどの簡単な機能を提供します。
loop
最後まで再生すると一番最初に自動的に戻ります。ループ再生ですね。
muted
ミュート(無音)になります。
preload
再生されるかどうかに関わらずファイル全体をダウンロードします。noneを指定した場合はダウンロードせず、metadataを指定するとファイルの長さなどの情報のみサーバから取得します。

audioタグを利用しない

Audioクラスを利用するとHTMLのaudioタグを一切使うことなく、JavaScriptのみで完結します。

const bgm1 = new Audio("op.ogg");

btn.addEventListener("click", ()=>{
  bgm1.play();  
});

audioタグが提供する便利機能(対応ファイル形式を自動的に判別、preloadなど)は自分で実装することになるのでご注意を。

再生時のコントロール方法

ボリューム

ボリュームの調整は文字通りvolumeプロパティを介して行います。volumeを参照すると現在設定されているボリュームが返ります。volumeには0〜1の間の値を設定します。0が無音、1が最大音量です。

const bgm1 = document.querySelector("#bgm1");       // <audio>
const btn  = document.querySelector("#btn-play");   // <button>

btn.addEventListener("click", ()=>{
  const volume = bgm1.volume;  // 現在のボリュームを取得
  bgm1.volume = volume + 0.2;  // 現在のボリュームを上げる
  bgm1.play();
});

注意点としてiPhoneやiPadなどiOSのWebブラウザではボリュームの変更ができません。参照は可能ですが常に1が返ってきます。

またmutedプロパティにtrueを渡しても同様にミュートすることができます。意図的に無音にする場合はこちらの方が分かりやすそうですね。

bgm1.muted = true;   //ミュート
bgm1.muted = false;  //ミュート解除

フェードイン/フェードアウト

長くなったので別の記事にしました。以下をご覧ください。 blog.katsubemakito.net

原理としてはsetInterval()で少しずつ音量を変化させてあげる感じです。こちらもiOSのWebブラウザでは動作しませんのでご注意を。

曲の長さを知りたい

durationプロパティを参照します。

const bgm1 = document.querySelector("#bgm1");       // <audio>
const btn  = document.querySelector("#btn-play");   // <button>

btn.addEventListener("click", ()=>{
  console.log( bgm1.duration );  // コンソールに表示
});

ミリ秒単位で返ってきますね。

51.644082

現在の再生位置を知りたい

currentTimeプロパティを参照します。

const bgm1 = document.querySelector("#bgm1");       // <audio>
const btn  = document.querySelector("#btn-play");   // <button>

btn.addEventListener("click", ()=>{
  if( ! bgm1.paused ){
      bgm1.pause();
      console.log( bgm1.currentTime );  // 停止した再生位置をコンソールに表示
  }
  else{
    bgm1.play();
  }
});

こちらもミリ秒単位で返却されますね。

1.600941
5.156189

再生位置を変更したい

いわゆるシークするには、先ほどのcurrentTimeに移動先の秒数を設定します。

const bgm1 = document.querySelector("#bgm1");       // <audio>
const btn  = document.querySelector("#btn-play");   // <button>

btn.addEventListener("click", ()=>{
  bgm1.currentTime = 10.0;  // 10秒の位置から再生開始
  bgm1.play();
}

絶対的な位置ではなく、相対的に3秒だけ先送りしたい場合は単純にプラスしてあげるだけです。

bgm1.currentTime += 3.0;

イベント処理

たくさんのイベントが用意されていますが、主要なものを抜粋します。

イベント 説明
loadedmetadata 再生時間などのメタ情報の読み込みが完了した
loadeddata 現在の再生位置で再生を開始できる準備が完了した
play 再生を開始した
pause 再生を停止した
ended 再生が完了した
timeupdate 再生位置が変更された
volumechange ボリュームが変更された
error エラーが発生した

addEventListenerでイベント毎に処理を定義します。

const bgm1 = document.querySelector("#bgm1");  // <audio>

bgm1.addEventListener("loadeddata", ()=>{
  console.log("event: 再生できるよ!");
});
bgm1.addEventListener("play", ()=>{
  console.log("event: 再生を開始したよ!");
});

その他のイベントはMDNなどをご覧ください。 developer.mozilla.org

課題

onloadイベントが取れない

現時点で音声ファイルの読み込み(ダウンロード)が完了したイベントが取れません。ストリーミング再生が前提となっており再生可能になったら(途中までダウンロードしたら)再生するという考え方が基本なようです。これだと例えばボタンを押した瞬間に鳴らしたいといった場合に困りますよね。

これを検知するためには別の方法を取る必要があります。別の記事にしましたので詳しくはこちらを参照ください。 blog.katsubemakito.net

iOSでボリュームの調整ができない

iOSのブラウザを対象環境とする場合に、音量の調整が必要な場合はWeb Audio APIを利用する必要があります。

参考ページ