Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
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,671 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/

×