4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Blue Cat's Plug'n Script - DTMプログラミング言語探訪

Last updated at Posted at 2021-07-21

DTMプログラミング言語探訪

Blue Cat's Plug'n Script

概要

Blue Cat AudioのBlue Cat's Plug'n Scriptは、VSTやAUプラグインを作成するためのスクリプティング環境です。DAWを終了したりプラグインを読みなおすことなくトライ&エラーでプラグイン開発が可能という特長があります。スクリプト自体高速で動くほか、C++ネイティブコードのサポート、ビルドしたバイナリ形式で配布可能など、商用レベルのプラグイン開発にも使用できる能力を持っています。

用途

DAWのプラグインすなわち信号処理アプリケーションの開発環境です。

  • インストゥルメントプラグイン開発
  • エフェクトプラグイン開発

プラグインだけでなく、スタンドアロンインストゥルメントとしての実行環境も用意されています。

言語仕様

  • AngelScript
  • イベントコールバック
  • 信号処理API
  • GUI API

AngelScriptはC++と非常に似たシンタックスを持つ言語です。一見するとC++と見分けがつきにくいこともあります。
コールバックや信号処理の書き方はVSTSDKやJUCEに触れたことがある人なら迷うことなく扱うことができます。

公式マニュアル Blue Cat's Plug'n Script User Manual

GUI仕様

可変パラメータを定義するとデフォルトで高品質のGUIが表示されます。
独自のGUIをデザインしてKUIML言語で定義したものを使用することもできます。

プログラム例

モノシンセ

サイン波を鳴らすモノシンセの例です。
AngelScriptは拡張子が.asも使われるようですが、なぜかサンプルプログラムの拡張子はすべて.cxxなのでそれに従いました。.asでも.cxxでも読み込み可能です。

Sine.cxx
#include "../library/Midi.hxx"
#include "../library/Constants.hxx"

string name = "Sine Oscillator";
string description = "Sinewave mono synth";

double amplitude = 0;
double phase = 0;
double omega = 0;
uint8  noteNum = 0;
const  double pi2 = 2 * PI;

// MIDI event handler
void handleMidiEvent(const MidiEvent& e)
{
  switch(MidiEventUtils::getType(e))
  {
    case kMidiNoteOn:
    {
      amplitude = double(MidiEventUtils::getNoteVelocity(e)) / 127.0;
      noteNum = MidiEventUtils::getNote(e);
      phase = 0;
      omega = pi2 * pow(2,((double(noteNum - 69.0)) / 12.0)) * 440.0 / sampleRate;
      break;
    }
    case kMidiNoteOff:
    {
      if(noteNum == MidiEventUtils::getNote(e))
        amplitude=0;
      break;
    }
  }
}


// Audio generator
void processBlock(BlockData& data)
{
  uint nextEvent = 0;

  for(uint i = 0; i < data.samplesToProcess; i++)
  {
    while(nextEvent != data.inputMidiEvents.length && data.inputMidiEvents[nextEvent].timeStamp <= double(i))
    {
      handleMidiEvent(data.inputMidiEvents[nextEvent]);
      nextEvent++;
    }

    double val = amplitude * sin(phase);
    for(uint ch=0; ch < audioOutputsCount; ch++)
    {
      data.samples[ch][i] = val;
    }

    phase += omega;
  }

  while(phase > pi2)
    phase -= pi2;
}

int getTailSize()
{
  return -1;
}

name、descriptionで設定した文字列がプラグイン画面に表示されます。

getTailSize()は、空間系エフェクトのように入力オーディオに対して出力オーディオのリリースが長いようなときに長さを指定する関数とのことです。シンセサイザーのような入力オーディオがない場合は-1を返します。
bc1.png

GUI

上記のモノシンセを2オシレーターにしてノブを追加した例です。

Sine2.cxx
#include "../library/Midi.hxx"
#include "../library/Constants.hxx"

string name = "2 Sine Oscillators";
string description = "Sinewave mono synth";

string[] inputParametersNames = {"Osc1", "Osc2"};
double[] inputParameters(inputParametersNames.length);
double[] inputParametersDefault = {0.5, 0.5};

enum Params
{
    kOsc1 = 0,
    kOsc2
};

double amplitude = 0;
double phase1 = 0;
double phase2 = 0;
double omega1 = 0;
double omega2 = 0;
uint8  noteNum = 0;
const  double pi2 = 2 * PI;

// MIDI event handler
void handleMidiEvent(const MidiEvent& e)
{
  switch(MidiEventUtils::getType(e))
  {
    case kMidiNoteOn:
    {
      amplitude = double(MidiEventUtils::getNoteVelocity(e)) / 127.0;
      noteNum = MidiEventUtils::getNote(e);
      phase1 = 0;
      phase2 = 0;
      omega1 = pi2 *pow(2,((double(noteNum - 69.0)) / 12.0)) * 440.0 / sampleRate;
      omega2 = omega1 * 2.0;
      break;
    }
    case kMidiNoteOff:
    {
      if(noteNum == MidiEventUtils::getNote(e))
        amplitude=0;
      break;
    }
  }
}

// Audio generator
void processBlock(BlockData& data)
{
  uint nextEvent = 0;

  for(uint i = 0; i < data.samplesToProcess; i++)
  {
    while(nextEvent != data.inputMidiEvents.length && data.inputMidiEvents[nextEvent].timeStamp <= double(i))
    {
      handleMidiEvent(data.inputMidiEvents[nextEvent]);
      nextEvent++;
    }

    double val = amplitude * (
      sin(phase1) * inputParameters[kOsc1] + 
      sin(phase2) * inputParameters[kOsc2]);
    for(uint ch=0; ch < audioOutputsCount; ch++)
    {
      data.samples[ch][i] = val;
    }

    phase1 += omega1;
    phase2 += omega2;
  }

  while(phase1 > pi2)
    phase1 -= pi2;
  while(phase2 > pi2)
    phase2 -= pi2;
}

int getTailSize()
{
  return -1;
}

inputParametersNames、inputParameters、inputParametersDefaultを定義すると適切な位置に適切なコントローラーが表示されます。値はinputParameters[パラメータ連番]で参照できます。
bc2.png

右側にあるGUI Options、GUI ControlsからさまざまなGUIの種類を選ぶことができます。
bc9.png

エフェクター

次にエフェクターの例です。これもVSTSDKやJUCEとあまりかわりありません。

Fuzz.cxx
string name="Fuzz";
string description="Fuzz Fx";

array<string> inputParametersNames = {"Clip"};
array<double> inputParameters(inputParametersNames.length);
array<double> inputParametersDefault = {0.5};

void processSample(array<double>& sig)
{
    for(uint ch = 0; ch <audioInputsCount; ch++)
    {
        double val = sig[ch];
        val *= 20.0 * inputParameters[0];

        if(val > 1.0)
            val = 1.0;
        else if(val < -1.0)
            val = -1.0;

        val /= 10.0;

        sig[ch] = val;
    }
}

一旦ゲインを20倍にブーストした後に±1.0でクリップさせ、1/10に下げる処理をしています。
バイパスしたときに聴感上の不自然さを抑えるために1/20ではなく1/10にしてみました。
bc8_2.png

実行方法

ユーザースクリプトは以下の場所に置いておくことが想定されているようです。ライブラリを相対パスで利用する関係上、他の場所に保存することはおすすめしません。
C:\Users\ユーザ名\Documents\Blue Cat Audio\Blue Cat's Plug'n Script\Scripts

サンプルスクリプトはVST3の場合、以下の場所に格納されています。
C:\Program Files\Common Files\VST3\BC Plug'n Script VST3 data\Resources\dsp-scripts\

エフェクターの場合は「BC Plug'n Script」を、インストゥルメントの場合は「BC Plug'n Script Synth」をDAWのプラグインとして挿入します。

一見グレーアウトしているように見える鉛筆アイコンをクリックすると編集モードになります。ちなみに虫眼鏡アイコンは検索ではなくズームです。
bc3.png

右側のスクリプト名をクリックしてスクリプトを選択します。
bc4.png

スクリプトにエラーがある場合はプラグイン画面の下段に表示されます。ここをクリックすると過去のエラー履歴も含めたログファイルが開かれます。
bc5.png

スクリプトファイル名の上にある鉛筆アイコンをクリックするとエディタでスクリプトを編集できます。
bc6_2.png

スクリプトが完成したらプラグイン画面のタイトル部分をクリックしてプリセット(.preset)として保存します。プリセットはスクリプトの場所やGUIの設定値などが記述されたXMLファイルです。
bc7.png

感想

信号処理の書きやすさという点では、JUCEやVSTSDKとさほど変わりません。AngelScriptなのでC++の煩雑さがわずかに軽減されている感じはありますがほとんど同じ感覚です。ただ、自動的に高品質のGUIが配置される点や、定型的なプロジェクト構成を用意する必要がないのは非常に楽です。
Blue Cat's Plug'n Scriptとテキストエディタさえあれば、大きなSDKやライブラリや統合環境を用意する必要がないのは好印象です。またトライ&エラーで書いていけるところも気軽で良かったです。
$99という価格なので普及の限界はあるように思います。なお、デモ版の制限は起動時にウィンドウが出るのと、毎分0.5秒間バイパスされる程度なので、実際に開発を経験して実用性を確認してから購入することが可能です。

DTMプログラミング言語探訪

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?