メインコンテンツまでスキップ

チュートリアル:プラグインパラメータの追加

📚 Source Page

オーディオプラグインにパラメータを追加して、デジタルオーディオワークステーションからのコントロールとオートメーションを可能にします。 オーディオパラメータをオーディオ処理に使用し、そのユーザーインターフェイスを作成する方法を学びます。

レベル:初級

プラットフォーム:Windows, macOS, Linux

クラス:AudioParameterFloat, AudioParameterBool, GenericAudioProcessorEditor,

はじめる

このチュートリアルのデモ・プロジェクトのダウンロードはこちらから:PIP | ZIP プロジェクトを解凍し、Projucerで最初のヘッダーファイルを開きます。

このステップでヘルプが必要な場合は、 Projucerパート1:Projucerを使い始める を参照してください

また、JUCEを使ってオーディオプラグインを構築し、これをあなたの好きなオーディオホスト(デジタルオーディオワークステーション - DAW とも呼ばれます) にロードする方法も知っておく必要があります。 基本的なAudio/MIDIプラグインを作る パート1:セットアップ を参照してください:

デモプロジェクト

このデモ・プロジェクトは、JUCE/examples/Plugins ディレクトリにある GainPlugin プロジェクトに基づいています。 このプラグインは、1つのパラメータを使って入力信号のゲインを変更するだけです。

Logic Pro X上でのゲインプラグインのUI
Logic Pro X上でのゲインプラグインのUI

ゲインプロセッサー

TutorialProcessor クラスのコードのほとんどは、Audio Plug-In プロジェクト・テンプレートを使用したときにProjucerによって生成されるものと同じです。 簡単にするために、プロセッサーのコードを.cppファイルと.hファイルに分けるのではなく、1つの.hファイルにまとめました。

パラメータの設定

プロセッサーには、各パラメーターのオーディオ・パラメーター・メンバーを格納する必要があります。 私たちの場合は1つだけです:

private:
//==============================================================================
juce::AudioParameterFloat* gain;

//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TutorialProcessor)
};

プロセッサは、プラグインが必要とするパラメータをコンストラクタに割り当てて追加します。 この単純な例では、設定するパラメータは1つだけです:

    TutorialProcessor()
{
addParameter (gain = new juce::AudioParameterFloat ("gain", // parameterID
"Gain", // parameter name
0.0f, // minimum value
1.0f, // maximum value
0.5f)); // default value
}
注記

パラメータ・オブジェクトの所有権はベース・クラス(AudioProcessor)が持っているので、派生プロセッサ・クラスでは生のポインタを使用してパラメータを格納します。 これは、派生クラスの後で基底クラスが解体されることが確実にわかっているので安全です。 これに加えて、プロセッサのエディタ・コンポーネントがプロセッサ・オブジェクトよりも先に削除されることも想定できます。

パラメータIDは、このパラメータの一意な識別子でなければならない。 これは変数名のようなもので、英数字とアンダースコアが使用できますが、スペースは使用できません。 パラメータ名は画面に表示される名前です。

これに加えて、AudioParameterFloat クラスでは、パラメータが表現できる値の範囲を指定できます。 AudioParameterFloat クラスには、代わりに NormalisableRange<float> オブジェクトを使用できる別のコンストラクタもあります。 JUCEは全てのパラメータ値を[0, 1]の範囲で保存します。 上に示したコードを次のように書き換えることができます:

addParameter (gain = new juce::AudioParameterFloat ("gain",                                      // parameter ID
"Gain", // parameter name
juce::NormalisableRange<float> (0.0f, 1.0f), // parameter range
0.5f)); // default value

しかし、NormalisableRange<float> オブジェクトを使えば、スキューファクターを指定することもできます。 これは、プラグインで周波数や時間のプロパティを表すパラメータを使用する必要がある場合に特に便利です。

注記

AudioParameterFloat クラスには、値をテキストに変換したり、その逆に変換したりするために指定できるラムダ関数もオプションで用意されています。 これは、パラメータの値を文字列として表示したい場合や、ユーザーが値を入力できるようにしたい場合に特に便利です。

ゲイン処理の実行

パラメータが作成され、追加されると、プラグインはこれらのパラメータオブジェクトと対話することができます。 この例では、TutorialProcessor::processBlock() 関数でゲインの値を取得します:

    void processBlock (juce::AudioSampleBuffer& buffer, juce::MidiBuffer&) override
{
buffer.applyGain (*gain);
}

AudioSampleBuffer::applyGain() 関数は、バッファ内の全チャンネルの全サンプルにゲイン値を適用します。

これは、オーディオパラメータクラスを使用する際に使用するイディオムを示しています。 この場合、AudioParameterFloat を使っているので、floatが得られます。

他の AudioParameterXXX クラスも同様の動作をします:

パラメータの保存と取得

オーディオを処理するルーチンを提供するだけでなく、プラグインの状態全体をメモリブロックに保存したり取得したりするメソッドも提供する必要があります。 これには、すべてのパラメータの現在値を含める必要がありますが、必要であれば、他のステート情報を含めることもできます(たとえば、プラグインがファイルを扱う場合、ファイルパスを保存することもできます)。

シンプルなゲイン・プラグインでは、保存するのはゲイン値だけです。 これを保存するのは、浮動小数点値をバイナリ形式で書き込むのと同じくらい簡単です:

    void getStateInformation (juce::MemoryBlock& destData) override
{
juce::MemoryOutputStream (destData, true).writeFloat (*gain);
}

AudioProcessor::getStateInformation() コールバックは、プラグインの状態を保存する必要があるときに呼び出されます。 例えば、ユーザーがDAWのプロジェクトを保存したり、プリセットを保存したりするときに呼び出されます(DAWによっては)。 この関数に渡される MemoryBlock オブジェクトには、 好きなものを入れることができます。

AudioProcessor::setStateInformation() 関数は、その逆を行う必要があります:メモリロケーションからデータを読み取り、プラグインの状態を復元する必要があります:

    void setStateInformation (const void* data, int sizeInBytes) override
{
*gain = juce::MemoryInputStream (data, static_cast<size_t> (sizeInBytes), false).readFloat();
}
エクササイズ

ゲインパラメータをバイナリ形式ではなく、文字列として保存してみてください。

ゲイン・プロセッサーの改善

このゲイン・プロセッサーにはいくつか改善点がある:

  • ゲインを変えると信号に不連続性が生じ、ゲインを素早く変調すると小さなクリック音として聞こえる。
  • プラグインの状態を保存するには、XMLを使うと便利です。

ゲインの変化を滑らかにする

AudioSampleBufferクラスを使えば、バッファのブロックサイズ全体にわたってゲインのランプ変化を簡単に実行できる。 そのためには、前のオーディオ・コールバックのゲイン・パラメータの値を保存する必要があります。 まず、TutorialProcessor クラスにメンバ変数を追加します [1]:

private:
//==============================================================================
juce::AudioParameterFloat* gain;
float previousGain; // [1]

//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TutorialProcessor)
};

そして、この値が TutorialProcessor::preparePlay() 関数内で初期化されていることを確認する:

    void prepareToPlay (double, int) override
{
previousGain = *gain;
}

最後に、TutorialProcessor::processBlock() 関数を変更して、ゲインランプを実行する:

    void processBlock (juce::AudioSampleBuffer& buffer, juce::MidiBuffer&) override
{
auto currentGain = gain->get();

if (juce::approximatelyEqual (currentGain, previousGain))
{
buffer.applyGain (currentGain);
}
else
{
buffer.applyGainRamp (0, buffer.getNumSamples(), previousGain, currentGain);
previousGain = currentGain;
}
}

ここでは、値が変化していなければ、単純に一定のゲインを適用していることがわかる。 値が変化した場合は、ゲイン・ランプを適用し、次回のために previousGain 値を更新する。

注記

この修正版プラグインのソースコードは、デモプロジェクトの AudioParameterTutorial_02.h ファイルにあります。

エクササイズ

平滑化アルゴリズムを変更し、処理ブロックサイズに依存しないようにする。

XMLを使ってプロセッサーの状態を保存する

プラグインの状態をバイナリ形式で保存することで、プラグインの状態を保存するためのメモリやストレージの使用量が少なくて済みます。 しかし、XMLや JSON のような形式を使用した方が便利な場合もあります。 これにより、デバッグが容易になり、保存された状態情報を将来のバージョンのプラグインと互換性を持たせることも簡単になります。 特に、XMLでは以下のことが簡単にできます:

  • 情報ブロックにないパラメータをデフォルト値に設定する。
  • 情報ブロックにバージョン情報を含めることで、異なるバージョンのプラグインの前方互換性と後方互換性を扱うことができます。

ゲイン・プラグインの状態をXMLに保存するには、次のようにする:

    void getStateInformation (juce::MemoryBlock& destData) override
{
std::unique_ptr<juce::XmlElement> xml (new juce::XmlElement ("ParamTutorial"));
xml->setAttribute ("gain", (double) *gain);
copyXmlToBinary (*xml, destData);
}

AudioProcessor::copyXmlToBinary() 関数は、XMLをバイナリー・ブロブに変換する便利なヘルパー関数です。 状態を取得するには、その逆を行います:

    void setStateInformation (const void* data, int sizeInBytes) override
{
std::unique_ptr<juce::XmlElement> xmlState (getXmlFromBinary (data, sizeInBytes));

if (xmlState.get() != nullptr)
if (xmlState->hasTagName ("ParamTutorial"))
*gain = (float) xmlState->getDoubleAttribute ("gain", 1.0);
}

AudioProcessor::getXmlFromBinary() 関数は、AudioProcessor::copyXmlToBinary() 関数で作成されたバイナリデータをXMLに変換します。

重要なのは、ここでエラーチェックが行われていることです。 情報ブロックがXMLでない場合、この関数は何もしません。 また、"ParamTutorial" という タグ名 があるかどうかをチェックし、見つかった場合のみ処理を進めます。 ゲイン・パラメーターが見つからなければ、ゲインの値もデフォルトの1.0になる。 バージョン情報を追加するのは、この目的のために別の属性を追加するのと同じくらい簡単です。 その後、エラー・チェックを増やすことで、異なるバージョンの状態情報を扱えるようになる。

注記

この修正版プラグインのソースコードは、デモプロジェクトの AudioParameterTutorial_03.h ファイルにあります。

位相反転パラメータの追加

ゲイン・プラグインに phase invert パラメーターを追加してみましょう!

ブーリアン・パラメータの追加

まず、TutorialProcessor クラスに AudioParameterBool* メンバを追加します [2]:

private:
//==============================================================================
juce::AudioParameterFloat* gain;
juce::AudioParameterBool* invertPhase; // [2]

float previousGain;

//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TutorialProcessor)
};

次に、TutorialProcessor コンストラクタにパラメータを割り当てて追加する必要がある [3]:

    TutorialProcessor()
{
addParameter (gain = new juce::AudioParameterFloat ("gain", "Gain", 0.0f, 1.0f, 0.5f));
addParameter (invertPhase = new juce::AudioParameterBool ("invertPhase", "Invert Phase", false)); // [3]
}

もちろん、booleanパラメータには指定可能な範囲はなく、デフォルト値しかありません。 TutorialProcessor::getStateInformation() 関数を更新する必要がある [4]:

    void getStateInformation (juce::MemoryBlock& destData) override
{
std::unique_ptr<juce::XmlElement> xml (new juce::XmlElement ("ParamTutorial"));
xml->setAttribute ("gain", (double) *gain);
xml->setAttribute ("invertPhase", *invertPhase); // [4]
copyXmlToBinary (*xml, destData);
}

そして、TutorialProcessor::setStateInformation() 関数 [5]:

    void setStateInformation (const void* data, int sizeInBytes) override
{
std::unique_ptr<juce::XmlElement> xmlState (getXmlFromBinary (data, sizeInBytes));

if (xmlState.get() != nullptr)
{
if (xmlState->hasTagName ("ParamTutorial"))
{
*gain = (float) xmlState->getDoubleAttribute ("gain", 1.0);
*invertPhase = xmlState->getBoolAttribute ("invertPhase", false); // [5]
}
}
}

オーディオ処理コードを追加する必要がある:

    void processBlock (juce::AudioSampleBuffer& buffer, juce::MidiBuffer&) override
{
auto phase = *invertPhase ? -1.0f : 1.0f; // [6]
auto currentGain = *gain * phase; // [7]

if (juce::approximatelyEqual (currentGain, previousGain))
{
buffer.applyGain (currentGain);
}
else
{
buffer.applyGainRamp (0, buffer.getNumSamples(), previousGain, currentGain);
previousGain = currentGain;
}
}

ここで注目してほしい:

  • [6]: invertPhase パラメータの状態に応じて、+1または-1を選択する。
  • [7]: これに gain パラメータの値を掛ける。
  • この関数の残りのコードは、平滑化手法を含めて同じです。

最後に、previousGainTutorialProcessor::prepareToPlay() 関数で初期化する必要がある:

    void prepareToPlay (double, int) override
{
auto phase = *invertPhase ? -1.0f : 1.0f;
previousGain = *gain * phase;
}

概要

このチュートリアルでは、AudioProcessor クラスでのオーディオパラメータの使用について学びました。特に

  • プロセッサの可変パラメータを表す AudioParameterFloat オブジェクトを作成する。
  • AudioParameterFloat オブジェクトの値を使用して、オーディオ処理を制御する。
  • プロセッサのステート情報にパラメータデータを格納し、取り出す。
  • AudioParameterBool オブジェクトを使って、オンまたはオフの状態にあるパラメータを表す。

関連項目