WebAudioAPI
実践的
プログラミング

Practical Web Audio API Programming

藍 圭介 小樽商科大学
Ai Keisuke @aike1000

2013.11.30

CC BY Temari 0...
誰?
今日話すこと
• Web Audio APIは何がすごいのか?
• 簡単なプログラミング例
• はまりやすい落とし穴
• 楽器アプリ作ってみる
Web Audio APIは
何がすごいのか?
http://middle-earth.thehobbit.com/
http://labs.gooengine.com/mozlod/
http://www.modulargrid.net/e/racks/synth
WebAudioSynth
アナログモデリングシンセ

http://aikelab.net/websynth
PG01Web
メタルギターサンプリング音源

http://aikelab.net/pg01/
Beatonica
ソーシャルグラフで音楽自動生成

http://beatonica.com/
でも、ウェブで音を
鳴らすのって
前からあったよね?
<EMBED	 SRC="○○.mid">

Flash

Java Applet

<Audio SRC=”○○.mp3”>
これらは基本的に、サウンド
再生プログラムをブラックボ
ックス化してHTMLに埋め込
んでいたに過ぎない
CC BY-NC-SA Peter Dutton http://www.flickr.com/photos/joeshlabotnik/77...
Web
Audio
API
http://www.w3.org/TR/webaudio/
ウェブブラウザが
直接サポートする
音声信号処理API
CC BY-ND Hector Lazo http://www.flickr.com/photos/hector-lazo/3101873296/
Web Audio API

•音そのものをJavaScriptで直接操作
•プラグイン不要で環境依存しにくい
•由緒正しい(W3C)
注目度が高く進化が速い
•
CC BY-ND Hector Lazo http://www.flickr.co...
モジュール指向
モジュール同士を接続してプログラミング

• オシレーター
• オーディオバッファソース
• ゲイン
• フィルター
• ディレイ
• スクリプトプロセッサー

• パン
• コンプレッサー
• コンボルバー
• アナライザー
...
これによって
何が変わるの?
△

これまでできなかった
何かができるようになる

◎

サウンドプログラミングの
ハードルが飛躍的に下がる!
Web Audio API
• 特殊な開発環境不要
• 必要なのは普通のブラウザだけ
• 無料
• コンパイル不要
• 速いデバッグサイクルで試行錯誤しやすい
• 特殊な実行環境不要
• メールやウェブで共有した相手も同じ音が聞ける
サウンド
プログラミング
の自由化
Public Domain Eugène_Delacroix http://en.wikipedia.org/wiki/Liberty_Leading_the_People
誰でも今すぐ
サウンド
プログラミングが
始められる!
簡単な
プログラミング例
オシレーターで音を出してみる
var ctx = new webkitAudioContext();
var osc = ctx.createOscillator();
osc.type = 1; // 矩形波
osc.connect(ctx....
やかましい

CC BY-NC-SA Timothy Hart http://www.flickr.com/photos/tmhart/6229873723/
ゲインノードで音量ダウン
	

 var ctx = new webkitAudioContext();
	

 	

 var osc = ctx.createOscillator();
osc.type = 1;
	

 	

 var v...
wavファイルを再生する
BufferSource

CC BY Jason Ralston http://www.flickr.com/photos/jasonrphotography/2885155839/
wavファイルを再生(1/2)
XHRでサーバからwavファイルを取得する関数

var loadwav = function(file, callback) {
var xhr = new XMLHttpRequest();
xhr.open(...
wavファイルを再生(2/2)
このloadwav関数でwavをバッファに読み込み

var kacha;
loadwav('kacha.wav', function(buf) { kacha = buf; });

BufferSourceを生...
キーを押すたびにwavを再生
window.document.onkeyup = function(evt){
var src = ctx.createBufferSource();
if (evt.keyCode == 13)
src.buf...
WebRTCで
マイク入力した
音声を加工する
CC BY-NC-ND National Film and Sound Archive Australia http://www.flickr.com/photos/nfsa/4580070895/
マイク入力の基本
GetUserMedia()の第2引数に関数を渡す
渡した関数でMediaStreamSourceノード生成
var audioproc = function(stream) {
var mic = ctx.createMed...
ディレイは
声が遅れて聞こえるよ
でもなんか違くない?

MediaStreamSource
(Mic)

Delay

destination

http://aikelab.net/webaudiodemo/delay1/
ディレイとゲインで
フィードバックループを作る
var mic = ctx.createMediaStreamSource(stream);
mic.connect(delay);
mic.connect(ctx.destination);
d...
コンボルバーで簡単リバーブ
XHRでサーバからIRファイルを取得して設定

var convolver = ctx.createConvolver();
var xhr = new XMLHttpRequest();
xhr.open("GET...
IRファイルとは、実在の
残響音の特徴をwavファイル
として記録したもの
ある日のIR収録風景
波形を直接加工する
ScriptProcessor

CC BY-NC thaths http://www.flickr.com/photos/thaths/5852964925/
みんな大好きディストーション
波形をブーストしてからクリップ
var fuzz = ctx.createScriptProcessor(1024, 1, 1);
fuzz.onaudioprocess = function(event) {
v...
FFTでピッチチェンジャー
var pshift = function(val, indata) {
this.fft.forward(indata);
for (var i = 0; i < stream_length; i++) {
a_r...
はまりやすい
落とし穴
CC BY-NC-SA @notnixon http://www.flickr.com/photos/sansbury/3274031514/
Oscillatorや
BufferSourceは
1音鳴らすごとに使い捨て
wav,mp3ファイル
を鳴らすにはWeb
サーバが必要
ベンダープレフィックス問題
var ctx = new webkitAudioContext();
var ctx = new AudioContext();

window.AudioContext
= window.AudioContext...
旧API名問題
createJavaScriptNode

createScriptProcessor

createGainNode

createGain

createDelayNode

createDelay
Panが3次元
手軽な2次元Panを作るのは
けっこう面倒だったり
pan.setPosition( 1.0, 0, -1.0);
pan.setPosition(-1.0, 0, -1.0);
こうするとそれっぽいけど普通の2次元Panとは微...
テストしづらい
• 上限値、下限値のチェックなど音にな
る前のおおまかな検証は可能

• 本当に意図した音色(波形)になって
いるかの自動チェックは難しい
バッドノウハウ集
• ScriptProcessorの入力数0はNG
• 特にiOSの場合ダミーで手前に

BufferSourceを接続する必要あり

• iOSでは音を鳴らす前にユーザーの
アクションが必須

• IRファイルは48kHzステ...
楽器アプリ
作ってみる
マルチトラック
シーケンサー
CC BY-NC-SA Matthew Davidson http://www.flickr.com/photos/stretta/531963218/
簡易シーケンサーの作成 (ドラム音源)
ゲイン生成、wavファイル読み込み
var Drum = function(ctx) {
this.ctx = ctx;
this.vol = ctx.createGain();
this.vol.gai...
簡易シーケンサーの作成 (ドラム音源)
16分音符4回ごとにBufferSource生成、キック、ハットを鳴らす

Drum.prototype.play = function(n, tim) {
if (n % 4 == 0) {
var sr...
簡易シーケンサーの作成 (ベース音源)
ゲインの生成、MIDIノートナンバーでシーケンス設定

var Bass = function(ctx) {
	

 this.ctx = ctx;
	

 this.vol = ctx.createGa...
簡易シーケンサーの作成 (ベース音源)
毎回オシレーターノード生成
MIDIノートナンバーから周波数算出
Bass.prototype.play = function(n, tim) {
	

 var osc = ctx.createOsci...
簡易シーケンサーの作成 (シンセ音源その1)
シーケンスが違うだけでベース音源とほぼ同じ
	

 this.seq = [69, 72, 76, 74, 71, 69, 72, 76];
}
Synth.prototype.play = fun...
簡易シーケンサーの作成 (シンセ音源その2)
ディレイを追加
	

	

	

	


this.delay = ctx.createDelay();
this.delay.delayTime.value = 0.2;
this.feedbac...
簡易シーケンサーの作成 (シンセ音源その3)
周波数をずらしたオシレーターを3個重ねる
Synth.prototype.play = function(n, tim) {
	

 for (i = 0; i < 3; i++) {	

// 3...
簡易シーケンサーの作成 (シンセ音源その4)
フィルターを追加してLFOでゆっくり動かす
	

	

	

	

	

	

	

	

	

	

	


this.lpf = ctx.createBiquadFilter();
this.l...
できた。
簡易シーケンサーの作成 (完成版)
全部鳴らす
var drum = new Drum(ctx);
var bass = new Bass(ctx);
var synth = new Synth(ctx);
var play = functio...
GUIをつけてパターンを
選べるようにすれば
本格的な楽器になります
Webrhy
GUIをつけてパターンを選べるようにしたらこんな感じ

http://aikelab.net/webrhy/
あとは自由な発想で
音楽をハックするだけ
Public Domain Eugène_Delacroix http://en.wikipedia.org/wiki/Liberty_Leading_the_People
Thank You!
@aike1000
http://d.hatena.ne.jp/aike/
Upcoming SlideShare
Loading in …5
×

Practical Web Audio API Programming

5,228 views

Published on

HTML5 Conference 2013
http://aikelab.net/webaudiodemo
https://github.com/aike/webaudiodemo

Published in: Technology, Business

Practical Web Audio API Programming

  1. 1. WebAudioAPI 実践的 プログラミング Practical Web Audio API Programming 藍 圭介 小樽商科大学 Ai Keisuke @aike1000 2013.11.30 CC BY Temari 09 http://www.flickr.com/photos/34053291@N05/4658010136/
  2. 2. 誰?
  3. 3. 今日話すこと • Web Audio APIは何がすごいのか? • 簡単なプログラミング例 • はまりやすい落とし穴 • 楽器アプリ作ってみる
  4. 4. Web Audio APIは 何がすごいのか?
  5. 5. http://middle-earth.thehobbit.com/
  6. 6. http://labs.gooengine.com/mozlod/
  7. 7. http://www.modulargrid.net/e/racks/synth
  8. 8. WebAudioSynth アナログモデリングシンセ http://aikelab.net/websynth
  9. 9. PG01Web メタルギターサンプリング音源 http://aikelab.net/pg01/
  10. 10. Beatonica ソーシャルグラフで音楽自動生成 http://beatonica.com/
  11. 11. でも、ウェブで音を 鳴らすのって 前からあったよね?
  12. 12. <EMBED SRC="○○.mid"> Flash Java Applet <Audio SRC=”○○.mp3”>
  13. 13. これらは基本的に、サウンド 再生プログラムをブラックボ ックス化してHTMLに埋め込 んでいたに過ぎない CC BY-NC-SA Peter Dutton http://www.flickr.com/photos/joeshlabotnik/7778898726/
  14. 14. Web Audio API http://www.w3.org/TR/webaudio/
  15. 15. ウェブブラウザが 直接サポートする 音声信号処理API CC BY-ND Hector Lazo http://www.flickr.com/photos/hector-lazo/3101873296/
  16. 16. Web Audio API •音そのものをJavaScriptで直接操作 •プラグイン不要で環境依存しにくい •由緒正しい(W3C) 注目度が高く進化が速い • CC BY-ND Hector Lazo http://www.flickr.com/photos/hector-lazo/4109744948/
  17. 17. モジュール指向 モジュール同士を接続してプログラミング • オシレーター • オーディオバッファソース • ゲイン • フィルター • ディレイ • スクリプトプロセッサー • パン • コンプレッサー • コンボルバー • アナライザー • ウェーブシェイパー 等のモジュールが用意されている CC BY-NC cutwithflourish http://www.flickr.com/photos/26735065@N00/4229039436/
  18. 18. これによって 何が変わるの?
  19. 19. △ これまでできなかった 何かができるようになる ◎ サウンドプログラミングの ハードルが飛躍的に下がる!
  20. 20. Web Audio API • 特殊な開発環境不要 • 必要なのは普通のブラウザだけ • 無料 • コンパイル不要 • 速いデバッグサイクルで試行錯誤しやすい • 特殊な実行環境不要 • メールやウェブで共有した相手も同じ音が聞ける
  21. 21. サウンド プログラミング の自由化 Public Domain Eugène_Delacroix http://en.wikipedia.org/wiki/Liberty_Leading_the_People
  22. 22. 誰でも今すぐ サウンド プログラミングが 始められる!
  23. 23. 簡単な プログラミング例
  24. 24. オシレーターで音を出してみる var ctx = new webkitAudioContext(); var osc = ctx.createOscillator(); osc.type = 1; // 矩形波 osc.connect(ctx.destination); osc.start(0); http://aikelab.net/webaudiodemo/osc1/
  25. 25. やかましい CC BY-NC-SA Timothy Hart http://www.flickr.com/photos/tmhart/6229873723/
  26. 26. ゲインノードで音量ダウン var ctx = new webkitAudioContext(); var osc = ctx.createOscillator(); osc.type = 1; var vol = ctx.createGain(); vol.gain.value = 0.2; // 0.0 ∼ 1.0 osc.connect(vol); vol.connect(ctx.destination) osc.start(0); http://aikelab.net/webaudiodemo/osc2/
  27. 27. wavファイルを再生する BufferSource CC BY Jason Ralston http://www.flickr.com/photos/jasonrphotography/2885155839/
  28. 28. wavファイルを再生(1/2) XHRでサーバからwavファイルを取得する関数 var loadwav = function(file, callback) { var xhr = new XMLHttpRequest(); xhr.open("GET", file, true); xhr.responseType = "arraybuffer"; xhr.onload = function() { ctx.decodeAudioData(xhr.response,function(buf){ callback(buf); }, function(){}); }; xhr.send(); }
  29. 29. wavファイルを再生(2/2) このloadwav関数でwavをバッファに読み込み var kacha; loadwav('kacha.wav', function(buf) { kacha = buf; }); BufferSourceを生成してバッファを指定(毎回必要) var src = ctx.createBufferSource(); src.buffer = kacha; src.connect(ctx.destination); src.start(0);
  30. 30. キーを押すたびにwavを再生 window.document.onkeyup = function(evt){ var src = ctx.createBufferSource(); if (evt.keyCode == 13) src.buffer = tan; // returnキーは「ターン」 else src.buffer = kacha; // 他のキーは「カチャ」 src.connect(ctx.destination); src.start(0); return false; } http://aikelab.net/webaudiodemo/kachatan/
  31. 31. WebRTCで マイク入力した 音声を加工する CC BY-NC-ND National Film and Sound Archive Australia http://www.flickr.com/photos/nfsa/4580070895/
  32. 32. マイク入力の基本 GetUserMedia()の第2引数に関数を渡す 渡した関数でMediaStreamSourceノード生成 var audioproc = function(stream) { var mic = ctx.createMediaStreamSource(stream); mic.connect(delay); delay.connect(ctx.destination); }; navigator.webkitGetUserMedia({audio : true}, audioproc, function(e) { console.log(e); } );
  33. 33. ディレイは 声が遅れて聞こえるよ でもなんか違くない? MediaStreamSource (Mic) Delay destination http://aikelab.net/webaudiodemo/delay1/
  34. 34. ディレイとゲインで フィードバックループを作る var mic = ctx.createMediaStreamSource(stream); mic.connect(delay); mic.connect(ctx.destination); delay.connect(gain); gain.connect(delay); gain.connect(ctx.destination); Feedback MediaStreamSource (Mic) Delay Gain Wet destination Dry http://aikelab.net/webaudiodemo/delay2/
  35. 35. コンボルバーで簡単リバーブ XHRでサーバからIRファイルを取得して設定 var convolver = ctx.createConvolver(); var xhr = new XMLHttpRequest(); xhr.open("GET", "ir.wav", true); xhr.responseType = "arraybuffer"; xhr.onload = function() { ctx.decodeAudioData(xhr.response,function(buf){ convolver.buffer = buf; }, function(){}); }; xhr.send(); http://aikelab.net/webaudiodemo/reverb/
  36. 36. IRファイルとは、実在の 残響音の特徴をwavファイル として記録したもの
  37. 37. ある日のIR収録風景
  38. 38. 波形を直接加工する ScriptProcessor CC BY-NC thaths http://www.flickr.com/photos/thaths/5852964925/
  39. 39. みんな大好きディストーション 波形をブーストしてからクリップ var fuzz = ctx.createScriptProcessor(1024, 1, 1); fuzz.onaudioprocess = function(event) { var sin = event.inputBuffer.getChannelData(0); var sout = event.outputBuffer.getChannelData(0); var limit = 0.2; for (var i = 0; i < sin.length; i++) { var sig = sin[i] * 6; // Boost if (sig > limit) sig = limit; // Clip if (sig < -limit) sig = -limit; // Clip sout[i] = sig; } }; http://aikelab.net/webaudodemo/fuzz/
  40. 40. FFTでピッチチェンジャー var pshift = function(val, indata) { this.fft.forward(indata); for (var i = 0; i < stream_length; i++) { a_real[i] = 0; a_imag[i] = 0; } for (var i = 0; i < stream_length; i++) { var index = parseInt(i * val); var eq = 1.0; if (i > stream_length / 2) { eq = 0; } if ((index >= 0) && (index < stream_length)) { a_real[index] += fft.real[i] * eq; a_imag[index] += fft.imag[i] * eq; } } return this.fft.inverse(this.a_real, this.a_imag); } pitchShifter.onaudioprocess = function(event) { var sin = event.inputBuffer.getChannelData(0); var sout = event.outputBuffer.getChannelData(0); var data = pshift(2.0, sin); for (var i = 0; i < sin.length; i++) { sout[i] = data[i]; } }; DSP.js by cobanbrook https://github.com/corbanbrook/dsp.js/ http://aikelab.net/webaudiodemo/pitch/
  41. 41. はまりやすい 落とし穴 CC BY-NC-SA @notnixon http://www.flickr.com/photos/sansbury/3274031514/
  42. 42. Oscillatorや BufferSourceは 1音鳴らすごとに使い捨て
  43. 43. wav,mp3ファイル を鳴らすにはWeb サーバが必要
  44. 44. ベンダープレフィックス問題 var ctx = new webkitAudioContext(); var ctx = new AudioContext(); window.AudioContext = window.AudioContext || window.webkitAudioContext; var ctx = new AudioContext();
  45. 45. 旧API名問題 createJavaScriptNode createScriptProcessor createGainNode createGain createDelayNode createDelay
  46. 46. Panが3次元 手軽な2次元Panを作るのは けっこう面倒だったり pan.setPosition( 1.0, 0, -1.0); pan.setPosition(-1.0, 0, -1.0); こうするとそれっぽいけど普通の2次元Panとは微妙に違う 参考 http://www.g200kg.com/jp/docs/webaudio/panner.html
  47. 47. テストしづらい • 上限値、下限値のチェックなど音にな る前のおおまかな検証は可能 • 本当に意図した音色(波形)になって いるかの自動チェックは難しい
  48. 48. バッドノウハウ集 • ScriptProcessorの入力数0はNG • 特にiOSの場合ダミーで手前に BufferSourceを接続する必要あり • iOSでは音を鳴らす前にユーザーの アクションが必須 • IRファイルは48kHzステレオのみ ※これらはAPIのバージョンアップによって今後変わる可能性もあります
  49. 49. 楽器アプリ 作ってみる
  50. 50. マルチトラック シーケンサー CC BY-NC-SA Matthew Davidson http://www.flickr.com/photos/stretta/531963218/
  51. 51. 簡易シーケンサーの作成 (ドラム音源) ゲイン生成、wavファイル読み込み var Drum = function(ctx) { this.ctx = ctx; this.vol = ctx.createGain(); this.vol.gain.value = 0.4; this.vol.connect(ctx.destination); } this.kick = null; this.hat = null; var self = this; loadwav('wav/kick.wav', function(buf) { self.kick = buf; }); loadwav('wav/hat.wav', function(buf) { self.hat = buf; });
  52. 52. 簡易シーケンサーの作成 (ドラム音源) 16分音符4回ごとにBufferSource生成、キック、ハットを鳴らす Drum.prototype.play = function(n, tim) { if (n % 4 == 0) { var src = ctx.createBufferSource(); src.buffer = this.kick; src.connect(this.vol); src.start(tim); } if (n % 4 == 2) { var src = ctx.createBufferSource(); src.buffer = this.hat; src.connect(this.vol); src.start(tim); } http://aikelab.net/webaudiodemo/seq1/ }
  53. 53. 簡易シーケンサーの作成 (ベース音源) ゲインの生成、MIDIノートナンバーでシーケンス設定 var Bass = function(ctx) { this.ctx = ctx; this.vol = ctx.createGain(); // Gain Node this.vol.gain.value = 0.3; this.vol.connect(ctx.destination); this.seq = [ 33, 33, 40, 33, 33, 40, 33, 33, 33, 33, 40, 33, 33, 40, 33, 33, 31, 31, 31, 38, 38, 31, 31, 38, 29, 29, 36, 29, 36, 29, 29, 29 ]; }
  54. 54. 簡易シーケンサーの作成 (ベース音源) 毎回オシレーターノード生成 MIDIノートナンバーから周波数算出 Bass.prototype.play = function(n, tim) { var osc = ctx.createOscillator(); osc.type = 2; // ノコギリ波 osc.frequency.value = 440.0 * Math.pow(2.0, (this.seq[n % 32] - 69.0) / 12.0); osc.connect(this.vol); osc.start(tim); osc.stop(tim + 0.08); } http://aikelab.net/webaudiodemo/seq2/
  55. 55. 簡易シーケンサーの作成 (シンセ音源その1) シーケンスが違うだけでベース音源とほぼ同じ this.seq = [69, 72, 76, 74, 71, 69, 72, 76]; } Synth.prototype.play = function(n, tim) { var osc = ctx.createOscillator(); osc.type = 2; // ノコギリ波 osc.frequency.value = 440.0 * Math.pow(2.0, (this.seq[n % 32] - 69.0) / 12.0); osc.connect(this.vol); osc.start(tim); osc.stop(tim + 0.10); } http://aikelab.net/webaudiodemo/seq3/
  56. 56. 簡易シーケンサーの作成 (シンセ音源その2) ディレイを追加 this.delay = ctx.createDelay(); this.delay.delayTime.value = 0.2; this.feedback = ctx.createGain(); this.feedback.gain.value = 0.3; this.vol.connect(ctx.destination); this.vol.connect(this.delay); this.delay.connect(this.feedback); this.feedback.connect(this.delay); this.feedback.connect(ctx.destination); http://aikelab.net/webaudiodemo/seq4/
  57. 57. 簡易シーケンサーの作成 (シンセ音源その3) 周波数をずらしたオシレーターを3個重ねる Synth.prototype.play = function(n, tim) { for (i = 0; i < 3; i++) { // 3 Oscillators var osc = ctx.createOscillator(); osc.type = 2; var detune = 3 * i; // 3Hz周波数をずらす osc.frequency.value = 440.0 * Math.pow(2.0, (this.seq[n % 8] - 69.0) / 12.0) + detune; osc.connect(this.vol); osc.start(tim); osc.stop(tim + 0.10); } http://aikelab.net/webaudiodemo/seq5/ }
  58. 58. 簡易シーケンサーの作成 (シンセ音源その4) フィルターを追加してLFOでゆっくり動かす this.lpf = ctx.createBiquadFilter(); this.lpf.type = 0; // LPF this.lpf.Q.value = 20; this.lpf.frequency.value = 4000; this.angle = 0.0; var self = this; this.lfo = function() { self.angle += 0.1; if (self.angle > 2 * Math.PI) self.angle -= 2 * Math.PI; self.lpf.frequency.value = 4000 + Math.sin(self.angle) * 3000; } http://aikelab.net/webaudiodemo/seq6/ setInterval(this.lfo, 100);
  59. 59. できた。
  60. 60. 簡易シーケンサーの作成 (完成版) 全部鳴らす var drum = new Drum(ctx); var bass = new Bass(ctx); var synth = new Synth(ctx); var play = function() { var t = ctx.currentTime; for (var i = 0; i < 128; i++) { t += 0.1; drum.play(i, t); bass.play(i, t); synth.play(i, t); } } http://aikelab.net/webaudiodemo/seq7/
  61. 61. GUIをつけてパターンを 選べるようにすれば 本格的な楽器になります
  62. 62. Webrhy GUIをつけてパターンを選べるようにしたらこんな感じ http://aikelab.net/webrhy/
  63. 63. あとは自由な発想で 音楽をハックするだけ Public Domain Eugène_Delacroix http://en.wikipedia.org/wiki/Liberty_Leading_the_People
  64. 64. Thank You! @aike1000 http://d.hatena.ne.jp/aike/

×