JUCEプラグイン開発、AudioProcessorValueTreeStateの設定手順

毎回APVTSの使い方を忘れてしまいます。

後々管理が楽とはいえ、設定する場所は結構あるよね~

AudioProcessorValueTreeState(以下APVTS)をプラグインプロジェクト作成毎に忘れてしまい、毎回いろいろと調べるので、どこをどのように設定するのかを備忘録します。

前回実装したミニマムなフィルタープラグインにカットオフ周波数の可変パラメータをひとつ追加するという内容を例としてやっていきます。

目次

こんな人の役に立つかも

・JUCEプラグインのパラメータを追加したい人

・AudioProcessorValueTreeStateの設定をしたい人

・AudioProcessorValueTreeStateの設定を忘れやすい人

概要

パラメータ保存に関する処理はなしとして、シンプルにAPVTSにパラメータを追加してGUIコンポーネント(例としてスライダー)に紐づけするという例を備忘録していきます。

大まかに、次の流れでパラメータをAPVTSで設定します。

1.プロセッサー側(PluginProcessor)の設定

□ 1-1.APVTSとパラメータの宣言

□ 1-2.APVTSとパラメータの初期化

□ 1-3.createEditor関数の変更

□ 1-4.任意の処理の記述

2.エディタ側(PluginEditor)の設定

□ 2-1.エディタのコンストラクタ引数の変更

□ 2-2.アタッチメントの定義等

□ 2-3.エディタのprivate変数の定義

□ 2-4.コンストラクタでGUIコンポーネント等の初期化

□ 2-5.GUIコンポーネントの描画

1.プロセッサー側

1-1.APVTSとパラメータの宣言

「PluginProcessor.h」の〇〇AudioProcessorクラスに記載します。

APVTSクラスでパラメータを管理するため、次のようにprivateな変数定義を行いました。cutoffParamというfloat型の数値をパラメータ例としていきます。

private:
    //[1-1]
    juce::AudioProcessorValueTreeState parameters;
    std::atomic<float>* cutoffParam = nullptr;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Filter_plugin02AudioProcessor)
};

私の例では、プロジェクト名がFilter_plugin02なので、「Filter_plugin02AudioProcessor」クラスに追記しました。

1-2.APVTSとパラメータの初期化

「PluginProcessor.cpp」に記載します。

コンストラクタでcutoffParamパラメータを初期化します。ここは、初期化→紐づけの順番です。

Filter_plugin02AudioProcessor::Filter_plugin02AudioProcessor()
#ifndef JucePlugin_PreferredChannelConfigurations
//...略...
#endif
    //[1-2]AudioProcessorValueTreeStateの初期化です。
    ,parameters(*this, nullptr, juce::Identifier("APVTSTutorial"),
        {
            std::make_unique<juce::AudioParameterFloat>("cutoff",  // ID
                                                        "Cutoff",  // name
                                                         0.0f,     // min
                                                         22000.0f, // max
                                                         0.0f)// default
        })
{
    //IDを指定してパラメータの紐づけです。
    cutoffParam = parameters.getRawParameterValue("cutoff");
}

IDとnameがややこしいので、個人的には、IDは頭文字小文字、nameには頭文字大文字というような何となくルールを使っています。

※パラメータを複数設定する場合、次のように、カンマで区切って追加します。

//...略...
    ,parameters(*this, nullptr, juce::Identifier("APVTSTutorial"),
        {
            std::make_unique<juce::AudioParameterFloat>("cutoff",
                                                        "Cutoff",
                                                         0.0f,
                                                         22000.0f,
                                                         0.0f),//←カンマで区切る
            //2個目のパラメータ
            std::make_unique<juce::AudioParameterFloat>("qParam",
                                                        "QParam",
                                                        juce::NormalisableRange<float>(0.01f, 4.0f),
                                                        0.70710678f,
                                                        "Qparam",
                                                        juce::AudioProcessorParameter::genericParameter,
                                                        [](float value, int) {return juce::String(value, 3);}
                                                        )
        })
//...略...

booleanをパラメータとしたい場合、以下のようにAudioParameterBoolを利用することができます。

std::make_unique<juce::AudioParameterBool>("tf1",      // parameterID
                                           "Tf1",      // parameter name
                                           false)      // default

1-3.createEditor関数の変更

createEditor関数には、Editorのコンストラクタ引数を変更して、APVTSクラスのparameters変数を渡す必要があるので、次のように第二引数を追加します。

juce::AudioProcessorEditor* Filter_plugin02AudioProcessor::createEditor()
{
    //[1-3]引数でAPVTSクラスのparametersを渡します。
    return new Filter_plugin02AudioProcessorEditor (*this, parameters);
}

1-4.任意の処理の記述

その他、プロセッサー側では、パラメータをprocessBlock関数でパラメータを利用するなど、任意の処理を記載します。

例として、パラメータ値をデバッグ出力する場合、float型の値としてパラメータを取得して表示することができます。

void Filter_plugin02AudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages)
{

    //[1-4]一度float型として受けます。
    float val = *cutoffParam;
    DBG("cutoffParam:" << val);
}

2.エディタ側

2-1.エディタのコンストラクタ引数の変更

ここからは、「PluginEditor.h」に記載します。

class Filter_plugin02AudioProcessorEditor  : public juce::AudioProcessorEditor
{
public:
    //[2-1]コンストラクタの引数を変更しました。
    Filter_plugin02AudioProcessorEditor (Filter_plugin02AudioProcessor&, juce::AudioProcessorValueTreeState& vts);

//...略...
//...[2-2]へ続きます...

コンストラクタの第一引数をプロセッサ側のクラス名とする点に注意です。また、第二引数としてvtsを取ります。今回は、これを新たに追加しました。第一引数のクラスは、「〇〇AudioProcessor&」となり、〇〇には、プロセッサ側のクラス名、一般的にテンプレートから生成した場合、プロジェクト名が入ります。

2-2.アタッチメントの定義等

今回は、スライダーへパラメータの紐づけを行うので、SliderAttachmentクラスを利用しています。typedefで長い名称宣言を短縮しています。


    //[2-2-1]アタッチメントの名称を省略する定義です。
    typedef juce::AudioProcessorValueTreeState::SliderAttachment SliderAttachment;
//...[2-3]へ続きます。...

2-3.エディタのprivate変数の定義

エディタのprivateな変数として、次のように定義します。

    //[2-3]パラメータのための変数を定義します。
    juce::AudioProcessorValueTreeState& valueTreeState;
    juce::Slider cutoffSlider;
    std::unique_ptr<SliderAttachment> cutoffAttachment;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Filter_plugin02AudioProcessorEditor)
};

APVTSクラスの変数を受け取るためのvalueTreeState、スライダーコンポーネントのcutoffSlider、パラメータへの紐づけのためのcutoffAttachmentを定義しています。

2-4.コンストラクタでGUIコンポーネント等の初期化

ここからは、「PluginEditor.cpp」への記載となります。

//パラメータ追加によりコンストラクタ引数へ第二引数vstを追加します。(右のほうに隠れています^^;)
Filter_plugin02AudioProcessorEditor::Filter_plugin02AudioProcessorEditor(Filter_plugin02AudioProcessor& p, juce::AudioProcessorValueTreeState& vts)
    : AudioProcessorEditor(&p), valueTreeState(vts) ,audioProcessor(p)
{
    //コンポーネントの設定を行います。
    addAndMakeVisible(cutoffSlider);
    cutoffAttachment.reset(new SliderAttachment(valueTreeState, "cutoff", cutoffSlider));

    setSize (400, 300);
}

2-5.GUIコンポーネントの描画

任意のコンポーネントを描画します。今回は、背景塗りつぶしとスライダーの配置を行いましたので、次のように変更を行いました。

paint関数を背景の塗りつぶし描画処理をするのみに置き換えると次のようになります。

void Filter_plugin02AudioProcessorEditor::paint (juce::Graphics& g)
{
    g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));

}

今回例で利用した「cutoffSlider」をGUIに配置すると次のようにresized関数にsetBoundsで指定位置に配置しました。

void Filter_plugin02AudioProcessorEditor::resized()
{
    cutoffSlider.setBounds(10, 10, 200, 30);
}

これで、スライダーの数値をAPVTSで管理するfloatの数値に紐づき、その値をProcessorで利用できるようになりました。

やはり、APVTSの使い方に慣れていくということは必要ですね~

コンボボックスの場合

コンボボックスを紐付けするときは、プロセッサー側で定義するパラメータはAudioParameterFloatで定義しておき、GUIをコンボボックスコンポーネントとすることで、紐付けができました。スライダーとの違いは次の通りです。

PluginEditor.hのプラグインの本体のエディタークラスにて、以下のようにコンボボックスに関連するクラスでオブジェクトを宣言します。

    typedef juce::AudioProcessorValueTreeState::SliderAttachment SliderAttachment;
    //[1]アタッチメントクラスをtypedefします。
    typedef juce::AudioProcessorValueTreeState::ComboBoxAttachment ComboBoxAttachment;
//...略...

private:
//...略...
    //[2]コンボボックスコンポーネントに関連するprivateなメンバを定義します。
    juce::ComboBox TheComboBox;
    std::unique_ptr<ComboBoxAttachment> ComboAttachment;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (_01_panda_filterAudioProcessorEditor)
};

[1]アタッチメントを「ComboBoxAttachment」クラスとします。

[2]privateなメンバとしてComboBoxクラス、アタッチメントを準備します。

PluginEditor.cppの●●PluginEditorクラス内のコンストラクタにて、コンボボックスを初期化します。

//...略...
    //[1]コンボボックスの初期化を追加しました。
    addAndMakeVisible(TheComboBox);
    ComboAttachment.reset(new ComboBoxAttachment(valueTreeState, "slope", SlopeComboBox));
    TheComboBox.addItem("項目1", 1);//項目を追加します。
    TheComboBox.addItem("項目2", 2);//項目を追加します。
    TheComboBox.addItem("項目3", 3);//項目を追加します。
    TheComboBox.setSelectedId(1);//初期の項目を設定します。
 
    setSize (500, 300);
}

resized関数でコンボボックスを配置します。

void ●●AudioProcessorEditor::resized()
{
    TheComboBox.setBounds(10, 10, 100, 20);
}

コンボボックスの変化でパラメータに渡される数値は、1番目の項目が選択されていると、「0」の数値2番目の項目の時、「1」という値、3番目の時「2」という値がパラメータに現れます。

※2021/10/29追記
ComboBoxAttachmentで接続されるときに、APVTSパラメータの値がどうなるかという点について、公式のドキュメントによると、デフォルトでは、パラメータの範囲全体で直線的に割り振られるということでした。どうやらパラメータの最小値から最大値までを均等に割り、その値となるようです。例えば、最小値1、最大値256というパラメータに4項目のコンボボックスを紐づける場合、1つ目の項目を選択すると「1」、2つ目の項目では「82」が、3つ目の項目では「171」、4つ目の項目で「256」というような値が返ってきました。そのため、APVTSのパラメータの値の範囲をコンボボックスと同じ数にするという方法が必要なことが分かりました。

スライダーは少数でパラメータが変化するのに対して、コンボボックスは整数値でパラメータが変化するようなイメージとなりました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次