JUCEで作るUnity Native Audio Plugin


JUCEって何?

JUCE (Jules' Utility Class Extensions)は、C++言語によるマルチメディア系アプリケーションの開発を支援するフレームワークです。クロスプラットフォーム設計のライブラリと、付属されているプロジェクトジェネレータ『Projucer』から各種IDE(VisualStudio, Xcode, Makefile)向けにプロジェクトファイルを出力することで、ワンソースからWindows, macOS, Linux, iOS, Android で動作するアプリケーションを作成することができます。
公式サイト

JUCEの最大の特徴として、オーディオプラグインを開発するためのテンプレートが充実していることが挙げられます。VST/AudioUnit/AAX/RTASプラグインといった、DTMユーザーにはお馴染みのプラグインフォーマットを開発するのに長けており、日本国内外で多くの採用事例があります。
JUCEについてより詳しく知りたい場合は、こちらの記事JUCE ハンズオン 〜JUCEをはじめよう〜や、JUCE Advent Calendar 2017をご覧いただければと思います。

JUCE 5.4.0からUnity Native Audio Pluginが正式サポート

これまではVST/AudioUnit/AAXといったDAW向けのオーディオプラグインのビルドがサポートされていましたが、5.4.0からはUnityゲームエンジンのNative Audio Pluginも仲間に加わりました。
これにより、VSTとして開発していたDSPのコードを、そのままUnity上で実行することができるようになります。

参考: https://juce.com/discover/stories/unity-native-plug-in-support

Unity Native Audio Pluginとは

UnityのAudio Mixerコンポーネントにインサートすることができるプラグイン形式のDSPです。

参考: https://docs.unity3d.com/Manual/AudioMixerNativeAudioPlugin.html

新規プロジェクトを作成する

それでは、サイン波を生成するオーディオプラグイン『HelloSinewave』を作成してみましょう。
プロジェクト作成ウィザードからAudio Plugi-Inを選択します。

プロジェクト設定で[Plugin Formats]内のUnityにチェックを入れる

プロジェクトをIDEで開く

プロジェクト一覧にUnity用のプロジェクトが追加されていることが分かります。

HelloSinewaveのコーディング

PluginProcessor.hを開き、メンバ変数を追加します。

class HelloSinewaveAudioProcessor  : public AudioProcessor
{
~~~省略~~~

private:
    AudioParameterFloat* angleDelta;    // プラグインのパラメータ
    float angle = 0;                    // サイン波の位相

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HelloSinewaveAudioProcessor)
};

PluginProcessor.cppを開き、パラメータのインスタンス生成・登録と、サイン波をレンダリングする処理を実装します。

HelloSinewaveAudioProcessor::HelloSinewaveAudioProcessor()
#ifndef JucePlugin_PreferredChannelConfigurations
     : AudioProcessor (BusesProperties()
                     #if ! JucePlugin_IsMidiEffect
                      #if ! JucePlugin_IsSynth
                       .withInput  ("Input",  AudioChannelSet::stereo(), true)
                      #endif
                       .withOutput ("Output", AudioChannelSet::stereo(), true)
                     #endif
                       )
#endif
{
    // パラメータのインスタンス生成とパラメータの登録
    angleDelta = new AudioParameterFloat("ANGLE_DELTA", "Angle Delta", 0.0f, 1.0f, 0.1f);
    addParameter(angleDelta);
}

void HelloSinewaveAudioProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
{
    ScopedNoDenormals noDenormals;
    auto totalNumInputChannels  = getTotalNumInputChannels();
    auto totalNumOutputChannels = getTotalNumOutputChannels();

    for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
        buffer.clear (i, 0, buffer.getNumSamples());

    // サイン波をレンダリングする処理
    for (int sampleIndex = 0; sampleIndex < buffer.getNumSamples(); sampleIndex++)
    {
        angle += angleDelta->get();

        for (int channel = 0; channel < totalNumInputChannels; ++channel)
        {
            auto* channelData = buffer.getWritePointer(channel);
            channelData[sampleIndex] = std::sinf(angle) * 0.3f;
        }
    }
}

ビルドを実行

ビルドが完了すると、ファイル名にaudioplugin_の接頭辞が追加された動的ライブラリが生成されます。

Unityで実行する

Unityでプロジェクトを作成する

Unityを開き、Native Audio Pluginの実行に必要な作業を行います。

  • AudioSourceコンポーネントをシーンに追加する
  • AudioMixerをアセットに追加する
  • AudioMixerをAudioSourceコンポーネントのOutputにセットする
  • audioplugin_HelloSinewaveをアセットに追加する

AudioMixerにプラグインを追加する

AudioMixerファイルを選択したら、インスペクターから[Add Effect]をクリックして、リストに表示されたaudioplugin_HelloSinewaveをクリックします。

シーンを再生する

シーンを再生すると、audioplugin_HelloSinewaveによってミキサーのトラックにサイン波が流れます。
インスペクターの[Edit in Playmode]をクリックすると、シーン再生時にパラメータをインスペクターから操作することができます。
[Angle Delta] パラメータを操作すると、サイン波のピッチ(周波数)が上下することが確認できます。

プラグインのパラメータをC#スクリプトから操作できるようにする

インスペクターに表示されたパラメータ名を右クリックすると、コンテキストメニューにExpose xxxという項目が表示されます。
この項目を有効にしたパラメータはC#スクリプトからアクセスすることができるようになります。
スクリプト用に抽出されたパラメーターは、SetFloat 関数を使って設定することができます。

参考: オーディオミキサーパラメーターの抽出 (Exposed Parameters)

注意点

Unity Native Audio PluginではMIDI入出力を扱うことができません。
そのため、MIDIデータをトリガーとして処理を実行するようなインストゥルメント系などを移植する場合は、別途トリガー系のパラメータを用意しておくなどの実装が必要になるでしょう。