< 目次

第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サービスの使用方法については、このプログラマーズ・ガイドの第II部「MIDI」を参照してください。この章では、インストール済みのMIDIサービスにアクセスするためにアプリケーション・プログラムが呼び出すJava 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にロードできる一連のInstrumentです。Instrument (インストゥルメント)は、特定の種類のサウンドを作るサウンド合成アルゴリズムの実装です。この中には付随する名前と情報の文字列も含まれます。SoundBankはMIDI仕様のバンクにほぼ対応していますが、拡張性があり、アドレス可能な集合です。MIDIバンクの集合と考える方が適切です。(SoundBanksSynthesizersを理解するための情報は、第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の外部に、通常はその種類のサウンドバンクをロードできるシンセサイザのベンダーによって作成されます。サウンドバンク・ファイルを作成するためのエンドユーザー・ツールをベンダーが提供する場合もあります。

 


Copyright © 1993, 2020, Oracle and/or its affiliates. All rights reserved.