ディレイの音にフィルターをかけたいね〜
プラグイン内でフィルターがかけられると便利だよね〜
JUCEディレイプラグイン、ディレイ音へのフィルタリングのカットオフ周波数を変更できるようにしました。現状、ディレイの音にはハイパスフィルターを通していますので、今回はこのハイパスフィルターのカットオフ周波数を可変にするように改造しました。フィルター部分をより高機能にしていくことで、音作りの幅も広がりそうです。GUI的には要検討ですが^^;
こんな人の役に立つかも
・JUCEプラグイン開発をしている人
・本ブログのディレイプラグイン作成をしている人
・ディレイのエフェクト音をフィルターで加工したい人
改造の方向性
Delayクラス内のフィルターモジュールの固定値を可変パラメータとしたいので、AudioProcessorクラスで定義するAPVTSパラメータのポインタをprepare関数での初期化時に渡すというような点を追加していこうと思います。
AudioProcessorクラス内で定義するパラメータの保持する変数のポインタをDelayクラス(ProcessorChainクラス内に存在するDelayクラスのインスタンス)に渡して利用できるようにします。
以下の手順で機能を追加します。
①Delayクラスへポインタ、cutoffParam_filterを追加
②Delayクラスへアクセサ「setCutoffFreq」を追加
③AudioProcessorクラスへカットオフ周波数のAPVTSパラメータを追加
④エディタ側へスライダーの追加
Delayクラスへの変更
ディレイクラスで、フィルターのカットオフ周波数をパラメータの値で変更できるようにします。
template <typename Type, size_t maxNumChannels = 2>
class Delay
{
public:
//...略...
template <typename ProcessContext>
void process(const ProcessContext& context) noexcept
{
//...略...
//[3]パラメータを取得してカットオフ周波数に設定します。
Type freq = *cutoffParam_filter;
filterCoefs = juce::dsp::IIR::Coefficients<Type>::makeFirstOrderHighPass(sampleRate, freq);
//[3-1]このfor文で、全チャンネル分のフィルターにパラメータを反映させます。
for (auto& f : filters)
{
f.coefficients = filterCoefs;
}
//テスト用
//DBG("cutoff:" << freq);
for (size_t ch = 0; ch < numChannels; ++ch)
{
//...略...
}
}
//[2]アクセサを追加しました。
void setCutoffParam(std::atomic<float>* param) {
cutoffParam_filter = param;
}
private:
//...略...
//[1]ポインタを追加しました。
std::atomic<Type>* cutoffParam_filter = nullptr;
//...略...
};
[1]のように、APVTSのパラメータを紐づけるためのポインタをprivateなメンバ「cutoffParam_filter」を準備しました。そして、[2]では、cutoffParam_filterをAudioProcessorクラスから受け取ることができるようにアクセサ「setCutoffParam」を追加しました。setCutoffParamをAudioProcessorのprepareToPlay関数で実行することで、cutoffParam_filterにAudioProcessorクラスのカットオフのパラメータが紐付き、Delayクラスからも見ることができるようになります。
[3]では、カットオフ周波数のパラメータを更新することでフィルターの設定を書き換えています。
APVTSパラメータの追加
ここから、プラグイン本体クラスに、APVTSのパラメータを追加していく内容になります。
APVTSのパラメータ追加手順は、こちらの記事にもまとめておりますので、ご参照ください。
PluginProcessor.h
class TheDelayAudioProcessor : public juce::AudioProcessor, private juce::Timer
{
public:
//...略...
private:
//[1]追加しました
std::atomic<float>* cutoffParam = nullptr;
//...略...
PluginProcessor.cpp
コンストラクタ、メンバイニシャライザでパラメータの定義を行い、コンストラクタ処理で先ほどのポインタに紐付けます。
単位をHzとして、小数点第一位までの表記とするようにしました。
フィルターカットオフ周波数のパラメータは、0という値を与えられないので、最小値は1としました。デフォルト値を20Hzとしています。
//...略...
//[1]次のようにパラメータを定義しました。
std::make_unique<juce::AudioParameterFloat>("cutoff", // ID
"Cutoff", // name
juce::NormalisableRange<float>(1.0f,22000.0f),
20.0f,
"cutoff",
juce::AudioProcessorParameter::genericParameter,
[](float value, int) {return juce::String(value, 1) + "Hz";}
)
})
{
//...略...
//[2]追加しました。
cutoffParam = parameters.getRawParameterValue("cutoff");
}
prepareToPlay関数で、先ほどのDelayクラスのsetCutoffParamを呼び出してパラメータを渡してあげます。
void TheDelayAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
//...略...
//[1]このようにしてフィルターモジュールにアクセスします。
processorChain.get<DelayIndex>().setCutoffParam(cutoffParam);
[1]のように、ProcessorChain内のフィルターモジュールにアクセスしてsetCutoffParamを呼び出します。この時に、AudioProcessorから受け取っているAPVTSパラメータのカットオフ周波数のポインタを渡しました。これで、ディレイクラスのインスタンスにAPVTSのパラメータへのポインタが渡され、パラメータを可変とすることができました。
エディタ側
GUIへカットオフ周波数のスライダーを設定していきます。
PluginEditor.h
スライダーコンポーネント、スライダーアタッチメントを追加しました。スライダーアタッチメントクラスは、上ですでにtypedefしているものを利用しています。
class TheDelayAudioProcessorEditor : public juce::AudioProcessorEditor
{
//...略...
//スライダーの準備です。
juce::Slider cutoffSlider;
std::unique_ptr<SliderAttachment> cutoffAttachment;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TheDelayAudioProcessorEditor)
};
PluginEditor.cpp
コンストラクタ内でスライダーの初期化を行います。
//コンストラクタ
//[1]スライダーを初期化しました。
addAndMakeVisible(cutoffSlider);
cutoffAttachment.reset(new SliderAttachment(valueTreeState, "cutoff", cutoffSlider));
//[2]以下の一連の設定で、スライダーの中心を1000Hzとします。
cutoffSlider.setRange(1.0f,22000.0f);
cutoffSlider.setSkewFactorFromMidPoint(1000.0f);
cutoffSlider.setValue(100.0f);
setSize (400, 300);
}
[1]では、いつも通りのスライダー可視化、パラメータへの紐付けを行なっています。
[2]では、周波数を扱う際に、線形に変化するスライダーの変化の割合が操作しづらいので、中心を1000Hzとするような変更を行なっています。これは、設定に順番があり、この点を模索する記事をこちらでも書いておりますので、ご参照ください。
resized関数で、GUIにスライダーを配置しました。
void TheDelayAudioProcessorEditor::resized()
{
//...略...
//[1]
cutoffSlider.setBounds(30,150,200,30);
}