JUCEには、簡易にリバーブを付加できるモジュールがあるようです。
JUCEモジュール、至れり尽くせりだね~
JUCEチュートリアル、Intorduction to DSPの最後の項目、「Adding a simple reverb」を進めていきます。JUCEのリバーブモジュールを利用して、今まで実装してきたシンセの音に、残響を加えるような内容となっています。
今までの記事はこちらになります。
オシレータの作成、CustomOscillatorクラスの実装
オシレータにいろいろな波形を設定、ルックアップテーブルによる波形の生成
Introduction to DSP、LFOでモジュレーションを実装
こんな人の役に立つかも
・JUCEチュートリアル、Introduction to DSPをやっている人
・JUCEのReverbモジュールを利用したい人
・JUCEで残響を簡易に実装したい人
juce::dsp::Reverbモジュール
juce::dsp::Reverbモジュールは、juce::ReverbをProcessorChainで容易に利用することができるようなラッパーとのことです。ProcessorChainに与えて利用するために、juce::dsp::Reverbを利用します。
このモジュールは、FreeVerbの手法をベースにして作成されたということです。
https://www.dsprelated.com/freebooks/pasp/Freeverb.html
JUCEのリバーブモジュールには、パラメータとして、Reverb::Parameters構造体を与えることで残響をコントロールすることができます。
Parameters構造体には、ルームサイズなど、なじみのあるようなパラメータが並んでいます。
もう、プログラムを組むというより、プラグインをそのまま挿入するだけのようなイメージですね。
チュートリアルの実装では、パラメータを与えずに、デフォルトの状態で利用しています。
実装
PluginProcessor.hに記載している、AudioEngineクラスに、juce::dsp::Reverveモジュールを追加していきます。ProcessorChainクラスを利用した方法で追加していきます。
AudioEngineクラスは、今まで実装してきたVoiceクラスをまとめているようなクラスで、ここからVoiceクラスが生成されているようです。
AudioEngineクラスの音声処理部分、renderNextBlock(renderNextSubBlock)で残響処理を行うことで、すべての和音の発音に対して一度に残響を加えるようにしています。
AudioEngineクラスのprivateなメンバとして、[1]のリバーブのインデックスと、[2]のProcessorChainクラスを追加しました。
private:
//...略...
enum
{
reverbIndex//[1]リバーブのインデックスを定義します。(整数の0)
};
juce::dsp::ProcessorChain<juce::dsp::Reverb> fxChain;//[2]ProcessorChainクラスを定義します。
};
prepare関数にProcessorChainクラスのオブジェクトfxChainのprepareを呼び出すように追加します。
void prepare(const juce::dsp::ProcessSpec& spec) noexcept
{
setCurrentPlaybackSampleRate(spec.sampleRate);
for (auto* v : voices)
dynamic_cast<Voice*> (v)->prepare(spec);
fxChain.prepare(spec);//追加しました。
}
renderNextSubBlock関数は、有効な和音数分renderNextBlockを実行するような仕組みになっているようです。
void renderNextSubBlock(juce::AudioBuffer<float>& outputAudio, int startSample, int numSamples) override
{
MPESynthesiser::renderNextSubBlock(outputAudio, startSample, numSamples);
//音声にリバーブを追加します。
auto block = juce::dsp::AudioBlock<float>(outputAudio);//[1]
auto blockToUse = block.getSubBlock((size_t)startSample, (size_t)numSamples);//[2]
auto contextToUse = juce::dsp::ProcessContextReplacing<float>(blockToUse);//[3]
fxChain.process(contextToUse);//[4]
}
リバーブが付加されるような処理を追記します。
[4]のfxChainにContextを与えるために、[1]~[3]で出力音声を加工しています。outputAudioは、AudioBufferクラスの音声バッファデータで、そこからblockを作成[1]します。[2]で、blockからgetSubBlock関数で第一引数、をサンプルスタート番号、第二引数をバッファのサイズ(サンプル数)にして、AudioBlockのポインタをblock to useとして、取得します。[3]として、blockToUseをProcessContextReplacing関数でcontextToUseにしています。
ProcessorChain周りのところは詳細はまだあまりよくわかっていません。
別のチュートリアルにあるかもしれないから、チュートリアルをいろいろとやってみよう~
Contextや、ProcessorChainの処理内容などは、まだあまり深くおっていませんので、また機会がある時により深く調査していきたいなと思います。
残響感が付加されて、キー入力後も若干の残響の表示が出ています。