logo

Now Loading...

WEBサイト作りノウハウ WEBノウハウメモ

  • HOME
  • 記事一覧
  • Web Audio APIを使用して連続で音を鳴らす方法

Web Audio APIを使用して連続で音を鳴らす方法

cover image

カテゴリー:javascriptメモ

作成日付:2019年4月17日

更新日付:2019年7月5日

目的

Web Audio APIを使用してjavascriptでclickイベントを押す度に音を鳴らします。連打できます。

イメージはサンプラーのような、カオスパッドのような、複数のボタンに対して別々の音を鳴らすようにします。

<audio>だとpromiseの関係上うまくいかなかったため(方法はいろいろあるのだろうけども)

Web Audio APIだと非同期で簡単にできたのでメモ。

Web Audio APIのメリットはたくさんありますが、今回の目的で採用したメリットは以下

  • clickを連続でしても音がでる(非同期)(連打できる)
  • パンニングやエフェクトなど、拡張性が多い

結果

getElementsByClassNameでclass指定した複数のDOM要素に対して、addEventListenerのclickイベントを設定していきます。

音はWeb Audio APIを使用します。

DOM要素毎に処理を書くのが面倒だし読みづらいため、getElementsByClassNameでclass指定した複数のDOM要素に対して、HTML5Rocksで公開されいているBufferLoaderクラスで作った音配列を割り当てていきます。

サンプルAudio API使用のボタン

こうゆうことですね、下の□を押すと音がなります。連続で実行できます。

※注意 音が鳴ります。

サンプルコード全体

・sample-audioapi.js

<script> var soundArray = []; function BufferLoader(context, urlList, callback) { this.context = context; this.urlList = urlList; this.onload = callback; this.bufferList = new Array(); this.loadCount = 0; } BufferLoader.prototype.loadBuffer = function(url, index) { // Load buffer asynchronously var request = new XMLHttpRequest(); request.open("GET", url, true); request.responseType = "arraybuffer"; var loader = this; request.onload = function() { // Asynchronously decode the audio file data in request.response loader.context.decodeAudioData( request.response, function(buffer) { if (!buffer) { alert('error decoding file data: ' + url); return; } loader.bufferList[index] = buffer; if (++loader.loadCount == loader.urlList.length) loader.onload(loader.bufferList); }, function(error) { console.error('decodeAudioData error', error); } ); } request.onerror = function() { alert('BufferLoader: XHR error'); } request.send(); } BufferLoader.prototype.load = function() { for (var i = 0; i < this.urlList.length; ++i) this.loadBuffer(this.urlList[i], i); } window.AudioContext = window.AudioContext||window.webkitAudioContext; context = new AudioContext(); bufferLoader = new BufferLoader( context, [ 'https://XXXXX/XXXXXX/XXXXXXXX/HORN.wav', 'https://XXXXX/XXXXXX/XXXXXXXX/NOISE.wav', ], finishedLoading ); bufferLoader.load(); function finishedLoading(bufferList) { el = document.getElementsByClassName("sound"); for( var i = 0; i < bufferList.length ; i++ ) { var source = context.createBufferSource(); source.buffer = bufferList[i]; source.connect(context.destination); soundArray.push(source); } for( let i = 0; i < el.length ; i++ ) { el[i].addEventListener('click', function() { soundArray[i].start(0); soundArray[i] = context.createBufferSource(); soundArray[i].buffer = bufferList[i]; soundArray[i].connect(context.destination); }); } } </script>

方法

前提:HTML5Rocksで公開されいているBufferLoaderクラスを使用しています。

各DOM要素(ここではdiv)にはclassとして"sound"を割り当てています。

フロー

1変数定義とAudioContextの作成
2BufferLoaderの作成と引数として複数の音声データURLを渡す
3BufferLoaderのコールバック関数finishedLoadingで、DOM要素を取得しclickイベントを設定

1.変数定義とAudioContextの作成

各音データを格納する配列としてsoudArrayを定義しています。

次にAudioContextを定義します。Webkit系との場合わけでこの書き方だそうです。

書き方はMDNを参考にしています。

このようなコメントで記載されています。

Webkit/blink browsers need prefix, Safari won't work without window.

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

2.BufferLoaderの作成と引数として複数の音声データURLを渡す

次に取得してきたBufferLoaderクラスを生成し、引数として目的の音声データを複数渡しています。

ここでBufferLoaderが作成するbufferListの添え字と、後続でClassで取得するDOM要素配列の添え字が対応することを期待値としています。

音声データを指定した後、コールバック関数として"finishedLoading"を呼んでいます。


bufferLoader = new BufferLoader(
  context,
  [ 'https://xxxxxxxxxx/HORN.wav',
    'https://xxxxxxxxxx/NOISE.wav',
  ],
  finishedLoading
);
bufferLoader.load();

3.BufferLoaderのコールバック関数finishedLoadingで、DOM要素を取得しclickイベントを設定

BufferLoaderのコールバックされる関数です。ここではBufferList毎に音の設定をし、配列soudArrayにPushします。

そしてgetElementsByClassNameで取得したDOM要素配列に、それぞれsoundArrayを登録しています。

※addEventListenerを回しているfor文のカウントはletで定義しています。varだとスコープが広くclickイベントが発生する時点でカウントが2になってしまうため。(letだとブロック毎のiが定義される)

※clickイベント内でsoundArrayは1度実行した後に再度createBufferSource()を行っています。どうやらcreateBufferSourceは使い捨てらしい。


function finishedLoading(bufferList) {
  el = document.getElementsByClassName("sound");
  for( var i = 0; i < bufferList.length ; i++ ) {
    var source = context.createBufferSource();
    source.buffer = bufferList[i];
    source.connect(context.destination);
    soundArray.push(source);
  }
  for( let i = 0; i < el.length ; i++ ) {   <--- letで定義、varではclick時soundArrayのiは2となってしまう。
    el[i].addEventListener('click', function() {
      soundArray[i].start(0);   <--- 音の実行部分
      soundArray[i] = context.createBufferSource();   <---使い捨てらしいので一度使ったら再度定義をし直している。
      soundArray[i].buffer = bufferList[i];
      soundArray[i].connect(context.destination);
    });
  }
}

参考

その他詳細

各ブラウザの対応状況

Can i use の対応状況

Web Audio API