第13章「サービス・プロバイダ・インタフェースの概要」で、javax.sound.sampled.spi
とjavax.sound.midi.spi
の2つのパッケージはサウンド・サービスの開発者が使用する抽象クラスを定義していることを説明しました。サービス・プロバイダはこれらの抽象クラスのうちのいずれかのクラスのサブクラスを実装することにより、実行システムの機能性を拡張する新しいサービスを作成することができます。第14章ではjavax.sound.sampled.spi
パッケージの使用方法を説明しました。この章では、MIDIデバイスとMIDIファイルを取り扱うための新しいサービスを提供するjavax.sound.midi.spi
パッケージの使用方法について説明します。
既存のMIDIサービスだけをアプリケーション・プログラムで使用するプログラマは、この章を読まなくても差し支えありません。MIDIの概要とアプリケーション・プログラムにインストール済みのMIDIサービスの使用方法については、このプログラマーズ・ガイドの第II部「MIDI」を参照してください。この章では、インストール済みのMIDIサービスにアクセスするためにアプリケーション・プログラムが呼び出すJava Sound APIメソッドについての知識があることを前提とします。
javax.sound.midi.spi
パッケージには次の4つの抽象クラスが存在し、MIDIシステムを提供する4種類のサービスを表しています。
MidiFileWriter
は、MIDIファイル書込みサービスを提供します。これらのサービスにより、アプリケーション・プログラムはそのプログラムにより生成された、または処理されたMIDI Sequence
をMIDIファイルに書き込むことができます。MidiFileReader
は、ファイル読込みサービスを提供します。このサービスはアプリケーション・プログラムで使用するために、MIDIファイルからMIDI Sequence
を返します。MidiDeviceProvider
は、特定の種類のMIDIデバイスのインスタンス(1つまたは複数)を提供します。ハードウェア・デバイスが含まれることもあります。SoundbankReader
は、サウンドバンク・ファイル読込みサービスを提供します。SoundbankReader
の具象サブクラスは、所定のサウンドバンク・ファイルを構文解析して、Synthesizer
にロードできるSoundbank
オブジェクトを作成します。アプリケーション・プログラムは、サービス・オブジェクトのインスタンスを直接作成することはありません。サービス・オブジェクトがMidiDeviceProvider
のようなプロバイダ・オブジェクトでも、プロバイダ・オブジェクトから提供されるSynthesizer
のようなオブジェクトでも、同様です。また、プログラムがSPIクラスを直接参照することもありません。そのかわり、アプリケーション・プログラムはjavax.sound.midi
パッケージ内のMidiSystem
オブジェクトに要求を行います。MidiSystem
は、その要求を受けて、javax.sound.midi.spi
クラスの具象サブクラスを使ってこれらの要求を処理します。
標準MIDIファイル形式はType 0、Type 1、Type 2の3種類があり、Java Sound APIの実装でもこれらのすべてをサポートしています。これらのファイル形式は、ファイル内のMIDIシーケンス・データの内部表現が異なり、シーケンスの種類の違いに対応しています。この3種類のファイル形式のいずれかが実装でサポートされていない場合は、実装されていないファイル形式のサポートをサービス・プロバイダが提供することができます。標準MIDIファイル形式には、一部独自形式を追加して改変されたものがあり、標準の形式と同様に、サード・パーティのベンダーによるサポートを受けることができます。
MIDIファイルの書込み機能はMidiFileWriter
の具象サブクラスによって提供されます。この抽象クラスはjavax.sampled.spi.AudioFileWriter
とほぼ同じです。ここでも同様に、メソッドは書き込めるファイルのタイプを知るためのクエリー・メソッドと実際にファイルを書き込むためのメソッドに分けられます。AudioFileWriter
の場合と同様に、次の2つのクエリー・メソッドは具象メソッドです。
boolean isFileTypeSupported(int fileType) boolean isFileTypeSupported(int fileType, Sequence sequence)1つ目のメソッドは、指定されたタイプのMIDIファイルをファイル・ライターが書き込めるかどうかについての一般的な情報を提供します。2つ目のメソッドはさらに細かい指定が可能で、指定されたタイプのMIDIファイルに特定のSequenceを書き込めるかどうかを問い合わせます。通常は、これらの2つの具象メソッドをオーバーライドする必要はありません。デフォルト実装では、2つのメソッドはそれぞれ対応する2つのクエリー・メソッドの一方を呼び出して、返された結果に対して処理を反復します。これらの2つのクエリー・メソッドは抽象メソッドなので、サブクラスに実装する必要があります。
abstract int[] getMidiFileTypes() abstract int[] getMidiFileTypes(Sequence sequence)1つ目のメソッドは一般にサポートされるすべてのファイル・タイプの配列を返します。一般的な実装では、ファイル・ライターのコンストラクタ内でこの配列を初期化して、このメソッドからこの配列を返します。次のメソッドは、一連のファイル・タイプの中から、ファイル・ライターが所定のSequenceを書き込むことのできるサブセットを探します。MIDI仕様により、すべてのタイプのシーケンスをすべてのタイプのMIDIファイルに書き込めるとは限りません。
MidiFileWriter
サブクラスのwrite
メソッドは、所定のSequence
を指定されたタイプのMIDIファイルに適合するデータ形式にエンコードし、コード化されたストリームをファイルまたは出力ストリームに書き込みます。
abstract int write(Sequence in, int fileType, java.io.File out) abstract int write(Sequence in, int fileType, java.io.OutputStream out)これを行うには、
write
メソッドは、トラックを繰返し調べてSequence
を構文解析し、適応するファイル・ヘッダーを構築し、ヘッダーとトラックを出力に書き込む必要があります。MIDIファイルのヘッダー形式は当然、MIDI仕様で定義されています。ヘッダーに含まれる情報は、そのファイルがMIDIファイルであることを示す「マジック・ナンバー」、ヘッダーの長さ、トラック数、シーケンスのタイミング情報(除算形式と分解能)などです。MIDIファイルの残りの部分は、MIDI仕様により定義された形式のトラック・データです。
ここで、アプリケーション・プログラム、MIDIシステム、およびサービス・プロバイダがMIDIファイルの書込みでどのように連携するかを簡単に説明します。普通、アプリケーション・プログラムはファイルに保存するためのMIDI Sequence
を持っています。このプログラムは、ファイルを書き込もうとする前に、このSequence
に使用できるMIDIファイル形式がサポートされているかどうかを、MidiSystem
オブジェクトに問い合わせます。MidiSystem.getMidiFileTypes(Sequence)
メソッドは、システムが特定のシーケンスを書き込むことができるすべてのMIDIファイル・タイプの配列を返します。このメソッドは、インストールされているMidiFileWriter
サービスのそれぞれに対応するgetMidiFileTypes
メソッドを呼び出して、結果を収集し、整数の配列で返すことによりこれを行います。この配列は、所定のSequence
に対応するすべてのファイル・タイプのマスター・リストと考えることができます。ファイルにSequence
を書き込むときは、ファイル・タイプを表す整数と、書き込まれるSequence
と、出力先のファイルを指定する引数がMidiSystem.write
への呼出しに渡されます。MidiSystem
は、指定されたタイプを使って書込み要求を処理するインストール済みMidiFileWriter
を判断し、対応するwrite
を適切なMidiFileWriter
にディスパッチします。
MidiFileReader
抽象クラスはjavax.sampled.spi.AudioFileReader
クラスに類似しています。どちらのクラスも2つのオーバーロード・メソッドで構成され、それぞれがFile
、URL
、InputStream
のいずれか1つの引数を取ることができます。1つ目のオーバーロード・メソッドは、指定されたファイルのファイル形式を返します。MidiFileReader
の場合、APIは次のとおりです。
abstract MidiFileFormat getMidiFileFormat(java.io.File file) abstract MidiFileFormat getMidiFileFormat( java.io.InputStream stream) abstract MidiFileFormat getMidiFileFormat(java.net.URL url)具象サブクラスには、特定のMIDIファイル(またはストリームやURL)の形式を記述する
MidiFileFormat
オブジェクトを返すこれらのメソッドを実装する必要があります。ただし、そのファイル・タイプがそのファイル・リーダーでサポートされており、ファイルに有効なヘッダー情報が含まれていることが条件です。そうでない場合、InvalidMidiDataException
がスローされます。
もう1つのオーバーロード・メソッドは、所定のファイル、ストリーム、またはURLからMIDI Sequence
を返します。
abstract Sequence getSequence(java.io.File file) abstract Sequence getSequence(java.io.InputStream stream) abstract Sequence getSequence(java.net.URL url)
getSequence
メソッドはMIDI入力ファイル内のバイトの構文解析の作業を実際に行い、対応するSequence
オブジェクトを構成します。この作業は本質的に、MidiFileWriter.write
で使用される処理の反対です。この作業はMIDI仕様で定められたMIDIファイルの内容とJava Sound APIに定められたSequence
オブジェクトには1対1の対応関係があるため、構文解析の詳細手順は簡単です。getSequence
に渡されたファイルの中のデータをファイル・リーダーが解釈できない場合(ファイルが壊れていたり、MIDI仕様に従っていない場合など)は、InvalidMidiDataException
を発行します。
MidiDeviceProvider
は、特定タイプ(1つまたは複数)のMIDIデバイスを提供するファクトリと考えることができます。このクラスは、MIDIデバイスのインスタンスを返すメソッドと、そのプロバイダが提供できるデバイスの種類を確認するためのクエリー・メソッドで構成されます。
他のjavax.sound.midi.spi
サービスと同様に、アプリケーション開発者はMidiSystem
のメソッド(この場合はMidiSystem.getMidiDevice
とMidiSystem.getMidiDeviceInfo
)への呼出しを介してMidiDeviceProvider
サービスに間接的にアクセスします。MidiDeviceProvider
をサブクラス化する目的は新しい種類のデバイスを提供することなので、javax.sound.sampled.spi
パッケージのMixerProvider
で見た場合と同様に、サービスの開発者は返されるデバイス用に付随するクラスも作成する必要があります。そこでは、返されたデバイスのクラスはjavax.sound.sampled.Mixer
インタフェースを実装しました。ここでは、javax.sound.midi.MidiDevice
インタフェースを実装します。Synthesizer
やSequencer
のようなMidiDevice
のサブインタフェースを実装する場合もあります。
MidiDeviceProvider
の単一のサブクラスでも複数の種類のMidiDevice
を提供することができることから、このクラスのgetDeviceInfo
メソッドは利用可能な複数のMidiDevices
を列挙したMidiDevice.Info
オブジェクトの配列を返します。
abstract MidiDevice.Info[] getDeviceInfo()
返された配列に含まれる要素は1つの場合もあります。一般的なプロバイダの実装では、コンストラクタ内で配列を初期化し、それを返します。これによって、MidiSystem
は、インストール済みのMidiDeviceProviders
をすべて繰り返して、すべてのインストール済みデバイスのリストを構築できます。その後、MidiSystem
はこのリスト(MidiDevice.Info[]
配列)をアプリケーション・プログラムに返すことができます。
MidiDeviceProvider
には、次の具象クエリー・メソッドも含まれます。
boolean isDeviceSupported(MidiDevice.Info info)このメソッドにより、システムは特定の種類のデバイスについてプロバイダに問い合わせることができます。一般に、この便利なメソッドはオーバーライドする必要はありません。デフォルト実装では、getDeviceInfoから返された配列を繰り返して、各要素と引数とを比較します。
最後のMidiDeviceProvider
メソッドは、要求されたデバイスを返します。
abstract MidiDevice getDevice(MidiDevice.Info info)このメソッドは、最初に引数がこのプロバイダが提供できるデバイスを記述しているかどうかを確認しなければなりません。デバイスを記述していない場合は、
IllegalArgumentException
をスローします。引数に対応するデバイスがある場合は、そのデバイスを返します。
SoundBank
は、Synthesizer
にロードできる一連のInstrument
です。Instrument
(インストゥルメント)は、特定の種類のサウンドを作るサウンド合成アルゴリズムの実装です。この中には付随する名前と情報の文字列も含まれます。SoundBank
はMIDI仕様のバンクにほぼ対応していますが、拡張性があり、アドレス可能な集合です。MIDIバンクの集合と考える方が適切です。(SoundBanks
とSynthesizers
を理解するための情報は、第12章「サウンドの合成」を参照してください。)
SoundbankReader
は1つのオーバーロード・メソッドから成ります。このメソッドをシステムが呼び出してサウンドバンク・ファイルからSoundbank
オブジェクトを読み込みます。
abstract Soundbank getSoundbank(java.io.File file) abstract Soundbank getSoundbank(java.io.InputStream stream) abstract Soundbank getSoundbank(java.net.URL url)
SoundbankReader
の具象サブクラスは、プロバイダが定義した特定のSoundBank
、Instrument
、Synthesizer
の実装と連携して、システムがファイルから特定のSynthesizer
クラスのインスタンスにSoundBank
をロードできるようにします。Synthesizer
ごとに合成技術は大きく異なります。このため、Synthesizer
の合成処理に制御や仕様に関するデータを提供するInstrument
またはSoundBank
に保存されるデータの形式は多岐にわたります。合成技術によっては、必要なパラメータ・データが数バイトだけの場合や、膨大なサウンド・サンプルをベースにしている場合があります。SoundBank
内にどのようなリソースが存在するのかは、それらのロード先のSynthesizer
の特性に依存します。そのため、SoundbankReader
サブクラスのgetSoundbank
メソッドの実装には、特定の種類のSoundBank
に関する情報へのアクセス手段があります。さらに、SoundbankReader
の特定のサブクラスは、SoundBank
データを保存するための特定のファイル形式を認識します。このファイル形式はベンダー固有で独自形式の場合があります。
SoundBank
は単なるインタフェースです。SoundBank
オブジェクトの内容に関する制約はほとんどありません。このインタフェースを実装するためにオブジェクトがサポートする必要のあるメソッド(getResources
、getInstruments
、getVendor
、getName
など)では、オブジェクトに含まれるデータの要件は緩いものです。たとえば、getResources
とgetInstruments
は空白の配列を返すことができます。サブクラス化されたSoundBank
オブジェクトの実際の内容、特にインストゥルメントとインストゥルメント以外のリソースは、サービス・プロバイダによって決められます。そのため、サウンドバンク・ファイルを解析するメカニズムは、その種類のサウンドバンク・ファイルの仕様に完全に依存します。
サウンドバンク・ファイルはJava Sound APIの外部に、通常はその種類のサウンドバンクをロードできるシンセサイザのベンダーによって作成されます。サウンドバンク・ファイルを作成するためのエンドユーザー・ツールをベンダーが提供する場合もあります。