第 15 章: MIDI サービスの提供


 

第 13 章「サービスプロバイダインタフェースの概要」で、javax.sound.sampled.spijavax.sound.midi.spi の 2 つのパッケージはサウンドサービスの開発者が使用する抽象クラスを定義していることを説明しました。サービスプロバイダはこれらの抽象クラスのうちのいずれかのクラスのサブクラスを実装することにより、実行システムの機能性を拡張する新しいサービスを作成することができます。第 14 章では javax.sound.sampled.spi パッケージの使用方法を説明しました。この章では、MIDI デバイスと MIDI ファイルを取り扱うための新しいサービスを提供する javax.sound.midi.spi パッケージの使用方法について説明します。

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

はじめに

javax.sound.midi.spi パッケージには次の 4 つの抽象クラスが存在し、MIDI システムを提供する 4 種類のサービスを表しています。

アプリケーションプログラムは、サービスオブジェクトのインスタンスを直接作成することはありません。サービスオブジェクトが MidiDeviceProvider のようなプロバイダオブジェクトでも、プロバイダオブジェクトから提供される Synthesizer のようなオブジェクトでも、同様です。また、プログラムが SPI クラスを直接参照することもありません。その代わり、アプリケーションプログラムは javax.sound.midi パッケージ内の MidiSystem オブジェクトに要求を行います。MidiSystem は、その要求を受けて、javax.sound.midi.spi クラスの具象サブクラスを使ってこれらの要求を処理します。

MIDI ファイル書き込みサービスの提供

標準 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 にディスパッチします。

MIDI ファイル読み込みサービスの提供

MidiFileReader 抽象クラスは javax.sampled.spi.AudioFileReader クラスに類似しています。どちらのクラスも 2 つのオーバーロードメソッドで構成され、それぞれが FileURLInputStream のいずれか 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 を発行します。

特定の MIDI デバイスの提供

MidiDeviceProvider は、特定タイプ (1 つまたは複数) の MIDI デバイスを提供するファクトリと考えることができます。このクラスは、MIDI デバイスのインスタンスを返すメソッドと、そのプロバイダが提供できるデバイスの種類を確認するためのクエリーメソッドで構成されます。

ほかの javax.sound.midi.spi サービスと同様に、アプリケーション開発者は MidiSystem のメソッド、この場合は MidiSystem.getMidiDeviceMidiSystem.getMidiDeviceInfo への呼び出しを介して MidiDeviceProvider サービスに間接的にアクセスします。MidiDeviceProvider をサブクラス化することの目的は新しい種類のデバイスを提供することなので、javax.sound.sampled.spi パッケージの MixerProvider の場合と同様に、サービスの開発者は返されるデバイスに関するクラスを作成する必要があります。その場合、返されたデバイスのクラスは javax.sound.sampled.Mixer インタフェースを実装します。ここでは javax.sound.midi.MidiDevice インタフェースを実装します。SynthesizerSequencer のような 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 にロードできる一連の Instruments です。Instrument (インストゥルメント) は、特定の種類のサウンドを作るサウンド合成アルゴリズムの実装です。この中には付随する名前と情報の文字列も含まれます。SoundBank は MIDI 仕様のバンクにほぼ対応していますが、拡張性があり、アドレス可能な集合です。MIDI バンクの集合と考える方が適切です。SoundBankSynthesizer を理解するための情報は、第 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 の具象サブクラスは、プロバイダが定義した特定の SoundBankInstrumentSynthesizer の実装と連携して、システムがファイルから特定の Synthesizer クラスに SoundBank をロードできるようにします。Synthesizer ごとに合成技術は大きく異なります。このため、Synthesizer の合成処理にコントロールまたは指定データを提供する Instrument または SoundBank に保存されるデータの形式は多岐にわたります。合成技術によっては、必要なパラメータデータが数バイトだけの場合や、膨大なサウンドサンプルをベースにしている場合があります。SoundBank 内にどのようなリソースが存在するのかは、それらのロード先の Synthesizer の性質に依存します。そのため、SoundbankReader サブクラスの getSoundbank メソッドの実装には、特定の種類の SoundBank に関する情報へのアクセス手段があります。さらに、SoundbankReader の特定のサブクラスは、SoundBank データを保存するための特定のファイル形式を理解します。このファイル形式はベンダー固有で独自形式の場合があります。

SoundBank は単なるインタフェースです。SoundBank オブジェクトの内容に関する制約はほとんどありません。このインタフェースを実装するためにオブジェクトがサポートする必要のあるメソッド (getResourcesgetInstrumentsgetVendorgetName など) では、オブジェクトに含まれるデータ要件は緩いものです。たとえば、getResourcesgetInstruments は空白の配列を返すことができます。サブクラス化された SoundBank オブジェクトの実際の内容、特にインストゥルメントとインストゥルメント以外のリソースは、サービスプロバイダによって決められます。そのため、サウンドバンクファイルを解析する機構は、その種類のサウンドバンクファイルの仕様に完全に依存します。

サウンドバンクファイルは Java Sound API の外部に、通常はその種類のサウンドバンクをロードできるシンセサイザのベンダーによって作成されます。サウンドバンクファイルを作成するためのエンドユーザツールをベンダーが提供する場合もあります。