【JUCEプラグイン開発】ディレイエフェクト、テンポディレイ機能をトグルでオンオフする

パンダさん

テンポ同期している場合と、手動が切り替えられるようにしたいですね

コパンダ

久しぶりにトグルボタンを使ってみよう~

JUCEでのディレイプラグインの開発を進めています。現状、ディレイエフェクトがDAWのBPMに同期するのみの処理となってしまったので、トグルボタンを追加して、DAWのテンポ同期するか、手動のパラメータでディレイタイムを設定するかを選択できるようにします。

赤い枠のトグルボタン(一度押すとオン状態、もう一度押すとオフ状態となるようなボタン)を使って、テンポと同期するかどうかを選択できるようになります。テンポシンク機能と名付けてみます。

目次

こんな人の役に立つかも

・JUCEプラグイン開発をしている人

・JUCEでディレイプラグインを作成したい人

・JUCEディレイプラグインで、テンポ同期と手動を切り替えたい人

エディタ側

まずはトグルボタンを準備します。

class TheDelayAudioProcessorEditor  : public juce::AudioProcessorEditor
{
//...略...
    //トグルボタンを追加しました。
    juce::ToggleButton tempoSyncButton;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TheDelayAudioProcessorEditor)
};

コンストラクタにトグルボタンの初期化を行いました。

TheDelayAudioProcessorEditor::TheDelayAudioProcessorEditor (TheDelayAudioProcessor& p, juce::AudioProcessorValueTreeState& vts, juce::UndoManager& um)//20211104
//...略...
    //トグルボタンの初期化を行います。
    addAndMakeVisible(tempoSyncButton);
    tempoSyncButton.onStateChange = [this] {
        audioProcessor.setTempoSyncState(tempoSyncButton.getToggleState());
    };

    setSize (400, 300);
}

トグルボタンの状態変化時のラムダ式を「onStateChange」関数で登録しました。ラムダ式では、プロセッサ側に準備したbool変数へトグルボタンの状態を渡すような内容です。setSyncTmpoState関数は、後程プロセッサ側に実装するprivateなbool変数へのアクセサになります。トグルボタンの自身の状態をgetToggleState()関数で取得できますので、この返り値のbool型の値をアクセサの引数へと渡します。

そして、resized関数でトグルボタンを配置します。

void TheDelayAudioProcessorEditor::resized()
{
//...略...
    //GUI上に配置します。
    tempoSyncButton.setBounds(10, 240, 30, 30);
}

プロセッサ側

テンポシンクのトグルボタンの状態を受け取るための変数と、アクセサを追加します。

class TheDelayAudioProcessor : public juce::AudioProcessor, private juce::Timer
{
//...略...
    //[2]アクセサでtempopSyncStateを変更できるようにします。
    void setTempoSyncState(bool state) {
        bool val = state;
        tempopSyncState = val;
    }

private:
//...略...
    //[1]bool型の変数tempopSyncStateを準備します。
    bool tempopSyncState = false;

    //==============================================================================
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TheDelayAudioProcessor)
};

[1]のbool型、privateメンバにアクセスするため、[2]のアクセサを作成しています。

次に、トグルボタンの状態によって、パラメータ更新処理を変化させます。まずは、パラメータを一定間隔で更新しているTimerCallback関数の処理を見直しました。

class TheDelayAudioProcessor : public juce::AudioProcessor, private juce::Timer
{
public:
    //タイマーコールバック関数を次の内容に変更しました。
    void timerCallback() override
    {
        //[1]ローカル変数val2とval3を先頭にもってきました。
        float val2 = 0.0f;
        float val3 = 0.0f;

        //[2]テンポシンク時のディレイタイム計算です。
        if(tempopSyncState == true){
            playHead = this->getPlayHead();
            playHead->getCurrentPosition(currentPositionInfo);

            float selected_delayNum = *delayNumerator;
            float delayNum = 0.0f;
            if (selected_delayNum == 1.0f) {
                delayNum = 1.0f;
            }
            else if (selected_delayNum == 2.0f) {
                delayNum = 2.0f;
            }
            else if (selected_delayNum == 3.0f) {
                delayNum = 4.0f;
            }
            else if (selected_delayNum == 4.0f) {
                delayNum = 8.0f;
            }
            val2 = ((float)60.0 / current_bpm) * (4.0f / delayNum);

            if (val2 >= 2.0f) {
                val2 = 1.99f;
            }

            val3 = val2;
        }
        //[3]手動でディレイタイムを設定する処理です。
        else {
            val2 = *delayTime_L;
            val3 = *delayTime_R;

            if (val2 >= 2.0f) {
                val2 = 1.99f;
            }
            if (val3 >= 2.0f) {
                val3 = 1.99f;
            }

        }

        //[4]最後に、パラメータを反映します。
        processorChain.get<DelayIndex>().setDelayTime(0, val2);
        processorChain.get<DelayIndex>().setDelayTime(1, val3);
    }

[1]と[4]は共通の処理となるため、外に出しました。[2]の場合、トグルボタンがオンの時でDAWのテンポ情報からディレイタイムを計算します。[2]は、手動設定のスライダーからの値を取得してLとRそれぞれのディレイタイムを設定します。

prepare関数でも、同じようにトグルボタンの状態によってパラメータを設定するようにしました。同様の処理を行うだけです。

void TheDelayAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
    processorChain.prepare({ sampleRate, (juce::uint32) samplesPerBlock, 2 });

    //以下の処理を追加しました。(上記タイマーと同様の処理です。)
    float val2 = 0.0f;
    float val3 = 0.0f;

    if (tempopSyncState == true) {
        playHead = this->getPlayHead();
        playHead->getCurrentPosition(currentPositionInfo);

        float selected_delayNum = *delayNumerator;
        float delayNum = 0.0f;
        if (selected_delayNum == 1.0f) {
            delayNum = 1.0f;
        }
        else if (selected_delayNum == 2.0f) {
            delayNum = 2.0f;
        }
        else if (selected_delayNum == 3.0f) {
            delayNum = 4.0f;
        }
        else if (selected_delayNum == 4.0f) {
            delayNum = 8.0f;
        }
        val2 = ((float)60.0 / current_bpm) * (4.0f / delayNum);

        if (val2 >= 2.0f) {
            val2 = 1.99f;
        }

        val3 = val2;
    }
    else {
        val2 = *delayTime_L;
        val3 = *delayTime_R;

        if (val2 >= 2.0f) {
            val2 = 1.99f;
        }
        if (val3 >= 2.0f) {
            val3 = 1.99f;
        }
    }
    //ここまでの処理を追加しました。

    float val4 = *wetLevel;
    float val5 = *feedback;
    //processorChain.get<DelayIndex>().setMaxDelayTime(val1);
    processorChain.get<DelayIndex>().setDelayTime(0, val2);
    processorChain.get<DelayIndex>().setDelayTime(1, val3);
    processorChain.get<DelayIndex>().setWetLevel(val4);
    processorChain.get<DelayIndex>().setFeedback(val5);
}
よかったらシェアしてね!
目次
閉じる