第13章「サービス・プロバイダ・インタフェースの概要」に詳しい説明がありますが、Java Sound APIには、javax.sound.sampled.spi
とjavax.sound.midi.spi
という2つのパッケージが入っています。これらのパッケージは、サウンド・サービスの開発者が使用する抽象クラス群を定義しています。サービス・プロバイダは、これらの抽象クラス群の中の1つのクラスのサブクラスを実装およびインストールすることにより新しいサービスを登録して、実行システムの機能性を拡張します。この章では、サンプリング・オーディオを処理する新しいサービスを提供するために、実際にjavax.sound.sampled.spi
パッケージを使用する方法について説明していきます。
既存のオーディオ・サービスだけをアプリケーション・プログラムで使用するプログラマは、この章を省略しても差し支えありません。インストール済のオーディオ・サービスをアプリケーション・プログラムで使用する方法については、このプログラマーズ・ガイドの第I部「サンプリング・オーディオ」を参照してください。この章では、インストール済みのオーディオ・サービスにアクセスするためにアプリケーション・プログラムが呼び出すJava Sound APIメソッドについての知識があることを前提とします。
javax.sound.sampled.spi
パッケージには次の4つの抽象クラスが存在し、サンプリング・オーディオ・システムを提供する4種類のサービスを表しています。
AudioFileWriter
は、サウンド・ファイル書込みサービスを提供します。これらのサービスにより、アプリケーション・プログラムはオーディオ・データのストリームを特定のタイプのファイルに書き込むことができます。AudioFileReader
は、ファイル読込みサービスを提供します。これらのサービスにより、アプリケーション・プログラムはサウンド・ファイルの特性を確かめ、ストリームを取得して、そこからファイルのオーディオ・データを読み込むことができます。FormatConversionProvider
は、オーディオ・データ形式の変換サービスを提供します。これらのサービスにより、アプリケーション・プログラムはオーディオ・ストリームのデータ形式をほかの形式に変換することができます。MixerProvider
は、特定の種類のミキサー管理を提供します。このメカニズムにより、アプリケーション・プログラムは特定の種類のミキサーの情報を取得し、そのミキサーのインスタンスにアクセスすることができます。
サービスのインスタンスは、本質的に、アプリケーションの開発者から二重に隔離されています。アプリケーション・プログラムが、ミキサーや形式コンバータなどのサービス・オブジェクトのオーディオ処理タスクに必要なインスタンスを直接作成することはありません。また、これらのオブジェクトを管理するSPIクラスから直接オブジェクトを要求することもありません。アプリケーション・プログラムはjavax.sound.sampled
パッケージ内のAudioSystem
オブジェクトに対して要求を行い、AudioSystem
はSPIオブジェクトを使ってこれらのクエリーとサービス要求を処理します。
新しいオーディオ・サービスの存在は、ユーザーとアプリケーション・プログラマに対しては完全に透過的です。アプリケーション参照はすべてjavax.sound.sampled
パッケージの標準オブジェクト、主にAudioSystem
によって行われ、新しいサービスによって提供される特殊処理は完全に隠されます。
この章でも、前章と同様に、新しいSPIサブクラスをAcmeMixer
、AcmeMixerProvider
などの名前で呼びます。
最初に、比較的簡単なSPIクラスの1つであるAudioFileWriter
について説明します。
AudioFileWriter
のメソッドを実装しているサブクラスは、クラスでサポートされるファイル形式やファイル・タイプのクエリーを処理するために、一連のメソッドの実装を提供しなければなりません。また、サブクラスは、提供オーディオ・データ・ストリームを実際にFile
またはOutputStream
に書き出すメソッドも提供する必要があります。
AudioFileWriter
には、基底クラスに固定実装を持つ次の2つのメソッドが含まれています。
boolean isFileTypeSupported(AudioFileFormat.Type fileType) boolean isFileTypeSupported(AudioFileFormat.Type fileType, AudioInputStream stream)1つ目のメソッドは、このファイル・ライターが指定された種類のサウンド・ファイルを書き込むことができるかどうかを呼出し側に通知するメソッドです。このメソッドは汎用クエリーです。ファイル・ライターに適切なオーディオ・データが渡されることを前提として、そのファイル・ライターがその種類のファイルを書き込める場合は、
true
を返します。ただし、ファイルを書き込めるかどうかは、ファイル・ライターに渡される特定のオーディオ・データ形式に依存することがあります。1つのファイル・ライターですべてのオーディオ・データ形式をサポートするとは限らず、また、ファイル形式自体による制約もあります。すべての種類のオーディオ・データをすべての種類のサウンド・ファイルに書き込めるとは限りません。そのため、2つ目のメソッドはさらに細かい指定が可能で、特定のAudioInputStream
を特定のタイプのファイルに書き込めるかどうかを問い合わせます。
通常は、これらの2つの具象メソッドをオーバーライドする必要はありません。それぞれのメソッドは単なるラッパーであり、2つのクエリー・メソッドの一方を呼び出し、返された結果の繰返しを行います。これらの2つのクエリー・メソッドは抽象メソッドなので、サブクラスに実装する必要があります。
abstract AudioFileFormat.Type[] getAudioFileTypes() abstract AudioFileFormat.Type[] getAudioFileTypes(AudioInputStream stream)これらのメソッドは、上記の2つのメソッドに直接対応します。各メソッドは、サポートされるすべてのファイル・タイプの配列を返します。すべてのファイル・タイプとは、1つ目のメソッドの場合は、一般にサポートされているすべてのファイル・タイプを意味し、2つ目のメソッドの場合は、特定のオーディオ・ストリームでサポートされるすべてのファイル・タイプを意味します。前者のメソッドの典型的な実装では、単にそのファイル・ライターのコンストラクタが初期化する配列を返します。後者のメソッドの実装では、ストリームの
AudioFormat
オブジェクトを検査して、要求されたファイル・タイプがサポートしているデータ形式かどうかを確かめます。
AudioFileWriter
の最後の2つのメソッドは、実際のファイル書込み作業を行います。
abstract int write(AudioInputStream stream, AudioFileFormat.Type fileType, java.io.File out) abstract int write(AudioInputStream stream, AudioFileFormat.Type fileType, java.io.OutputStream out)これらのメソッドは、オーディオ・データを表すバイトのストリームを、3番目の引数で指定するストリームまたはファイルに書き込みます。これが実際にどのように行われるかは、指定されたファイル・タイプの構造により異なります。
write
メソッドは、この形式のサウンド・ファイルに指定されている方法(標準形式のサウンド・ファイルまたは新しい独自の形式)で、ファイルのヘッダーとオーディオ・データを書き込まなければなりません。
AudioFileReader
クラスは、サブクラスに実装する必要のある6つの抽象クラスと、2種類のオーバーロード・メソッドで構成されています。このオーバーロード・メソッドはそれぞれ、File
、URL
、InputStream
のいずれか1つの引数を取ることができます。1つ目のオーバーロード・メソッドは、指定されたファイルのファイル形式に関するクエリーを受け取ります。
abstract AudioFileFormat getAudioFileFormat( java.io.File file) abstract AudioFileFormat getAudioFileFormat( java.io.InputStream stream) abstract AudioFileFormat getAudioFileFormat( java.net.URL url)
getAudioFileFormat
メソッドの一般的な実装は、サウンド・ファイルのヘッダーを読み取って構文解析し、ファイル形式を確かめます。ヘッダーのどのフィールドを読み込むかについては、AudioFileFormatクラスの説明を参照してください。また、ヘッダーの構文解析方法を理解するには、そのファイル・タイプの仕様を参照してください。
このメソッドにストリームを引数として提供する呼出し側は、ストリームはメソッドにより変更されないものとしているので、ファイル・リーダーは通常、最初にストリームにマークを付ける必要があります。ヘッダーの最後まで読み込んだら、ファイル・リーダーはストリームを元の位置に戻さなければなりません。
2つ目のオーバーロード・メソッドAudioFileReader
は、AudioInputStreamを返すことによりファイルの読込みサービスを提供します。このストリームからファイルのオーディオ・データを読み込むことができます。
abstract AudioInputStream getAudioInputStream( java.io.File file) abstract AudioInputStream getAudioInputStream( java.io.InputStream stream) abstract AudioInputStream getAudioInputStream( java.net.URL url)一般に、
getAudioInputStream
の実装はAudioInputStream
をファイルのデータ・チャンクの先頭(ヘッダーの後ろ)の位置に戻して、読込みの用意をします。ただし、ファイル・リーダーが返すAudioInputStream
のオーディオ形式が、ファイルに含まれているデータをなんらかの方法でデコードしたデータのストリームを表すことがあります。重要なのは、そのメソッドが返すストリームが、ファイルに含まれるオーディオ・データを読み込める形式になっているということです。返されたAudioInputStream
オブジェクトにカプセル化されたAudioFormat
は、呼出し側にそのストリームのデータ形式を通知します。この形式は、通常はファイル自体のデータ形式と同じですが、必ずしも同じであるとは限りません。
一般に、返されるストリームはAudioInputStream
のインスタンスです。AudioInputStream
をサブクラス化することが必要になることはほとんどありません。
FormatConversionProvider
サブクラスは、あるオーディオ・データ形式を持つAudioInputStream
を別の形式のストリームに変換します。前者(入力)のストリームをソース・ストリーム、後者(出力)のストリームをターゲット・ストリームと呼びます。第2章「Sampledパッケージの概要」で説明したように、AudioInputStream
にはAudioFormat
が含まれ、AudioFormat
にはAudioFormat.Encoding
オブジェクトで表される、特定の種類のデータ・エンコーディングが含まれています。ソース・ストリーム内の形式とエンコーディングをそれぞれ「ソース形式」と「ソース・エンコーディング」と呼び、ターゲット・ストリーム内のそれらを同様に、「ターゲット形式」と「ターゲット・エンコーディング」と呼びます。
変換の作業は、FormatConversionProvider
のオーバーロードされた抽象メソッド(getAudioInputStream
)の中で行われます。このクラスにはまた、ターゲットとソースのサポートされるすべての形式とエンコーディングの抽象クエリー・メソッドがあります。特定の変換に関する問い合わせのためには、具象ラッパー・メソッドがあります。
次に、2種類のgetAudioInputStream
の形式を示します。
abstract AudioInputStream getAudioInputStream( AudioFormat.Encoding targetEncoding, AudioInputStream sourceStream)および
abstract AudioInputStream getAudioInputStream( AudioFormat targetFormat, AudioInputStream sourceStream)この2つの形式は、呼出し側が完全なターゲット形式を指定しているか、形式のエンコーディングのみを指定しているかによって、第1引数が異なります。
getAudioInputStream
の一般的な実装は、元の(ソース) AudioInputStream
をラップ・アラウンドするAudioInputStream
の新しいサブクラスを返し、read
メソッドが呼び出されたときにそのデータに対してデータ形式変換を適用することにより動作します。たとえば、AcmeCodec
という新しいFormatConversionProvider
サブクラスがAcmeCodecStream
という新しいAudioInputStream
サブクラスとともに動作する場合を考えます。
AcmeCodecの
2番目のgetAudioInputStream
メソッドの実装は次のようになります。
public AudioInputStream getAudioInputStream (AudioFormat outputFormat, AudioInputStream stream) { AudioInputStream cs = null; AudioFormat inputFormat = stream.getFormat(); if (inputFormat.matches(outputFormat) ) { cs = stream; } else { cs = (AudioInputStream) (new AcmeCodecStream(stream, outputFormat)); tempBuffer = new byte[tempBufferSize]; } return cs; }実際の形式変換は、
AudioInputStream
のサブクラスである、返されたAcmeCodecStream
の、新しいread
メソッドの中で行われます。返されたAcmeCodecStream
にアクセスするアプリケーション・プログラムは、単にこれをAudioInputStream
として処理をするため、実装についての詳細な知識は必要ありません。
FormatConversionProvider
の他のすべてのメソッドでは、そのオブジェクトがサポートする入出力のエンコーディングと形式に関する問合せが可能です。次の4つのメソッドは、抽象メソッドです。実装する必要があります。
abstract AudioFormat.Encoding[] getSourceEncodings() abstract AudioFormat.Encoding[] getTargetEncodings() abstract AudioFormat.Encoding[] getTargetEncodings( AudioFormat sourceFormat) abstract AudioFormat[] getTargetFormats( AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat)
すでに説明したAudioFileReader
クラスのクエリー・メソッドの場合と同様に、これらのクエリーは、オブジェクトのプライベート・データをチェックし、後の2つのメソッドについては引数と比較することにより処理されます。
残りの4つのFormatConversionProvider
メソッドは具象メソッドです。通常はオーバーライドする必要はありません。
boolean isConversionSupported( AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat) boolean isConversionSupported(AudioFormat targetFormat, AudioFormat sourceFormat) boolean isSourceEncodingSupported( AudioFormat.Encoding sourceEncoding) boolean isTargetEncodingSupported( AudioFormat.Encoding targetEncoding)
AudioFileWriter.isFileTypeSupported()
の場合と同様に、これらの各メソッドのデフォルトの実装は、本質的にはほかのクエリー・メソッドの1つを呼び出し、返された結果を繰返し実行するラッパーです。
MixerProvider
は名前が示すように、ミキサーのインスタンスを提供します。各具象MixerProvider
サブクラスは、アプリケーション・プログラムが使用するMixer
オブジェクトのファクトリの役目をします。もちろん、新しいMixerProvider
の定義は、Mixer
インタフェースの新しい実装を定義する場合にのみ必要です。上のFormatConversionProvider
の例でgetAudioInputStream
メソッドが返すAudioInputStream
のサブクラスが変換を行ったのと同様に、新しいAcmeMixerProvider
クラスにはgetMixer
メソッドがあり、Mixer
インタフェースを実装している別の新しいクラスのインスタンスを返します。この新しいクラスの名前をAcmeMixer
とします。ミキサーがハードウェアに実装されている場合は、プロバイダは要求されたデバイスのstaticインスタンスを1つしかサポートしないことがあります。その場合は、getMixer
の呼出しに対して、毎回このstaticインスタンスを返します。
AcmeMixer
はMixer
インタフェースをサポートするので、アプリケーション・プログラムがこのインタフェースの基本機能にアクセスするために、その他の情報は必要ありません。ただし、AcmeMixer
がMixer
インタフェースに定義されていない機能をサポートしており、この拡張された機能をアプリケーション・プログラムからアクセスできるようにする場合は、当然、ミキサーを、追加の文書で十分実証されたpublicメソッドとともにpublicクラスとして定義する必要があります。定義すると、この拡張された機能を利用しようとするプログラムがAcmeMixer
をインポートして、getMixer
から返されるオブジェクトをこの種類にキャストできるようになります。
次に、MixerProvider
のほかの2つのメソッドを示します。
abstract Mixer.Info[] getMixerInfo()および
boolean isMixerSupported(Mixer.Info info)これらのメソッドにより、オーディオ・システムは、アプリケーション・プログラムが必要とするデバイスをこの特定のプロバイダ・クラスが生成できるかどうかを判断できます。つまり、
AudioSystem
オブジェクトはインストールされているすべてのMixerProviders
を繰返し調べて、アプリケーション・プログラムがAudioSystem
に要求したデバイスを供給できるものがあるかどうかを確認します。第3章「オーディオ・システム・リソースへのアクセス」の「ミキサーの取得」を参照してください。getMixerInfo
メソッドは、このプロバイダ・オブジェクトから提供できるミキサーの種類に関する情報を含むオブジェクトの配列を返します。システムはこれらの情報オブジェクトを、ほかのプロバイダからの情報とともにアプリケーション・プログラムに渡します。
1つのMixerProvider
は複数の種類のミキサーを提供できます。システムはMixerProviderのgetMixerInfo
メソッドを呼び出すとき、このプロバイダがサポートするさまざまな種類のミキサーを識別する情報オブジェクトのリストを取得します。次にシステムは、MixerProvider.getMixer(Mixer.Info)
を呼び出して、目的のミキサーをそれぞれ取得することができます。
サブクラスには、その抽象メソッドとしてgetMixerInfo
を実装する必要があります。isMixerSupported
メソッドは具象であり、通常はオーバーライドする必要はありません。デフォルト実装では、単に提供されるMixer.Info
と、getMixerInfo
から返される配列内の各Mixer.Infoとを比較します。