JUCEでソフトシンセをつくる 3 ~アンプエンベロープ
前回まで
MIDI入力を通してAM・FMオシレータが鳴らせるようになりました。
今回は
ADSRエンベロープカーブを持つアンプを実装します。実装するアンプエンベロープの仕様等については、後ほど記載します。
ソースファイルの追加
下の画像のように追加します。
アンプの実装は、前回のオシレータの時と同様にパラメータ部をモジュール本体部から独立させてます ("AmpParameters", "Amplifier")。
また、オシレータもアンプもパラメータ部の実装については、共通する部分が多いので新たに"ParameterBase"クラスを設けて、それを継承する形で各モジュールのパラメータを実装していきましょう。これで、今後、さらにモジュールが増えることになっても実装をスムーズに進められるはずです。
Parameterの実装
まずは、各モジュールのParameterクラスの基底となるParameterBaseクラスを用意してやります。
これを継承してAmpParameterクラスを実装します。今回、必要なパラメータはADSR (Attack, Decay, Sustain, Release)の4種類になります。Sustain以外の時間に関するパラメータの表示上の単位次元は"秒"としておきます。Sustainに関しては無次元量としましょう (0 ~ 1.0 = 0 ~ 100 %)。
継承先では、コンストラクタでパラメータインスタンスを作製するだけです。これで、パラメータの実装はかなり楽になりました。また、前回作製した"OscParameter"も同様の形に修正しておきましょう(詳細は割愛)。
Amplifierの実装
ADSR Envを持つアンプモジュールの本体部を実装していきます。これから、実装するアンプエンベロープのイメージ図は以下のようになります。
よく見るタイプの図ですね。今回、エベロープカーブは全て直線にしてしまいます。図からわからる用に、エンベロープは、AttackStateからOffStateまでの5種類の状態をとります。基本的には、各状態に入ってからの経過サンプル時間を積算していき、パラメータADRで設定した時間を超えたら、次の状態へと遷移するような仕組みを実装します。ただし、OffState->AttackState、SustainState->ReleaseStateの遷移は、時間経過ではなく、それぞれ、Ampが"MidiOn"、"MidiOff"を受けっとたタイミングで状態の遷移が生じます。
さて、個々の状態において異なる内部パラメータは、そのカーブの傾き(sloop)になります。各状態におけるsloopを列挙すると以下のようになます。
状態が遷移する度にパラメータからsloopを計算して、1サンプル時間経過ごとに、gainにsloopの値を加算してやればよさそうです。
以上をまとめると次のようになります。
- MidiOn / MidiOffがあればAttack / Releaseへ状態遷移し、パラメータからsloop、初期gain、状態持続時間を計算。経過サンプル時間もリセット。
- gainの値に応じて、AudioBufferの信号強度を減衰させる。
- 1サンプル時間進めると同時に、gain値を更新 (sloopをgainに加算)。
- もし、状態遷移してからの経過サンプル時間がパラメータで設定した、ADRの時間を超えていれば。状態の遷移を行い、sloop、初期gain、状態持続時間を再計算、経過サンプル時間もリセット。
これを実装したものが、以下になります。
個々の状態における処理の違いはさほど複雑なものでもないので、わざわざ、StateパターンやStrategyパターンによる状態分けをする必要はないでしょう。また、先にも述べたようにSustain、OffStateは時間変化による内部パラメータの変化は起こり得ないので、勝手な遷移が起こらないように経過サンプル時間の積算を避けておきます。
SynthモジュールにAmplifierを組み込む
これまでのOscillatorの時と同様に、実装したAmplifierをOscillatorと共にSynthクラス内で走らせてやりましょう。
これまでとは違い、MidiOffを受け取ってもOscの発振を止めず、AmplifierがOffStateになったタイミングでOscの発振を止めるようにしています。
後は、いつものように、これをPluginProcessorのProcessBlockで走らせてやればOKです。
割愛した部分も含めた全ソースコードは、下記リポジトリにあるので適当に参考にして下さい。
動作確認
動画とるのがめんどくさいので、後で。