JUCEでソフトシンセをつくる 4 ~ ポリフォニック化
はじめに
前回までで、オシレータとAmpEnvを持つモノシンセができました。
今回は、これをポリフォニックシンセに拡張してやります。
モノからポリへ拡張する際の問題点
まずは、前回までのモノフォニックシンセにおけるMIDIと波形生成の流れをざっくりと図示すると、下記のような感じでしょうか (Activity図の正しい書き方を知らないので間違ってても許していただきたい)。
入力MIDI noteのOn / offに応じて、MonoSynth内のOscillatorとAmpEvnの状態が切り替わりホストAudioBufferサイズ分の波形データを生成します。では、これがポリフォニックの場合はどうなるべきかを考えてみます。ポリフォニックでは、同時に複数の波形を生成する必要があるため、発音数分だけのMonoSynth (今後はVoiceと表記)を用意する必要があります。したがって、単純に先のモノフォニックに倣えば、下図のようになるでしょうか。
さて、この図を元にポリフォニック処理を実装しようとすると、MIDI on / off signalを受けっとた際に、どの番号のVoiceを処理すべきかという問題が生じます。MIDI onの時には、AmpEnvがRelease / Off Stateにあるボイス(今後は空のVoiceと表記)が、優先的に処理されることが期待されます。また、MIDI offの場合は、的確に"入力されたMIDI note番号と同じNote音階で鳴っているボイス"が処理される必要があります。その他にも、各ボイスのマージや何やら色々考える必要はありますが、以下、ポリフォニックにおけるMIDI入力時の処理に絞って、その実装を記述します。
ポリフォニックの実装
引き続き、前回のプロジェクトをベースに拡張していきます。ポリフォニック処理は、新たにPolySynth Classを作成し、そこに記述していきます。下記に、今回実装したPolySynth Classのインターフェイス回りを記載します。
適切なVoiceのOn / offを実現するためには、各ボイスの状態(Note番号)を常に把握しておく必要があります。そこで、各ボイスのNote番号を保持しておく、"VoiceState"構造体を導入しました。MIDI入力の度に、このVoiceStateを書き換え、On / Off状態に応じて2つのリスト間を移動させます。Voice本体は、このVoiceStateを参照して、そのふるまいを決めます。
続いて、下記にMIDI入力時の具体的な処理を記載します。
大体のことは、コード中のコメントに書いてあるので、以下は蛇足です。
まず、MIDI on入力を受け取った時の処理ですが、空のボイスの有無で処理が分岐しますが、いずれにせよ、リストの先頭ボイスを上書きし、lisOnVoiceの末尾に要素を移しています。変更のあったボイスをリストの末尾から詰めていくことで、変更履歴の古い順にソートされた状態が保たれます。これは、MIDI off入力の時も同じで、MIDI offになったボイスは、lisOnVoiceからlisNoteOffの末尾に移されます。
MIDI offになった直後のボイスは、Amp EnvがまだRelease状態にある可能性が高く、可能な限り再度On状態になるまでの時間を遅らせたいというのが自然な要求かと思います。これについては、リスト内において、Offなったタイミングが古い順に空ボイスがソートされているため、自然にクリアすることができます (MIDI onの書き込みは、lisNoteOffの先頭に対して行うようになっているため、古い空ボイスから優先して使用されます) 。
残りのMIDI入力処理以外の部分については、下記のリポジトリに全ソースコードを置いておきます。
動作確認
ようやく、シンセらしくなってきました。
うんうん。今度は、ちゃんと他のVAシンセと同じ感じでポリフォニック化できているはず。 pic.twitter.com/AMhTsvvNc0
— アヲギリ (@Aogiri_m2d) 2017年6月18日
今後は
フィルターやLFOやら、いろいろ必要なものがありますが、正直、未定です。たぶん、FMについてのあれこれになりそう。