Featured image of the post

自作VSTリベンジ(JUCE C++)

お疲れ様です,Mass です.
JUCE Framework を利用したVSTの制作に再挑戦したので記録を残します.

以前JUCEに挑戦した際はは COx2 さん執筆のJUCE JAPANの本に記載のVSTを再現実装するところで終わってしまったため,今回はカスタムの機能を持ったVSTの実装に挑戦します.

VST とは?

Steinberg’s Virtual Studio Technology の略です.
DAWの一種であるCubaseを作っているSteinbergが提唱するプラグイン規格です.
DAWに楽器やエフェクトといった機能を追加するプラグインソフトウェアの規格です.
バージョンには VST,VST2,VST3がありますが,現行でSDK(開発用キット)が配布されているのはVST3のみのため,新しく開発するなら必然的にVST3になります.

JUCE Framework とは?

VST3の開発はコンセプトとの複雑化やドキュメントの不足により,VST2よりも開発が困難です.
JUCE FrameworkはそんなVST3の開発をフレームワークに則ることで容易にしました.フレームワークを利用することでオーディオの処理そのものの実装に注力することが可能になります.

実際の開発の手順はKashiwadeさんのこちらの記事が大変参考になります.

何を作るのか?

今回作ったのは Ring Mod Sidechain ができるVSTプラグインです.
機能としてはこちらのツイートを見ていただくとわかりやすいのですが,入力信号とサイドチェイン信号をRing Modulatorで掛け合わせたものを入力信号に足し合わせることで,サイドチェイン信号分の隙間が入力信号に生まれます.これを利用して入力信号のダッキングを行い,サイドチェイン信号をよく聞かせるのが Ring Mod Sidechain の簡単な仕組みです.

実装では,processBlockにて以下のように受けとった入力信号とサイドチェイン信号のLRのバッファに対して処理を行います.

Processor::processBlockImple(juce::AudioBuffer<T>& buffer) {
	auto* leftAudioBuff = buffer.getWritePointer(0);
  auto* rightAudioBuff = buffer.getWritePointer(1);
  auto* leftSideChainAudioBuff = buffer.getWritePointer(2);
  auto* rightSideChainAudioBuff = buffer.getWritePointer(3);
  auto buffLength = buffer.getNumSamples();
  // 以下に実際の処理を書く
  for (int samplesIdx = 0; samplesIdx < buffLength; samplesIdx++) {
  }
}

メインの処理は問題なく書けましたが,おまけでつけたLPF(ローパスフィルタ)の実装で引っかかりました.

今回,32/64bit float の処理をジェネリクスを利用して以下のように同時に書いたのですが,

template <typename T>
  void processBlockImpl(juce::AudioBuffer<T>& buffer,
                        juce::MidiBuffer& midiMessages);

  void processBlock(juce::AudioBuffer<float>& buffer,
                    juce::MidiBuffer& midiMessages) override {
    processBlockImpl(buffer, midiMessages);
  };
  void processBlock(juce::AudioBuffer<double>& buffer,
                    juce::MidiBuffer& midiMessages) override {
    processBlockImpl(buffer, midiMessages);
  };

LPFの実装として利用したjuce::dsp::IIR::Coefficients<float>::makeHighPass の精度をジェネリクスを利用して定義する方法を見つけることができませんでした.そのため 32/64bit float 用のフィルタのインスタンスを二つ保持し,精度によって使い分けるといった実装を行いました.

そしてビルドして出来上がったVSTがこちらになります.
デザインはAffinity Designer 2 を利用して自作しました.
GUIの実装は力尽きたのでハリボテですが,DAW側からパラメータを直接制御はできます.

実際に動作させてみたところ,DAW上で読み込んでダッキングはできましたがバッファのコピー速度に問題があり定期的にノイズが入ってしまう結果となりました.

こちらはJUCE側にバッファ配列の高速コピー用のメソッドが用意されているようなので,そちらを試して今後改善してみようと思います.

Image in a image block


最後になりますが,素敵な記事を書いてくださったKashiwadeさんありがとうございます.