APVTSを用いたスライダーUIの実装

概要

今回はAudioProcessorValueTreeStateを用いて簡単なGUIを実装していきます。

当記事は下記チュートリアルを参考に、さらに深掘りした内容を追加しながらまとめています。

JUCE 6 Tutorial 07 – Creating Parameters with the AudioProcessorValueTreeState Class (Pt 1 of 2)

JUCE 6 Tutorial 08 – The AudioProcessorValueTreeState Class Pt 2 of 2

下記はこの記事で作成するGUIです。

環境

  • macOS Big Sur 11.2.2
  • JUCE v6.0.8

メンバ変数の追加

AudioProcessorValueTreeStateを使用すると、従来の方法よりも簡単にパラメータを管理できるようになります。

まずはTestAudioProcessorクラスに、AudioProcessorValueTreeState型オブジェクトapvtsをメンバ変数に追加します。

class TestAudioProcessor  : public juce::AudioProcessor
{
public:
・・・
    juce::AudioProcessorValueTreeState apvts;
・・・
}

メンバ初期化子の追加

apvtsはAudioProcessorValueTreeStateクラスのオブジェクトなので、コンストラクタAudioProcessorValueTreeState()によって初期化されます。仮引数は以下のようになります。

AudioProcessorValueTreeState::AudioProcessorValueTreeState	
(	
AudioProcessor&	processorToConnectTo,
UndoManager* undoManagerToUse,
const Identifier&	valueTreeType,
ParameterLayout parameterLayout 
)		
引数説明
AudioProcessor& processorToConnectToオブジェクトで管理したいprocessor名を指定。
UndoManager* undoManagerToUse追加したいUndoManagerへのポインタを指定。
const Identifier& valueTreeTypeValueTreeを初期化する際に使用される識別子を指定。
ParameterLayout parameterLayoutAudioProcessorが使用する全ての値を指定。

今回は下記のように、コンストラクタTestAudioProcessor()にメンバ初期化子apvtsを追加しています。

TestAudioProcessor::TestAudioProcessor()
#ifndef JucePlugin_PreferredChannelConfigurations
     : AudioProcessor (BusesProperties()
                     #if ! JucePlugin_IsMidiEffect
                      #if ! JucePlugin_IsSynth
                       .withInput  ("Input",  juce::AudioChannelSet::stereo(), true)
                      #endif
                       .withOutput ("Output", juce::AudioChannelSet::stereo(), true)
                     #endif
                       ), apvts(*this, nullptr, "Parameters", createParameters())
#endif
{
}

第一引数の*thisはapvtsが所属するオブジェクトそのものを指します。第二引数のnullptrは、UndoManagerを利用しないということになります。第三引数の”Parameters”は、識別子として利用する文字列です。第四引数のcreateParameters()は、AudioProcessorが扱う値をこの関数の戻り値で指定しています。createParameters()はこの後宣言&定義していきます。

ParameterLayout

先ほど登場したメンバ関数createParameters()を宣言します。

class TestAudioProcessor  : public juce::AudioProcessor
{
・・・
private:
    juce::AudioProcessorValueTreeState::ParameterLayout createParameters();
・・・
};

次に定義部分を下記のように記述します。

juce::AudioProcessorValueTreeState::ParameterLayout TestAudioProcessor::createParameters()
{
    std::vector<std::unique_ptr<juce::RangedAudioParameter>> params;
    
    params.push_back(std::make_unique<juce::AudioParameterFloat>("GAIN", "Gain", 0.0f, 1.0f, 0.5f));
    
    return { params.begin(), params.end() };
}

まず動的配列クラスvectorを使用した配列paramsを宣言しています。この配列が持つ要素は、RangedAudioParameter型へのスマートポインタです。

次にparamsに対して、push_back関数を呼び出し、一つの要素を追加しています。その要素は、AudioParametersFloat型オブジェクトへのスマートポインタです。

コンストラクタAudioParametersFloat()は下記のような仮引数を持ちます。

AudioParameterFloat::AudioParameterFloat	
(	
String 	parameterID,
String 	parameterName,
float 	minValue,
float 	maxValue,
float 	defaultValue 
)		

つまり今回のAudioParametersFloat型オブジェクトは、パラメータIDが”GAIN”、パラメータ名が”Gain”、最小値が0、最大値が1、初期値が0.5で初期化されています。

そして処理の最後は、関数begin()とend()が返す値をreturnで返しています。begin()は配列の最初のイテレータを返し、end()はデータの終了位置を表すイテレータを返します。もちろんこの戻り値はAudioParameterLayout型です。

Sliderコンポーネントの作成と設定

Sliderコンポーネントを作成するために、SliderクラスのオブジェクトgainsliderをTestAudioProcessorEditorクラスのメンバ変数に追加します。

class TestAudioProcessorEditor : public juce::AudioProcessorEditor
{
・・・
private:
    juce::Slider gainSlider;
・・・
};

次にこのgainSliderに対して様々なメンバ関数を呼び出し、Sliderコンポーネントの詳細な設定をしていきます。

TestAudioProcessorEditor::TestAudioProcessorEditor (TestAudioProcessor& p)
    : AudioProcessorEditor (&p), audioProcessor (p)
{
    gainSlider.setSliderStyle (juce::Slider::SliderStyle::RotaryHorizontalVerticalDrag);
    gainSlider.setTextBoxStyle (juce::Slider::TextBoxBelow, true, 100, 50);
    addAndMakeVisible (gainSlider);
    
    setSize (400, 300);
}

それぞれのメンバ関数の説明は下記記事に詳しく書いているので、ぜひ参考にしてみてください。

JUCEチュートリアルでGainスライダーを作成する

次に下記のようにHello, World!の表示処理を削除し、サイズ感の設定をします。

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

void TestAudioProcessorEditor::resized()
{
    gainSlider.setBounds (getLocalBounds());
}

SliderAttachment

SliderAttachmentはSliderとAudioProcessorValueTreeStateのパラメータ間のコネクションを管理するクラスです。

具体的には、SliderAttachment型オブジェクトが存在する限り、この二者の間でパラメータの同期が行われます。このおかげでSliderとパラメータの紐付けが簡単に行えます。

もちろんSliderAttachment型オブジェクトが破棄されれば、このコネクションも破棄されます。一つ注意するべきことは、このオブジェクトが破棄される前にSliderとAudioProcessorValueTreeStateが破棄されてはならないというルールがあることです。

まずはSliderAttachment型へのスマートポインタであるgainSliderAtttachmentを宣言します。

private:
    juce::Slider gainSlider;
    
    std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> gainSliderAttachment;
    
    TestAudioProcessor& audioProcessor;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TestAudioProcessorEditor)
};

その後gainSliderAttachmentに、下記SliderAttachmentオブジェクトへのスマートポインタを代入します。

TestAudioProcessorEditor::TestAudioProcessorEditor (TestAudioProcessor& p)
    : AudioProcessorEditor (&p), audioProcessor (p)
{
    gainSlider.setSliderStyle (juce::Slider::SliderStyle::RotaryHorizontalVerticalDrag);
    gainSlider.setTextBoxStyle (juce::Slider::TextBoxBelow, true, 100, 50);
    addAndMakeVisible (gainSlider);
    
    gainSliderAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(audioProcessor.apvts, "GAIN", gainSlider);
    
    setSize (400, 300);
}

コンストラクタSliderAttachment()の仮引数は以下の通りです。

AudioProcessorValueTreeState::SliderAttachment::SliderAttachment	
(	
AudioProcessorValueTreeState& stateToUse,
const String&	parameterID,
Slider& slider 
)	

つまり今回の場合だと、第一引数でAudioProcessorValueTreeState型オブジェクトapvtsを渡し、第二引数でパラメータIDである”GAIN”を渡し、第三引数でSliderオブジェクトであるgainSliderを渡しています。

これでapvtsが保持するパラメータとgainSliderが紐づいたことになります。

GUIの確認

最後にビルドしてGUIを確認します。

下記のように初期値0.5、最小値0、最大値1など、AudioProcessorValueTreeState型オブジェクトが保持しているパラメータがSliderコンポーネントに反映されています。

最後に

今回はAudioProcessorValueTreeStateを用いて簡単なGUIの実装をしました。

確かに前回やったAudioProcessorを用いた方法よりも簡潔に記述できとても感動しました。

しかし、シンプルになっている分コード一つ一つの濃度が濃すぎて、理解がまだ不十分です。

もしかしたら間違っている解釈があるかもしれないので、ご指摘お願い致します。

次回以降はGUI中心に深掘りしていこうかなと考えています。

Comments

Copied title and URL