View
3.755
Download
13
Category
Preview:
DESCRIPTION
HTML5 Conference 2013 http://aikelab.net/webaudiodemo https://github.com/aike/webaudiodemo
Citation preview
2013.11.30
WebAudioAPI実践的プログラミング
藍 圭介
Practical Web Audio API Programming
Ai KeisukeCC BY Temari 09 http://www.flickr.com/photos/34053291@N05/4658010136/
@aike1000小樽商科大学
誰?
今日話すこと
•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
でも、ウェブで音を鳴らすのって
前からあったよね?
<EMBED SRC="○○.mid">
FlashJava Applet
<Audio SRC=”○○.mp3”>
これらは基本的に、サウンド再生プログラムをブラックボックス化してHTMLに埋め込んでいたに過ぎない
CC BY-NC-SA Peter Dutton http://www.flickr.com/photos/joeshlabotnik/7778898726/
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.com/photos/hector-lazo/4109744948/
モジュール指向
• オシレーター• オーディオバッファソース• ゲイン• フィルター• ディレイ• スクリプトプロセッサー
CC BY-NC cutwithflourish http://www.flickr.com/photos/26735065@N00/4229039436/
• パン• コンプレッサー• コンボルバー• アナライザー• ウェーブシェイパー
モジュール同士を接続してプログラミング
等のモジュールが用意されている
これによって何が変わるの?
これまでできなかった何かができるようになる
サウンドプログラミングのハードルが飛躍的に下がる!
△
◎
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.destination); osc.start(0);
http://aikelab.net/webaudiodemo/osc1/
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 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/
wavファイルを再生するBufferSource
CC BY Jason Ralston http://www.flickr.com/photos/jasonrphotography/2885155839/
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();}
wavファイルを再生(1/2)XHRでサーバからwavファイルを取得する関数
var kacha; loadwav('kacha.wav', function(buf) { kacha = buf; });
wavファイルを再生(2/2)このloadwav関数でwavをバッファに読み込み
var src = ctx.createBufferSource(); src.buffer = kacha; src.connect(ctx.destination); src.start(0);
BufferSourceを生成してバッファを指定(毎回必要)
キーを押すたびに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/
WebRTCでマイク入力した音声を加工する
CC BY-NC-ND National Film and Sound Archive Australia http://www.flickr.com/photos/nfsa/4580070895/
マイク入力の基本
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); });
GetUserMedia()の第2引数に関数を渡す渡した関数でMediaStreamSourceノード生成
ディレイは声が遅れて聞こえるよ
でもなんか違くない?
MediaStreamSource(Mic)
Delay destination
http://aikelab.net/webaudiodemo/delay1/
ディレイとゲインでフィードバックループを作る
MediaStreamSource(Mic)
Delay destinationGain
Dry
Wet
Feedback
var mic = ctx.createMediaStreamSource(stream); mic.connect(delay); mic.connect(ctx.destination); delay.connect(gain); gain.connect(delay); gain.connect(ctx.destination);
http://aikelab.net/webaudiodemo/delay2/
コンボルバーで簡単リバーブ
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();
XHRでサーバからIRファイルを取得して設定
http://aikelab.net/webaudiodemo/reverb/
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) { 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/
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]; }};
https://github.com/corbanbrook/dsp.js/
DSP.js by cobanbrook
http://aikelab.net/webaudiodemo/pitch/
CC BY-NC-SA @notnixon http://www.flickr.com/photos/sansbury/3274031514/
はまりやすい落とし穴
OscillatorやBufferSourceは
1音鳴らすごとに使い捨て
wav,mp3ファイルを鳴らすにはWeb
サーバが必要
ベンダープレフィックス問題
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var ctx = new AudioContext();
var ctx = new webkitAudioContext();
var ctx = new AudioContext();
旧API名問題
createJavaScriptNode
createGainNode
createDelayNode
createScriptProcessor
createGain
createDelay
Panが3次元
pan.setPosition( 1.0, 0, -1.0);
pan.setPosition(-1.0, 0, -1.0);
手軽な2次元Panを作るのはけっこう面倒だったり
こうするとそれっぽいけど普通の2次元Panとは微妙に違う
参考 http://www.g200kg.com/jp/docs/webaudio/panner.html
テストしづらい
•上限値、下限値のチェックなど音になる前のおおまかな検証は可能
•本当に意図した音色(波形)になっているかの自動チェックは難しい
バッドノウハウ集•ScriptProcessorの入力数0はNG
•特にiOSの場合ダミーで手前にBufferSourceを接続する必要あり
• iOSでは音を鳴らす前にユーザーのアクションが必須
• IRファイルは48kHzステレオのみ
※これらはAPIのバージョンアップによって今後変わる可能性もあります
楽器アプリ作ってみる
マルチトラックシーケンサー
CC BY-NC-SA Matthew Davidson http://www.flickr.com/photos/stretta/531963218/
簡易シーケンサーの作成 (ドラム音源)
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; });}
ゲイン生成、wavファイル読み込み
簡易シーケンサーの作成 (ドラム音源)
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); }}
16分音符4回ごとにBufferSource生成、キック、ハットを鳴らす
http://aikelab.net/webaudiodemo/seq1/
簡易シーケンサーの作成 (ベース音源)
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 ];}
ゲインの生成、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); }
毎回オシレーターノード生成MIDIノートナンバーから周波数算出
http://aikelab.net/webaudiodemo/seq2/
簡易シーケンサーの作成 (シンセ音源その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/
簡易シーケンサーの作成 (シンセ音源その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/
簡易シーケンサーの作成 (シンセ音源その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); }}
周波数をずらしたオシレーターを3個重ねる
http://aikelab.net/webaudiodemo/seq5/
簡易シーケンサーの作成 (シンセ音源その4)
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; } setInterval(this.lfo, 100);
フィルターを追加してLFOでゆっくり動かす
http://aikelab.net/webaudiodemo/seq6/
できた。
簡易シーケンサーの作成 (完成版)
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/
GUIをつけてパターンを選べるようにすれば
本格的な楽器になります
WebrhyGUIをつけてパターンを選べるようにしたらこんな感じ
http://aikelab.net/webrhy/
あとは自由な発想で音楽をハックするだけ
Public Domain Eugène_Delacroix http://en.wikipedia.org/wiki/Liberty_Leading_the_People
@aike1000http://d.hatena.ne.jp/aike/
Thank You!
Recommended