Upload
ryo-koizumi
View
682
Download
0
Embed Size (px)
DESCRIPTION
2014/5/19に社内LT発表会で発表する際に作成した資料です。 スライド中のソースコードはTypeScriptで書かれています。
Citation preview
WebAudio APIで ブラウザ上で動く DJアプリケーションは 作れるか? (WebAudio API アプリケーション作成入門)
2014.5.19 / Koizumi Ryo / @Mayu_mic
自己紹介
- 小泉 亮 (Koizumi Ryo) - @Mayu_mic - (株)ドワンゴ エンジニア - JavaScript(TypeScript) - London Elektricity, Squarepusher, mondo grosso, etc.
WebAudio APIとは?
W3C によって開発されている、 ウェブアプリケーション用の音声処理と 音声合成のための高レベルJavaScript API。
(http://ja.wikipedia.org/wiki/HTML5オーディオ)
HTML5 <Audio> 要素との違い
- 音の再生タイミングの制御ができる - 音の出力にエフェクトを載せることができる - 波形データの生成・編集・加工ができる - etc.
ブラウザ対応状況
(http://caniuse.com/#feat=audio-api)
ただし、細かいところで対応状況への違いあり (MediaStreamingに対してエフェクトが効かない@Safari など…)
多くの作曲および音楽制作アプリケーションが作れるようになります。 オーディオイベントのタイトなスケジューリングが必要なアプリケーションも実装でき、教育的なものとエンターテインメント的なものの両方が可能です。 ドラムマシン、デジタルDJアプリケーション、GarageBandにある機能の幾つかを持ったタイムラインベースのデジタルミュージック・プロダクションソフトウェアさえ書くことができます。
”W3C Working Draft 10 October 2013”
(http://g200kg.github.io/web-audio-api-ja/)
多くの作曲および音楽制作アプリケーションが作れるようになります。 オーディオイベントのタイトなスケジューリングが必要なアプリケーションも実装でき、教育的なものとエンターテインメント的なものの両方が可能です。 ドラムマシン、デジタルDJアプリケーション、GarageBandにある機能の幾つかを持ったタイムラインベースのデジタルミュージック・プロダクションソフトウェアさえ書くことができます。
”W3C Working Draft 10 October 2013”
(http://g200kg.github.io/web-audio-api-ja/)
本当?
音源 音源
エフェクト エフェクト
エフェクト
ミキサー
目標 : ブラウザ上だけで動く
DJアプリケーションを作る
WebAudio APIの基本
Source Node
Effect Node
Destination Node
基本は3種類のノードをつなぎ合わせる
音を出現させる
音を加工する
音の最終出力先を決める
一番簡単な形
SourceNode Destination
ソースから入力された音が、そのままDestinationに向かう
var source = context.createBufferSource(); source.connect(context.destination);
入力を加工する
SourceNode DestinationEffectNode
ソースから入力された音が、EffectNodeで加工されてDestinationに向かう
var source = context.createBufferSource(); var gainNode = context.createGain(); source.connect(gainNode); gainNode.connect(context.destination);
直列に連結したり、
複数のノードを受けることも可能
組み合わせることで、複雑な音の流れを制御する
DeckA
DeckB
Filter
Filter
Sample
Gain
GainGain
MasterGainDest
実際に DJアプリケーションを
設計する
音源 音源
SourceNode : 音を出現させる
- AudioBufferSourceNode
- MediaStreamingSourceNode
- OscillatorNode
MP3, Wavなどのサンプルを読み込んで(任意のタイミングで)再生する
Audioタグや、ネットワーク上からストリーミング経由で再生する
周期的な波形を生成する
SourceNode : 音を出現させる
- AudioBufferSourceNode
- MediaStreamingSourceNode
- OscillatorNode
MP3, Wavなどのサンプルを読み込んで(任意のタイミングで)再生する
Audioタグや、ネットワーク上からストリーミング経由で再生する
周期的な波形を生成する
AudioBufferNode : ロード
private _loadArrayBuffer(path: string, timeout: number = 20000): JQueryPromise<ArrayBuffer> { var d = $.Deferred(); setTimeout(d.reject, timeout); // タイムアウトで失敗させる ! var request = new XMLHttpRequest(); request.open("GET", path, true); request.responseType = 'arraybuffer'; ! request.addEventListener('load', (ev: Event) => { if (request.status == 200) { console.info(“ロード成功”); d.resolve(request.response); } d.reject(request.status.toString()); }); ! // エラーで失敗させる request.addEventListener('error', (ev: Event) => { console.error("ロード失敗"); d.reject(); }); ! request.send(); return d.promise(); }
- MP3などの音楽データをArrayBuffer形式で読み込む
AudioBufferNode : ロード
private _loadArrayBuffer(path: string, timeout: number = 20000): JQueryPromise<ArrayBuffer> { var d = $.Deferred(); setTimeout(d.reject, timeout); // タイムアウトで失敗させる ! var request = new XMLHttpRequest(); request.open("GET", path, true); request.responseType = 'arraybuffer'; ! request.addEventListener('load', (ev: Event) => { if (request.status == 200) { console.info(“ロード成功”); d.resolve(request.response); } d.reject(request.status.toString()); }); ! // エラーで失敗させる request.addEventListener('error', (ev: Event) => { console.error("ロード失敗"); d.reject(); }); ! request.send(); return d.promise(); }
- MP3などの音楽データをArrayBuffer形式で読み込む
※point※ jQuery.Deferred()をうまく使い 非同期処理させる
AudioBufferNode : デコード
private _decodeAudioBufferAsynchronously(data: ArrayBuffer, context: AudioContext) : JQueryPromise<AudioBuffer> { var d = $.Deferred(); context.decodeAudioData(data, (buffer: AudioBuffer) => { console.info('バッファのデコード完了'); d.resolve(buffer); }, () => { console.error("バッファのデコードに失敗"); d.reject(); }); return d.promise(); }
- 読み込んだArrayBufferをAudioBuffer形式にデコードする
load(buffer: ArrayBuffer): JQueryPromise<AudioBuffer> { return this._decodeAudioBufferAsynchronously(buffer) .done((buffer: AudioBuffer) => { this._buffer = buffer; this._isLoaded = true; }); }
AudioBufferNode : 再生
play(when: number = 0) { var source = this._context.createBufferSource(); // ソースは再生のたびに使い捨てる source.buffer = this._buffer; source.connect(context.destination); source.start(when); }
- ソースノードにbufferを入れて再生する
エフェクト エフェクト
エフェクト
EffectNode : 音を加工する
- GainNode - 音量を上げ下げする
- PannerNode - 音を左右に振る
- BiquadFilterNode - 音の周波数をフィルタリングする
- DynamicsCompressorNode - 音の音量の差異をつぶす
- ConvolverNode - 音にリバーブなどのエフェクトをかける
etc…
EffectNode : 音を加工する
- GainNode - 音量を上げ下げする
- PannerNode - 音を左右に振る
- BiquadFilterNode - 音の周波数をフィルタリングする
- DynamicsCompressorNode - 音の音量の差異をつぶす
- ConvolverNode - 音にリバーブなどのエフェクトをかける
etc…
GainNode : 音量を上げ下げする
set volume(value:number) { this._gainNode.gain.value = value; }
value = 0.5
Source GainNode Destination
vol vol
BiquadFilterNode : フィルターをつくる
set freq(value:number) { var minValue = 40; var maxValue = context.sampleRate / 2; var numberOfOctaves = Math.log(maxValue / minValue) / Math.LN2; var multiplier = Math.pow(2, numberOfOctaves * (value - 1.0)); this._filterNode.frequency.value = maxValue * multiplier; }
周波数
Gain Q
frequency
※Lowpassの場合
this._filterNode.type = this._filterType = <any>’lowpass'; // 他にhighpass, bandpassなど
ミキサー
ミキサーの設計 (2-channel: 例)
GainNode
Source Source
Effect EffectMixer
GainNode(Master)
Destination
GainNode
deckA deckB
Amp
CrossFader Pos
set crossFade(pos: number) { this._deckAGain.gain.value = Math.cos(pos * 0.5*Math.PI); this._deckBGain.gain.value = Math.cos((1.0 - pos) * 0.5*Math.PI); }
ミキサー : クロスフェーダー
deckA deckB
Destination Node : 音の最終出力先を決める
- AudioDestinationNode
- MediaStreamingAudioDestinationNode
デバイスのオーディオハードウェアから出力する
リモートストリーミング経由で出力する
今回話してないこと
- テンポチェンジ・BPM同期 - AudioBufferNodeでplayback rateをいじる(未確認) !
- 波形描画 - Analyzer Nodeを使う?(未確認)
!
- Monitor Out - オーディオ出力デバイスに依存する
参考
- 資料 - Web Audio API (日本語訳) - http://g200kg.github.io/web-audio-api-ja/
!- ソースコード - Getting Started with Web Audio API - HTML5 Rocks
- http://www.html5rocks.com/ja/tutorials/webaudio/intro/