音質調整のためのフィルターモジュールを追加していきます。
ハイパスフィルターを追加するんだね
ハイパスフィルターモジュールを追加することで、濁った低音をカットして、音質を補正していきます。チュートリアルの内容としては、juce::DSPのフィルターモジュールでハイパスフィルターをプロセッサチェインに追加するというような内容となります。
公式のチュートリアルはこちらの「Adjust the filter」の項目となります。
こんな人の役に立つかも
・JUCEのDSPチュートリアルを勉強している人
・JUCEチュートリアルの「Add distortion through waveshaping and convolution」を進めている人
実装するサウンド
最初の音が前回までのノンフィルター状態での音です。あとの8秒くらいからの音が、ハイパスフィルターをかけてウェーブシェーピングのソフトクリッピングで歪ませた音になります。
※プチプチしたり、あまり聴きやすい音ではないので、音量を下げた状態から音量を上げて適量にすることをお勧めします。音量は再生バーの右端にもあります。
Distortionクラス内のProcessorChainクラスへFilterモジュールを追加していきます。
実装
PluginProcessor.hに定義した、「Distortionクラス」のprivateなメンバです。フィルターを歪ませる前に入れたいので、[1] のように、enumのインデックスの一番最初に「filterIndex」を追加しました。
private:
enum
{
filterIndex,//[1]フィルターのインデックスを追加します。
preGainIndex,
waveshaperIndex,
postGainIndex
};
//[2]usingでフィルターモジュールの表記を短縮します。
using Filter = juce::dsp::IIR::Filter<Type>;
using FilterCoefs = juce::dsp::IIR::Coefficients<Type>;
//[3]ProcessorChainで行うDSP処理にFilterを追加します。
/*juce::dsp::ProcessorChain<juce::dsp::Gain<Type>, juce::dsp::WaveShaper<Type>, juce::dsp::Gain<Type>> processorChain;*/
//changed
juce::dsp::ProcessorChain<juce::dsp::ProcessorDuplicator<Filter, FilterCoefs>,
juce::dsp::Gain<Type>, juce::dsp::WaveShaper<Type>, juce::dsp::Gain<Type>> processorChain;
};
[2]では、usingでFilterモジュールの表記を短くして使いやすくしています。C++のusingについては、いくつかの使い方があるようなので、こちらを参考にしました。
[3]はProcessorChainでDSP処理を行う一連の流れに、Filterを追加したものに変更しました。上側のコメントアウトしてある行が以前のものになります。Filterモジュールは、ProcessorDuplicatorでこのように定義して利用します。シンプルにフィルターだけを実装したい場合は、こちらの記事もご参照ください。
ProcessorDuplicatorは、DSPモジュールをマルチチャンネル(ステレオになった時など)に対応させるものです。IIRフィルター、FIRフィルターなどのいくつかのDSPモジュールはモノラルのモジュールなので、このようにProcessorDuplicatorで対応する必要があるそうです。こちらのJUCEフォーラムのディスカッションを参考にしました。
次に、prepare関数で、Filterの初期化をします。
void prepare(const juce::dsp::ProcessSpec& spec)
{
//以下の2行を追加しました。
auto& filter = processorChain.template get<filterIndex>();//[1]
filter.state = FilterCoefs::makeFirstOrderHighPass (spec.sampleRate, 1000.0f);//[2]
processorChain.prepare(spec);
}
[1]で、ProcessorChainに追加したフィルタモジュールの参照を取得しています。[2]では、フィルタの係数を計算してフィルタモジュールに係数を設定します。makeFirstOrderHighPassは一次のハイパスフィルタの係数を求める関数で、引数にサンプリングレートと、カットオフ周波数を与えることで、係数を得ることができます。
フィルタの係数の計算は、数学的に求めますが、計算式に精通していなくても、概念的にわかっているだけで、十分なようにJUCEの関数が定義されていますので、数学や、フィルターの理論が苦手な人でも扱うことができるようになっています。ちなみに、双二次のフィルタは以前のシンプルなハイパスフィルターの実装で利用していますので、こちらもご参照ください。
これだけで、ハイパスフィルターの設定が完了です。
Distortionクラス全体
今回変更した、Distortionクラスの全体を記載します。
template <typename Type>
class Distortion
{
public:
Distortion()
{
auto& waveshaper = processorChain.template get<waveshaperIndex>();
waveshaper.functionToUse = [](Type x)
{
return std::tanh(x);
};
auto& preGain = processorChain.template get<preGainIndex>();
preGain.setGainDecibels(30.0f);
auto& postGain = processorChain.template get<postGainIndex>();
postGain.setGainDecibels(-10.0f);
}
void prepare(const juce::dsp::ProcessSpec& spec)
{
//ここを追加しました。
auto& filter = processorChain.template get<filterIndex>();
filter.state = FilterCoefs::makeFirstOrderHighPass (spec.sampleRate, 1000.0f);
processorChain.prepare(spec);
}
template <typename ProcessContext>
void process(const ProcessContext& context) noexcept
{
processorChain.process(context);
}
void reset() noexcept
{
processorChain.reset();
}
private:
enum
{
filterIndex,//フィルターのインデックスを追加しました。
preGainIndex,
waveshaperIndex,
postGainIndex
};
//DSP処理の順番にフィルタを追加しました。
using Filter = juce::dsp::IIR::Filter<Type>;
using FilterCoefs = juce::dsp::IIR::Coefficients<Type>;
//「フィルタ→入力ゲイン→ウェーブシェーピング→出力ゲイン」の処理順に定義します。
juce::dsp::ProcessorChain<juce::dsp::ProcessorDuplicator<Filter, FilterCoefs>,
juce::dsp::Gain<Type>, juce::dsp::WaveShaper<Type>, juce::dsp::Gain<Type>> processorChain;
};