複数の画像を1枚にまとめるCSSスプライトはかなり前から利用されていますが、今回はその音声ファイル版である「AudioSprite」を作成してみます。
音声ファイルの作成にはNode.jsのモジュール「audiosprite」を、再生には「Howler.js」を利用します。
AudioSpriteファイルを作成する
メリット
CSSスプライトと同じですが、1つはリクエスト数を減らすことによる処理の高速化、もう1つはファイル容量の削減が期待できます。
ファイルの内容によって圧縮が効く物とそうでない物があるので一概に言えませんが、今回利用した3つのファイルを例にすると、以下の通りファイル形式によっては大きくダイエット出来ているのがわかります。
- 結合前 (合計77,110byte)
- puch1.mp3 - 11,284 byte
- puch2.mp3 - 12,538 byte
- puch3.mp3 - 53,288 byte
- 結合後
- Ogg形式 - 36,699 byte
- M4A形式 - 53,199 byte
- AC3形式 - 112,012 byte
- MP3形式 - 112,893 byte
AC3やMP3では逆に大きくなってしまいましたね。
インストール
ffmpeg
audiosprite
をインストールする前にffmpegを入れる必要があります。
macOSの場合はbrewで簡単に入ります。公式ドキュメントだとOgg形式に対応するため--with-theora --with-libvorbis
のオプションが必要だと書いてあるのですが、最近のバージョンであれば不要なようです。逆に付けるとエラーになるので外します(付けなくてもOgg形式も出力可能です)。
$ brew install ffmpeg
今回は4.2.2が入りました。
$ ffmpeg -version ffmpeg version 4.2.2 Copyright (c) 2000-2019 the FFmpeg developers built with Apple clang version 11.0.0 (clang-1100.0.33.17) (以下略)
Windowsの方は以下からダウンロードしたあと、環境変数PATHにインストール先のディレクトリを設定してください。
https://ffmpeg.zeranoe.com/builds/ffmpeg.zeranoe.com
audiosprite
こちらもnpmで一発です。npmが入っていない場合はNode.jsを入れると自動的に入ります。
$ npm install -g audiosprite
Node.jsのモジュールなのですが利用するのは同梱されているコマンドです。名称はそのまま「audiosprite」です。
$ audiosprite --help info: Usage: audiosprite [options] file1.mp3 file2.mp3 *.wav
音声ファイルを結合
効果音ラボさんから次のファイルをお借りしました。
これを1つのファイルに結合してみます。先ほどインストールした「audiosprite」コマンドに結合後のファイル名、Howler.jsをこのあと利用するという意思表示、結合したい全ファイル名をオプションとして渡します。
$ audiosprite -o punch -f howler punch1.mp3 punch2.mp3 punch3.mp3 (中略) info: All done
すると以下のように5つのファイルが新たに作成されます。……増えとるやないか(゚д゚)!
大丈夫ですw
3つのファイルを結合した上でさらに4種類のファイル形式でそれぞれ出力してくれています。JSONファイルは各ファイルの開始と終了のタイミングが記録してありこのあと登場する「Howler.js」で活躍します。
また冒頭でも述べましたが、Oggでは圧縮が効いて半分以下にまでダイエットすることができました。
- 結合前 (合計77,110byte)
- puch1.mp3 - 11,284 byte
- puch2.mp3 - 12,538 byte
- puch3.mp3 - 53,288 byte
- 結合後
- Ogg形式 - 36,699 byte
- M4A形式 - 53,199 byte
- AC3形式 - 112,012 byte
- MP3形式 - 112,893 byte
AudioSpriteを再生する
Howler.js
Howler.jsの基本的な利用方法については過去の記事を参照してください。 blog.katsubemakito.net
サンプル
以下から実際のサンプルをお試しいただけます。 miku3.net
- 3つのボタンを押すとそれぞれ違った音が再生されます。
- 音声ファイルの読み込みが完了するまでボタンは無効状態です。
ソースコード
実際の処理は外部ファイルapp.js
で行っています。
HTML
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>AudioSprite - Howler.js Sample3</title> <style> .punch{ width:100px; height:40px; padding:10px; font-size:18px; } </style> </head> <body> <h1>AudioSprite - Howler.js Sample3</h1> <!-- 再生ボタン (ロード完了まで利用不可) --> <button id="btn-play1" type="button" class="punch" disabled>小</button> <button id="btn-play2" type="button" class="punch" disabled>中</button> <button id="btn-play3" type="button" class="punch" disabled>大</button> <script src="howler.min.js"></script> <script src="app.js"></script> </body> </html>
JavaScript
インスタンスを生成する際に、audiospriteコマンドから出力されたJSONファイルを参考にしながら記述します。
// インスタンスを生成 const SE1 = new Howl({ src: [ "punch.ogg", "punch.m4a", "punch.mp3", "punch.ac3" ], sprite: { "punch1": [ 0, 407.98185941043084], // 小 "punch2": [2000, 783.6734693877552], // 中 "punch3": [4000, 1257.142857142857] // 大 }, onload: ()=>{ // ボタンを有効にする document.querySelectorAll(".punch").forEach((element)=>{ element.removeAttribute("disabled"); }); } }); /** * [event] ボタンクリック時に実行 */ // 小 document.querySelector("#btn-play1").addEventListener("click", ()=>{ SE1.play("punch1"); }); // 中 document.querySelector("#btn-play2").addEventListener("click", ()=>{ SE1.play("punch2"); }); // 大 document.querySelector("#btn-play3").addEventListener("click", ()=>{ SE1.play("punch3"); });