初級
Adobe AIR 2.0 beta のリリースによって、Microphone クラスのインスタンスからSampleDataEvent.SAMPLE_DATAイベントがディスパッチされるようになりました。これは、マイク入力の波形データを扱えるようになったという意味で、具体的にはマイクから入力された音の波形を表示したり、録音したりできるようになりました。
これまでの Microphone API では、下に示すような、マイク入力レベルしか得ることができませんでした。(※マイク入力レベルとは、「音量」と考えて頂いて問題ないと思います。)
重要: このサンプルアプリケーションは、AIR 2 betaの機能を体験するために作成されています。お試しになる場合は、AIR 2 beta ランタイムを先にインストールする必要があります。なお、既にAIR 1.xのランタイムがOSにインストールされている場合は、上書きされてしまいますのでご注意ください。元に戻す場合は、AIR 2 beta ランタイムをアンインストールし、AIR 1.5.2 ランタイムをインストールします。詳細はリリースノート(英語) をご覧ください。
// マイクをアクティブにした後に Event.ENTER_FRAME で入力レベルを読み取る
private var _mic:Microphone = Microphone.getMicrophone();
〜中略〜
private functoin _enterFrameHandler(event:Event):void
{
trace(_mic.activityLevel); // 0~100の数値
}
ですから、今までマイク入力をオーディオデータとして取り扱いたい場合は、Flash Media Server などのサーバー製品を利用しサーバーサイドで処理する方法以外に手段はありませんでした。
さて、本記事では、AIR 2.0 の Microphone API によって得られる情報についてや、その扱い方について書いてみたいと思います。
マイク入力を扱うにあたっての予備知識のお話です。とても簡単です。
まず、「音」。
「音」の正体は空気の振動です。人間は耳で音を感知します。空気の振動によって耳の中にある鼓膜という薄い膜が振動します。その鼓膜の振動の具合によって人間は音を聞き取ることができます。
次に、「マイク」。
マイクも、先に例示した人間の鼓膜と同じ仕組みで、空気の振動によってマイクに内蔵している膜が震えます。マイクはその震えた量を検知しアナログの連続的な電気信号に変えます。
最後に、「デジタル変換」。
マイクが接続されているPC内部では、マイクからくる連続的な電気信号をデジタル(数値データ)に変換します。
という訳で、AIRアプリケーションでは「空気の振動」は「数値データ」として取り扱われることになります。
波形データとは、先ほど説明した「数値データ」のことです。
波形データは、SampleDataEvent.SAMPLE_DATA イベントで取得することができます。私の憶測ですが、一回のイベントで最大 8192 個の波形データが取得できるようです。 SampleDataEvent.SAMPLE_DATA イベントの発生頻度は、つねに定期的ではありません。無音状態になればイベントは送出されません。
_mic = Microphone.getMicrophone();
_mic.rate = 22;
_mic.gain = gain;
_mic.addEventListener(SampleDataEvent.SAMPLE_DATA, _dataHandler);
private function _dataHandler(event:SampleDataEvent):void
{
// 1回のイベントで(最大)8192個の数値データが ByteArray として得られる
var data:ByteArray = event.data;
while(data.bytesAvailable)
{
var sample:Number = data.readFloat();
trace(sample); // 値は -1 〜 1
}
}
マイク音声の1秒間ぶんに相当するデータ量は、サンプリング周波数によって変わります。サンプリング周波数(1秒間にアナログ信号をデジタルの数値に変換する回数のことです)は、Microphone.rate で指定することができます。
rate に設定する値の分かりやすい例としては、CDの音質で44.1KHz(1秒間に44,100回)です。MDの音質で22KHz(1秒間に22,050回)です。
Microphone API では上記5種類のサンプリングデータを取得することができますが、逆にそれ以外の周波数(たとえば 16KHz )でサンプリングすることはできません。
先に例示した SampeDataEvent.SAMPLE_DATA イベントハンドラーの ByteArray データを用いて波形を表示させることができます。SAMPLE_DATA イベントハンドラーの中で描画すると、音が無い状態の描画が行えませんので、Event.ENTER_FRAME と組み合わせることにしてみます。たとえば、以下のような感じになると思います。
private var _drawData:Vector. = new Vector.<Number>();
// ENTER_FRAME でこの値を読み取ることにする
public function get drawData():Vector.{
if (_drawData.length < DATA_PER_ENTER_FRAME)
return _drawData;
else
return _drawData.splice(0, DATA_PER_ENTER_FRAME);
}
// マイクのモニタリング開始
private function _start():void{
_mic = Microphone.getMicrophone();
_mic.rate = 44;
_mic.addEventListener(SampleDataEvent.SAMPLE_DATA, _dataHandler);
}
private function _dataHandler(event:SampleDataEvent):void{
// 表示できなかった分は捨てる
_drawData = new Vector.<Number>();
while (event.data.bytesAvailable){
var a:Number = event.data.readFloat();
_drawData.push(a);
}
}
rate = 44 の場合、 44,100/秒のデータがありますが、もちろん思いっきり端折って描画しても見た目にはそんなに違和感はありません。
これは、上記サンプルアプリで「えあー2.0」の声の波形です。
1フレームにつき120個のデータを描画させています。
サンプルアプリケーションは、ここからダウンロードできます。
ここからぐっと難しいお話になります。キーワードがわからない方は、すみませんが検索して調べて下さい。
波形データを FFT(高速フーリエ変換)解析すると、あるタイミングにおける周波数スペクトルを取得することができます。周波数スペクトルを取得することによって、イコライザー表示や音階(ドレミ)取得ができます。
BGMつきのFlash サイトの隅っこに sound on/off ボタンとともに小さく表示されたイコライザーをよく見かけると思います。
たぶん、SoundMixer.computeSpectrum() 関数を利用して FFT し、表示させています。(たぶん。)
残念ながら Microphone API で取扱う波形は、SoundMixer.computeSpectrum() は利用できません。
ですから、リアルタイムに波形データを FFT するには、自前で FFTアルゴリズムを用意し、 SampeDataEvent.SAMPLE_DATA イベントを受け取った瞬間( or 直後)に FFT しなければなりません。
FFTのプログラミングは、検索すればたくさんヒットしますので、バンド数の少ないイコライザーを表示するだけであれば、リアルタイムにFFTさせても大丈夫かもしれません。
以上で Microphone API のお話は終わりです。
AIR 2.0 からは、NativeProcess が実装され、ネイティブアプリケーションとの連携ができるようになりました。
音声ランチャーである VoiceLauncher は、マイクで拾った声を、テキスト文字列に変換し、候補のアプリケーションを起動させます。
音声認識エンジンには、Julius を用いました。
音声インターフェースは、他にももっといろんな楽しみ方があると思います。ぜひ AIR 2.0 で楽しいアプリケーションを作ってみて下さい。
Tutorials and samples |
AIR blogs |
More |
AIR Cookbooks |
More |
| 01/20/2012 | Skinnable Transform Tool |
|---|---|
| 01/18/2012 | Recording webcam video & audio in a flv file on local drive |
| 12/12/2011 | Date calculations using 'out-of-the-box' functions |
| 11/29/2011 | Button compatibility with NativeComboBox |