基本的なAudio/MIDIプラグインを作る パート2:プラグインのコーディング
このチュートリアルでは、コンピュータをセットアップし、 JUCE を使ってオーディオプラグイン(VST3 と AudioUnit)を開発するための Projucer プロジェクトを作成します。 最後には、"Hello, World!" と言って、 Cubase や REAPER のような VST3 ホストにロードできるオーディオプラグインが完成します。
レベル:中級
プラットフォーム:Windows, macOS, Linux
プラグイン形式:VST, VST3, AU, Standalone
クラス:AudioProcessorEditor, AudioProcessor, Slider, MidiMessage, MidiBuffer
はじめる
Projucerを起動し、TutorialPlugin という名前で 新しいオーディオプラグインプロジェクトを作成します。 その方法を覚えていない場合は、Projucerパート1:Projucerを始める を参照してください。
オリエンテーション
新しく作成されたオーディオ・プラグイン・プロジェクトには、主に2つのクラスが含まれます。
PluginProcessor
はオーディオと MIDIのIOと処理ロジックを処理し、PluginEditor
は画面上のGUIコントロールやビジュアライゼーションを処理します。
この2つの間で情報をやり取りする場合、プロセッサをエディタの親と考えるのがベストです。プラグイン・プロセッサは1つしかありませんが、エディタは複数作成できます。各エディターはプロセッサーへの参照を持ち、オーディオスレッドの情報やパラメータを編集したり、アクセスしたりすることができます。 このプロセッサー・スレッドに情報を設定したり取得したりするのはエディターの仕事であり、その逆はありません。
PluginProcessor.cpp
で編集する主な関数は processBlock()
メソッドです。
これはオーディオデータとMIDIデータの両方を受信し、プラグイン出力に出力します。
PluginEditor.cpp
ファイルで変更する主な関数はコンストラクタで、
ここでウィンドウとGUIオブジェクトを初期化してセットアップし、paint()
メソッドで追加コントロールとカスタムGUIコンポーネント を描画します。
エディタコンストラクタには現在、プラグインウィンドウのサイズを設定する setSize (400, 300)
というメソッドがあります。
この単純なアプリケーションのために、(200, 200)
の小さなウィンドウを作ってみましょう。
TutorialPluginAudioProcessorEditor::TutorialPluginAudioProcessorEditor (TutorialPluginAudioProcessor& p)
: AudioProcessorEditor (&p), audioProcessor (p)
{
// ここでプラグインのエディターサイズを設定します
setSize (200, 200);
}
シンプルなGUIコントロールを作成する
MIDIメッセージの音量を変更するためのスライダーオブジェクトを作ります。
エディターのヘッダーファイルにmidiVolume [1]という新しい Slider オブジェクトを作ります:
class TutorialPluginAudioProcessorEditor : public juce::AudioProcessorEditor
{
public:
TutorialPluginAudioProcessorEditor (TutorialPluginAudioProcessor&);
~TutorialPluginAudioProcessorEditor();
//===================================================================
void paint (juce::Graphics&) override;
void resized() override;
private:
// このリファレンスは、編集者が以下のことを素早く実行できるように提供されています
// 作成したプロセッサオブジェクトにアクセスする
TutorialPluginAudioProcessor& audioProcessor;
juce::Slider midiVolume; // [1]
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TutorialPluginAudioProcessorEditor)
};
AudioProcessorEditor は、オーディオプラグインにおいて、スタンドアロンアプリの メインコンテンツコンポーネント と同じ役割を果たします。 チュートリアル:メインコンポーネント を参照してください
このスライダーのプロパティはエディターコンストラクタの様々な関数で設定できます。
また、addAndMakeVisible (&midiVolume)
を呼び出して、スライダーをエディターに取り付けなければなりません。
様々なスライダーのスタイルとパラメーターがあるので、自分のプロジェクトで使って試してみてください。
このチュートリアルでは、エディターコンストラクターが次のようになるようにスライダーパラメーターを調整します:
TutorialPluginAudioProcessorEditor::TutorialPluginAudioProcessorEditor (TutorialPluginAudioProcessor& p)
: AudioProcessorEditor (&p), audioProcessor (p)
{
// ここでプラグインのエディターサイズを設定します
setSize (200, 200);
// これらは、スライダーオブジェクトのパラメーターを定義します
midiVolume.setSliderStyle (juce::Slider::LinearBarVertical);
midiVolume.setRange (0.0, 127.0, 1.0);
midiVolume.setTextBoxStyle (juce::Slider::NoTextBox, false, 90, 0);
midiVolume.setPopupDisplayEnabled (true, false, this);
midiVolume.setTextValueSuffix (" Volume");
midiVolume.setValue(1.0);
// この関数はエディタにスライダーを追加します
addAndMakeVisible (&midiVolume);
}
JUCEのウィンドウには resized()
というメソッドがあり、ウィンドウの初期化時に一度呼び出され、
ユーザによってウィンドウのサイズが変更されるたびに呼び出されます(リサイズが有効な場合)。
これは、スライダー(および他の GUI コンポーネント)のサイズと位置を設定するのに適した場所です。
void TutorialPluginAudioProcessorEditor::resized()
{
// 引数 (x, y, width, height) でスライダーの位置とサイズを設定します
midiVolume.setBounds (40, 30, 20, getHeight() - 60);
}
また、paint()
関数で "Hello World" テキストを "Midi Volume" に変更し、一番上に移動させましょう。
この関数はすべてのカスタムシェイプやGUI要素をウィンドウに描画する場所です。
void TutorialPluginAudioProcessorEditor::paint (juce::Graphics& g)
{
// ウィンドウ全体を白く塗りつぶす
g.fillAll (juce::Colours::white);
// 現在の描画色を黒に設定する
g.setColour (juce::Colours::black);
// フォントサイズを設定し、テキストをスクリーンに描画する
g.setFont (15.0f);
g.drawFittedText ("Midi Volume", 0, 0, getWidth(), 30, juce::Justification::centred, 1);
}
コンポーネントとそのメソッド paint()
および resized()
については、
チュートリアル:Graphicsクラス と
チュートリアル:親コンポーネントと子コンポーネント
を参照してください。
このプログラムを実行すると、ホストエディター上で次のようなプラグインが作成されるはずです:
プロセッサ・クラスに制御情報を渡す
これで、調整できるコントロールができましたが、実際には何もコントロールできません。 入力されるMIDIデータをインターセプトして、ノート・オン・ボリュームをスライダーのボリュームに置き換える必要があります。 プロセッサー・スレッドでMIDIエフェクトをコントロールするためにスライダーの値を取得するには、 プロセッサー・スレッドに新しい変数を作成し、スライダーを使って変更できるようにする必要があります。
プロセッサークラスのヘッダーに noteOnVel
という新しい public float 変数を作成します。
これはスライダーで設定する変数です。
public:
float noteOnVel;
スライダーが変更されるたびにこの値を設定する必要があります。 そのためにスライダー・リスナーのコールバック関数を使います。 どのクラスでもスライダー・リスナーの機能を継承できますが、このチュートリアルの目的では、この機能をエディター・クラスに追加します。
リスナーについてのより詳しい説明は、 チュートリアル:リスナーとブロードキャスター をご覧ください
継承 [2]とデフォルトのコールバック関数 [3]を追加して、エディタークラスは次のようになります:
class TutorialPluginAudioProcessorEditor : public juce::AudioProcessorEditor,
private juce::Slider::Listener // [2]
{
public:
TutorialPluginAudioProcessorEditor (TutorialPluginAudioProcessor&);
~TutorialPluginAudioProcessorEditor();
//==================================================================
// これは標準的なジュースの塗り方だが...
void paint (juce::Graphics& g) override;
void resized() override;
private:
void sliderValueChanged (juce::Slider* slider) override; // [3]
//==================================================================
// このリファレンスは、編集者が以下のことを素早く実行できるように提供されています
// 作成したプロセッサ・オブジェクトにアクセスする
TutorialPluginAudioProcessor& audioProcessor;
juce::Slider midiVolume;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TutorialPluginAudioProcessorEditor)
};
次に、エディターのコンストラクタで、ボリューム・スライダーにスライダーリスナーを追加します:
TutorialPluginAudioProcessorEditor::TutorialPluginAudioProcessorEditor (TutorialPluginAudioProcessor& p)
: AudioProcessorEditor (&p), audioProcessor (p)
{
// ...
// スライダーにリスナーを追加する
midiVolume.addListener (this);
}
...そして、パブリック・プロセッサ・ボリューム変数を設定するリスナー関数を挿入する:
void TutorialPluginAudioProcessorEditor::sliderValueChanged (juce::Slider* slider)
{
audioProcessor.noteOnVel = midiVolume.getValue();
}
これで、プロセッサー・クラスの変数をコントロールするスライダーができました。 このプロセッサ変数を使ってMIDIデータを変更する必要がある。
MIDIノートの修正
プロセッサークラスの processBlock()
メソッドは、MIDIとオーディオの両方のバッファをリアルタイムで受信し、生成します。
MIDIバッファを繰り返し処理して、noteOnタイプのシグナルをインターセプトし、そのベロシティをスライダーの値に設定します。
MIDIメッセージはすべてこの関数に渡されます。
通過するMIDIを変更するために、processedMidi
という新しい MidiBuffer
オブジェクトを作成し、変更したMIDIシグナルをこの新しいバッファに追加してから、最後に元のバッファと入れ替えます(こうすることで、直接変更する問題を避けることができます)。
processBlock()
メソッド内の現在のコードを削除し(これはオーディオバッファを処理するもので、このチュートリアルでは必要ありません)、以下のコードに置き換えます。
void TutorialPluginAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages)
{
buffer.clear();
juce::MidiBuffer processedMidi;
for (const auto metadata : midiMessages)
{
auto message = metadata.getMessage();
const auto time = metadata.samplePosition;
if (message.isNoteOn())
{
message = juce::MidiMessage::noteOn (message.getChannel(),
message.getNoteNumber(),
(juce::uint8) noteOnVel);
}
processedMidi.addEvent (message, time);
}
midiMessages.swapWith (processedMidi);
}
ホスト環境でプラグインを実行すると、プラグインを通して送られてくるすべてのMIDIノートオン信号が、スライダーで設定した値を持っていることがわかります。
上記の if()
ステートメントは、他のタイプの入力MIDI信号を変更したり、様々な変換やエフェクトを適用したりするのにも使えます。
これらのメソッドを使えば、より複雑なエフェクトやGUIを構築することができます。
ボタンやスライダなどの他のGUIコンポーネントを試してみたり、 JUCEの機能を体験するためにJUCE DemoRunnerをチェックしたり、詳細についてはAPIドキュメントを参照してください。
入力されたMIDIノートを使ってオーディオを生成することは、今後のチュートリアルで取り上げます
(チュートリアル:MIDIシンセサイザーを作るを参照)。
とりあえず、JUCE/examples/Plugins
にある AudioPluginDemo
を見てください。
概要
このチュートリアルを読めば、次のことができるようになるはずだ:
- 基本的なGUIを備えたオーディオプラグインを作成します
- プラグインにMIDIデータを受信させます