基本的な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のI/Oと処理ロジックを処理し、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 オブジェクトを作ります:
classTutorialPluginAudioProcessorEditor:public juce::AudioProcessorEditor
{
public:
TutorialPluginAudioProcessorEditor(TutorialPluginAudioProcessor&);
~TutorialPluginAudioProcessorEditor();
//===================================================================
voidpaint(juce::Graphics&)override;
voidresized()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コンポーネント)のサイズと位置を設定するのに適した場所です。
voidTutorialPluginAudioProcessorEditor::resized()
{
// 引数 (x, y, width, height) でスライダーの位置とサイズを設定します
midiVolume.setBounds(40,30,20,getHeight()-60);
}
また、paint() 関数で "Hello World" テキストを "Midi Volume" に変更し、一番上に移動させましょう。この関数はすべてのカスタムシェイプやGUI要素をウィンドウに描画する場所です。
voidTutorialPluginAudioProcessorEditor::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]を追加して、エディタークラスは次のようになります:
classTutorialPluginAudioProcessorEditor:public juce::AudioProcessorEditor,
private juce::Slider::Listener// [2]
{
public:
TutorialPluginAudioProcessorEditor(TutorialPluginAudioProcessor&);
~TutorialPluginAudioProcessorEditor();
//==================================================================
// これは標準的なジュースの塗り方だが...
voidpaint(juce::Graphics& g)override;
voidresized()override;
private:
voidsliderValueChanged(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);
}
...そして、パブリック・プロセッサ・ボリューム変数を設定するリスナー関数を挿入する:
voidTutorialPluginAudioProcessorEditor::sliderValueChanged(juce::Slider* slider)
{
audioProcessor.noteOnVel= midiVolume.getValue();
}
これで、プロセッサー・クラスの変数をコントロールするスライダーができました。このプロセッサ変数を使ってMIDIデータを変更する必要がある。
MIDIノートの修正
プロセッサークラスのprocessBlock() メソッドは、MIDIとオーディオの両方のバッファをリアルタイムで受信し、生成します。MIDIバッファを繰り返し処理して、noteOnタイプのシグナルをインターセプトし、そのベロシティをスライダーの値に設定します。
MIDIメッセージはすべてこの関数に渡されます。通過するMIDIを変更するために、processedMidi という新しいMidiBufferオブジェクトを作成し、変更したMIDIシグナルをこの新しいバッファに追加してから、最後に元のバッファと入れ替えます(こうすることで、直接変更する問題を避けることができます)。processBlock() メソッド内の現在のコードを削除し(これはオーディオバッファを処理するもので、このチュートリアルでは必要ありません)、以下のコードに置き換えます。
voidTutorialPluginAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages)
{
buffer.clear();
juce::MidiBuffer processedMidi;
for(constauto metadata: midiMessages)
{
auto message= metadata.getMessage();
constauto 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データを受信させます