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

第 14 章

サンプリングオーディオサービスの提供

第 13 章「サービスプロバイダインタフェースの概要」に詳しい説明がありますが、JavaTM Sound API には、javax.sound.sampled.spijavax.sound.midi.spi という 2 つのパッケージが入っています。これらのパッケージは、サウンドサービスの開発者が使用する抽象クラス群を定義しています。サービスプロバイダは、これらの抽象クラス群の中の 1 つのクラスのサブクラスを実装、インストールすることにより新しいサービスを登録して、実行システムの機能性を拡張します。この章では、サンプリングオーディオの処理という新しいサービスを提供するために、実際に javax.sound.sampled.spi パッケージを使用する方法について説明していきます。

既存のオーディオサービスだけをアプリケーションプログラムで使用するプログラマは、この章を読まなくても差し支えありません。インストール済みのオーディオサービスをアプリケーションプログラムで使う方法については、このマニュアル(『Java Sound API プログラマーズガイド』) の第 I 部「サンプリングオーディオ」を参照してください。 この章では、インストール済みのオーディオサービスにアクセスするためにアプリケーションプログラムが呼び出す JavaTM Sound API メソッドについての知識があることを前提とします。

はじめに

javax.sound.sampled.spi パッケージには次の 4 つの抽象クラスが存在し、サンプリングオーディオシステムを提供する 4 つの異なるサービスを説明しています。

第 13 章で説明したように、サービスプロバイダは実行システムの機能性を拡張することができます。一般的な SPI クラスには 2 タイプのメソッドがあります。1 つは、特定のプロバイダから提供されるサービスのタイプのクエリーに応答するタイプで、もう 1 つは、新しいサービスを直接実行するか、そのサービスを実際に提供するオブジェクトのインスタンスを返すタイプです。実行環境のサービスプロバイダ機構は、インストールされるサービスとオーディオシステムの登録および、新しいサービスプロバイダクラスの管理を行います。

サービスのインスタンスは、本質的に、アプリケーションの開発者からニ重に隔離されています。アプリケーションプログラムが、ミキサーやフォーマットコンバータなどのサービスプログラムのオーディオ処理タスクに必要なインスタンスを直接作成することはありません。また、これらのオブジェクトを管理する SPI クラスから直接オブジェクトを要求することもありません。アプリケーションプログラムは javax.sound.sampled パッケージ内の AudioSystem オブジェクトに対して要求を行い、AudioSystem は SPI オブジェクトを使ってこれらのクエリーとサービス要求を処理します。

新しいオーディオサービスの存在は、ユーザとアプリケーションプログラマに対しては完全に透過的です。アプリケーション参照はすべて javax.sound.sampled パッケージの標準オブジェクト、主に AudioSystem によって行われ、新しいサービスによって提供される特殊処理は完全に隠されます。

この章では前章から引き続き、新しい SPI サブクラスを AcmeMixerAcmeMixerProvider などの名前で呼びます。

オーディオファイル書き込みサービスの提供

最初に、比較的簡単な 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 つのクエリーメソッドは抽象メソッドなので、サブクラスに実装する必要があります。

abstract AudioFileFormat.Type[] getAudioFileTypes()
abstract AudioFileFormat.Type[] getAudioFileTypes(AudioInputStream stream)
これらのメソッドは、上記の 2 つのメソッドに直接対応します。各メソッドは、サポートされるすべてのファイルタイプの配列を返します。すべてというのは、はじめのメソッドの場合は、一般にサポートされているファイルタイプのすべて、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 種類のオーバーロードメソッドにより構成されます。このオーバーロードメソッドはそれぞれ、FileURLInputStream のいずれか 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)
	
および
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;
    }
実際の形式変換は、返された AcmeCodecStreamread メソッドの中で行われます。AcmeCodecStreamAudioInputStream のサブクラスです。返された 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 インスタンスを返します。

AcmeMixerMixer インタフェースをサポートするので、アプリケーションプログラムはミキサーの基本機能にアクセスするための情報はこれ以上は必要ありません。ただし、Mixer インタフェースに定義されていない機能を AcmeMixer がサポートしており、この拡張された機能をアプリケーションプログラムからアクセスできるようにする場合には、当然、ミキサーを、追加の文書で十分実証された public メソッドとともに public クラスとして定義する必要があります。定義をすると、この拡張された機能を利用しようとするプログラムが AcmeMixer をインポートして、getMixer から返されるオブジェクトをこのタイプにキャストできるようになります。

次に、MixerProvider のその他の 2 つのメソッドを示します。

abstract Mixer.Info[] getMixerInfo()
および
boolean isMixerSupported(Mixer.Info info)
これらのメソッドにより、オーディオシステムは、アプリケーションプログラムが必要とするデバイスをこの特定のプロバイダクラスが生成できるかどうかを判断できます。つまり、AudioSystem オブジェクトはインストールされているすべての MixerProviders を繰り返し調べて、アプリケーションプログラムが AudioSystem に要求したデバイスを供給できるものがあるか探します。第 3 章「オーディオシステムリソースへのアクセス」「ミキサーの取得」を参照してください。getMixerInfo メソッドは、このプロバイダオブジェクトから提供できるミキサーの種類に関する情報を含むオブジェクトの配列を返します。システムはこれらの情報オブジェクトを、他のプロバイダからの情報と共にアプリケーションプログラムに渡します。

1 つの MixerProvider は複数の種類のミキサーを提供できます。システムは MixerProvidergetMixerInfo メソッドを呼び出すとき、このプロバイダがサポートするさまざまな種類のミキサーを識別する情報オブジェクトのリストを取得します。次にシステムは、MixerProvider.getMixer(Mixer.Info) を呼び出して、目的のミキサーをそれぞれ取得することができます。

サブクラスには、その抽象メソッドとして getMixerInfo を実装する必要があります。 isMixerSupported メソッドは具象であり、通常はオーバーライドする必要はありません。 デフォルト実装では単純に、提供される Mixer.Info と、getMixerInfo から返される配列内の各 Mixer.Info とを比較します。



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

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