センドリターンで使いたいとき、ドライ音を調整できるといいですよね
音声処理部分でドライ音にゲインを追加できるか、みてみよう~
ディレイエフェクト開発の備忘録です。今回は、ドライ音を調整できる機能を追加しました。エフェクトとして、センドリターンで使うときに、ウェット音(エフェクトがかかっている音)だけを出力ができるようにしたいと思いました。一般的に、MIXという調整方法もたくさんあるようですが、ドライ音とウェット音をそれぞれ独立で操作できる方が、分かりやすく、実装も簡単で、幅広く応用できそうなので、ドライ、ウェットというパラメータで調整したいと考えました。
実装の概要
今回の大まかな変更点です。
①ドライ音用のAPVTSパラメータを準備します。
パラメータは、ダイヤルスライダーで変更できるようにUIも作成します。
②音声処理でレベル計算の追加をします。
Delayクラス内の音声処理部分に、パラメータ値を渡して、レベルの値とします。
実装
パラメータとスライダーを追加
プロセッサ側
ドライ音用のメンバをPluginProcessor.hのprivateメンバに追加しました。APVTSのパラメータの追加方法については、こちらの記事にもまとめておりますので、ご参照くださいませ。
juce::AudioProcessorValueTreeState parameters;
std::atomic<float>* maxDelayTime = nullptr;
std::atomic<float>* delayTime_L = nullptr;
std::atomic<float>* delayTime_R = nullptr;
std::atomic<float>* wetLevel = nullptr;
std::atomic<float>* feedback = nullptr;
std::atomic<float>* dryLevel = nullptr;//追加しました。
PluginProcessor.cppのメンバイニシャライザ部分とコンストラクタ内に、ドライ音のAPVTSパラメータを追加しました。
パラメータは、0~1の間で変化するようにしています。
//[1]以下のパラメータに設定します。
std::make_unique<juce::AudioParameterFloat>("dryLevel", // ID
"DryLevel", // name
0.0f, // min
1.0f, // max
0.8f) // default
,
//...略...
{
maxDelayTime = parameters.getRawParameterValue("maxDelayTime");
delayTime_L = parameters.getRawParameterValue("delayTimeL");
delayTime_R = parameters.getRawParameterValue("delayTimeR");
wetLevel = parameters.getRawParameterValue("wetLevel");
feedback = parameters.getRawParameterValue("feedback");
dryLevel = parameters.getRawParameterValue("dryLevel");//[2]パラメータを紐づけます。
//...略...
エディタ側
先ほど追加したパラメータをコントロールするためのダイヤルスライダーを追加していきます。
PluginEditor.hに、スライダーコンポーネントと、アタッチメントを追加しました。
private:
TheDelayAudioProcessor& audioProcessor;
juce::AudioProcessorValueTreeState& valueTreeState;
//juce::Slider delayMaxTimeSlider;
juce::Slider delayTimeLSlider;
juce::Slider delayTimeRSlider;
juce::Slider wetLevelSlider;
juce::Slider feedbackSlider;
juce::Slider dryLevelSlider;//[1]スライダーを追加します。
//std::unique_ptr<SliderAttachment> delayMaxTimeAttachment;
std::unique_ptr<SliderAttachment> delayTimeLAttachment;
std::unique_ptr<SliderAttachment> delayTimeRAttachment;
std::unique_ptr<SliderAttachment> wetLevelAttachment;
std::unique_ptr<SliderAttachment> feedbackAttachment;
std::unique_ptr<SliderAttachment> dryLevelAttachment;//[2]アタッチメントを追加します。
PluginEditor.hのコンストラクタ内でドライ音のダイヤルスライダーの設定を行います。
ロータリースライダーにして、以前定義したカスタマイズしたLookAndFeelを反映させています。これで画像ありのダイヤルになります。
//ドライスライダーの各種設定を行います。
addAndMakeVisible(dryLevelSlider);
dryLevelAttachment.reset(new SliderAttachment(valueTreeState, "dryLevel", dryLevelSlider));
dryLevelSlider.setSliderStyle(juce::Slider::Rotary);
dryLevelSlider.setLookAndFeel(&dialLookAndFeel);
dryLevelSlider.setTextBoxStyle(juce::Slider::TextBoxBelow, false, 64, 28);
dryLevelSlider.setColour(juce::Slider::textBoxTextColourId, juce::Colours::white);
dryLevelSlider.setColour(juce::Slider::textBoxOutlineColourId, juce::Colour::fromRGBA(0,0,0,0));
カスタマイズしたLookAndFeelはでコンストラクタでnullptrとします。
TheDelayAudioProcessorEditor::~TheDelayAudioProcessorEditor()
{
delayTimeLSlider.setLookAndFeel(nullptr);
delayTimeRSlider.setLookAndFeel(nullptr);
wetLevelSlider.setLookAndFeel(nullptr);
dryLevelSlider.setLookAndFeel(nullptr);//LookAndFeelのnull設定を行います。
//...略...
resized関数で、ダイヤルを配置しました。
void TheDelayAudioProcessorEditor::resized()
{
delayTimeLSlider.setBounds(232, 120, 64, 94);
delayTimeRSlider.setBounds(323, 120, 64, 94);
wetLevelSlider.setBounds(60, 205, 64, 87);
dryLevelSlider.setBounds(60, 93, 64, 87);//スライダーを配置しました。
音声処理部分
Delayクラスの変更
先ほど定義したドライ音のパラメータを受け取ることができるように、Delayクラスを変更して行きます。
まずは、privateなメンバに、ドライ音のパラメータを格納するためのメンバを準備しました。
private:
std::atomic<Type>* cutoffParam_filter = nullptr;
std::array<DelayLine<Type>, maxNumChannels> delayLines;
std::array<size_t, maxNumChannels> delayTimesSample;
std::array<Type, maxNumChannels> delayTimes;
Type feedback{ Type(0) };
Type wetLevel{ Type(0) };
Type dryLevel{ Type(0) };//dryパラメータを受け取るメンバを準備します。
このメンバに値を格納するためのアクセサを準備します。setDryLevelという関数名にしました。プラグイン本体クラスから呼び出すことで、メンバにパラメータ値を渡すことができるようになりました。
//publicなメンバ関数として、次のような関数を追加しました。
void setDryLevel(Type newValue) noexcept
{
jassert(newValue >= Type(0) && newValue <= Type(1));
dryLevel = newValue;
}
process関数内の出力音を作成する計算で、inputSampleという値(入力された音声サンプル値)に対してドライレベルの値を掛け合わせます。渡される値は0~1で変化するので、0の時inputが無音になり、1のとき入力レベルとなります。
void process(const ProcessContext& context) noexcept
{
//...略...
for (size_t i = 0; i < numSamples; ++i)
{
auto delayedSample = filter.processSample(dline.get(delayTime));
auto inputSample = input[i];
auto dlineInputSample = std::tanh(inputSample + feedback * delayedSample);
dline.push(dlineInputSample);
//次の出力音声の計算で、dryLevelをinputにかけ合わせました。
auto outputSample = inputSample * dryLevel + wetLevel * delayedSample;
output[i] = outputSample;
//...略...
プラグイン本体側の設定
先ほど定義したDelayクラスをprocessBlock毎に実行するようにします。val6というローカル変数にAPVTSの値を取得して、setDryLevel関数で値を渡します。
//...略...
//dryLevelのパラメータ値を受け取り、数値を設定します。
float val6 = *dryLevel;
processorChain.get<DelayIndex>().setDryLevel(val6);
auto block = juce::dsp::AudioBlock<float>(buffer).getSubBlock((size_t)0, (size_t)buffer.getNumSamples());
auto context = juce::dsp::ProcessContextReplacing<float>(block);
processorChain.process(context);
}