JUCEプラグイン、サイン波を出力するオシレータプラグインに周波数設定スライダーを追加

周波数を可変としてAudioPluginHostからサイン波を出力するようなツールを作成したいです。

実験用のツールとしてはいい感じにできてきたね~

前回実装した440Hzのサイン波出力プラグインアプリに、周波数を可変とするパラメータを追加して、スライダーで周波数を操作できるように変更しました。

前回の記事はこちらです。前回の実装からの差分を記載していきます。

目次

こんな人の役に立つかも

・JUCEプラグインを作成したい人

・JUCEでサイン波形を出力するプラグインを作成したい人

・周波数をスライダーで操作したい人

実装

PluginProcessor.h

AudioProcessorValueTreeState(以下APVTS)クラスを利用したパラメータを追加しています。

privaclass TheBasicSineAudioProcessor  : public juce::AudioProcessor
{
//...略...

private:

//...略... 
   //以下のprivateなメンバを追加しました。
   //[1]APVTSに係るメンバです。
    juce::AudioProcessorValueTreeState parameters;
    std::atomic<float>* Freqency = nullptr;
    double sampleRate_from_prepareToPlay;//[2]
    int numberOfOscillators = 1;//[3]

    //==============================================================================
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TheBasicSineAudioProcessor)
};

[1]はAPVTSでパラメータを追加するためのメンバです。APVTSクラスでのパラメータ追加はこちらの記事もご参照ください。

[2]はsampleRateを受け取るprepareToPlayの値をprocessBlockでも利用できるように変数を作成しました。

[3]は、オシレータの個数設定です。以前はprepareToPlay関数ないでローカル変数として作成していましたが、こちらに移動させました。processBlock関数でも利用するためです。

PluginProcessor.cpp

APVTSのパラメータを初期化します。

TheBasicSineAudioProcessor::TheBasicSineAudioProcessor()
//...略...
    //[1]パラメータ追加、設定します。
    , parameters(*this, nullptr, juce::Identifier("APVTSTutorial"),
        {
            std::make_unique<juce::AudioParameterFloat>("frequency",  // ID
                                                        "Frequency",  // name
                                                         0.0f,     // min
                                                         22000.0f, // max
                                                         0.0f)// default
        })
{
    //[2]紐づけます。
    Freqency = parameters.getRawParameterValue("frequency");
}

[1]でパラメータ設定、[2]で先ほど宣言したメンバ「Frequency」に紐づけています。

prepareToPlay関数

次に、prepareToPlay関数を変更していきます。

//==============================================================================
void TheBasicSineAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
    //[1]以下の変数はprivateなメンバにしたので削除します。
    //auto numberOfOscillators = 1;

    //[2]サンプルレートを取得します。
    sampleRate_from_prepareToPlay = sampleRate;

    //[3]周波数設定をパラメータから取得します。
    float freq_val = *Freqency;

    for (auto i = 0; i < numberOfOscillators; ++i)
    {
        auto* oscillator = new SineOscillator();

        oscillator->setFrequency(freq_val, (float)sampleRate);//[4]周波数をパラメータから取得した変数に変更しました。
        oscillators.add(oscillator);
    }
    level = 0.1f / (float)numberOfOscillators;
}

上記コメント部分を変更していきました。

変更点としてのポイントは、以下の3つです。

・サンプルレートをクラスのprivateなメンバとして保持する点

・オシレータの数の「numberOfOscillators」変数をprivateなメンバにした点

・周波数の設定をAPVTSのパラメータから[3]のように取得して利用すること

です。

processBlock関数内でも動的に周波数の変更をおこない、「setFrequency」関数を呼び出したいので、先の2つの変数はprivateなメンバとしてクラスの中にもっていきました。

processBlock関数

また、processBlock関数も次のように変更しました。次のコメントの部分を追加しました。

void TheBasicSineAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages)
{
//...略...

    //[1]周波数をパラメータから取得します。
    float freq_val = *Freqency;
    for (auto i = 0; i < numberOfOscillators; ++i)
    {
        //[2]周波数の指定を取得したものに変更しています。(freq_val)
        oscillators.getUnchecked(i)->setFrequency(freq_val, (float)sampleRate_from_prepareToPlay);
    }
    //追加ここまで

    auto* leftBuffer = buffer.getWritePointer(0);
    auto* rightBuffer = buffer.getWritePointer(1);

//...略...
}

ここでは、オシレータを管理している「OwnedArray」クラスの配列から、特定の番号のオシレータオブジェクトを呼び出して「setFrequency」関数で周波数を設定する点です。「getUnchecked」にインデックス番号を引数で入れることで、OwnedArrayに登録されているオシレータを取得できます。

createEditor関数

APVTSによるパラメータ管理を行うため、エディタ側へ渡すパラメータを引数として追加しました。

juce::AudioProcessorEditor* TheBasicSineAudioProcessor::createEditor()
{
    //引数parametersを追加します。
    return new TheBasicSineAudioProcessorEditor (*this, parameters);
}

PluginEditor.h

GUI側のAPVTSクラスのパラメータ管理のための設定を行いました。[1]~[3]のように追記しています。

class TheBasicSineAudioProcessorEditor  : public juce::AudioProcessorEditor
{
public:
    //[1]コンストラクタに引数を追加しました。
    TheBasicSineAudioProcessorEditor (TheBasicSineAudioProcessor&, juce::AudioProcessorValueTreeState& vts);
    ~TheBasicSineAudioProcessorEditor() override;

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

    //[2]スライダーアタッチメントクラスをtypedefします。
    typedef juce::AudioProcessorValueTreeState::SliderAttachment SliderAttachment;

private:
    //[3]APVTSクラスでパラメータを管理するために以下のメンバを追加しました。
    juce::AudioProcessorValueTreeState& valueTreeState;
    juce::Slider FrequencySlider;
    std::unique_ptr<SliderAttachment> FrequencyAttachment;

    TheBasicSineAudioProcessor& audioProcessor;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TheBasicSineAudioProcessorEditor)
};

PluginEditor.cpp

コンストラクタ

[1]と[2]のように、コンストラクタの引数にvtsを渡してメンバイニシャライザでValueTreeStateのオブジェクトを初期化します。

//==============================================================================
TheBasicSineAudioProcessorEditor::TheBasicSineAudioProcessorEditor (TheBasicSineAudioProcessor& p, juce::AudioProcessorValueTreeState& vts)//[1]引数にvtsを追加しています。
    : AudioProcessorEditor (&p), audioProcessor (p)
    , valueTreeState(vts)//[2]ValueTreeStateの初期化を追加しました。
{
    //[3]スライダーの可視化と、パラメータの紐づけを行います。
    addAndMakeVisible(FrequencySlider);
    FrequencyAttachment.reset(new SliderAttachment(valueTreeState, "frequency", FrequencySlider));

   //[4]スライダーの中央値は1000Hzとします。
    FrequencySlider.setRange(0.0, 22000.0);
    FrequencySlider.setSkewFactorFromMidPoint(1000.0);

    setSize (400, 300);
}

[3]では、スライダーオブジェクトを可視化、その後、スライダーアタッチメントクラスでスライダー「FrequencySlider」をパラメータ「frequency」と紐づけました。

[4]では、スライダーの中央値を1000として、可聴領域での周波数の変化を操作しやすくしています。

この中央値設定は、[3]のあとの紐づけ後、setRangeしてsetSkew…という順番が重要です。

paint関数

paint関数では、背景の塗りつぶしのみとしました。

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

    //これ以下のデフォルトの描画を削除しました。
}

resized関数

resized関数で、「FrequencySlider」をGUIに配置しました。

void TheBasicSineAudioProcessorEditor::resized()
{
    //スライダーコンポーネントを配置します。
    FrequencySlider.setBounds(10, 10, 200, 30);
}

動作検証をしていて、WindowsのReaperではプラグインとして読み込むことができますが、未だLogicで動作させられていないので、プラグイン設定のあたりも色々と検討しなければいけません。とりあえず今回は、AudioPluginHostで検証をしています。

よかったらシェアしてね!
目次
閉じる