オーディオ・キャプチャと、PCM形式での保存
ロジック・コードの記述
以上を踏まえて、ロジック・コード(MainPage.xaml.vb)を記述していきます。
名前空間の読み込み
入出力を可能にする、System.IO名前空間をインポートしておきます。
クラスの作成
Silverlightでオーディオ情報を取得するには、AudioSinkからカスタムオーディオシンクを派生させ、AudioSink.CaptureSource からオーディオ情報を受け取ります。そこで、「Inherits System.Windows.Media.AudioSink」と入力します。すると、下記のOnCaptureStarted、OnCaptureStopped、OnFormatChange、OnSamples メソッドのコードの大半が、自動的に追加されます。基本的な処理だけであれば、必要な作業は、パラメータや多少のコードを変更したり追加する程度です。
OnCaptureStartedメソッドは、オーディオ・データのキャプチャを開始した時に呼び出されます。この時点で、新規MemoryStreamを生成しておきます。MemoryStreamの引数に、内部配列のサイズを指定して初期化する場合は、32bit型整数を指定します。
OnCaptureStoppedは停止した時に、OnFormatChangeは、オーディオ形式が変更された時に、呼び出されます。ただし、オーディオのキャプチャを開始すると、OnFormatChangeが、最初のキャプチャに対して1回以上呼び出されることになっています。そこで、OnFormatChangeで取得するオーディオ形式を利用します。
OnSamplesは、オーディオ・サンプルのキャプチャを完了したときに呼び出されます。Stream.Writeメソッドに引数を指定してストリームに書き込みます。
音声データを生成するには、ストリームとフォーマット形式を取得しておく必要があるので、上記に続けて、次の処理を記述します。
変数の宣言
キャプチャの開始と停止に利用する、新規CaptureSourceを宣言します。CaptureSource は、オーディオキャプチャを、オーディオキャプチャ・デバイスから利用するために使うクラスです。また、先に作成したクラスを録音と保存の際に利用するため宣言しておきます。
ページがロードされた時の処理
CaptureDeviceConfiguration を用いて、システムで利用可能なオーディオデバイスの情報を取得し、デバイスからキャプチャにアクセスする際の、アクセス許可を与えます。GetAvailableAudioCaptureDevices で、システム上で利用可能なオーディオデバイスのコレクションを取得します。
[録音]ボタンがクリックされた時の処理
「録音」「停止」「保存」の3つのボタンをクリックした時の処理を、順次記述していきます。あらかじめ、各々のボタンについて、MainPage.xaml.vbの「MainPage」と「Declarations」を選択して、イベントのコードを追加しておきます(図6)。
図6:MainPage.xaml.vbの画面で、3つのボタンについてイベントを追加しておく(クリックで拡大) |
まず、「録音」ボタンがクリックされた時の処理を記述します。
このサンプルは、Trustedモードのアウト・オブ・ブラウザで実行させますので、インストールを促すメッセージを表示します。
先に作成したCaptureSourceに、オーディオをキャプチャします。CaptureSource.Startメソッドで、キャプチャを開始します。
'■「録音ボタン」がクリックされた時の処理
なお、今回はTrusteモードで実行させていますが、Trustedモードを指定しない場合は、キャプチャ関連のAPIを呼び出すためにユーザのアクセス許可を得る必要があるため、前回と同様、CaptureDeviceConfiguration.RequestDeviceAccess メソッドを用いて、キャプチャデバイスに対するアクセスを要求するように記述しなければなりません(リスト中のコメントアウト部分)。その場合、前掲の図1の実行時には、アクセス許可を問うメッセージが表示されます(図7)。
図7:Trustedモードを指定しない場合は、アクセス許可の確認メッセージが表示される(クリックで拡大) |
[停止]ボタンがクリックされた時の処理
「停止」ボタンがクリックされた時は、CaptureSource.Stop メソッドで、キャプチャデバイスからのキャプチャを停止します。
'■「停止ボタン」がクリックされた時の処理
[保存]ボタンがクリックされた時の処理
最後に「保存」ボタンがクリックされた時の処理を記述します。このサンプルでは、拡張子はwavとして保存しますので、ファイルの種類を決定するフィルター文字列に"Audio Files (*.wav)|*.wav"を指定して、新しいファイルダイアログを開きます。
音声データの入出力には、文字列用のStreamWriterではなく、バイトの入出力用のStream クラスを使います。
キャプチャしたデータをもとに、PCM形式のデータを生成する処理を実行します。この生成処理は、取得した多数のデータを利用するため、引数を渡して別プロシジャ(後述のsaveMyDataプロシジャ)として記述します。
'■「保存ボタン」がクリックされた時の処理
音声データを生成して書き込む処理
先のSaveButton_Clickプロシジャから引数を受け取り、キャプチャしたデータやフォーマット形式をもとにして、PCM形式のデータを生成する処理を記述していきます。生成するデータの内容は、前ページの表1の通りです。
まず、保存する音声データはバイナリ形式ですから、BinaryWriterクラスを使い、新しいインスタンスを生成します。
次に、データ生成の際の計算に利用する4つの変数を、あらかじめ宣言しておきます。チャンネル数、サンプリングレート、量子化ビット数、データ長です。Option Strict Onを指定していますので、型指定を間違えないように注意します。
さらに、3つのチャンクの順に、PCM形式データを順次生成して書き込んでいきます。fmtの後には半角スペースが必要ですので、注意してください。文字列を出力する際には、文字をUnicode 文字配列へコピーする、ToCharArray メソッドを使います。このメソッドを使わなければ正しいデータが生成されません。
dataチャンクについては、音声の実データを生成して書き込みます。
まず、データ長から1減算した数の、バイト配列を作成します。音声を順次読み取るためのカウンタ用変数を宣言します。FileStream.Seek メソッド で、音声データの走査を開始する起点を、0の位置に設定します。構文は、returnValue = instance.Seek(offset, origin)です。
データ長の数だけ、データの読み取りと書き込みを繰り返します。データを読み取るStream.Readメソッド の構文は、returnValue = instance.Read(buffer, offset, count) です。bufferにはバイト配列を指定します。offsetには、ストリームから読み取ったデータを格納する開始点の位置を指定します。この位置は0から始まるため、ここでは0を指定しています。countには、ストリームから読み取る最大バイト数、ここではデータ長を指定しています。
これで、指定したバイト配列の、offset から (offset + count -1) までの値が、キャプチャしたソースから読み取られたバイトに置き換わります。以上の順序で、音声データを生成します。
一連の処理が完了した時点で、「保存しました」というメッセージを表示します。
'■音声データを生成して書き込む処理
以上のコードが書けたら、デバッグして動作を確認してみてください。出力結果は音声なので、画面上では結果をお伝えできません。読者の皆さまそれぞれで試してください。
もし、タイプミスなどで正しいデータを生成できていない場合は、再生時に、「ファイルを再生できません。プレーヤーがそのファイルの種類をサポートしていないか、そのファイルの圧縮に使用したコーデックをサポートしていない可能性があります」というメッセージが表示されます(図8)。正しいデータを保存した場合は、前掲図3のように、問題なく再生されます。
図8:正しいデータを生成できていない場合は、再生時にメッセージが表示される |
次回は、センサーから入力されたデータの取得と利用について紹介します。