DartでWeb Audio APIを使って音を鳴らしてみます。
以下のページを参考にします。
Getting Started with Web Audio API - HTML5 Rocks
Getting Started with the Web Audio API | Dart: Structured web apps
上のページではJavaScriptでの説明になっていますが、Web Audio APIはWeb標準のAPIですので、言語が変わってもAPI仕様は変わりません。(変わりません、というかWeb標準API自体が、API仕様を統一させましょう、というものなので、仕様を変えてしまっては非難を浴びるだけです)
下のページは、上の解説を元にしたDartでのサンプルです。
下ごしらえ
pubspec.yaml
には特別な記述は行いません。
name: dartwebaudio
dependencies:
browser: any
pub get
して依存パッケージを取得しておきます。
$ pub get
以下の様なディレクトリ構成でファイルを作成していきます。
├── pubspec.yaml
└── web
├── dartwebaudio.dart
├── index.html
└── sample.ogg
HTML側には、音再生用のボタンを用意しておきます。
<title>Web Audio on Dart</title>
<input type="button" value="loading..."></input>
<script type="application/dart" src="dartwebaudio.dart"></script>
<script src="packages/browser/dart.js"></script>
ボタンに表示する文言を"loading..."
にして、音素材の読み込みが完了したら"play"
に切り替えて再生できる状態になったことがわかるようにする工夫をしてみます。
下ごしらえが済みましたので、Web Audio APIを使っていきます。
Dartのコードを書く
まずはdart:html
とdart:web_audio
をimport
しておきます。
import 'dart:html';
import 'dart:web_audio';
Web Audioで用いる各種変数を用意しておきます。
AudioContext context;
AudioBuffer sampleBuffer;
AudioContext
を作っておきます。
main() {
context = new AudioContext();
}
次に音素材を読み込みます。
音素材の対応フォーマットはWebブラウザごとに異なりますので注意が必要です。
主要Webブラウザごとの対応フォーマットについては以下のページが詳しいです。
Media formats supported by the HTML audio and video elements - HTML | MDN
今回は素直に(?)OGGで行きましょう。sample.oggを適当に作成しましょう。Audacityを使うと簡単に作成できます。
では、このsample.oggを読み込ませる処理を書きます。
// main関数を省略
HttpRequest request = new HttpRequest()
..requestType = 'arraybuffer'
..open('GET', 'sample.ogg');
request.onLoad.listen((e) => _onLoad(request));
request.onError.listen((e) {
throw 'File load error';
});
request.send();
HttpRequest
でrequestType
を'arraybuffer'
とし、'GET'
で'sample.ogg'
を読み込みます。
データ読み込まれた時に_onLoad
関数が呼ばれるように、HttpRequest.onLoad
に_onLoad
をlisten
させておきます。
最後に、request.send()
で読み込みを開始させます。
_onLoad
関数を用意します。
_onLoad(HttpRequest request) {
context
.decodeAudioData(request.response)
.then((AudioBuffer buffer) {
sampleBuffer = buffer;
InputElement playButton = document.query('#playbutton');
playButton
..value = 'play'
..onClick.listen((e) => _play());
});
}
AudioContext.decodeAudioData()
にHttpRequest
で得たバッファデータを渡すことで、音源データをデコードさせます。
decodeAudioData()
はFuture
のオブジェクトを返しますので、then()
でデコード完了後の処理を渡します。
decodeAudioData()
が返すFuture
はFuture<AudioBuffer>
となっており、then()
でAudioBuffer
のオブジェクトを受け取ることができます。このバッファをsampleBuffer
に入れておきます。
このthen()
が呼ばれた時点で音素材の読み込みとデコードが完了しているので、音再生用のボタンの文字を"loading..."
から"play"
に差し替えます。
そして、このボタンが押された時に呼ばれる処理を設定します。再生時に読んでいる_play()
の処理は以下のようになります。
_play() {
AudioBufferSourceNode source = context.createBufferSource()
..buffer = sampleBuffer
..connectNode(context.destination)
..start(0);
}
AudioContext.createBufferSource()
でAudioBufferSourceNode
のオブジェクトを作成し、これのbuffer
プロパティに先ほど用意したsampleBuffer
を設定します。
AudioSourceNode.connectNode()
でAudioContext.destination
へノードを接続します。Web Audioにおけるノード接続の仕組みは音の様々な操作加工のために使用でき、ノードの接続を操作することによってそれを行うことができます。今回は、単にバッファの音源をそのまま再生するだけで良いので、AudioBufferSourceNode
を最終的な音出力のノードとなるAudioContext.destination
へ接続しています。
その後、AudioBufferSourceNode.start()
を呼び、音の再生を行います。引数の0
は、再生されるタイミングを指示するもので、今回は即時再生されてほしいので0
を指定します。
以上で、音の再生が可能になります。
dartwebaudio.dart
の全体像は以下のようになります。
import 'dart:html';
import 'dart:web_audio';
AudioContext context;
AudioBuffer sampleBuffer;
main() {
context = new AudioContext();
HttpRequest request = new HttpRequest()
..responseType = 'arraybuffer'
..open('GET', 'sample.ogg');
request.onLoad.listen((e) => _onLoad(request));
request.onError.listen((e) {
throw 'File load error';
});
request.send();
}
_onLoad(HttpRequest request) {
context
.decodeAudioData(request.response)
.then((AudioBuffer buffer) {
sampleBuffer = buffer;
InputElement playButton = document.query('#playbutton');
playButton
..value = 'play'
..onClick.listen((e) => _play());
});
}
_play() {
AudioBufferSourceNode source = context.createBufferSource()
..buffer = sampleBuffer
..connectNode(context.destination)
..start(0);
}
DEMO
DEMOとして以下のページを用意しました。今回のサンプルを動かすことができます。
動作環境
dart2js
にてJavaScriptへコンパイルされていますので、Web Audio APIに対応したブラウザであれば、動かすことが可能だという想定でしたが、FirefoxとSafariではエラーを確認しており、今のところChromeでしか動作を確認できていません。
型指定
Dartの型指定はオプショナルで、var
としてJavaScriptのように変数が型を持たない記述方法も可能ですが、今回はなるべく変数の型を指定する書き方にしました。これにより、Dartの型チェックの恩恵を受けることができます。