映画などの映像作品やゲームでは、左で発生した音は左側のスピーカーから、右側の音は右側のスピーカーから強く出力した方が臨場感が増すため日常的に利用されています。HTML5ではWebAudioAPIを簡単に扱えるHowler.jsで手軽に実現することができます。
基本的な原理
stereo()
単純にstereo()
メソッドを呼び出すだけで実現できます。
const sound = new Howl({ src: ["foo.mp3"] }); sound.play(); sound.stereo(-1); // 左側から出力 sound.stereo(0); // 中央から出力(左右からバランスよく出力) sound.stereo(1); // 右側から出力
stereo()
メソッドに渡す引数は下図のように-1に近づくほど左側に、+1に近づくほど右側から出力されます。デフォルトは中央(=0)です。
注意点
内部的にはWebAudioAPIが利用されているのですが、HTMLをダブルクリックで開いたりブラウザにドラッグ&ドロップした場合、ステレオの調整が効きません。必ずサーバを通して実行する必要があります。1時間くらいハマりましたw
Howler.jsの基本的な利用方法は以下を参照ください。 blog.katsubemakito.net
サンプル
実行例
以下から実際のサンプルをお試しいただけます。 miku3.net
- 音声ファイルの読み込みが完了するまでボタンは無効状態です。
- 再生ボタンを押すとBGMが再生され、再生位置が左右に行ったり来たりを繰り返します。
- 音源とトラックの位置が連動しています。
- ヘッドホンかイヤホンを付けないと分かりづらいかもしれません。
今回、音源にはいつの間にかフリー素材になっていた「バニラ求人」のテーマソングを利用しています。 vanilla-official.jp
そういえば「天気の子」にも登場してましたねw
ソースコード
HTML
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Howler.js Sample4</title> <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous"> <style> #contents{ width:800px; text-align:center;} #board{ border:1px solid gray; } #btn-start{ width:300px; height:40px; margin-top:10px; font-size:16px;} footer{ text-align:right; font-size:12px; } </style> </head> <body> <section id="contents"> <h1>Howler.js Sample4</h1> <canvas id="board" width="600" height="200"></canvas> <form> <button id="btn-start" type="button" disabled><i class="fas fa-play"></i></button> </form> </section> <script src="howler.min.js"></script> <script src="app.js"></script> </body> </html>
JavaScript
const btn = document.querySelector("#btn-start"); // <button> let pan = -1; // 音が発生する位置(-1〜1の範囲で指定。-1に近いほど左で鳴る) let way = 1; // 進行方向のコントロール用(端に行く度に+1, -1が交互に入れ替わる) let timerid; //---------------------------------------------------- // インスタンスを生成 //---------------------------------------------------- const bgm1 = new Howl({ src: ["vanilla.mp3"], loop: true, // ロード完了時に実行 onload: ()=>{ btn.removeAttribute("disabled"); // ボタンを使用可能にする } }); /** * [event] 再生ボタンクリック時に実行 */ btn.addEventListener("click", ()=>{ // true=>再生中, false=>停止 if( bgm1.playing() ){ bgm1.pause(); } else{ bgm1.stereo(pan); bgm1.play(); } }); /** * [event] 再生開始直後に実行 */ bgm1.on("play", ()=>{ btn.innerHTML = '<i class="fas fa-pause"></i>'; // 「一時停止ボタン」に変更 timerid = setInterval(()=>{ // 0.1ずつ足すor引く pan = parseFloat( (pan + (0.1 * way)).toFixed(2) ); // JSは小数点の計算が苦手なのでまどろっこしい書き方になる // 両端を超えそうなら進行方向を反転する if( pan <= -1.0 || pan >= 1.0 ){ way *= -1; } // 発生源をセット bgm1.stereo(pan); }, 100); }); /** * [event] 一時停止直後に実行 */ bgm1.on("pause", ()=>{ btn.innerHTML = '<i class="fas fa-play"></i>'; // 「再生ボタン」に変更 clearInterval(timerid); }); /** * [event] 再生が停止した直後に実行 */ bgm1.on("end", ()=>{ btn.innerHTML = '<i class="fas fa-play"></i>'; // 「再生ボタン」に変更 clearInterval(timerid); }); //---------------------------------------------------- // アドトラックを描画 //---------------------------------------------------- const adtruck = new Image(); const board = { canvas: null, ctx: null }; // canvasのオブジェクトなどを準備 board.canvas = document.querySelector("#board"); board.ctx = board.canvas.getContext("2d"); // 画像をロードする adtruck.src = "./adtruck.png"; adtruck.onload = update; // 下にあるupoad()関数をセット // ディスプレイのリフレッシュレートに合わせてcanvasを更新する function update(){ draw(); window.requestAnimationFrame(update); // 描画が終わったら自分自身を呼びだす } // canvasに描画する function draw(){ // canvasの全領域をクリア board.ctx.clearRect(0, 0, board.canvas.width, board.canvas.height); // トラックを描画 const x = 550 - ((1 - pan) * 10 * 35); // panの値をx座標に変換 board.ctx.drawImage(adtruck, x, 30); // y座標は適当 }
素材
画像
音楽
参考ページ
新海誠監督作品 天気の子 公式ビジュアルガイド
posted with amazlet at 20.03.12
KADOKAWA (2019-08-30)
売り上げランキング: 62,393