[先頭の項目] [前の項目] [次の項目] [最後の項目]

第 2 章

Sampled パッケージの概要

ここでは、JavaTM Sound API のデジタルオーディオアーキテクチャの概要を説明します。アーキテクチャには javax.sound.sampled パッケージからアクセスすることができます。はじめに、パッケージの中心機能である書式付きオーディオデータの再生と取り込みについて説明します。 次に、再生と取り込みに必要な 3 つの基本要素である、オーディオデータのフォーマット、ライン、およびミキサーについて説明します。 Line インタフェースとそのサブインタフェースについては簡単に説明します。

設計目標

Java Sound API の要素の考察を始める前に、javax.sound.sampled パッケージの位置づけについて説明します。

作業の中心はデータ移送

javax.sound.sampled パッケージは、音声の移送に関係するものです。別に表現すると、Java Sound API は、再生と取り込みが中心であるといえます。Java Sound API が取り組む作業は主として、書式付きのオーディオデータのバイトをどのようにしてシステムの中に、また、システムの外へ移動させるかということです。この作業には、オーディオ入出力デバイスを開くことと、リアルタイムのサウンドデータでいっぱいになっている複数のバッファの管理が伴います。また、(入力と出力の) 複数のオーディオストリームを 1 本のストリームにミキシングする作業も伴います。ユーザがサウンドのフローの開始、一時停止、再開、停止を要求する時、システム内外へのサウンドの移送は、適切に取り扱われる必要があります。

基本のオーディオ入出力を中心的にサポートするため、Java Sound API にはさまざまなオーディオデータ形式間での変換用のメソッドと、共通タイプのサウンドファイルの読み込み/書き込み用のメソッドが提供されています。ただし、この API は包括的なサウンドファイルツールキットを目指すものではありません。特定の Java Sound API の実装では、ファイルタイプやデータ形式変換の拡張セットのサポートを必要としません。サードパーティのサービスプロバイダから、既存の実装にプラグインして追加のファイルタイプとファイル変換をサポートできるモジュールが提供されています。

オーディオのバッファ付き処理とバッファなし処理

Java Sound API では、オーディオの移送をバッファ付きのストリーミング方式とバッファなしのメモリ内方式の両方で処理することができます。ここでは「ストリーミング」という用語はオーディオバイトのリアルタイム処理という一般的な意味で使用し、インターネット上でよく使用されるオーディオ送信という特定の形式で使用される場合を指しません。つまり、オーディオのストリームとは単に、処理 (再生、録音など) されるレートとほぼ同じレートで到着する連続したオーディオバイト群です。バイトに対する操作は、すべてのデータが到着するより前に開始します。ストリーミングモデルで、特にオーディオ出力よりもオーディオ入力の場合に、サウンドの長さと、すべてが到着するまでの時間があらかじめ分かっているとは限りません。操作が停止されるまで、バッファ内のオーディオデータを一度に 1 つずつ処理するだけです。オーディオ出力 (再生) の場合も、再生するサウンドが大きすぎて一度にメモリに入りきらないときは、データをバッファリングする必要があります。つまり、オーディオバイトを複数のチャンク (塊) に分けてサウンドエンジンに渡し、サウンドエンジンは適切な時に各サンプルの再生の処理を行います。各チャンクの適正なデータ量を簡単に知るための機構が用意されています。

Java Sound API では、再生のみの場合にはバッファなしの移送も可能ですが、すべてのオーディオデータが手元にあり、メモリに入りきらないほど大きくないことが条件です。この場合は、アプリケーションプログラムによるオーディオのバッファリングは必要ありませんが、必要であればバッファ付きのリアルタイム技法を利用することもできます。または、あらかじめサウンド全体を一度にメモリにロードしておいてから再生を行うこともできます。この方法ではあらかじめすべてのサウンドデータがロードされているので、たとえばユーザが [開始 (Start)] ボタンをクリックすると同時に再生を開始できます。この方法は、最初のバッファがいっぱいになるまで待つ必要のあるバッファ付きモデルよりも有利です。さらに、バッファなしのメモリ内方式のモデルでは、簡単にサウンドをループ (循環) させたりデータ上の任意の位置にセットすることができます。

この、再生のための 2 つのモデルについては第 4 章「オーディオの再生」で詳細に説明します。バッファ付きの録音については、第 5 章「オーディオの取り込み」で説明します。

基本要素: フォーマット、ミキサー、ライン

Java Sound API を使ってサウンドを再生したり取り込んだりするには、少なくとも、書式付きオーディオデータ、ミキサー、およびラインの 3 つの要素が必要です。 次に、それぞれについて説明します。

書式付きオーディオデータとは

書式付きオーディオデータとは、いくつかの標準形式のうちのいずれかにフォーマットされているサウンドを指します。Java Sound API では、データ形式とファイル形式を区別しています。

データ形式

データ形式は、「raw」サンプリングオーディオデータ、たとえばすでにサウンドファイルから読み取られているサンプルやマイクロフォン入力から取り込まれているサンプルなどの、一連のバイトを解釈する方法を示します。たとえば、1 つのサンプルが何ビットで構成されているか (サウンドの最短瞬間の表示) や、サウンドのサンプリングレート (サンプリングの間隔) を知る必要があります。再生または取り込みの設定を行うときは、再生または取り込みを行うサウンドのデータ形式を指定します。

Java Sound API ではデータ形式は AudioFormat オブジェクトで表されます。このオブジェクトには次の属性があります。

PCM は音の波形のエンコーディング手法の 1 つです。Java Sound API には、振幅の線形量子化を使うものと符号付き/符号なしの整数値を使うものの 2 種類の PCM エンコーディングがあります。線形量子化とは、サンプル内に保存されている数値がその瞬間の元の音圧に正比例し (ひずみは除外)、同様にその瞬間の音で振動するスピーカや鼓膜の変位に比例することを意味します。たとえば、コンパクトディスクは線形 PCM エンコーディングサウンドを使用します。Mu-law エンコーディングと a-law エンコーディングは一般的な非線形エンコーディングで、オーディオデータをより小さく圧縮することができます。これらのエンコーディング手法は、通常、電話や発話の録音に使用されます。非線形エンコーディングでは非線形関数を使って、元の音声の振幅を内部値にマップします。この関数は、ボリュームの大きい音より小さい音に、より高い振幅分解能をもたせるように設計することができます。

1 つのフレームには、特定の時間のすべてのチャネルのデータが含まれます。PCM エンコーディングデータの場合は、フレームは単に所定の瞬間におけるすべてのチャネルの同時サンプルのセットであり、それ以外の情報は付随しません。この場合、フレームレートはサンプリングレートに等しく、バイト単位のフレームサイズは、チャネル数×ビット単位のサンプルサイズ÷バイト内のビット数で求められます。

その他のエンコーディング手法では、フレームにサンプル以外の情報が付随することがあります。またフレームレートがサンプリングレートとまったく異なることがあります。たとえば、MP3 (MPEG-1 Audio Layer 3) エンコーディングを考えます。この手法は Java Sound API の現バージョンでは明示的に言及してはいませんが、Java Sound API の実装またはサードパーティサービスプロバイダによりサポートされる場合があります。MP3 では、各々のフレームにはチャネルごとのサンプルだけでなく、一連のサンプルのまとまった圧縮データが含まれます。各フレームにサンプル群全体が縮約されるため、フレームレートはサンプリングレートよりも遅くなります。フレームにはヘッダも含まれます。ヘッダがあっても、バイト単位のフレームサイズは PCM フレームの等しい数値のバイトサイズよりも小さくなります(MP3 は、PCM よりもコンパクトなデータにすることを目的としています)。このようなエンコーディングでは、サンプリングレートとサンプルサイズとは、符号化された音声の最終的な変換目的であり、デジタル-アナログ変換機 (DAC) に渡される、PCM データのことです。

ファイル形式

ファイル形式はサウンドファイルの構造を、ファイル内の raw オーディオデータの形式だけでなくファイル内に保存できる他の情報を含めて指定します。サウンドファイルには、WAVE (WAV としても知られ、PC に関連する場合が多い)、AIFF (Macintosh に関連する場合が多い)、AU (UNIX システムに関連する場合が多い) などのさまざまな標準形式があります。サウンドファイルのタイプが異なるとそれぞれの構造も異なります。たとえば、ファイルのヘッダ内のデータの配列が異なることがあります。ヘッダには説明情報が含まれ、通常はファイルの実際のオーディオサンプルの前にありますが、ファイル形式によっては説明とオーディオデータを連続した「チャンク」の形にすることもあります。ヘッダには、そのオーディオをサウンドファイルに保存するために使用したデータ形式の指定が含まれます。これらのどのタイプのサウンドファイルもさまざまなデータ形式をとることができ (通常は 1 つのファイルには 1 つのデータ形式です)、また、異なるファイル形式のファイルに同じデータ形式を使用することもできます。

Java Sound API ではファイル形式は AudioFileFormat オブジェクトで表されます。このオブジェクトには次の属性があります。

AudioSystem クラス (第 3 章「オーディオシステムリソースへのアクセス」で説明) には、異なるファイル形式でサウンドの読み込みと書き込みを行うためのメソッドと、異なるデータ形式間における形式の変換のためのメソッドがあります。これらのメソッドのいくつかでは、AudioInputStream と呼ばれる一種のストリームを介してファイルの内容にアクセスできます。AudioInputStream は一般的な Java InputStream クラスのサブクラスで、順次に読み出されるバイト群を縮約しています。 AudioInputStream はスーパークラスの InputStream に、そのバイトのオーディオデータ形式の知識 (AudioFormat オブジェクトで表される) を追加したものです。サウンドファイルを AudioInputStream として読み込むことにより、サウンドファイルの構造 (ヘッダ、チャンクなど) を考慮する必要なくサンプルに直接アクセスできます。1 回のメソッド呼び出しで、データ形式とファイルタイプについて必要なすべての情報を取得できます。

ミキサーとは

サウンド用のアプリケーションプログラミングインタフェース (API) の多くは、オーディオデバイスという概念を使用します。「デバイス」とは、多くの場合、物理的な入出力装置へのソフトウェアインタフェースのことです。たとえば、サウンド入力デバイスは、マイクロフォン入力、ラインレベルのアナログ入力、場合によってはデジタルオーディオ入力などの、サウンドカードの入力機能を表すことがあります。

Java Sound API では、デバイスは、Mixer オブジェクトによって表されます。ミキサーの目的は、1 つまたは複数のオーディオ入力ストリームと、1 つまたは複数のオーディオ出力ストリームを処理することです。通常の場合は、ミキサーは実際、複数の入力ストリームを混合して 1 本の出力ストリームにします。 Mixer オブジェクトは、サウンドカードのような物理装置のサウンドミキシング機能を表すことができます。そのような装置では、さまざまな入力装置からコンピュータに送られるサウンドやアプリケーションプログラムから出力装置へ送られるサウンドをミキシングする必要があります。

また一方で、Mixer オブジェクトは、物理装置への固有のインタフェースを持たずに完全にソフトウェア内に実装したサウンドミキシング機能を表すこともできます。

Java Sound API では、サウンドカード上のマイクロフォン入力などの構成要素は、それ自体ではデバイス (ミキサー) とは見なされず、ミキサーへの入出力のポートと見なされます。ポートは通常、ミキサーに入る、またはミキサーから出て行く 1 本のオーディオストリームを作ります (ただし、ストリームはステレオのように複数チャネルでも可)。ミキサーには、このようなポートが複数あることがあります。たとえば、サウンドカードの出力機能を表すミキサーは複数のオーディオストリームをミキシングして、ミキサーに接続しているさまざまな出力ポートのどれか 1 つまたはすべてに混合した信号を送ります。これらの出力ポートとして、ヘッドフォンジャック、内蔵スピーカ、ラインレベル出力などがあります。

Java Sound API のミキサーの概念を理解しやすくするために、ライブコンサートやレコーディングスタジオに出かけてみると実際にミキシングコンソールがどういうものかわかります。(下図を参照)。

物理ミキシングコンソール

物理装置としてのミキサーには複数の「ストリップ」(スライスとも呼ばれる) があり、それぞれのストリップは 1 つのオーディオ信号が処理のためにミキサーに送られる経路を表します。ストリップには、そのストリップ内の信号のボリュームとパン (ステレオイメージでの配置) を調節するためのつまみとコントロールがあります。また、ミキサーにはリバーブ (残響) などのサウンドエフェクトのための独立したバスがあり、内部または外部のリバーブユニットにこのバスを接続できます。各ストリップには、そのストリップの信号のどれぐらいの量をリバーブ付きにするかを調節するポテンショメータがあります。リバーブ付き (「ウェット」) ミックスは次にストリップからの「ドライ」信号とミキシングされます。物理ミキサーは、ミキシングされたこの最終信号を出力バスに送り、出力バスは通常はテープレコーダ (またはディスクによる録音システム) とスピーカに接続しています。

ライブコンサートのステレオ録音を思い出してみてください。ステージ上の多数のマイクや電子楽器から出ているケーブル (または無線接続) はミキシングコンソールの入力プラグに差し込まれています。各々の入力は、図に示すようにミキサーの別々のストリップにつながっています。音響技師が、ゲイン、パン、リバーブ調節の設定を決めます。すべてのストリップとリバーブユニットの出力が 2 本のチャネルにミキシングされます。この 2 本のチャネルは、ミキサーの 2 つの出力に送られます。この出力には、ステレオテープレコーダの入力に接続しているケーブルが差し込まれています。この 2 本のチャネルは、アンプを介して音楽の種類とホールの大きさに合わせて選択されたスピーカに送られます。

次に、録音スタジオで、楽器や歌手の音声をマルチトラックテープレコーダの別々のトラックに録音する場合を考えます。すべての楽器と歌手の音声を録音し終わってから、録音技師が「ミックスダウン」を行い、テープ録音されたすべてのトラックをまとめて、コンパクトディスクに配布可能な 2 チャネル (ステレオ) 録音にします。この場合、ミキサーの各ストリップへの入力はマイクロフォンではなく、マルチトラック録音の 1 つのトラックです。ここでも、録音技師はストリップのコントロールを使って、各トラックのボリューム、パン、およびリバーブの量を決定することができます。ライブコンサートの例と同様に、ミキサーの出力は再びステレオレコーダと複数のステレオスピーカに送られます。

上記の 2 つの例は、ミキサーの 2 つの使用方法を示しています。複数の入力チャネルを取り込んで少数のトラックにまとめて記録し、ミキシング済みの信号を保存する方法と、複数のトラックを再生しながら少数のトラックにミックスダウンする方法です。

Java Sound API では、ミキサーは入力 (オーディオの取り込み) と出力 (オーディオの再生) に同様に使用できます。入力の場合、ミキシングのためのオーディオを取得するソースは、1 つ以上の入力ポートです。ミキサーは、取り込んでミキシングしたオーディオストリームをターゲットに送ります。ターゲットは、アプリケーションプログラムがミキシング済みオーディオデータを取り出すことのできるオブジェクトです。オーディオ出力の場合は、状況が逆になります。ミキサーのオーディオソースは、1 つ以上のアプリケーションプログラムがサウンドデータを書き込むことのできる複数のバッファを持つ 1 つ以上のオブジェクトであり、ミキサーのターゲットは 1 以上の出力ポートです。

ラインとは

物理ミキシングコンソールの例は Java Sound API のラインの概念の理解にも役立ちます。

ラインとは、デジタルオーディオ「パイプライン」、つまりオーディオをシステムの内外に移送するための経路の 1 つの要素です。通常、ラインはミキサーへの入力または出力の経路です (技術的には、ミキサー自体も 1 種のラインです)。

オーディオ入出力ポートはラインです。これらは、物理ミキシングコンソールに接続されているマイクロフォンとスピーカに似ています。ラインの種類にはこのほか、アプリケーションプログラムがミキサーから入力オーディオを取得したりミキサーに出力オーディオを送るために使用するデータ経路があります。これらのデータ経路は、物理ミキシングコンソールに接続されているマルチトラックレコーダのトラックに似ています。

Java Sound API のラインと物理的なミキサーのラインの違いの 1 つは、Java Sound API を流れるオーディオデータはモノラルの場合とマルチチャネル (例: ステレオ) の場合があることです。一方、物理ミキサーの入力と出力はそれぞれ、通常は単一チャネルのサウンドです。物理ミキサーから複数の出力チャネルを取り出すには、通常は複数の物理出力が使われます (少なくともアナログサウンドの場合は、デジタル出力ジャックはマルチチャネルの場合が多い)。Java Sound API では、1 つのライン内のチャネルの数は、そのラインを流れているデータの AudioFormat によって指定されます。

オーディオ出力構成の中のライン

ここで、ラインとミキサーについて、いくつかの種類を挙げて考えてみます。次の図は、Java Sound API の実装の一部として使用できる、簡単なオーディオ出力システム内の数種類のラインを示します。

オーディオ出力用ラインの設定例

この例では、アプリケーションプログラムはオーディオ入力ミキサーの使用可能な入力へのアクセスを持っています。それは、1 つまたは複数のクリップとソースデータラインです。クリップとは、オーディオデータをあらかじめロードしてから再生できるミキサー入力 (ラインの一種) です。ソースデータラインは、オーディオデータのリアルタイムのストリームを受け取るミキサー入力です。アプリケーションプログラムはサウンドファイルからクリップにオーディオデータをあらかじめロードします。次に、アプリケーションプログラムはその他のオーディオデータを、1 回に 1 バッファ分ずつソースデータラインに送ります。各ラインにはリバーブ、ゲイン、パンのコントロールがあります。ミキサーはすべてのラインからデータを読み込み、ドライのオーディオ信号とウェットの (リバーブをかけた) 信号をミキシングします。ミキサーは、最終的な出力を、スピーカ、ヘッドフォンジャック、ラインアウトジャックなどの 1 つ以上の出力ポートに配信します。

この図では、さまざまなラインは独立した矩形で表されていますが、これらはすべてミキサーが所有するもので、ミキサーに欠かせないものと考えられます。リバーブ、ゲイン、およびパンの矩形は、ラインを流れているデータに対してミキサーが実行する処理コントロールを表すもので、ラインではありません。

これは、この API でサポートできるミキサーの一例です。すべてのオーディオ構成に、図で示されているすべての機能があるわけではありません。個々のソースデータラインでパンをサポートしない場合や、ミキサーがリバーブを実装しない場合などもあります。

オーディオ入力構成のライン

単純なオーディオ入力システムも同様です。

オーディオ入力用ラインの設定例

ここでは、データは 1 つ以上の入力ポート (通常はマイクロフォンまたはライン入力ジャック) からミキサーに流入します。ゲインとパンが調節され、ミキサーは取り込んだデータを、ミキサーのターゲットデータラインを経由してアプリケーションに送ります。ターゲットデータラインは、ストリーミングされた入力サウンドの混合が含まれるミキサー出力です。もっとも単純なミキサーのターゲットデータラインは 1 つだけですが、取り込んだデータを同時に複数のターゲットデータラインに配信できるミキサーもあります。

Line インタフェースの階層

ここまではラインとミキサーについて、機能面からいくつか見てきましたが、ここからは、プログラムの視点からの考察を行います。いくつかの種類のラインは、基本的インタフェース Line のサブインタフェースによって定義されています。インタフェースの階層を次に示します。

Line インタフェースの階層

基本インタフェース Line には、すべてのラインに共通する最小限の機能が記述されています。

次に、Line インタフェースのサブインタフェースについて考えます。

Ports は、オーディオデバイスに対するオーディオの入力または出力のための単純なラインです。すでに説明したとおり、一般的なポートの種類には、マイクロフォン、ライン入力、CD-ROM ドライブ、スピーカ、ヘッドフォン、ライン出力があります。

Mixer インタフェースは当然ミキサーを表し、すでに説明したようにハードウェアデバイスまたはソフトウェアデバイスです。Mixer インタフェースは、ミキサーのラインを取得するためのメソッドを提供します。 これらのメソッドには、ターゲットデータラインが含まれます。ミキサーのラインには、オーディオをミキサーに送り込むソースラインと、ミキサーがミキシング済みのオーディオを送り出すターゲットラインがあります。オーディオ入力ミキサーの場合は、ソースラインはマイクロフォン入力などの入力ポートで、ターゲットラインはオーディオをアプリケーションプログラムに送る TargetDataLines (このあとで説明) です。一方、オーディオ出力ミキサーの場合は、ソースラインはアプリケーションプログラムがオーディオデータを送り込む ClipsSourceDataLines (このあとで説明) で、ターゲットラインはスピーカなどの出力ポートです。

Mixer は、1 つ以上のソースラインと 1 つ以上のターゲットラインを持つものと定義されます。この定義によれば、ミキサーは実際にデータをミキシングしなくてもかまわないので、ミキサーのソースラインが 1 本だけという可能性もあります。 Mixer API はさまざまなデバイスを包含するためのものですが、通常の場合は、この API でミキシングがサポートされています。

Mixer インタフェースでは同期がサポートされるので、複数のミキサーのラインを同期グループとして扱うよう指定できます。指定後は、各ラインを個別に制御する必要はなく、グループのラインのどれかに単一のメッセージを送ることにより、グループのすべてのデータラインを開始、停止、終了することができます。この機能を持つミキサーを使うと、ライン間でサンプル精度の同期が得られます。

汎用の Line インタフェースには、再生と録音の開始と停止を行う手段はありません。そのためには、データラインが必要です。 DataLine インタフェースは、Line の機能以外に次の補足的なメディア関連機能を提供します。

  • オーディオ形式
    各データラインのデータストリームには、オーディオ形式が関連付けられています。
  • メディアポジション
    データラインは、メディア内の現在の位置をサンプルフレーム数で表して通知できます。これは、オープンされてからデータラインより取り込まれたか、またはレンダリングされたサンプルフレーム数を表します。
  • バッファサイズ
    データラインの内部バッファのサイズ (バイト数) です。ソースデータラインの場合は、内部バッファにデータの書き込みができます。 ターゲットデータラインの場合は、内部バッファからデータの読み込みができます。
  • レベル (オーディオ信号の現在の振幅)

  • 再生または取り込みの開始および停止

  • 再生と取り込みの一時停止と再開

  • フラッシュ (未処理のデータをキューから破棄する)

  • ドレイン (すべての未処理データが掃きだされ、データラインのバッファが空になるまでブロックする)

  • アクティブ状態
    アクティブな再生動作またはミキサーの入出力オーディオデータの取り込み動作に携わっているデータラインは「アクティブ状態」とみなされます。
  • イベント
    アクティブな再生動作またはミキサーの入出力オーディオデータの取り込み動作の開始時と終了時に、START および STOP イベントが発行されます。
  • TargetDataLine は、ミキサーからオーディオデータを受け取ります。通常、ミキサーはマイクロフォンなどのポートからオーディオデータを取り込んでいるため、ターゲットラインのバッファにデータを置く前に、取り込んだオーディオを処理またはミキシングできます。TargetDataLine インタフェースは、ターゲットデータラインのバッファからデータを読み込むためのメソッド、および現在読み込みが可能なデータ量を特定するためのメソッドを提供します。

    SourceDataLine は、再生用のオーディオデータを受け取ります。SourceDataLine は、再生用にソースデータラインのバッファにデータを書き込むためのメソッド、およびラインがブロックされずに受け取る用意ができているデータ量を特定するためのメソッドを提供します。

    Clip は、再生前にオーディオデータをロードしておくことができるデータラインです。データはストリーミングではなくプリロードされることから、クリップの持続時間が再生前にわかるため、メディア内での開始位置を任意に選択できます。クリップはループできます。 つまり、再生時に 2 つの指定したループ点間のすべてのデータを、指定された回数または無期限に繰り返すことができます。

    この章では、サンプリングオーディオ API の重要なインタフェースとクラスのほとんどについて説明しました。次章からは、これらのオブジェクトをアプリケーションプログラムからアクセスして使用する方法について説明します。



    [先頭の項目] [前の項目] [次の項目] [最後の項目]

    Copyright © 2000, Sun Microsystems Inc. All rights reserved.