ボイス・アサイナ (8)

今回は「シングル・アサイン」の場合を考えます。
シングル・アサインの場合には、同じ MIDI チャンネル、同じ音程の音に対しては、2つ以上のボイスを割り当てることはありません。
したがって、ノートオン・メッセージに対して、アクティブ・キューと、ホールド・キューの両方をサーチして、同じ音程の音がすでに発音中かどうかを調べる必要があります。
キュー内をサーチした結果、すでに発音中のボイスが見つかったら、いったんキューから外してノートオフ処理を行い、改めてアクティブ・キューの最後尾に追加してノートオン処理を行います。
キューに発音中のボイスがなければ、通常通りにフリー・キューからボイスを取り出して、ノートオン処理を行い、アクティブ・キューの最後尾に追加します。
まとめると、ある MIDI チャンネルのノートオン・メッセージに対して、

  • 「ホールド・キュー」の先頭から順に要素をサーチしていき、 同じノート番号のボイスがあるかどうかを調べる
    • 同じノート番号のボイスが見つかったら、
      • そのボイスにノートオフ処理 (強制ダンプ) を施し、キューから外す
      • そのボイスにノートオン処理 (アタック開始) を施し、「アクティブ・キュー」の最後尾に追加する
  • 同じノート番号のボイスが見つからなければ、「アクティブ・キュー」に対しても同様の処理をする
  • 両方のキューに見つからない場合は、
    • 「フリー・キュー」の先頭からボイスを1個取り出す
    • ボイスにノート番号に対応する音程データをセットし、アタック処理を開始する
    • ボイスを「アクティブ・キュー」の最後尾に追加する

という処理を行います。
ノートオフ・メッセージおよび Hold1 コントロールに対する処理は以前と同じです。
ただし、このままではオーバーラップする「同音連打」の場合に問題がありますが、それについては後で述べます。
例で示すと、まず、下の初期状態から

ACTV 4 (C#)
HOLD 2 (F#) 1 (A#)
FREE  0   5   3 

ノートオンがオーバーラップしないケースとして、「F#」のノートオン・メッセージが来た場合を考えると、ホールド・キューをサーチしてボイス番号 2 の F# の音が見つかるので、

2

ACTV 4 (C#)
HOLD 1 (A#)
FREE  0   5   3 

ボイス 2 をホールド・キューから外し、強制ダンプ処理をして発音を終了させたのが上の状態です。
その後、同じボイス 2 で同じノート番号 F# のノートオン処理を行い、アクティブ・キューの最後尾に追加したのが次の状態です。

ACTV 4 (C#) 2 (F#)
HOLD 1 (A#)
FREE  0   5   3 

次は、オーバーラップした同音連打の場合、つまり、ノートオフ・メッセージが来ないうちに同じ音のノートオン・メッセージが来る場合を考えます。
ここでの例では、現在ノートオン中の「C#」のノートオン・メッセージが来る場合です。
まず、初期状態からアクティブ・キューのサーチで見つかった C# を発音中のボイス番号 4 のボイスをキューから外し、強制ダンプ処理を行ったのが下の図です。

4
ACTV

HOLD 2 (F#) 1 (A#)
FREE  0   5   3 

その後、同じボイス 4 で同じノート番号 C# のノートオン処理を行い、アクティブ・キューの最後尾に追加したのが下の図で、初期状態と同じ形になっています。

ACTV 4 (C#)
HOLD 2 (F#) 1 (A#)
FREE  0   5   3 

ここで、ちょっと困ったことになっています。
それは、ダンパー・ペダルを離して (Hold1 がオフになって) C# の音のノートオフ・メッセージが来ると C# の音の発音が終了してしまうことです。
「オーバーラップした 2 音」の場合、1 回目のノートオフは実質的に無視され、2 回目のノートオフで本当に発音が終了しなければなりません。
上の状態は初期状態と区別が付かないので、1 回目のノートオフで発音が終了してしまいます。
「マルチ・アサイン」の場合には、オーバーラップした 2 音を発音中であれば、実際にボイスが 2 個使われているので、2つのノートオフと 1 対 1 に対応していたのですが、「シングル・アサイン」ではボイスの実体はひとつしかないので、「保留」されているノートオンがいくつあるかという情報を記憶しておかなければなりません。
前述のアルゴリズムに、「ノートオン・カウント」を追加して修正したアルゴリズムを示します。

ノートオン・メッセージの処理

  • 「ホールド・キュー」の先頭から順に要素をサーチしていき、 同じノート番号のボイスがあるかどうかを調べる
    • 同じノート番号のボイスが見つかったら、
      • そのボイスにノートオフ処理 (強制ダンプ) を施し、キューから外す
      • そのボイスのノートオン・カウントを 1 増やし、ノートオン処理 (アタック開始) を施し、「アクティブ・キュー」の最後尾に追加する
  • 同じノート番号のボイスが見つからなければ、「アクティブ・キュー」に対しても同様の処理をする
  • 両方のキューに見つからない場合は、
    • 「フリー・キュー」の先頭からボイスを1個取り出す
    • ボイスにノート番号に対応する音程データをセットし、ノートオン・カウントを 1 増やし、アタック処理を開始する
    • ボイスを「アクティブ・キュー」の最後尾に追加する

ノートオフ・メッセージの処理

  • 「アクティブ・キュー」の先頭から順に要素をサーチしていき、 同じノート番号のボイスがあるかどうかを調べる
  • 同じノート番号のボイスが見つかったら、そのボイスのノートオン・カウントを 1 減らす
    • Hold1 オフの場合
      • ノートオン・カウントがゼロになったらノートオフ処理 (リリース開始) を施し、キューから外し、「フリー・キュー」の最後尾に追加する
      • ノートオン・カウントがゼロでなければ何もしない
    • Hold1 オンの場合
      • ノートオン・カウントがゼロになったら、キューから外し、「ホールド・キュー」の最後尾に追加する
      • ノートオン・カウントがゼロでなければ何もしない

となります。
また、フリー・キューにボイスを登録する際に、あらかじめノートオン・カウントをゼロに初期化しておきます。