phi

I'm a Game Programmer and Frontend Engineer passionate about programming education. Math / C / C++ / C# / JavaScript / HTML5 / CSS3 / Python

phiaryjust a creator

Web Audio API で音を再生しよう

9 years ago

オレの Advent Calendar 2015 - Adventar の 13 日目です.

むか〜し書いた『Web上でピアノ作ろう』的なエントリーが軒並みダメになっていたので もろもろ書きなおそうと思います.

Chrome だとたまにしか音がならない, Safari だともはや動かない, Firefox は当時音再生系の API が違ったので考慮していないという悲惨な状況でした. IE は論外ですね.

そこで, さすがにこのままこのエントリーを野放しにするわけにもいかないので書きなおしていこうと思います.

この頃は, Web Audio API がまだまだ実用レベルではなかったので Audio 要素を使って音を鳴らしていたのですが, 今ではだいぶ Web Auido API も安定してきたのでこちらを使った再生方法に書き換えていきます.

ってことで今回はその布石として Web Audio API の Getting Started, 最小サンプルを作っていきたいと思います.

Web 上での音を再生する方法は3通り?

Web 上で音を再生する方法は現状3通りあります.
一つずつ紹介していきたいと思います.

みんな大好き(だった) Flash で音を再生

flash player

Flash プラグインをブラウザにインストールして, Flash 内で処理を書くことでサウンドを再生します.
昔は, Web 上で音を再生する唯一の手段でした. いきなり音が鳴り出すサイトは大抵これ使ってます.

Audio 要素を使って音を再生

html5 audio

最も手軽な方法ですね.

JavaScript で下記のように書くことでカンタンに音を鳴らせます.

new Audio('sound.mp3').play();  

これは要素でもあるので HTML 上に下記のように埋め込むこともできます.

<audio src='sound.mp3' autoplay />  

ただ, これは色々と制約が多く動作も不安定です.

特に iPhone, Android で使用する際, ユーザのインタラクション(タッチイベントやスクロールイベントなど)内でしか 再生できない という問題があります.

new Audio('sound.mp3').play(); // 再生されない  
elm.onclick = function() {  
  new Audio('sound.mp3').play(); // 再生される
}

また, 同じサウンドデータを同時に再生できないといった問題もあり, ゲームなどで使うには難しい状況でした.

Web Audio API で音を再生

web audio api

今回の本題となる Web Audio API ですね.

Web Audio API は, HTML5 から導入された, 音ファイルを Web 上で扱うための API です.

ちょっと使い方は複雑ですが, Audio 要素にあったような制約がなく 深いところまでサウンドを扱うことができます.

詳しくは下で解説していきます.

Web Audio Demo

今回作った Web Audio API の最小サンプルです. Play ボタンをクリックすると音が再生されるのがわかるかと思います.

また, 下記のリンクから単体起動, もしくはダウンロードできます.

Web Audio API - Getting Started or Download

Web Audio API を使ったコード

上のデモの全体コードになります.

HTML

<!doctype html>

<html>  
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, user-scalable=no" />
    <meta name="apple-mobile-web-app-capable" content="yes" />

    <title>Web Audio API Getting Started</title>
    <meta name="description" content="html5,webaudioapi,javascript" />
    <style>
    #btn {
      font-size: 4rem;
      padding: 1rem 2rem;
    }
    </style>
    <script src='main.js'></script>
  </head>
  <body>
    <button id='btn'>Play</button>
  </body>
</html>  

JavaScript

main.js の実装です.

/*
 * main.js
 */

window.AudioContext = window.AudioContext || window.webkitAudioContext;  
var context = new AudioContext();

// Audio 用の buffer を読み込む
var getAudioBuffer = function(url, fn) {  
  var req = new XMLHttpRequest();
  // array buffer を指定
  req.responseType = 'arraybuffer';

  req.onreadystatechange = function() {
    if (req.readyState === 4) {
      if (req.status === 0 || req.status === 200) {
        // array buffer を audio buffer に変換
        context.decodeAudioData(req.response, function(buffer) {
          // コールバックを実行
          fn(buffer);
        });
      }
    }
  };

  req.open('GET', url, true);
  req.send('');
};

// サウンドを再生
var playSound = function(buffer) {  
  // source を作成
  var source = context.createBufferSource();
  // buffer をセット
  source.buffer = buffer;
  // context に connect
  source.connect(context.destination);
  // 再生
  source.start(0);
};

// main
window.onload = function() {  
  // サウンドを読み込む
  getAudioBuffer('se.mp3', function(buffer) {
    // 読み込み完了後にボタンにクリックイベントを登録
    var btn = document.getElementById('btn');
    btn.onclick = function() {
      // サウンドを再生
      playSound(buffer);
    };
  });
};

Web Audio API を使ってみよう

主に JavaScript 部分の解説を行います.

Step1. Web Audio をクロスブラウザ対応させよう

一部のブラウザではまだ Web Audio 用に使う AudioContext にベンダープレフィックスが付いています. 今後は全て外れるとは思いますが念のため対応させておきましょう.

存在チェックして webkit をくっつけてるだけです.

window.AudioContext = window.AudioContext || window.webkitAudioContext;  
var context = new AudioContext();  

Step2. AudioBuffer を読み込もう

getAudioBuffer() の部分ですね. これは, 一見複雑に見えますが xhr(ajax) で音声ファイルを読み込み, Audio 用にデコードしているだけです.

テキストではなく arraybuffer としてロードしたいので

responseType = 'arraybuffer';  

を指定します.

ロード完了後に step1 で生成した context の decodeAudioData を使って ArrayBuffer を AudioBuffer に変換しています.

// array buffer を audio buffer に変換
context.decodeAudioData(req.response, function(buffer) {  
  // コールバックを実行
  fn(buffer);
});

Step3. サウンドを再生する処理を作ろう

playSound() 部分の解説になります.

ここでは Step2 で作成された buffer が渡される前提の関数として汎用的に作ります.

// サウンドを再生
var playSound = function(buffer) {  
  ...
};

まず, node を作ります.

// source を作成
var source = context.createBufferSource();  

次に引数として渡された buffer をセットします.

// buffer をセット
source.buffer = buffer;  

これを context にひも付けます

// context に connect
source.connect(context.destination);  

あとは再生するだけです.

// 再生
source.start(0);  

以前は noteOn() という start() と同等のメソッドもあったのですが 廃止されたので使わないよう注意してください.

私のライブラリはこれで軒並み一度死にました.

やることは多いですが, 一旦関数化しておけば buffer を渡すだけなので 気軽に色々なところで音を再生できますね.

Step4. 実際に音を再生してみよう

あとは, Step1, 2, 3 で作ってきたものを使うだけです. 今回は html 側でボタンを設置して, click すると音を再生するようにしています.

  // サウンドを読み込む
  getAudioBuffer('se.mp3', function(buffer) {
    // 読み込み完了後にボタンにクリックイベントを登録
    var btn = document.getElementById('btn');
    btn.onclick = function() {
      // サウンドを再生
      playSound(buffer);
    };
  });

これでボタンをクリックすると se.mp3 が再生されるようになります.

近々 WebAudio API でピアノを作るエントリー書きます! たまに覗いてもらえると幸いです.

Reference

  • 音楽ファイルは 魔王魂 さんのものを使用させて頂いています.