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

概要

今回はJUCEチュートリアルでGainスライダーを作成する手順を自分なりに+αしてまとめていきます。

当記事は、下記二つのチュートリアルを融合させた内容です。

Juce Tutorial 14- Creating Your First Plugin (Pt 1)

Tutorial: Create a basic Audio/MIDI plugin, Part 2: Coding your plug-in

環境

  • macOS Big Sur 11.2.2
  • JUCE 6.0.7
  • Ableton Live 10 Suite

新規プロジェクトの作成

オーディオプラグイン用のプロジェクトをGainTutorialという名前で作成します。

右上付近のXcodeのアイコン(Save and Open in IDE)をクリックし、Xcode用にプロジェクトファイルを出力します。

すると下記のようにXcodeが開けるはずです。

UIの実装

スライダーコンポーネントの宣言

JUCEにおいて、GUIの実装はPluginEditor.hとPluginEditor.cppで行います。

まずは、PluginEdior.hのGainTutorialAudioProcessorEditorクラス内にSliderクラスのオブジェクトgainSliderを宣言します(15行目)。

class GainTutorialAudioProcessorEditor  : public juce::AudioProcessorEditor
{
public:
    GainTutorialAudioProcessorEditor (GainTutorialAudioProcessor&);
    ~GainTutorialAudioProcessorEditor() override;

    //==============================================================================
    void paint (juce::Graphics&) override;
    void resized() override;

private:

    GainTutorialAudioProcessor& audioProcessor;
    
    juce::Slider gainSlider;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GainTutorialAudioProcessorEditor)
};

スライダーコンポーネントの設定

PluginEditor.cppで編集する箇所は、下記コンストラクタの定義部分です。先ほど宣言したgainSliderに対して、様々なメンバ関数を呼び出し、GUIの詳細な設定をしています。

GainTutorialAudioProcessorEditor::GainTutorialAudioProcessorEditor (GainTutorialAudioProcessor& p)
    : AudioProcessorEditor (&p), audioProcessor (p)
{
    setSize (200, 400);
    
    gainSlider.setSliderStyle (juce::Slider::LinearVertical);
    gainSlider.setTextBoxStyle (juce::Slider::TextBoxBelow, true, 100, 25);
    gainSlider.setRange (0.0, 1.0);
    gainSlider.setValue (0.5);

    addAndMakeVisible (gainSlider);
}

登場したメンバ関数を一つずつ詳しくみていきます。

setSize()

setSize()は、Componentクラスのメンバ関数で、ウィンドウのサイズを調整します。

void Component::setSize	(int newWidth, int newHeight);

第一引数には横の長さ、第二引数には縦の長さを指定します。

以下複数の設定例を挙げていきます。

setSize(400, 300);
setSize(200, 400);

setSliderStyle()

setSliderStyle()は、Sliderクラスのメンバ関数で、スライダの種類を変更します。

void Slider::setSliderStyle	(SliderStyle newStyle);

引数には列挙体SliderStyleの列挙子を指定します。

以下複数の設定例を挙げていきます。

gainSlider.setSliderStyle(juce::Slider::LinearHorizontal);
gainSlider.setSliderStyle(juce::Slider::RotaryVerticalDrag);
gainSlider.setSliderStyle(juce::Slider::LinearVertical);

setTextBoxStyle()

setTextBoxStyle()はSliderクラスのメンバ関数で、テキストボックスの位置を変更します。

void Slider::setTextBoxStyle (TextEntryBoxPosition newPosition, 
                              bool isReadOnly, 
                              int textEntryBoxWidth,
                              int textEntryBoxHeight);		

それぞれの引数の説明は以下の通りです。

引数説明
TextEntryBoxPosition newPositionどこにテキストボックスを置くか決める。
列挙体TextEntryBoxPositionの列挙子を指定する。
bool isReadOnly値を入力できるようにするかどうか決める。
trueだと有効になり、読み取り専用になる。
int textEntryBoxWidthピクセル単位でテキストボックスの幅を指定する。
int textEntryBoxHeightピクセル単位でテキストボックスの高さを指定する。

今回は下記ように設定されていました。

gainSlider.setTextBoxStyle(juce::Slider::TextBoxBelow, true, 100, 25);

つまり、テキストボックスはスライダーの下に設置し、読み取り専用で、テキストボックスの幅と高さは100 × 25であるということです。実際のGUIは下記のようになります。

setRange()

setRange()はSliderクラスのメンバ関数で、スライダーの値の範囲を決めます。

void Slider::setRange	(double newMinimum, double newMaximum, double newInterval = 0);

それぞれの引数の説明は以下の通りです。

引数説明
double newMinimum最小値を設定する。
double newMaximum最大値を設定する。
double newInterval = 0増減する間隔を設定する。

以下複数の設定例を挙げていきます。

gainSlider.setRange(0.0, 1.0, 0.1);

今回は下記の設定です。この場合だと第三引数は0で初期化されるので、非常に細かい間隔で増減します。

gainSlider.setRange(0.0, 1.0);

setValue()

setValue()はSliderクラスのメンバ関数で、スライダーの初期値を設定します。

void Slider::setValue	(double newValue, 
                       NotificationType notification = sendNotificationAsync);

それぞれの引数と説明は以下の通りです。

引数説明
double newValue初期値を設定する。
値の範囲(最大値と最小値)が決まっていれば、その制限を受ける。
ex)最大値を2、初期値を4に設定した場合、初期値は2になる。
NotificationType notification = sendNotificationAsyncスライダーの値が変わった時、そのイベントをどう通知するかどうか(同期、非同期など)

今回は下記の設定なので、初期値は0.5になります。

gainSlider.setValue(0.5);

addAndMakeVisible()

addAndMakeVisible()はComponentクラスのメンバ関数で、指定したコンポーネントをウィンドウに追加して表示します。

void Component::addAndMakeVisible	(Component * child, int zOrder = -1);		
引数説明
Component * child新しく追加したいコンポーネントのアドレスを指定する。
int zOrder = -1コンポーネントをどれくらい前面 or 後面に持ってくるかインデックスで指定する。

今回の場合は下記のようにスライダーコンポーネントであるgainSliderのアドレスを渡しています。そのおかげでウィンドウにスライダーが表示されます。

addAndMakeVisible(&gainSlider);

このメンバ関数を呼び出さないと下記のように何も表示されないので注意です。

コンポーネントの表示色

コンポーネントの表示色は、Coloursで定義されている色の名前を指定すると、簡単に変更することができます。今回は背景色にtransparentBlackを指定しました。

void GainTutorialAudioProcessorEditor::paint (juce::Graphics& g)
{
    g.fillAll (juce::Colours::transparentBlack);
    //g.setColour (juce::Colours::black);
    //g.setFont (15.0f);
    //g.drawFittedText ("Hello World!", getLocalBounds(), juce::Justification::centred, 1);
}

fillAll()はGraphicsクラスのメンバ関数で、指定した色で背景を塗りつぶしてくれます。

Hello World用の表示処理はコメントアウトします。

コンポーネントの配置

コンポーネントの配置は、resized()内でコンポーネントのメンバ関数setBounds()を呼び出して行います。

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

resized()はウィンドウの生成時やサイズ変更時に実行される関数です。

その内にあるsetBounds()は、コンポーネントの位置と大きさを変更するメンバ関数です。ここではgetLocalBounds()が返す戻り値を使用して、コンポーネントの位置と大きさを変更します。

イベント処理の実装

現時点では、GUIを実装しただけなので、スライダーを動かしたとしても何も変化は起きません。

ここからはコンポーネントが操作された時に、オーディオ処理をコントロールしている変数の値が変更されるように、GUIにイベント処理を実装していきます。

まずはスライダーによって変化した値を保持するための変数を宣言しておきます。

class GainTutorialAudioProcessor  : public juce::AudioProcessor
{
public:
    double rawVolume;
・・・
};

リスナークラスの継承

イベント処理を実装するためには、コンポーネントにイベントが発生した際に呼び出す関数が定義されている、リスナークラスを継承しておく必要があります。

コンポーネントごとにリスナークラスは用意されているため、今回はSlider::Listenerというスライダーコンポーネントに対応したリスナークラスを使用します(1行目)。

また、sliderValueChanged()を宣言しておきます(12行目)。この関数はイベントハンドラという、何かのイベントをトリガーとして実行されるものです。つまり、スライダーコンポーネントにおけるイベント(スライダーの値の変更)をトリガーとして実行されます。

class GainTutorialAudioProcessorEditor  : public juce::AudioProcessorEditor, private juce::Slider::Listener
{
public:
    GainTutorialAudioProcessorEditor (GainTutorialAudioProcessor&);
    ~GainTutorialAudioProcessorEditor() override;

    //==============================================================================
    void paint (juce::Graphics&) override;
    void resized() override;

private:
    void sliderValueChanged (juce::Slider *slider) override;
    
    GainTutorialAudioProcessor& audioProcessor;
    
    juce::Slider gainSlider;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GainTutorialAudioProcessorEditor)
};

イベントリスナーの登録

下記の10行目のようにaddLister()を使用することで、イベントリスナーを登録することができます。

GainTutorialAudioProcessorEditor::GainTutorialAudioProcessorEditor (GainTutorialAudioProcessor& p)
    : AudioProcessorEditor (&p), audioProcessor (p)
{    
    setSize (200, 400);
    
    gainSlider.setSliderStyle(juce::Slider::LinearVertical);
    gainSlider.setTextBoxStyle(juce::Slider::TextBoxBelow, true, 100, 25);
    gainSlider.setRange(0.0, 1.0);
    gainSlider.setValue(0.5);
    gainSlider.addListener(this);  

    addAndMakeVisible(gainSlider);
}

イベントハンドラの実装

スライダーが変化した時に、実行される処理を実装します。

下記コードは、値が変化したスライダーがgainSliderであった時、audioProcessorクラスのメンバ変数(rawvolume)にスライダーの値を代入する処理です。

void GainTutorialAudioProcessorEditor::sliderValueChanged(juce::Slider* slider)
{
    if (slider == &gainSlider)
    {
        audioProcessor.rawVolume = gainSlider.getValue();
    }
}

processBlock()

processBlock()は、ホストからMIDIやオーディオのバッファのアドレスを受け取り、オーディオサンプルに対してオーディオ処理を実行する関数です。

下記14〜17行目を追記してオーディオ処理を完成させます。

void GainTutorialAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages)
{
    juce::ScopedNoDenormals noDenormals;
    auto totalNumInputChannels  = getTotalNumInputChannels();
    auto totalNumOutputChannels = getTotalNumOutputChannels();

    for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
        buffer.clear (i, 0, buffer.getNumSamples());

    for (int channel = 0; channel < totalNumInputChannels; ++channel)
    {
        auto* channelData = buffer.getWritePointer (channel);

        for (int sample = 0; sample < buffer.getNumSamples(); ++sample)
        {
            channelData[sample] = buffer.getSample(channel, sample) * rawVolume;
        }
    }
}

動作確認

AudioPluginHost

まずはAudioPluginHostで動作確認をします。

cmd + aでAudio Settingsを開き、Input/Outputが適切に設定されていることを確認しておきます。

次にcmd + pでAvailable Pluginsを開き、左下のOptionsから”Scan for new or updated VST3 plug-ins”をクリックしてスキャンを実行します。

画像に alt 属性が指定されていません。ファイル名: スクリーンショット-2021-03-21-12.04.18-1024x617.png

下記のようにAudio Input/OutputとGainTutorialプラグインを接続します。

実際にスライダーを動かしてみて、音量が変わるのかどうか確認します。

Ableton Live

最後にAbleton Live 10 Suiteで確認します。

適当なサンプルを鳴らしながら、スライダーを上下させて音が変化することを確認します。

最後に

今回はチュートリアルのGainTutorialプラグインの作成手順を、自分で少し深掘りした内容を加えながらまとめてみました。

チュートリアルでの説明は非常にあっさりしたものであるため、結局これは何をしているのだろうといった疑問が次々と沸いてきました。

しかしJUCEのリファレンスをみて深掘りしていくと、まだまだわからないことだらけですが、少しは腑に落ちた部分も出てきました。

課題としてはC++がいまいち読めないことです。ここは引き続きC++の入門書を進めて基礎を固めて行こうと思います。

次回以降も同じようにJUCEチュートリアルで学んだことを投稿します。

参考文献

Juce Tutorial 14- Creating Your First Plugin (Pt 1)

Tutorial: Create a basic Audio/MIDI plugin, Part 2: Coding your plug-in

JUCE JAPAN vol.1 JUCEではじめるVST/AUプラグイン制作

JUCEでアプリケーション開発、プラグインコーディング3、GUIスライダーの値を取得する

Comments

Copied title and URL