第 5 章: オーディオの取り込み


 

「取り込み (Capture)」とは、コンピュータの外部から信号を取得する処理のことです。 一般的なアプリケーションでのオーディオ取り込みとは録音のことです。たとえば、マイクロフォン入力をサウンドファイルに録音する場合などです。 ただし、取り込みと録音は同義語ではありません。録音という用語には、アプリケーションが受け取ったサウンドデータを常に保存するという意味があるためです。オーディオを取り込むアプリケーションの場合は、必ずしもオーディオを保存するとは限りません。 データが入ってくるとそのデータになんらかの処理 (発話をテキストに変換して複写するなど) を行いますが、バッファの処理が終わるとただちにバッファ内のデータを破棄する場合もあります。

第 2 章「Sampled パッケージの概要」で説明したように、JavaTM Sound API の実装の一般的なオーディオ入力システムは、次の要素で構成されています。

  1. 入力ポート (マイクロフォンポート、ライン入力ポートなど)。受け取ったオーディオデータをミキサーに送ります。
  2. ミキサー。入力データをターゲットデータラインに送ります。
  3. ターゲットデータライン (少なくとも 1 つ)。アプリケーションがデータを取得する場所です。

    一般に、同時にオープンできる入力ポートは 1 つですが、オーディオ入力ミキサーで複数ポートからのオーディオをミキシングすることもできます。 また、ポートを持たない代わりに、ネットワーク上でオーディオ入力を取得するミキサーによるシステム構成も考えられます。

    第 2 章「Sampled パッケージの概要」 「Line インタフェースの階層」で、TargetDataLine インタフェースについて簡単に説明しました。 TargetDataLine は、第 4 章「オーディオの再生」で詳細に説明した SourceDataLine インタフェースに直接類似しています。 すでに説明したように、SourceDataLine は、次の要素で構成されています。

同様に、TargetDataLine も次の要素で構成されます。

TargetDataLine のセットアップ

ターゲットデータラインを取得する手順については、第 3 章「オーディオシステムリソースへのアクセス」で説明しましたが、もう一度ここに示します。

TargetDataLine line;
DataLine.Info info = new DataLine.Info(TargetDataLine.class, 
format); // format is an AudioFormat object
if (!AudioSystem.isLineSupported(info)) { // Handle the error ... } // Obtain and open the line. try {
line = (TargetDataLine) AudioSystem.getLine(info);
line.open(format); } catch (LineUnavailableException ex) { // Handle the error ... }

AudioSystem ではなく MixergetLine メソッドを呼び出すこともできます。

この例で示すように、ターゲットデータラインを取得したあとは、第 4 章「オーディオの再生」で説明したソースデータラインの場合とまったく同様に、DataLineopen メソッドを呼び出してそのラインを予約します。 単一パラメータの open メソッドでは、そのラインのバッファはデフォルトサイズになります。 パラメータが 2 個の open メソッドを呼び出して、アプリケーションの必要に応じたサイズにバッファを設定することもできます。

    void open(AudioFormat format, int bufferSize)
バッファサイズが大きいと遅れが生じ、バッファサイズが小さいとデータを必要な速度で取得できない場合にオーディオに不連続部が発生するおそれがあります。このため、バッファサイズを決めるときは、両方の兼ね合いを考慮する必要があります。 オーディオを取り込む場合、バッファがいっぱいになったら一定時間内にデータを取り出さなければ、データがオーバーフローする可能性があります。 オーバーフローが起こると、取り込まれたデータの一部が破棄されるので、クリックノイズや音飛びの原因になります。 これは、アンダーフローが原因で音が途切れる可能性のある再生の場合と逆の状況です (バッファサイズの決定方法の詳細は、第 4 章「オーディオの再生」を参照)。

TargetDataLine からのデータの読み込み

ラインがオープンされるとデータを取りこむ準備が整いますが、そのラインはまだアクティブではありません。 オーディオの取り込みを実際に開始するには、DataLinestart メソッドを使用します。 このメソッドは、アプリケーションが読み取れるように、そのラインのバッファに入力オーディオデータを送り始めます。 アプリケーションは、ラインからの読み込みの準備が整っていないときに start を呼び出してはなりません。取り込みバッファへの書き込みのために無駄な処理が発生し、オーバーフローが生じて、データは破棄されます。

バッファからのデータの取得を開始するには、TargetDataLine の read メソッドを呼び出します。

int read(byte[] b, int offset, int length)
このメソッドは、配列 b に、length で指定したバイト数のデータを offset で指定したオフセット位置から読み込もうとします。 このメソッドは、実際に読み込まれたバイト数を返します。

SourceDataLine の write メソッドと同様に、このメソッドはバッファの何倍ものデータを要求された場合は要求された量のデータが配信されるまでブロックされるので、バッファに入りきらない量のデータを要求することができます。

録音時にアプリケーションがハングアップするのを防ぐために、次の例のように、すべてのオーディオ入力を取得し終わるまでループ内で繰り返して read メソッドを使用できます。

// Assume that the TargetDataLine, line, has already
// been obtained and opened.
ByteArrayOutputStream out  = new ByteArrayOutputStream();
int numBytesRead;
byte[] data = new byte[line.getBufferSize() / 5];

// Begin audio capture.
line.start();

// Here, stopped is a global boolean set by another thread.
while (!stopped) {
   // Read the next chunk of data from the TargetDataLine.
   numBytesRead =  line.read(data, 0, data.length);
   // Save this chunk of data.
   out.write(data, 0, numBytesRead);
}     
この例では、データを読み込むためのバイト配列のサイズはラインのバッファの 1/5 に設定されています。 この配列をラインのバッファと同じ大きさにしてバッファ全体を読み込みたい場合は、タイミングを正確に計測しなければなりません。これは、データを読み込んでいる間にミキサーがラインにデータを送る必要が生じるとデータがダンプされるからです。 この例のように、ラインのバッファサイズの数割程度を使うことにより、アプリケーションはラインのバッファへのアクセスをミキサーとうまく共有することができます。

TargetDataLineread メソッドは、 バイト配列、その配列内のオフセット、読み込む入力データのバイト数の 3 つの引数を取ります。 この例では、3 番目の引数は単にバイト配列の長さです。 read メソッドは、実際に配列に読み込まれたバイト数を返します。

一般に、データはこの例のようにループ内でラインから読み込みます。 while ループの中で、取得したデータのチャンクがアプリケーションに適した方法で処理されます。この例では、ByteArrayOutputStream に書き込まれます。 ここには示されていませんが、独立したスレッドを使用してブール変数 stopped を設定し、ループを終了する方法もあります。 このブール変数の値は、ユーザが [停止] ボタンをクリックしたとき、およびリスナーがラインから CLOSE または STOP イベントを受け取ったときに true に設定されます。 CLOSE イベントにはリスナーが必須であり、STOP イベントにもリスナーを登録することをお勧めします。 STOP イベントにリスナーがないと、ラインがなんらかの原因で停止したときに stopped が true に設定されず、while ループの繰り返しごとに読み込まれるバイト数が 0 バイトになり、実行速度が速くなって CPU サイクルが浪費されます。 さらに、この例では、取り込みが再びアクティブになったときにもう一度ループに入ります。

ソースデータラインの場合と同様に、ターゲットデータラインもドレインまたはフラッシュすることができます。 たとえば、入力をファイルに録音する場合は、ユーザが [停止] ボタンをクリックしたら drain メソッドを呼び出します。 drain メソッドを呼び出すと、ミキサーの残りのデータがターゲットデータラインのバッファに送られます。 データをドレインしない場合は、取り込まれたサウンドは最後で打ち切られたように見えます。

データをフラッシュしたい場合もあります。 データのフラッシュまたはドレインを行わないと、ミキサーにデータが残ります。 ミキサーにデータが残っていると、取り込みが再開されたときに、新しい録音の先頭にそのサウンドが入ってしまいます。 したがって、取り込みを再開する前にターゲットデータラインをフラッシュすることは有用です。

ラインのステータスの監視

TargetDataLine インタフェースは DataLine を継承しているので、ターゲットデータラインはソースデータラインと同様にイベントを生成します。ターゲットデータラインのオープン時、クローズ時、開始時、および停止時に必ずイベントを受け取るようにオブジェクトを登録することができます。 詳細は、第 4 章「オーディオの再生」「ラインのステータスの監視」を参照してください。

入力オーディオの処理

ほかのいくつかのソースデータラインと同様に、一部のミキサーのターゲットデータラインは、ゲイン、パン、リバーブ、またはサンプリングレートのコントロールのような信号処理コントロールを備えています。 入力ポートには同様のコントロール、特にゲインコントロールがある場合があります。 ラインがそのようなコントロールを備えているかどうかを調べる方法、およびそれらの使用方法の詳細は、第 6 章「コントロールを使ったオーディオ処理」を参照してください。