第 13 章「サービスプロバイダインタフェースの概要」に詳しい説明がありますが、JavaTM Sound API には、javax.sound.sampled.spi
と javax.sound.midi.spi
という 2 つのパッケージが入っています。これらのパッケージは、サウンドサービスの開発者が使用する抽象クラス群を定義しています。サービスプロバイダは、これらの抽象クラス群の中の 1 つのクラスのサブクラスを実装、インストールすることにより新しいサービスを登録して、実行システムの機能性を拡張します。この章では、サンプリングオーディオの処理という新しいサービスを提供するために、実際に javax.sound.sampled.spi
パッケージを使用する方法について説明していきます。
既存のオーディオサービスだけをアプリケーションプログラムで使用するプログラマは、この章を読まなくても差し支えありません。インストール済みのオーディオサービスをアプリケーションプログラムで使う方法については、このマニュアル(『Java Sound API プログラマーズガイド』) の第 I 部「サンプリングオーディオ」を参照してください。 この章では、インストール済みのオーディオサービスにアクセスするためにアプリケーションプログラムが呼び出す JavaTM 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)
true
を返します。ただし、ファイルを書き込めるかどうかは、ファイルライターに渡される特定のオーディオデータの形式に依存させることができます。1 つのファイルライターですべてのオーディオデータ形式をサポートするとは限らず、また、ファイル形式自体による制約もあります。すべての種類のオーディオデータをすべての種類のサウンドファイルに書き込めるとは限りません。そのため、後者のメソッドはさらに細かい指定が可能で、特定の AudioInputStream
を特定タイプのファイルに書き込めるかどうかを問い合わせます。
通常は、これらの 2 つの具象メソッドをオーバーライドする必要はありません。それぞれのメソッドは単なるラッパーとして、2 つの異種のクエリーメソッドのうちの 1 つを呼び出して、戻った結果の繰り返しを行うものです。これらの異種の 2 つのクエリーメソッドは抽象メソッドなので、サブクラスに実装する必要があります。
これらのメソッドは、上記の 2 つのメソッドに直接対応します。各メソッドは、サポートされるすべてのファイルタイプの配列を返します。すべてというのは、はじめのメソッドの場合は、一般にサポートされているファイルタイプのすべて、2 つめのメソッドの場合は特定のオーディオストリームでサポートされるファイルタイプのすべてという意味です。前者のメソッドの典型的な実装では単純に、そのファイルライターのコンストラクタが初期化する配列を返します。後者のメソッドの実装では、ストリームのabstract AudioFileFormat.Type[] getAudioFileTypes() abstract AudioFileFormat.Type[] getAudioFileTypes(AudioInputStream stream)
AudioFormat
オブジェクトを検査して、要求されたタイプのファイルがサポートしているデータ形式かどうかを確かめます。
AudioFileWriter
の最後の 2 つのメソッドは、実際のファイル書き込み作業を行います。
これらのメソッドは、オーディオデータを表すバイトのストリームを、3 番目の引数で指定するストリームまたはファイルに書き込みます。これが実際にどのように行われるかは、指定されたファイルのタイプの構造により異なります。abstract int write(AudioInputStream stream, AudioFileFormat.Type fileType, java.io.File out) abstract int write(AudioInputStream stream, AudioFileFormat.Type fileType, java.io.OutputStream out)
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
メソッドの一般的な実装は、サウンドファイルのヘッダを読んで構文解析し、ファイルの形式を確かめます。ヘッダのどのフィールドを読み取るかについては AudioFIle Format クラスの説明を、またヘッダの構文解析方法を理解するにはそのファイルタイプの仕様を参照してください。
このメソッドにストリームを引数として提供する呼び出し側は、ストリームがメソッドにより変更されないものと考えているので、ファイルリーダは通常、最初にストリームにマークを付けるべきです。ヘッダの最後まで読み込んだら、ファイルリーダはストリームを元の位置に戻さなければなりません。
もう 1 つのオーバーロードされる 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)
この 2 つの相違点は第 1 引数で、呼び出し側が完全なターゲット形式を指定しているか、形式のエンコーディングのみを指定しているかによります。abstract AudioInputStream getAudioInputStream( AudioFormat targetFormat, AudioInputStream sourceStream)
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; }
AcmeCodecStream
の read
メソッドの中で行われます。AcmeCodecStream
は AudioInputStream
のサブクラスです。返された 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
クラスのクエリーメソッドの場合と同様に、オブジェクトのプライベートデータをチェックし、メソッドが引数を取る場合は引数と比較することにより処理されます。
残りの 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
インタフェースをサポートするので、アプリケーションプログラムはミキサーの基本機能にアクセスするための情報はこれ以上は必要ありません。ただし、Mixer
インタフェースに定義されていない機能を AcmeMixer
がサポートしており、この拡張された機能をアプリケーションプログラムからアクセスできるようにする場合には、当然、ミキサーを、追加の文書で十分実証された 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
とを比較します。