Java Sound APIのMIDIパッケージを利用するプログラムのほとんどが、サウンドを合成するために使用されます。MIDIファイル、イベント、シーケンスおよびシーケンサの機能の詳細についてはほかの章で説明しましたが、それぞれの最終的な目標は音楽データをシンセサイザに送ってオーディオに変換することです。例外として、MIDIデータをミュージシャンが読める楽譜に変換するプログラムや、ミキシング・コンソールなどの外部のMIDI制御デバイスにメッセージを送信するプログラムがあります。
したがって、Synthesizer
インタフェースがMIDIパッケージの基本となります。この章では、シンセサイザを操作してサウンドを再生する方法について説明します。多くのプログラムではシーケンサを使ってMIDIファイル・データをシンセサイザに送るだけなので、多くのSynthesizer
メソッドを直接呼び出す必要はありません。しかし、この章の最後の方で説明するように、シーケンサやMidiMessage
オブジェクトを使わずに直接シンセサイザを制御する方法があります。
MIDI合成アーキテクチャは、MIDIに不慣れな場合には複雑に感じられるものですが、要約すると、このAPIには、3つのインタフェースがあります。
また、次の4つのクラスがあります。 このすべてのAPIを理解するために、次のセクションでMIDI合成についての基本事項をいくつか挙げて、MIDI合成とJava Sound APIとの関連性について説明します。第8章「MIDIパッケージの概要」の「Java Sound APIでのMIDIデバイス表現」の、「シンセサイザ」もあわせて参照してください。それ以降のセクションでは、APIについてさらに詳しく説明します。シンセサイザは、どのようにして音を生成するのでしょうか。実装によって異なりますが、シンセサイザには、1つまたは複数のサウンド合成技術が使用されています。たとえば、多くのシンセサイザではWavetable合成を使用します。Wavetableシンセサイザはメモリーに保存されているオーディオ片を読み出して、さまざまなサンプリング・レートで再生し、ループさせて、さまざまなピッチとデュレーションの音符を作り出します。たとえば、ノートC#4 (MIDIノート・ナンバー61)を奏でるサックスの音を合成するには、中央C (MIDIノート・ナンバー60)のサックス演奏の録音から非常に短い断片を利用して、この断片を、録音時よりもわずかに速いサンプリング・レートで繰り返すことにより、やや高いピッチのロング・ノートが作成されます。このほかに、保存されたオーディオを利用しないで、別のアルゴリズムを使ってオーディオをゼロから生成する周波数変調(FM)、加算合成、物理モデリング方式を使うシンセサイザなどがあります。
すべての合成技術に共通することは、多くの種類のサウンドを合成できることです。異なるアルゴリズムを使ったり、同じアルゴリズム内でパラメータ設定を変えたりすることにより、さまざまなサウンド合成が可能になります。インストゥルメントは、特定の種類のサウンドを合成する場合に使用する仕様を表します。そのサウンドは、伝統的な楽器(ピアノやバイオリンなど)をエミュレートすることもあれば、その他の種類の音源(電話やヘリコプタの音など)、または実世界に存在しない音をエミュレートする場合もあります。General MIDIと呼ばれる仕様には128のインストゥルメントが標準として定義されていますが、ほとんどのシンセサイザではこれ以外のインストゥルメントの音も合成できます。多くのシンセサイザには、常時利用できる多数のインストゥルメントが組み込まれており、一部のシンセサイザには追加のインストゥルメントをロードするためのメカニズムもあります。
インストゥルメントはベンダー固有である場合があります。つまり、1つのシンセサイザまたは同じベンダー製のいくつかのモデルのシンセサイザにのみ適用可能であるということです。この非互換性は、2つの異なるシンセサイザで別のサウンド合成技術を使用する場合、または基本的な技術は同じでも内部アルゴリズムや内部パラメータが異なる場合に発生します。合成技術の細部は独自性が強いことが多いので、非互換性の問題はよく発生します。Java Sound APIには、あるシンセサイザがあるインストゥルメントをサポートしているかどうかを調べる方法が用意されています。
インストゥルメントは、通常、プリセットされているものとみなすことができるため、そのインストゥルメントの音を作成するための合成技術の細部まで知る必要はありません。しかし、インストゥルメントの音をいろいろと変化させることはできます。ノート・オン・メッセージは、各ノートのピッチとボリュームを指定します。また、コントローラ・メッセージやシステム専用メッセージなど、ほかのMIDIコマンドを使ってサウンドを変更することもできます。
多くのシンセサイザは、同時に複数の異なるインストゥルメントのノートを再生できるという意味で、マルチティンバー(multimbral)またはポリティンバー(polytimbral)と呼ばれます。音色(timbre)とは、それにより聞き手が楽器の音を他の楽器の音と区別できる、特徴的な音の性質のことです。マルチティンバー・シンセサイザは、一度に1つのインストゥルメントだけではなく、実際の楽器のアンサンブル全体をエミュレートできます。MIDIシンセサイザは通常、複数の異なるMIDIチャネルを利用することによりこの機能を実装しています。MIDIチャネルは、MIDI仕様によりデータの送信が許可されているチャネルです。この場合、シンセサイザは実際は複数の音源ユニットの集合であり、各ユニットは異なるインストゥルメントをエミュレートし、別々のMIDIチャネルから受け取ったメッセージに個別に応答します。MIDI仕様で提供されるチャネルの数は16なので、一般的なMIDIシンセサイザでは16種類までの異なるインストゥルメントを同時に演奏できます。シンセサイザはMIDIコマンド群のストリームを受け取ります。コマンド群の多くはチャネル・コマンドです。(チャネル・コマンドは、特定のMIDIチャネルを対象とするもの。詳細は、MIDI仕様を参照。)シンセサイザがマルチティンバーの場合は、それぞれのチャネル・コマンドを、コマンドに示されたチャネル番号に従って適正な音源ユニットに配送します。
Java Sound APIでは、これらの音源ユニットは、MidiChannel
インタフェースを実装するクラスのインスタンスです。synthesizer
オブジェクトには、少なくとも1つのMidiChannel
オブジェクトがあります。シンセサイザがマルチティンバーの場合は、このオブジェクトは複数(通常は16)です。それぞれのMidiChannel
は、別々の音源ユニットを表します。
シンセサイザのMidiChannel
オブジェクトには多少独自性があるため、すべてのチャネルに異なるインストゥルメントを割り当てる必要はありません。たとえば、16台のピアノのアンサンブルのように、16チャネルのすべてにピアノの音色を再生させても構いません。たとえば、チャネル1、5、8がギター、チャネル2と3がパーカッション、チャネル12がベースの音色を再生するというように、グループ分けすることも可能です。特定のMIDIチャネルで再生されているインストゥルメントを動的に変更することもできます。これは、プログラム・チェンジ(program change)と呼ばれます。
ほとんどのシンセサイザでは、指定時間内にアクティブにできるインストゥルメントの数は16以下ですが、一般には、もっと多く提供されているインストゥルメントの中から選択して、必要に応じて特定のチャネルに割り当てることができます。
インストゥルメントはシンセサイザの中で階層的に編成されています。第8章MIDIパッケージの概要で説明したように、インストゥルメントはバンク番号とプログラム番号により配列されています。バンクとプログラムは、2次元のインストゥルメント表の行と列と考えることができます。バンクはプログラムの集合です。MIDI仕様では、1つのバンクに128までのプログラムを許可しており、128までのバンクを許可しています。ただし、シンセサイザによっては、1つだけまたは少数のバンクしかサポートしない場合や、1つのバンクでサポートされるプログラムの数が128より少ない場合もあります。
Java Sound APIでは、その階層構造にさらに上位のレベルがあります。サウンドバンクです。サウンドバンクには128までのバンクを収容でき、それぞれに128までのインストゥルメントが含まれます。サウンドバンク全体をメモリーにロードできるシンセサイザもあります。
現在のサウンドバンクからインストゥルメントを1つ選択するには、バンク番号とプログラム番号を指定します。MIDI仕様では、これを2つのMIDIコマンド、bank-selectとprogram-changeを使って行います。Java Sound APIでは、バンク番号とプログラム番号の組み合わせは、Patch
オブジェクトにカプセル化されています。MIDIチャネルの現在のインストゥルメントを変更するには、新しいパッチを指定します。パッチは、現在のサウンドバンク内にあるインストゥルメントの2次元インデックスと考えることができます。
サウンドバンクもまた、数値によってインデックスが付けられていると思われるかもしれませんが、MIDI仕様は、これに対応していません。Java Sound APIでは、Soundbank
オブジェクトはサウンドバンク・ファイルを読み込むことによって取得します。シンセサイザがサウンドバンクをサポートしている場合は、インストゥルメントをシンセサイザに必要に応じて個別にロードすることも、一度にすべてロードすることもできます。多くのシンセサイザにはデフォルトの組込みサウンドバンクがあります。このサウンドバンクに含まれるインストゥルメントはシンセサイザからいつでも利用できます。
シンセサイザが同時に再生できる音色の数とノートの数を区別することは重要です。音色については、すでに「チャネル」で説明しました。同時に複数のノートを再生する能力をポリフォニと呼びます。マルチティンバーでないシンセサイザでも、一般には複数のノート(すべてが同じ音色で、ピッチが異なる)を同時に演奏することができます。たとえば、Gメジャー3やBマイナー7のようなコードを演奏するには、ポリフォニが必要です。リアルタイムでサウンドを生成するシンセサイザには、同時に合成できるノートの数に制限があります。Java Sound APIでは、シンセサイザのこの制限はgetMaxPolyphony
メソッドにより報告されます。
ボイス(voice)とは、単一のノートの連続したもので、たとえば、1人で歌うことのできるメロディなどです。ポリフォニは、合唱団が歌う各パートのような複数のボイスで構成されます。たとえば32ボイスのシンセサイザは同時に32のノートを演奏できます。ただし、MIDIの文献の一部では、「ボイス」という用語は「インストゥルメント(instrument)」や「音色(timbre)」と似た、これと別の意味で使用されています。
着信のMIDIノートを特定のボイスに割り当てる処理は、ボイス・アロケーションと呼ばれます。シンセサイザはボイスのリストを保持しており、どのボイスがアクティブ(現在出ている音がある)であるかを追跡します。ノートの音が鳴り止むと、ボイスはアクティブでなくなります。つまり、シンセサイザが受け取る要求で、次のノートを受け入れることができます。MIDIコマンド群の着信ストリームが、シンセサイザが生成できるよりも多くのノートを同時に要求することはよくあります。シンセサイザのすべてのボイスがアクティブなときは、次のノート・オン要求をどのように処理すべきでしょうか。シンセサイザには様々な技法を実装できます。もっとも新しく要求されたノートを無視したり、もっとも古く開始されたノートなど、他のノートを中断することによって次のノートを演奏したりすることができます。
MIDI仕様では要求されていませんが、シンセサイザは個々のボイスの内容を公開することができます。Java Sound APIには、この目的のためにVoiceStatus
クラスがあります。
VoiceStatus
は、ボイスの現在のアクティブ/非アクティブ・ステータス、MIDIチャネル、バンク番号とプログラム番号、MIDIノート番号、MIDIボリュームを報告します。
次に、この背景知識をもとにJava Sound APIの音の合成の仕様について考察します。
多くの場合、プログラムはほとんどの合成APIを明示的に呼び出すことなく、Synthesizer
オブジェクトを利用することができます。たとえば、標準のMIDIファイルを再生する場合を考えます。ファイルをSequence
オブジェクトにロードし、そのオブジェクトを再生するためにそのシーケンサからデータをデフォルトのシンセサイザに送信させます。シーケンス内のデータは随意にシンセサイザを制御し、正しい時期に正しいノートを再生します。
しかし、この簡単なシナリオでは不十分な場合もあります。シーケンスには正しい音楽が含まれているのにインストゥルメントが正しく再生されないことがあります。この状況は、MIDIファイルの作成者の意図したインストゥルメントと現在シンセサイザにロードされているインストゥルメントが異なる場合に起こります。
MIDI 1.0仕様は、bank-selectコマンドとprogram-changeコマンドについて規定しています。これらのコマンドは、各MIDIチャネルで現在どのインストゥルメントを再生中であるかに関連します。ただし、この仕様では、各パッチ位置(バンク番号とプログラム番号)にどのインストゥルメントを置くべきかについては定義していません。最新のGeneral MIDI仕様では、特定のインストゥルメントのサウンドに対応する128のプログラムを含むバンクを定義することにより、この問題に対処しています。General MIDIシンセサイザは、指定されたものと同種の128のインストゥルメントを使用します。仕様の内容が異なるGeneral MIDIのシンセサイザの再生するサウンドは、同じインストゥルメントを再生しているつもりの場合でも、かなり異なることがあります。ただし、どのGeneral MIDIシンセサイザでファイルを再生するかにかかわらず、MIDIファイルの大部分について、まったく同じでなくても、類似した音が再生されなければなりません。
それにもかかわらず、すべてのMIDIファイル作成者が、General MIDIで定義された128の音色の制限内でファイルを作成しようとするわけではありません。ここでは、シンセサイザがデフォルトで持っているインストゥルメントのセットを変更する方法を説明します。シンセサイザにデフォルトがないということは、シンセサイザにアクセスしたときにインストゥルメントがロードされていないことを示すので、必ず最初にこのAPIを使います。
シンセサイザに現在、目的のインストゥルメントがロードされているかどうかを確認するには、次のSynthesizer
メソッドを呼び出します。
Instrument[] getLoadedInstruments()次に、返された配列を繰返し調べ、現在ロードされているインストゥルメントを確認します。多くのアプリケーションでは、そのインストゥルメントの名前をユーザー・インタフェースに表示して(
Instrument
のgetName
メソッドを使用)、そのインストゥルメントを使用するかほかのインストゥルメントをロードするかどうかをユーザーに確認します。Instrument
APIには、インストゥルメントが属しているサウンドバンクを報告するメソッドがあります。サウンドバンクの名前は、プログラムまたはユーザーがインストゥルメントの種類を確認するのに役に立ちます。
Soundbank getDefaultSoundbank()デフォルトのサウンドバンクが得られます。
Soundbank
APIには、サウンドバンクの名前、ベンダー、バージョン番号を検索するためのメソッドがあり、これによりプログラムまたはユーザーはバンクを識別することができます。ただし、そのシンセサイザをはじめて使用するときは、デフォルトのサウンドバンクからシンセサイザにインストゥルメントがロードされていることを前提とすることはできません。たとえば、あるシンセサイザには多種多様な組込みインストゥルメントが用意されていても、メモリーの制約により、自動的にはロードされない可能性もあります。
ユーザーの決定により、現在ロードされているものとは別のインストゥルメントをロードする場合があります(プログラムが決定する場合もあります)。次のメソッドを使って、シンセサイザに組み込まれている(サウンドバンク・ファイルからロードする必要のない)インストゥルメントを確認します。
Instrument[] getAvailableInstruments()これらのインストゥルメントをロードするには、次のメソッドを呼び出します。
boolean loadInstrument(Instrument instrument)このインストゥルメントがロードされるシンセサイザ内の位置は、そのインストゥルメントの
Patch
オブジェクト(Instrument
のgetPatch
メソッドにより検索)で指定されます。
他のサウンドバンクからインストゥルメントをロードするには、まずSynthesizerの
isSupportedSoundbank
メソッドを呼び出して、そのサウンドバンクがそのシンセサイザに対応するかどうかを確認します。対応しない場合は、システム内の他のシンセサイザについて処理を反復し、そのサウンドバンクをサポートするシンセサイザを探します。その後、次のどちらかのメソッドを呼び出して、サウンドバンクからインストゥルメントをロードします。
boolean loadAllInstruments(Soundbank soundbank) boolean loadInstruments(Soundbank soundbank, Patch[] patchList)名前が示すように、最初のメソッドは指定されたサウンドバンクからすべてのインストゥルメントをロードし、次のメソッドは指定されたインストゥルメントをサウンドバンクからロードします。また、
Soundbankの
getInstruments
メソッドを使ってすべてのインストゥルメントにアクセスしてから、loadInstrument
を使って1つずつインストゥルメントを選択してロードすることもできます。
ロードするインストゥルメントがすべて同じサウンドバンクに属する必要はありません。loadInstrument
またはloadInstruments
を使って、あるサウンドバンクから特定のインストゥルメントの組をロードし、別のサウンドバンクから別のインストゥルメントの組をロードし、以下同様に別のバンクからもロードすることができます。
各インストゥルメントには、そのインストゥルメントをロードすべきシンセサイザ内の位置を指定するPatch
オブジェクトがあります。位置は、バンク番号とプログラム番号によって定義されます。位置を変更するためにパッチのバンク番号またはプログラム番号を変更するAPIはありません。
ただし、あるインストゥルメントを、パッチで指定された位置以外にロードすることは可能で、それには次のSynthesizer
メソッドを使用します。
boolean remapInstrument(Instrument from, Instrument to)このメソッドは、シンセサイザから第1引数のインストゥルメントをアンロードし、第1引数のインストゥルメントにより占められていたパッチ位置に第2引数のインストゥルメントをロードします。
あるプログラム位置にインストゥルメントをロードすると、その位置に先にロードされていたインストゥルメントは自動的にアンロードされます。新しいインストゥルメントに自動的に置き換えないで、明示的にインストゥルメントをアンロードすることもできます。Synthesizer
には、3つのロード・メソッドに対応する3つのアンロード・メソッドがあります。シンセサイザがプログラム・チェンジ・メッセージを受け取ったときに、そのメッセージが現在インストゥルメントがロードされていないプログラム位置を選択していた場合は、そのメッセージが送信されたMIDIチャネルからはサウンドは再生されません。
一部のシンセサイザは、インストゥルメントに加えて、その他の情報もサウンドバンクに保存します。たとえば、Wavetableシンセサイザは、1つまたは複数のインストゥルメントからアクセス可能なオーディオ・サンプルを保存します。サンプルは複数のインストゥルメントにより共有される場合があるので、どのインストゥルメントからも独立してサウンドバンクに保存されます。Soundbank
インタフェースとInstrument
クラスのどちらにも、getSoundbankResources
メソッド・コールが提供されています。これにより、SoundbankResource
オブジェクトのリストが返されます。これらのオブジェクトの細部は、そのサウンドバンクが設計されているシンセサイザに固有に設定されます。Wavetable合成では、リソースが、1つの録音の断片から取得した一連のオーディオ・サンプルをカプセル化したオブジェクトである場合があります。別の合成技術を使用するシンセサイザでは、種類の違うオブジェクトはシンセサイザのSoundbankResources
配列に保存される場合があります。
Synthesizer
インタフェースには、シンセサイザの機能に関する情報を返すメソッドがあります。
public long getLatency() public int getMaxPolyphony()待ち時間(latency)は、MIDIメッセージがシンセサイザに配送される時間と、シンセサイザがそれに対応する結果を実際に作成する時間との間の最悪の場合の遅れを示します。たとえば、あるシンセサイザではノートオン・イベントを受信してからオーディオを生成するまでに数ミリ秒を要します。
getMaxPolyphony
メソッドは、そのシンセサイザが同時に再生できるノートの数を示します。これについては、この章の前のセクション「MIDI合成の理解」の「ボイス」で説明しました。また、すでに説明したように、シンセサイザはボイスの情報を提供することができます。これを行うには、次のメソッドを使用します。
public VoiceStatus[] getVoiceStatus()返された配列内の各
VoiceStatus
は、ボイスの現在のアクティブ/非アクティブ・ステータス、MIDIチャネル、バンク番号とプログラム番号、MIDIノート番号、MIDIボリュームを報告します。通常、配列の長さはgetMaxPolyphony
により返される値と同じです。シンセサイザが演奏中でない場合は、すべてのVoiceStatus
オブジェクトのアクティブ・ステータス・フィールドは、false
に設定されています。
シンセサイザの現在のステータスについて、MidiChannel
オブジェクトを検索してその状態を問い合わせて、ステータスについての付加情報を入手することができます。この機能については、次のセクションでさらに詳しく説明します。
シンセサイザのMidiChannel
オブジェクトに直接アクセスした方が便利な場合や、直接アクセスが必要になる場合があります。ここでは、そのような場合について説明します。
シーケンス(MIDIファイルから読み取ったものなど)を使うときは、シンセサイザにMIDIコマンドを送る必要はありません。単にそのシーケンスをシーケンサにロードし、シーケンサをシンセサイザに接続して実行させるだけです。イベントのスケジューリングはシーケンサが処理し、その結果は予測できる音楽演奏となります。これは、欲しい音楽が事前にわかっていて、音楽をファイルから読み込む場合に適しています。
ただし、演奏の進行中に音楽が生成される場合もあります。たとえば、ユーザー・インタフェースに音楽用のキーボードやギターのフレットボードが表示され、ユーザーがマウスをクリックして自由にノートを再生させる場合があります。また、アプリケーションが、音楽を演奏するためにではなく、ユーザーの動作に応答してサウンド・エフェクトを生成するためにシンセサイザを使うことがあります。これは、ゲームで一般的に使われる方法です。また、アプリケーションがファイルから読み込んだ音楽を演奏しているときに、ユーザーがユーザー・インタフェースから対話形式で動的にその音楽を変更できるようにする場合もあります。どの場合でも、MIDIメッセージは将来の決まった時点にスケジュールされるのではなく、ただちに配送される必要があるので、アプリケーションはシンセサイザに直接コマンドを送ります。
シーケンサを使わずにシンセサイザにMIDIメッセージを送る方法は、少なくとも2つあります。1つは、MidiMessage
を構築し、Receiver
のsendメソッドを使ってシンセサイザに渡す方法です。たとえば、中央C (MIDIノート・ナンバー60)をMIDIチャネル5 (one-based)にただちに生成するには、次のようにします。
ShortMessage myMsg = new ShortMessage(); // Play the note Middle C (60) moderately loud // (velocity = 93)on channel 4 (zero-based). myMsg.setMessage(ShortMessage.NOTE_ON, 4, 60, 93); Synthesizer synth = MidiSystem.getSynthesizer(); Receiver synthRcvr = synth.getReceiver(); synthRcvr.send(myMsg, -1); // -1 means no time stamp2つ目は、message-passing層(
MidiMessage
とReceiver
API)を両方ともバイパスし、シンセサイザのMidiChannel
オブジェクトと直接対話する方法です。まず、次のSynthesizer
メソッドを使ってシンセサイザのMidiChannel
オブジェクトを検索します。
public MidiChannel[] getChannels()次に、目的の
MidiChannel
メソッドを直接呼び出します。これは、対応するMidiMessages
をシンセサイザのReceiver
に送って、シンセサイザ自体のMidiChannels
を使って通信を処理させるよりも、より直接的な方法です。たとえば、前の例に対応するコードは次のようになります。
Synthesizer synth = MidiSystem.getSynthesizer(); MidiChannel chan[] = synth.getChannels(); // Check for null; maybe not all 16 channels exist. if (chan[4] != null) { chan[4].noteOn(60, 93); }
MidiChannel
インタフェースには、MIDI仕様で定められている「チャネル・ボイス」と「チャネル・モード」メッセージに1対1で対応するメソッドがあります。noteOnメソッドの使用方法については、前述の例で説明しました。これらの標準メソッドのほかに、Java Sound APIのMidiChannel
インタフェースには、対応するボイスまたはモードの"set"メソッドで設定された最新の値を検索する"get"メソッドが追加されています。
int getChannelPressure() int getController(int controller) boolean getMono() boolean getOmni() int getPitchBend() int getPolyPressure(int noteNumber) int getProgram()これらのメソッドはユーザーにチャネルの状態を表示したり、続いてチャネルに送る値を決める場合に利用できます。
MIDI仕様では要求されていませんが、Java Sound APIは、チャネルごとにソロとミュートの概念を追加します。これらの概念は、MIDIシーケンスのトラック上のミュートとソロに似ています。第11章「MIDIシーケンスの再生、記録、および編集」の「シーケンス内の個別のトラックのミュートまたはソロ機能」を参照してください。
ミュートがオンの場合は、このチャネルは無音となりますが、ほかのチャネルには影響しません。ソロがオンの場合は、このチャネルおよびソロがオンになっているほかのチャネルが演奏されますが(ミュートされている場合を除く)、ほかのチャネルは無音となります。ソロとミュートの両方がオンになっているチャネルは無音となります。MidiChannel
APIには、次の4つのメソッドがあります。
boolean getMute() boolean getSolo() void setMute(boolean muteState) void setSolo(boolean soloState)
インストールされているMIDIシンセサイザにより生成されるオーディオは、一般にサンプリング・オーディオ・システムを介して配送されます。プログラムがオーディオ再生のアクセス権を持っていない場合は、シンセサイザのサウンドを聞くことはできません。この場合、セキュリティ例外がスローされます。オーディオのアクセス権の詳細は、第3章「オーディオ・システム・リソースへのアクセス」の「オーディオ・リソースを使用するためのアクセス権」を参照してください。