【JUCEプラグイン開発】ディレイエフェクト、ドライレベルを追加する

パンダさん

センドリターンで使いたいとき、ドライ音を調整できるといいですよね

コパンダ

音声処理部分でドライ音にゲインを追加できるか、みてみよう~

ディレイエフェクト開発の備忘録です。今回は、ドライ音を調整できる機能を追加しました。エフェクトとして、センドリターンで使うときに、ウェット音(エフェクトがかかっている音)だけを出力ができるようにしたいと思いました。一般的に、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);
}
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次