注:5.0のリリースには、すべてのMIDIデバイスで機能する新しいリアルタイムの |
MIDIの世界では、シーケンサとは、タイムスタンプ付きのMIDIメッセージのシーケンスを正確に再生または記録できるハードウェアまたはソフトウェア・デバイスのことです。同様に、Java Sound APIでは、Sequencer
抽象インタフェースは、MidiEvent
オブジェクトのSequences
の再生および記録が可能なオブジェクトのプロパティを定義します。一般に、Sequencer
はこれらのMidiEvent
シーケンスを、標準MIDIファイルからロードするか、または標準MIDIファイルに保存します。シーケンスは、編集も可能です。この章では、Sequencer
オブジェクトの使用方法、およびタスクを実行する上で必要な関連クラスおよびインタフェースについて説明します。
Sequencer
は、テープ・レコーダに例えてみると、その本質を捉えることができます。シーケンサとテープ・レコーダは、多くの点で類似しています。テープ・レコーダがオーディオを再生するのに対し、シーケンサはMIDIデータを再生します。シーケンスとは、MIDI音楽データをマルチトラック、リニア、時間順で記録したものです。シーケンサはその音楽データに対して、様々な速度での再生、巻き戻し、特定区間の往復、ファイルへの記録または保存目的でのファイルへのコピーなどの処理を行います。
第10章「MIDIメッセージの送信および受信」では、デバイスが一般に、Receiver
オブジェクトとTransmitter
オブジェクトのいずれかまたは両方を保持することを説明しました。音楽を再生する場合、デバイスは通常Receiver
経由でMidiMessages
を受信します。Receiverは、通常Sequencer
に属するTransmitter
からMidiMessageを受信しています。このReceiver
を保持するデバイスとして、Synthesizer
があります。これはオーディオを直接生成するか、またはMIDIの出力ポートとして機能します。出力ポートとして機能する場合は、MIDIデータを物理ケーブル経由でなんらかの外部機器に送信します。同様に、音楽を録音する場合、一般に、タイムスタンプ付きの一連のMidiMessages
がSequencer
の所有するReceiver
に送信されます。Sequencerは、MidiMessagesをSequence
オブジェクト内に配置します。一般に、メッセージを送信するオブジェクトは、ハードウェア入力ポートに関連付けられたTransmitter
で、入力ポートは外部の楽器から取得したMIDIデータを中継します。ただし、メッセージの送信を行うデバイスが、他のSequencer
であるか、またはTransmitter
を所有する他のデバイスであることもあります。また、第10章で説明したように、Transmitter
をまったく使わないでプログラムからメッセージを送信することも可能です。
Sequencer
は、それ自体でReceivers
とTransmitters
の両方を保持しています。事実、記録時にはReceivers
経由でMidiMessages
を取得します。再生時には、Transmitters
を使って、記録(またはファイルからロード)したSequence
に格納されたMidiMessages
を送信します。
Java Sound API内でのSequencer
の役割を理解する1つの方法は、MidiMessages
の集合体/非集合体と捉えることです。独立した、一連の別個のMidiMessages
は、音楽イベントのタイミングのマークとなる独自のタイムスタンプとともにSequencer
に送信されます。MidiMessages
はMidiEvent
オブジェクトにカプセル化され、Sequencer.record
メソッドのアクションによってSequence
オブジェクトに収集されます。Sequence
はMidiEvents
の集合体を含むデータ構造で、通常は一連の音符(多くの場合は楽曲全体)を表しています。再生時に、Sequencer
は、Sequence
内のMidiEvent
オブジェクトからMidiMessages
を再度抽出し、1つまたは複数のデバイスに送信します。デバイスは、それらのサウンド化、保存、変更、または他のデバイスへの引渡しを行います。
シーケンサの中には、トランスミッタもレシーバも保持しないものもあります。たとえば、この種のシーケンサは、Receivers
経由でMidiMessages
を受信する代わりに、キーボード・イベントまたはマウス・イベントの結果としてMidiEvents
をゼロから作成します。同様に、MidiMessages
を別個のオブジェクトに関連付けられたReceiver
に送信する代わりに、内部のシンセサイザ(実際にはシーケンサと同じオブジェクトの場合もある)と直接通信することにより、音楽を再生します。ただし、この章の後半では、Receivers
およびTransmitters
を使用する一般的なシーケンサを前提に説明します。
第10章「MIDIメッセージの送信および受信」で説明したように、シーケンサを使用しなくても、アプリケーション・プログラムから直接MIDIメッセージをデバイスに送信できます。プログラムは、メッセージを送信するたびにReceiver.send
メソッドを呼び出せば良いのです。これは直接的な方法で、プログラム自体がリアルタイムにメッセージを作成する場合に有用です。たとえば、画面に表示されたピアノの鍵盤をユーザーがクリックして音を再生するプログラムについて考えましょう。プログラムは、マウスダウン・イベントをキャッチするとすぐに適切なノート・オン・メッセージをシンセサイザに送信します。
第10章の説明にあるように、プログラムで、デバイスの受信側に送信するMIDIメッセージにタイムスタンプを含めることができます。ただし、このようなタイムスタンプは、タイミングを微調整して処理に要する待機時間を補正する場合にのみ使用されます。呼出し側は通常、任意のタイムスタンプを設定することはできません。Receiver.send
に渡される時間の値は、現在の時間に近い値でなければなりません。さもないと、受信デバイスがメッセージを正確にスケジュールできなくなります。これは、アプリケーション・プログラムで、リアルタイムのイベントに応答して各メッセージを生成する代わりに、曲全体のMIDIメッセージのキューを前もって作成する場合、Receiver.send
の各呼出しがほぼ正確な時間に行われるようにするためには、スケジュールの際に細心の注意を払う必要があることを意味します。
さいわい、大半のアプリケーション・プログラムでは、このようなスケジューリングの問題を気にする必要はありません。プログラムは、Receiver.send
自体を呼び出す代わりに、Sequencer
オブジェクトを使用して、MIDIメッセージのキューを管理できます。シーケンサは、メッセージのスケジューリングおよび送信を行います。つまり、正確なタイミングで音楽を再生します。シーケンサを利用する方法は、一般に、リアルタイムではない一連のMIDIメッセージをリアルタイムの一連のメッセージに変換する場合(再生時など)や、その逆を実行する場合(記録時など)に便利です。シーケンサは、MIDIファイルからのデータ再生、およびMIDI入力ポートからのデータ記録でよく使用されています。
Sequencer
APIについての詳しい考察に入る前に、シーケンスに格納されるデータの種類を説明します。
Java Sound APIでは、シーケンサは記録済みMIDIデータの編成方法について、標準MIDIファイル仕様に厳密に準拠しています。すでに説明したように、Sequence
は、的確なタイミングで編成されるMidiEvents
の集合です。ただし、Sequence
にはリニアな一連のMidiEvents
より大きな構造が含まれます。Sequence
には、総合的なタイミング情報の他にTracks
の集まりが含まれます。MidiEvent
データを保持するのは、このTracks
です。このため、シーケンサにより再生されるデータは、Sequencer
、Track
、およびMidiEvent
という3階層のオブジェクトで構成されています。
これらのオブジェクトの従来の使用法は、Sequence
が曲全体または曲の1セクションを表し、各Track
はアンサンブル中の1つの音声または演奏者に対応していました。この様式では、特定のTrack
のデータはすべて、その音声または演奏者用に予約された特定のMIDIチャネルにエンコードされます。
このデータ編成方法は、シーケンスを編集する場合には有用ですが、Tracks
を使用する従来のやり方の1つにすぎないことに留意してください。Track
クラス自体の定義には、異なるMIDIチャネルの混合MidiEvents
を含まないという規定はありません。たとえば、マルチチャネルのMIDI構成全体をミックスして1つのTrack
に記録することができます。また、標準MIDIファイルのType 0には、Type 1やType 2とは異なり、定義上1トラックしか含まれません。このため、このようなファイルから読み取ったSequence
は単一のTrack
オブジェクトしか保持しません。
第8章「MIDIパッケージの概要」の説明にあるように、Java Sound APIには、大半の標準MIDIメッセージの基になっている2または3バイトのrawシーケンスに対応するMidiMessage
オブジェクトが含まれています。MidiEvent
は、MidiMessage
をイベントの発生時を指定するタイミング値とともにパッケージングしたものです。シーケンスとは、実際には、3階層ではなく4または5階層のデータで構成されているといえます。見かけ上の最下層であるMidiEvent
には、より低レベルのMidiMessage
が含まれ、またMidiMessage
オブジェクトには標準MIDIメッセージを構成するバイト配列が含まれるためです。
Java Sound APIでは、MidiMessages
をタイミング値と関連付けるための2種類の方法が用意されています。1つ目は、「シーケンサをいつ使用するか」で説明した方法です。この方法の詳細については、第10章「MIDIメッセージの送信および受信」の「トランスミッタを使わずにメッセージをレシーバに送信する方法」および「タイムスタンプの理解」を参照してください。Receiver
のsend
メソッドがMidiMessage
引数およびタイムスタンプ引数を取ることが説明されています。そのようなタイムスタンプは、マイクロ秒単位でのみ表現されます。
MidiMessage
が指定されたタイミングを保持する2つ目の方法は、MidiMessageをMidiEvent
内でカプセル化する方法です。この場合、タイミングはティックと呼ばれる、より抽象的な単位で表現されます。
1ティックのデュレーションは、シーケンスによって異なります。ただし、1つのシーケンス内で異なることはありません。値は、標準MIDIファイルのヘッダーに格納されます。ティックのサイズは、次の2種類の単位のどちらかを使って指定されます。
一方SMPTEの場合、単位は絶対時間に基づくため、テンポという概念を適用することはできません。4種類のSMPTE規約が存在し、各規約は秒単位での動画フレーム数を示しています。秒単位のフレーム数として、24、25、29.97、30のどれかを指定できます。SMPTEタイム・コードとともに使用することで、ティックのサイズをフレームの何分の一かで表現できます。
Java Sound APIでは、Sequence.getDivisionType
を呼び出すことで、特定のシーケンスで使われている単位(PPQ、またはSMPTE単位のどれか)を判断できます。単位がわかったら、Sequence.getResolution
を呼び出してティックのサイズを計算できます。2つ目の方法では、除算形式がPPQの場合は、4分音符ごとのティック数が返され、除算形式がSMPTE規格の1つである場合は、SMPTEフレームごとのティック数が返されます。PPQの場合は、次の式を使ってティックのサイズを取得できます。
ticksPerSecond = resolution * (currentTempoInBeatsPerMinute / 60.0); tickSize = 1.0 / ticksPerSecond;
framesPerSecond = (divisionType == Sequence.SMPTE_24 ? 24 : (divisionType == Sequence.SMPTE_25 ? 25 : (divisionType == Sequence.SMPTE_30 ? 30 : (divisionType == Sequence.SMPTE_30DROP ?
29.97)))); ticksPerSecond = resolution * framesPerSecond; tickSize = 1.0 / ticksPerSecond;
Java Sound APIでのシーケンス内のタイミング定義は、標準MIDIファイル仕様の定義を忠実に反映したものです。ただし、重要な相違点が1つあります。MidiEvents
に含まれるティック値は、増分(デルタ)時間ではなく、累積時間を示すものです。標準MIDIファイルでは、各イベントのタイミング情報は、シーケンス内での、前のイベントの開始以降の経過時間を表します。これは増分(デルタ)時間と呼ばれます。一方、Java Sound APIでは、ティックは増分値ではなく、前のイベントの時間値に増分(デルタ)値を加えた値になります。つまり、Java Sound APIでは、各イベントのタイミング値は、シーケンス内の前のイベントのタイミング値よりも常に大きくなります。イベントが同時に発生することになっている場合は、タイミング値は等しくなります。各イベントのタイミング値は、シーケンスの開始を起点とする経過時間を示します。
要約すると、Java Sound APIは、タイミング情報を、MIDIティック単位またはマイクロ秒単位で表現します。MidiEvents
は、タイミング情報をMIDIティックに換算して格納します。ティックのデュレーションは、Sequenceの
総合的なタイミング情報に基づいて算出されます。シーケンスがテンポに基づくタイミングを使用する場合、現時点の音楽上のテンポが使用されます。一方、Receiver
に送信されるMidiMessage
に関連付けられたタイムスタンプは、常にマイクロ秒単位で表現されます。
この設計の目標の1つは、時間に関する概念の矛盾を避けることです。PPQ単位を使用するMidiEvents
内での時間の単位を解釈するのは、Sequencer
の役割です。Sequencerは現在のテンポを考慮しつつこれを解釈してマイクロ秒単位の絶対時間に変換します。さらに、シーケンサは、メッセージを受信するデバイスのオープン時からの相対時間もマイクロ秒単位で表現できなければなりません。シーケンサは複数のトランスミッタを保持することができます。各トランスミッタは、種類のまったく異なるデバイスへの関連付けが可能な異種のレシーバにメッセージを送ります。このため、シーケンサには、複数の変換を同時に実行して、各デバイスがその時間の概念に沿ったタイムスタンプを確実に受信するための機能が求められます。
問題をさらに複雑にする要素として、複数の異なるデバイスで時間の概念を更新するときに、複数の異なるソース(オペレーティング・システムのクロックや、サウンド・カードにより維持されるクロックなど)から実行する場合があることです。これは、タイミングがシーケンサのタイミングに相関して変化する可能性があることを意味します。シーケンサとの同期を維持するために、デバイスによってはシーケンサの時間概念に自身を「スレーブ」にすることを許可するものがあります。マスター/スレーブの設定については、「シーケンサの高度な機能」で説明します。
Sequencer
インタフェースが提供するメソッド群は、次のようなカテゴリに分類されます。
Sequence
オブジェクトからロードしたり、現在ロードされているシーケンス・データをMIDIファイルに保存するメソッド群。Sequence
内での現時点の再生/録音位置へのシャトルなどの機能を実現する。Sequencer
では、再生時に、テンポを変更すること、特定のTracks
の音を消すこと(ミュート)、およびほかのオブジェクトとの同期状態を変更することが可能。Sequencer
がある種のMIDIイベントを処理する際に通知を受ける「リスナー」オブジェクトを登録する高度なメソッド群。
Sequencer
メソッドを呼び出す場合でも、まず、システムからSequencer
デバイスを取得して、プログラムで使用できるように予約する必要があります。
アプリケーション・プログラムは、Sequencer
をインスタンス化しません。Sequencer
インタフェースそのものであると考えられます。ただし、Java Sound APIのMIDIパッケージのすべてのデバイスと同様、Sequencer
へはstatic MidiSystem
オブジェクトを介してアクセスできます。第9章「MIDIシステム・リソースへのアクセス」で説明したように、デフォルトのSequencer
を取得するには、次のMidiSystem
メソッドを使用します。
static Sequencer getSequencer()
次のコード・フラグメントは、デフォルトのSequencer
を取得し、必要なすべてのシステム・リソースを獲得して、Sequencerを操作可能にします。
Sequencer sequencer; // Get default sequencer. sequencer = MidiSystem.getSequencer(); if (sequencer == null) { // Error -- sequencer device is not supported. // Inform user and return... } else { // Acquire resources and make operational. sequencer.open(); }
open
の呼出しにより、シーケンサ・デバイスがこのプログラム用に予約されます。一度に再生できるシーケンスは1つだけであるため、シーケンサを共有してもあまり意味はありません。シーケンサの使用を完了したら、close
を呼び出して、シーケンサをほかのプログラムから利用可能にします。
デフォルトでないシーケンサは、第9章「MIDIシステム・リソースへのアクセス」で説明したような方法で取得できます。
シーケンサをシステムから取得して予約したら、シーケンサが再生するデータをロードする必要があります。通常、これを実行するには、次の3つの方法のいずれかが使用されます。
MidiEvent
オブジェクトをこれらのトラックに追加することにより行う
InputStream
に送り、その後、Sequencer.setSequence(InputStream)
を使ってシーケンサに直接読み込む方法です。この方法では、Sequence
オブジェクトを明示的に作成しません。実際は、内部でSequence
を作成することさえないSequencer
実装もあります。これは、シーケンサによっては、データをファイルから直接処理する組込みメカニズムを持っているためです。
もう1つは、Sequence
を明示的に作成する方法です。データを再生する前にシーケンスを編集する場合は、この方法を使用する必要があります。この方法では、MidiSystemの
オーバーロードされたメソッドであるgetSequence
を呼び出します。このメソッドは、InputStream
、File
、またはURL
からシーケンスを取得できます。このメソッドが返すSequence
オブジェクトをSequencer
にロードすることにより再生が実行されます。File
からSequence
オブジェクトを取得してsequencer
にロードするコード例(前述のコードを拡張したもの)を次に示します。
try { File myMidiFile = new File("seq1.mid"); // Construct a Sequence object, and // load it into my sequencer. Sequence mySeq = MidiSystem.getSequence(myMidiFile); sequencer.setSequence(mySeq); } catch (Exception e) { // Handle error and/or return }
MidiSystemの
getSequence
メソッドと同様、問題が発生するとsetSequence
はInvalidMidiDataException
、InputStream
形式の場合にはIOException
をスローする場合があります。
Sequencer
の開始と停止は、次のメソッドを使って行われます。
void start()
void stop()
Sequencer.start
メソッドは、シーケンスの再生を開始します。再生は、シーケンス内の現在位置から始まることに留意してください。すでに説明したように、setSequence
メソッドを使って既存のシーケンスをロードすると、シーケンサの現在位置が初期化されてシーケンスの冒頭に位置が設定されます。stop
メソッドはシーケンサを停止しますが、現在のSequence
を自動的に巻き戻すことはしません。位置を再設定しないで、停止したSequence
を開始すると、シーケンスの再生が現在位置から再開されます。この場合、stop
メソッドは、一時停止の機能を果たします。ただし、再生を開始する前に現在のシーケンス位置を任意の値に設定するためのさまざまなSequencer
メソッドがあります。これらのメソッドについては、あとで説明します。
すでに説明したように、Sequencer
は一般に、1つ以上のTransmitter
オブジェクトを保持します。MidiMessage
は、このオブジェクトを介してReceiver
に送信されます。Sequencer
は、これらのTransmitter
を介してSequence
を再生します。再生は、具体的には、現在のSequence
に含まれるMidiEvent
に対応する、適切にタイミングを取ったMidiMessage
を発行することにより実行されます。このため、Sequence
再生の設定手順には、Sequencerの
Transmitter
オブジェクトに対してsetReceiver
メソッドを呼び出すこと、つまり再生されたデータを利用するデバイスへの出力の配線が含まれます。Transmitter
とReceiver
の詳細は、第10章「MIDIメッセージの送信および受信」を参照してください。
MIDIデータを、まずSequence
に取り込み、次にファイルに取り込むには、前述の手順に加えていくつかの追加手順を実行する必要があります。Sequence
内のTrack
への記録を実行するために必要な手順を次に示します。
MidiSystem.getSequencer
を使って、記録に使用する新たなシーケンサを取得します。setReceiver
メソッドを使って、記録用のSequencer
に関連付けられたReceiver
にデータを送信します。Sequence
オブジェクトを作成します。このオブジェクトに記録されたデータを格納します。Sequence
オブジェクトの作成時に、シーケンスの総合的なタイミング情報を指定する必要があります。例を示します。
Sequence mySeq; try{ mySeq = new Sequence(Sequence.PPQ, 10); } catch (Exception ex) { ex.printStackTrace(); }
Sequence
のコンストラクタは、引数としてdivisionType
とタイミング分割を取ります。divisionType
引数には、タイミング分割引数の単位を指定します。この場合、作成するSequence
のタイミング分割に、4分音符1つにつき10パルスを指定します。Sequence
コンストラクタのオプション引数として、トラック引数の数を指定します。これを指定することにより、初期シーケンスが指定数(何も指定しない場合は空)のTrack
で始まります。指定しない場合、Sequence
は初期Track
なしで作成されるため、必要に応じて後で追加します。Sequence.createTrack
を使って、Sequence
内に空のTrack
を作成します。Sequence
が初期Tracks
を使って作成された場合は、この手順は不要です。Sequencer.setSequence
を使って、記録を受信する新規Sequence
を選択します。setSequence
メソッドは、既存のSequence
をSequencer
に結合します。これは、テープ・レコーダにテープをセットする動作に類似しています。Track
ごとにSequencer.recordEnable
を呼び出します。必要に応じて、Sequence.getTracks
を呼び出して、Sequence
内の利用可能なTracks
への参照を取得します。Sequencer
に対してstartRecording
を呼び出します。Sequencer.stop
またはSequencer.stopRecording
を呼び出します。MidiSystem.write
を使って、記録されたSequence
をMIDIファイルに保存します。MidiSystem
のwrite
メソッドは、引数の1つにSequence
を取り、そのSequence
をストリームまたはファイルに書き込みます。多くのアプリケーション・プログラムでは、シーケンスをファイルからロードして作成できます。また、かなり多くのアプリケーション・プログラムでは、シーケンスをライブのMIDI入力(録音)から取り込んで作成することもできます。ただし、プログラム的に、またはユーザーからの入力に応じて、MIDIシーケンスを最初から作成する必要のあるプログラムもあります。フル装備のシーケンサ・プログラムは、既存シーケンスの編集機能に加え、ユーザーが手動で新規シーケンスを生成するための機能も備えています。
これらのデータ編集操作は、Sequencer
メソッドではなく、データ・オブジェクト自体がメソッドであるSequence
、Track
、およびMidiEvent
を使ってJava Sound API内で実現されています。Sequence
コンストラクタの1つを使って空のシーケンスを作成し、次のSequence
メソッドを呼び出すことによってそのシーケンスにトラックを追加できます。
Track createTrack()ユーザーによるシーケンスの編集が可能なプログラムの場合は、トラックを削除する際に次の
Sequence
メソッドが必要になります。
boolean deleteTrack(Track track)
いったんシーケンスにトラックが格納されたら、Track
クラスのメソッドを呼び出してトラックの内容を変更できます。Track
に含まれるMidiEvent
は、Track
オブジェクト内にjava.util.Vector
として格納されます。Track
は、リスト内のイベントへのアクセス、およびリストへのイベントの追加や削除を実行するためのメソッド・セットを提供します。add
およびremove
メソッドは、その名前からわかるように、指定されたMidiEvent
をTrack
に対して追加または削除します。get
メソッドも提供されています。このメソッドはTrackの
イベント・リストへのインデックスをとり、そこに格納されたMidiEvent
を返します。また、size
およびtick
メソッドもあります。これらは、それぞれトラック内のMidiEvent
数、およびTick
の総数で表現されたトラックのデュレーションを返します。
トラックに追加する前にイベントを新規作成する場は、MidiEvent
コンストラクタを使用します。イベントに埋め込まれたMIDIメッセージを指定または変更する場合は、適切なMidiMessage
サブクラス(ShortMessage
、SysexMessage
、またはMetaMessage
)のsetMessage
メソッドを呼び出すことができます。イベントを発生させる時間を変更する場合は、MidiEvent.setTick
を呼び出します。
これらの低レベル・メソッドを組み合わせることにより、フル装備のシーケンサ・プログラムで必要とされる基本編集機能が提供されます。
この章では、これまでMIDIデータの単純な再生と記録を中心に説明してきました。このセクションでは、Sequencer
インタフェースおよびSequence
クラスのメソッドを介して利用できる、いくつかの高度な機能について説明します。
シーケンス内でのシーケンサの現在位置を取得する2つのSequencer
メソッドが存在します。最初のメソッドを次に示します。
long getTickPosition()
これは、シーケンスの先頭からの位置をMIDIティックで返します。2番目のメソッドを次に示します。
long getMicrosecondPosition()
これは、現在位置をマイクロ秒で返します。このメソッドは、シーケンスが、MIDIファイルまたはSequence
内への格納時のデフォルトのレートで再生されていることを前提にしています。以下で説明するように、再生スピードを変更した場合、返される値に変更はありません。
同様に、ある単位に従ってシーケンサの現在位置を設定できます。
void setTickPosition(long tick)
void setMicrosecondPosition(long microsecond)
すでに説明したように、シーケンスのスピードはテンポで表されます。テンポはシーケンスの途中で変更することができます。シーケンスには、標準MIDIのテンポチェンジ・メッセージをカプセル化するイベントを含めることができます。このようなイベントを処理する場合、シーケンサは指示されたテンポに合わせて再生スピードを変更します。また、次のSequencer
メソッドのいずれかを呼び出すことにより、テンポをプログラム上で変更することもできます。
public void setTempoInBPM(float bpm) public void setTempoInMPQ(float mpq) public void setTempoFactor(float factor)最初の2つのメソッドはそれぞれ、テンポを1分あたりのビート数、4分音符あたりのマイクロ秒数で設定します。これらのメソッドのどちらかが再度呼び出されるまで、またはシーケンス内でテンポチェンジ・イベントが見つかるまで、テンポは指定された値のままです。いずれかの時点で、現在のテンポは新たに指定されたテンポにオーバーライドされます。
3番目のメソッドであるsetTempoFactor
は、上の2つとは本質的に性質が異なります。このメソッドは、テンポチェンジ・イベントまたは上記の最初の2つのメソッドのどちらかによってシーケンサに設定されたすべてのテンポを基準化(増減)します。デフォルトのスカラーは1.0 (変更なし)です。このメソッドにより再生または記録の速度が公称テンポより速くなっても、遅くなっても(係数が1.0以外の場合)、公称テンポは変更されません。つまり、実際の再生または記録スピードにテンポ係数が影響しても、getTempoInBPM
およびgetTempoInMPQ
が返すテンポ値は、テンポ係数による影響を受けません。また、テンポチェンジ・イベントまたは最初の2つのメソッドのどちらかによってテンポが変更された場合でも、テンポは最後に設定されたテンポ係数によって基準化されます。ただし、新たなシーケンスをロードすると、テンポ係数は1.0に再設定されます。
シーケンスの除算形式がPPQではなく、SMPTEタイプのいずれかである場合、これらのテンポチェンジ指示はすべて無効になることに留意してください。
シーケンサを使用する立場からは、特定のトラックを消して、音そのものを集中的に聞きとることができれば、好都合です。フル装備のシーケンサ・プログラムでは、ユーザーが再生するときに音を出すトラックを選択できます。厳密には、シーケンサはサウンド自体を作成するものではないため、シーケンサが作成するMIDIメッセージ・ストリームに加えるトラックをユーザーが選択します。通常、各トラックには、ミュート・ボタンとソロ・ボタンの2種類のグラフィカル・コントロールがあります。ミュート・ボタンをアクティブにすると、選択が解除されるまで、トラックからはどのような状況でも音は出ません。ソロ機能は、あまり知られていません。簡単に言うと、ソロ機能はミュート機能と逆の機能です。任意のトラックのソロ・ボタンをアクティブにすると、そのソロ・ボタンがアクティブになっているトラックのサウンドのみが出力されます。ソロ機能により、少数のトラックを試演する場合に、他のトラックをすべてミュートしなくても、ただちに試演を開始できます。通常、ミュート・ボタンはソロ・ボタンよりも優先されます。つまり、両方のボタンがアクティブ化されている場合、そのトラックの音は出力されません。
Sequencer
メソッドを使うと、トラックのミュートやソロ機能およびトラックの現時点のミュートやソロの状態の問い合わせを簡単に実行できます。たとえば、デフォルトのSequencer
を取得済みで、シーケンス・データをロードしたとします。シーケンスの5番目のトラックをミュートするには、次のように記述します。
sequencer.setTrackMute(4, true); boolean muted = sequencer.getTrackMute(4); if (!muted) { return; // muting failed }上記のコードには、注意すべき点がいくつかあります。まず、シーケンスのトラック番号は、0から始まり、総トラック数 - 1で終わります。また、
setTrackMute
の2番目の引数はboolean型です。trueの場合、要求はトラックのミュートです。trueでない場合、要求は指定されたトラックのミュート解除です。最後に、ミュート機能が有効かどうかをテストするため、Sequencer getTrackMute
メソッドを呼び出して問い合わせるトラック番号を渡します。この例で予想したとおりにtrue
が返されれば、ミュート要求が受け入れられています。false
が返された場合、失敗したことを意味します。
ミュート要求の失敗には、さまざまな原因が考えられます。たとえば、setTrackMute
呼出しで指定されたトラック番号が総トラック数を超過している場合や、シーケンサがミュート機能をサポートしていない場合などがあります。getTrackMute
を呼び出すと、要求が成功したか失敗したかを判断できます。
getTrackMute
によって返されるboolean型の値は、障害が発生したことは示しますが、その理由は示しません。無効なトラック番号がsetTrackMute
メソッドに渡されたことが原因で障害が発生したのかを調べることができます。これを行うには、Sequence
のgetTracks
メソッドを呼び出します。その結果、シーケンス内の全トラックを含む配列が返されます。setTrackMute
呼出しで指定されたトラック番号がこの配列の長さを超過している場合、指定したトラック番号が無効であったことがわかります。
この例では、ミュート要求が成功すると、シーケンスの再生時に5番目のトラックからも、現在ミュートが設定されているほかのすべてのトラックからもサウンドは出力されません。
トラックのソロ機能に使用するメソッドや技術は、ミュート機能の場合に類似しています。あるトラックのソロ機能を有効にするには、Sequence
のsetTrackSolo
メソッドを呼び出します。
void setTrackSolo(int track, boolean bSolo)
setTrackMute
の場合と同様、最初の引数にはゼロから始まるトラック番号を指定します。2番目の引数がtrue
の場合、そのトラックはソロ・モードに設定されます。そうでない場合は、ソロ・モードには設定されません。
デフォルトでは、トラックにはミュート機能もソロ機能も設定されません。
Sequencer
には、Sequencer.SyncMode
と呼ばれる内部クラスがあります。SyncMode
オブジェクトは、MIDIシーケンサで扱われている時間をマスターまたはスレーブ・デバイスと同期化する方法の1つを表します。シーケンサをマスターと同期させる場合、シーケンサはマスターから送信される特定のMIDIメッセージに応答して現在の時間を調整します。シーケンサがスレーブを保持する場合も同様に、シーケンサは、MIDIメッセージを送信してスレーブのタイミングを制御します。
シーケンサが利用できるマスターを指定するための3つの定義済みモードは、INTERNAL_CLOCK
、MIDI_SYNC
、およびMIDI_TIME_CODE
です。後ろの2つは、シーケンサが別のデバイスからMIDIメッセージを受信する場合に機能します。この2つのモードは、それぞれシステムのリアルタイム・クロック・メッセージまたはMIDIタイム・コード(MTC)メッセージに基づいてシーケンサの時間を再設定します。(これらのメッセージ・タイプの詳細は、MIDI仕様を参照してください。)これら2つのモードは、スレーブ・モードとして使用することも可能です。その場合、シーケンサは対応するMIDIメッセージ・タイプをレシーバに送信します。4番目のモードであるNO_SYNC
は、シーケンサからレシーバにタイミング情報を送信しない場合に使用します。
引数に、サポートされているSyncMode
オブジェクトを指定してsetMasterSyncMode
メソッドを呼び出すことにより、シーケンサのタイミングを制御する方法を指定できます。同様に、setSlaveSyncMode
メソッドを使って、シーケンサがレシーバに送信するタイミング情報が設定されます。この情報は、シーケンサをマスター・タイミング・ソースとして使用するデバイスのタイミングを制御します。
シーケンスの各トラックには、複数種類のMidiEvents
を含めることができます。含めることのできるイベントには、ノート・オン・メッセージとノート・オフ・メッセージ、プログラム・チェンジ・イベント、コントロール・チェンジ・イベント、メタイベントなどがあります。Java Sound APIは、最後の2つのイベント・タイプ(コントロール・チェンジ・イベントとメタイベント)用の「リスナー」インタフェースを指定します。これらのインタフェースは、シーケンスの再生中にこのようなイベントが発生した場合に通知を受け取るために使用します。
ControllerEventListener
インタフェースをサポートするオブジェクトは、Sequencer
が特定のコントロール・チェンジ・メッセージを処理する際、通知を受け取ることができます。コントロール・チェンジ・メッセージは、標準タイプのMIDIメッセージであり、ピッチ・ベンドのホイールやデータ・スライダなどのMIDIコントローラの値が変わったことを表します。(コントロール・チェンジ・メッセージの詳細は、MIDI仕様を参照してください。)シーケンスの処理中にこのようなメッセージが処理されると、シーケンサからデータを受信中の任意のデバイス(シンセサイザのことが多い)に対し、パラメータ値をいくつか更新するよう指示が出されます。パラメータは、通常、サウンド合成の状況を制御します。たとえば、コントローラがピッチ・ベンドのホイールである場合は、現在出力中の音のピッチがパラメータによって制御されます。シーケンスの記録中のコントロール・チェンジ・メッセージは、メッセージを作成した外部の物理デバイスのコントローラが動かされたか、またはそのような動作がソフトウェアでシミュレートされたことを意味します。
ControllerEventListener
インタフェースの使用方法を次に示します。ControllerEventListener
インタフェースを実装するクラスを開発したとします。この場合、作成したクラスには次のメソッドが含まれます。
void controlChange(ShortMessage msg)さらに、このクラスのインスタンスを作成して、それに
myListener
という変数を割り当てたとします。そして、次の文をプログラム内に含めます。
int[] controllersOfInterest = { 1, 2, 4 }; sequencer.addControllerEventListener(myListener, controllersOfInterest);この場合、シーケンサがMIDIコントローラ番号1、2、または4のコントロール・チェンジ・メッセージを処理するたびに、このクラスの
controlChange
メソッドが呼び出されます。つまり、Sequencer
が要求を処理して任意の登録済みコントローラの値を設定する際、Sequencer
は作成したクラスのcontrolChange
メソッドを呼び出します。指定された制御デバイスへのMIDIコントローラ番号の割当てに関する詳細は、MIDI 1.0仕様を参照してください。
controlChange
メソッドに渡されるShortMessage
には、影響を受けるコントローラ番号、およびコントローラに設定される新規の値が含まれます。コントローラ番号は、ShortMessage.getData1
メソッドを使って取得できます。また、コントローラの新規の設定値は、ShortMessage.getData2
メソッドを使って取得できます。
その他の特殊な種類のイベント・リスナーは、MetaEventListener
インタフェースにより定義されます。標準MIDIファイル1.0仕様によると、メタ・メッセージは、MIDIワイヤー・プロトコルの対象外のメッセージで、MIDIファイルに埋め込むことができるメッセージです。これは、シンセサイザにとって意味のないメッセージですが、シーケンサはこのメッセージを解釈できます。メタ・メッセージには、指示(テンポチェンジ・コマンドなど)、歌詞などのテキスト、およびほかのインジケータ(end-of-trackなど)が含まれます。
MetaEventListener
のメカニズムは、ControllerEventListener
のメカニズムに類似しています。シーケンサによるMetaMessage
の処理時にインスタンスが通知を必要とする任意クラス内にMetaEventListener
インタフェースを実装します。この場合、クラスに次のメソッドを追加します。
void meta(MetaMessage msg)
このクラスのインスタンス登録は、次のように、SequencerのaddMetaEventListener
メソッドへの引数としてインスタンスを渡すことにより実行します。
boolean b = sequencer.addMetaEventListener (myMetaListener);これは、
ControllerEventListener
インタフェースが採用する方法とは若干異なります。登録する理由が、選択したものだけを受信するためではなく、すべてのMetaMessages
を受信するためであるからです。シーケンサは、シーケンス内でMetaMessage
を見つけると、myMetaListener.meta
を呼び出して見つけたMetaMessage
を渡します。meta
メソッドは、MetaMessage
引数に対してgetType
を呼び出して、メッセージ・タイプを示す0 - 127の整数(標準MIDIファイル1.0仕様の定義に準拠)を取得します。