< 目次

第8章: MIDIパッケージの概要

第1章「Java Sound APIの紹介」では、Java Sound APIのMIDI機能の概要を説明しました。この章は、読者が第1章を読み終えていることを前提としています。ここでは、javax.sound.midiパッケージを介してアクセスするJava Sound APIのMIDIアーキテクチャについてより詳しく説明します。復習または導入として、MIDI自体の基本機能について説明してから、本題のJava Sound APIのMIDI機能について説明します。その後、Java Sound APIがMIDIを処理する方法について説明し、続く章で扱うプログラミング・タスクに備えます。この章では、MIDI APIについて、主にデータとデバイスの2つの面から考察します。

MIDIについて: ワイヤーとファイル

MIDI (Musical Instrument Digital Interface)は、電子キーボードなどの電子楽器とパーソナル・コンピュータとの通信プロトコルを定義する標準規格です。MIDIデータは、実演中は、特殊ケーブルを介して送信されます。また、標準形式のファイルに格納して、後で再生や編集を行うこともできます。

ここでは、Java Sound APIから離れて、MIDIの基本事項について復習します。MIDIについての知識がある読者は復習として、MIDIの知識がない読者は導入として活用し、後述するJava Sound APIのMIDIパッケージの説明に備えてください。MIDIを十分理解している読者は、このセクションを読まずに次のセクションに進んでも構いません。本格的なMIDIアプリケーションを作成する場合、MIDIに精通していないプログラマは、このドキュメントに記載されている説明よりもさらに詳細なMIDIに関する説明が必要になります。その場合は、http://www.midi.orgからハード・コピーでのみ入手可能な『Complete MIDI 1.0 Detailed Specification』を参照してください(Webから入手できるのは簡約版のみ)。

MIDIは、ハードウェア仕様であり、かつソフトウェア仕様でもあります。MIDIの設計を理解するには、その歴史を理解するのが早道です。MIDIは、本来、シンセサイザなどの電子キーボード楽器間での音楽的イベント(キーの押下など)の引渡しを目的として設計されました。第1章で説明したとおり、MIDIデータは、主にミュージシャンのジェスチャを伝達する制御イベントで構成されています。MIDIデータには、イベントから生じるオーディオは含まれません。シーケンサと呼ばれるハードウェア機器にはシンセサイザを制御する楽譜のシーケンスが記憶されており、楽器演奏の記録および再生を可能にします。その後、ハードウェア・インタフェースの発展により、MIDI機器をコンピュータのシリアル・ポートに接続して、シーケンサをソフトウェア上に実装することが可能になりました。さらに最近では、コンピュータのサウンド・カードがMIDI I/Oと楽器音の合成のためのハードウェアを組み込んでいます。現在では、多くのMIDIユーザーは、外部のMIDI機器に接続せずにサウンド・カードだけで、MIDIを楽しんでいます。CPUの速度も向上したため、シンセサイザをソフトウェアとして実装することも可能になりました。サウンド・カードは、通常オーディオI/O用にのみ必要ですが、アプリケーションによっては、外部MIDIデバイスとの通信に必要な場合もあります。

MIDI仕様の中でハードウェアに関する部分は少なく、ここにはMIDIケーブルのピン配列およびケーブルを差し込むジャックについて規定されています。ハードウェアに関する部分は、プログラミングの際に考慮に入れる必要はありません。シーケンサやシンセサイザなどの本来必須ハードウェアだった機器類が、現在ではソフトウェアで実装されているため、MIDIハードウェア・デバイスに関して大半のプログラマが理解する必要があるとすれば、MIDIにおけるメタファを理解するためだけです。ただし、いくつかの重要な音楽用アプリケーションでは、外部MIDIハードウェア・デバイスが依然として重要な役割を果たしているため、Java Sound APIはMIDIデータの入力および出力をサポートしています。

MIDI仕様のソフトウェアに関する部分には、幅広い内容が含まれています。ソフトウェアに関する部分では、MIDIデータの構造、およびシンセサイザなどのデバイスがMIDIデータに応答する方法が記述されています。MIDIデータでは、ストリーミングまたはシーケンシングが可能であることを理解することが重要です。この双対関係は、『Complete MIDI 1.0 Detailed Specification』の次の2つの部分を反映しています。

これから、MIDI仕様の2つの部分の目的を考察することにより、ストリーミングとシーケンシングの意味を学びます。

MIDIワイヤー・プロトコル内のストリーミング・データ

MIDI仕様の2つの部分のうち、最初の部分には、非公式のいわゆる「MIDIワイヤー・プロトコル」について記述されています。MIDIワイヤー・プロトコルは、当初から存在するMIDIプロトコルで、MIDIデータがMIDIケーブル(ワイヤー)経由で送信されているという前提に基づいています。ケーブルは、あるMIDIデバイスから別のデバイスにデジタル・データを送信します。各MIDIデバイスは、楽器またはそれに類似した機器の場合もあれば、MIDI対応のサウンド・カードやMIDIへの接続用シリアル・ポート・インタフェースを備えた汎用的なコンピュータの場合もあります。

MIDIデータは、MIDIワイヤー・プロトコルの定義に従ってメッセージに編成されます。種類の異なるメッセージは、ステータス・バイトと呼ばれるメッセージの最初のバイトで識別されます。(最上位ビットが1に設定されるバイトはステータス・バイトのみ。)メッセージ内でステータス・バイトに続くバイトは、データ・バイトと呼ばれます。チャネル・メッセージと呼ばれるMIDIメッセージが保持するステータス・バイトでは、そのうちの4ビットでチャネル・メッセージの種類を指定し、残りの4ビットでチャネル番号を指定します。このため、16のMIDIチャネルが存在することになります。これらの仮想チャネルのすべてまたは1つでチャネル・メッセージに応答するように、MIDIメッセージを受信するデバイスを設定できます。MIDIチャネルをオーディオ・チャネルと混同しないようにしてください。各MIDIチャネルは、異なる楽器音の送信に使用されます。たとえば、一般的なチャネル・メッセージであるノート・オンとノート・オフを考えてみましょう。これらはそれぞれ、音を鳴らす動作を開始および停止します。これらの2つのメッセージは、それぞれ2つのデータ・バイトを保持します。最初のデータ・バイトは音符のピッチを、2番目のデータ・バイトは「ベロシティ」(キーボード楽器の場合、キーを押すまたは離す速さ)を指定します。

MIDIワイヤー・プロトコルは、MIDIデータ用のストリーミング・モデルを定義します。このプロトコルの主な機能は、MIDIデータのバイトをリアルタイムに渡すこと、つまりストリーム配信を行うことです。データ自体にはタイミング情報は含まれません。各イベントの着信時間は正確であるとみなされて、着信時に処理されます。このモデルは、ライブ演奏により生成される音符の場合には問題ありませんが、後で再生するためにそれらの音符を記憶したり、リアルタイム以外で曲を構成したりする場合には不十分です。本来、MIDIは、多くのミュージシャンがコンピュータを使用するようになる前の時代に、楽器演奏用(キーボード奏者が複数のシンセサイザを制御する場合など)として設計されたことを考えると、この制約の理由を理解できます。(この仕様の最初のバージョンは、1984年に発表されました。)

標準MIDIファイルのシーケンス・データ

MIDI仕様内の標準MIDIファイルを扱った部分に、MIDIワイヤー・プロトコルのタイミング制限に関する記述があります。標準MIDIファイルは、MIDI イベントを含むデジタル・ファイルです。イベントは、MIDIワイヤー・プロトコルで定義された単なるMIDIメッセージに、イベントのタイミングを指定する情報が追加されたものです。(ただし、次のセクションで説明するように、MIDIワイヤー・プロトコル・メッセージに対応しないイベントも存在します。)追加のタイミング情報は、メッセージに記述された操作をいつ実行するかを示すバイト群です。つまり、標準MIDIファイルは、再生する音だけではなく、それぞれを正確にいつ再生するかを指定します。このため、楽譜にいくらか似ています。

標準MIDIファイル内の情報は、シーケンスと呼ばれます。標準MIDIファイルには、1つ以上のトラックが含まれます。ライブ演奏の場合、各トラックには、1つの楽器で演奏される音が含まれます。シーケンサは、シーケンスを読み取り、そこに含まれるMIDIメッセージを適切なタイミングで配信できるソフトウェアまたはハードウェア・デバイスです。シーケンサはオーケストラの指揮者に似た働きをします。つまり、すべての音符に関する情報(タイミングも含む)を保持し、音符をいつ演奏すべきかを他のエンティティに通知します。

Java Sound APIでのMIDIデータ表現

ここまでで、MIDI仕様がストリームおよびシーケンス対象の音楽データを処理する方法を概観しました。これから、Java Sound APIが音楽データを表現する方法について考察します。

MIDIメッセージ

MidiMessageは、「raw」MIDIメッセージを表現する抽象クラスです。通常、「raw」MIDIメッセージはMIDIワイヤー・プロトコルにより定義されたメッセージです。MIDIメッセージを標準MIDIファイル仕様により定義されたイベントとすることもできますが、その場合イベントのタイミング情報は含まれません。raw MIDIメッセージには、3つのカテゴリが存在します。各カテゴリは、次の3つのMidiMessageサブクラスによりJava Sound APIで表現されます。

MIDIイベント

すでに説明したように、標準MIDIファイルには、「raw」MIDIメッセージとタイミング情報のラッパーとして機能するイベントが含まれます。Java Sound APIのMidiEventクラスのインスタンスは、標準MIDIファイルに格納予定のイベントなどを表します。

MidiEventのAPIには、イベントのタイミング値を設定および取得するメソッドが含まれます。MidiMessageのサブクラスのインスタンスである、埋込みraw MIDIメッセージを取得するメソッドも存在します。このメソッドについては、後述します。(埋込みraw MIDIメッセージは、MidiEventの構成時にのみ設定可能です。)

シーケンスとトラック

すでに説明したように、標準MIDIファイルは、トラック内に配置するイベントを格納します。通常、ファイルは1つの音楽構成を表し、各トラックは1つの楽器の演奏パートを表します。楽器演奏者の演奏する各音は、音の開始を示すノート・オンと音の終了を示すノート・オフの少なくとも2つのイベントによって表現されます。トラックには、上述のメタイベントなどの、音に関連しないイベントが含まれることもあります。

Java Sound APIは、次の3つの階層でMIDIデータを編成します。

TrackMidiEventsのコレクション、SequenceTracksのコレクションです。この階層は、標準MIDIファイル仕様のファイル、トラック、およびイベントを反映しています。(注: これは、包含関係および所有権の階層であり、継承を示すクラス階層ではありません。これら3つのクラスはそれぞれ、java.lang.Objectを直接継承します。)

Sequencesは、MIDIファイルから読み込むことも、ゼロから作成することも可能で、TracksSequenceを追加(または削除)することによって編集できます。同様にMidiEventsは、シーケンス内のトラックに追加することも、トラックから削除することもできます。

Java Sound APIでのMIDIデバイス表現

前のセクションでは、Java Sound APIでMIDIメッセージを表現する方法を説明しました。ただし、MIDIメッセージは、孤立して存在しているわけではありません。一般に、あるデバイスから別のデバイスに送信されます。Java Sound APIを使用するプログラムは、MIDIメッセージを新たに作成できますが、シーケンサなどのソフトウェア・デバイスによりメッセージを作成したり、MIDI入力ポート経由でコンピュータの外部からメッセージを受信する方がより一般的です。このようなデバイスは、通常、シンセサイザやMIDI出力ポートなどの別のデバイスにメッセージを送信します。

MidiDeviceインタフェース

外部MIDIハードウェア・デバイスの中で多くのデバイスが、MIDIメッセージをほかのデバイスに送信したり、ほかのデバイスからメッセージを受信したりできます。同様に、Java Sound APIでは、MidiDeviceインタフェースを実装するソフトウェア・オブジェクトがメッセージの送信と受信を実行できます。このようなオブジェクトは、純粋にソフトウェアで実装することも、サウンド・カードのMIDI機能などのハードウェアへのインタフェースとすることも可能です。基本的なMidiDeviceインタフェースは、一般にMIDI入力ポートまたは出力ポートが必要とする機能のすべてを提供します。ただし、シンセサイザおよびシーケンサはそれぞれ、MidiDeviceのサブインタフェースであるMidiDevice: SynthesizerSequencerをそれぞれ実装します。

MidiDeviceインタフェースには、デバイスをオープンおよびクローズするためのAPIが含まれます。また、デバイスのテキスト表現(名前、ベンダー、バージョンなどを含む)を提供するMidiDevice.Infoと呼ばれる内部クラスも含まれます。すでにこのプログラマーズ・ガイドのサンプリング・オーディオに関する記述に目を通している読者は、このAPIに見覚えがあると感じるはずです。それは、このAPIの設計が、javax.sampled.Mixerインタフェースの設計と類似しているためです。このインタフェースは、オーディオ・デバイスを表し、類似した内部クラスMixer.Infoを保持します。

トランスミッタとレシーバ

大半のMIDIデバイスは、MidiMessagesの送信、受信、またはその両方を実行できます。デバイスは、所有する1つまたは複数のトランスミッタ・オブジェクトを介してデータを送信します。同様の方法で、デバイスは1つまたは複数のレシーバ・オブジェクトを介してデータを受信します。トランスミッタ・オブジェクトはTransmitterインタフェースを実装し、レシーバはReceiverインタフェースを実装します。

各トランスミッタは一度に1つのレシーバにのみ接続できます。その逆も同様です。MIDIメッセージを複数のデバイスに同時に送信するデバイスは、それぞれが異なるデバイスのレシーバに接続された複数のトランスミッタを保持することにより、これを可能にしています。同様に、一度に複数のソースからMIDIメッセージを受信可能なデバイスは、複数のレシーバを保持しています。

シーケンサ

シーケンサは、MIDIイベントのシーケンスの取込みおよび再生を行うデバイスです。シーケンサは、トランスミッタを保持します。これは、一般にシーケンサは、シーケンスに格納されたMIDIメッセージをシンセサイザやMIDI出力ポートなどの別のデバイスに送信するためです。シーケンサは、レシーバも保持します。これは、MIDIメッセージの取込みおよびシーケンスへの格納を実行するためです。そのスーパーインタフェースMidiDeviceに対して、Sequencerは基本的なMIDIシーケンス操作用のメソッドを追加します。シーケンサは、MIDIファイルからシーケンスをロードし、シーケンスのテンポの問合せと設定を行い、他のデバイスをそのシーケンスに同期させることができます。アプリケーション・プログラムは、オブジェクトを登録しておいて、シーケンサが特定の種類のイベントを処理する際に通知を受けるようにすることができます。

シンセサイザ

Synthesizerは、サウンドを生成するデバイスです。これは、オーディオ・データを生成するjavax.sound.midiパッケージ内の唯一のオブジェクトです。シンセサイザ・デバイスは、MIDIチャネル・オブジェクトのセットを制御します。MIDI仕様では16のMIDIチャネルが要求されているため、このセットは通常、16個のMIDIチャネル・オブジェクトで構成されます。これらのMIDIチャネル・オブジェクトは、MidiChannelインタフェースを実装するクラスのインスタンスであり、そのメソッドは、MIDI仕様の「チャネル音声メッセージ」および「チャネル・モード・メッセージ」を表します。

アプリケーション・プログラムは、シンセサイザのMIDIチャネル・オブジェクトのメソッドを直接呼び出すことにより、サウンドを生成できます。ただし、シンセサイザが、1つまたは複数のレシーバに送信されたメッセージへの応答としてサウンドを生成する場合の方が一般的です。たとえば、シーケンサまたはMIDI入力ポートからこれらのメッセージを送信できます。シンセサイザは、レシーバが取得した各メッセージを解析し、通常はイベント内に指定されたMIDIチャネル番号に応じて、対応するコマンド(noteOncontrolChangeなど)をMidiChannelオブジェクトの1つにディスパッチします。

MidiChannelは、これらのメッセージ内の音情報を使って音楽を合成します。たとえば、noteOnメッセージは、ノートのピッチおよび「ベロシティ」(ボリューム)を指定します。ただし、この音情報は十分ではありません。シンセサイザには、各音に対してオーディオ信号を作成する方法についての正確な指示も必要です。これらの指示は、Instrumentによって表されます。通常、各Instrumentは、様々な実際の楽器またはサウンド・エフェクトをエミュレートします。Instrumentは、シンセサイザであらかじめ設定されているようにすることも、サウンドバンク・ファイルからロードするようにすることも可能です。シンセサイザでは、Instrumentは、バンク番号(行)およびプログラム番号(列)で配置されます。

この章では、MIDIデータを理解する上で役立つ基礎的な情報を提供し、またJava Sound API内のMIDIに関連した重要なインタフェースおよびクラスを紹介しました。次章からは、これらのオブジェクトをアプリケーション・プログラムからアクセスして使用する方法について説明します。

 


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