CLever Audio Plugin (CLAP) プラグインをRustで作る
CLAPとは何か
CLever Audio Plug-in とは、Bitwigとu-heが共同で開発し2022年6月にリリースされた新しいオーディオプラグインの規格です。
CLAPはMITライセンスのもとで提供され、商用・非商用問わず誰でもプラグインの開発が可能です。
今回は、Rustを使ってゲインを50%にするCLAPプラグインを作成します。
前提
- Rust開発環境を構築済み
- CLAPに対応したDAWをインストール済み
- Windows x86-64を想定
プロジェクトの作成
新しいプロジェクトを作成します。
cargo new --lib claptest
Cargo.tomlを編集
動的リンクライブラリを生成するため cbylib を追加します。
[lib]
crate-type = ["cdylib"]
依存関係の追加
CLAPのRust用ライブラリは、Clackという低レベルで安全と主張されているラッパーを使います。
crates.ioで公開されていないため、以下のコマンドを入力して依存関係を追加します。
cargo add --git https://github.com/prokopyl/clack.git clack-plugin
cargo add --git https://github.com/prokopyl/clack.git clack-extensions --features clack-plugin,audio-ports
コードを書く
src/lib.rs
にプラグインのコードを書きます。
既に何かしらのコードが記述されている場合、全て削除してください。
今回書いたコードの全体はこちらで公開しています。
インポートと構造体の定義
audio_ports
はオーディオ入出力を行う場合に必要になります。
use clack_extensions::audio_ports::*;
use clack_plugin::prelude::*;
pub struct ClapTest;
Pluginトレイトの実装
impl Plugin for ClapTest {
type AudioProcessor<'a> = ClapTestAudioProcessor<'a>;
type Shared<'a> = ClapTestShared;
type MainThread<'a> = ClapTestMainThread<'a>;
fn declare_extensions(builder: &mut PluginExtensions<Self>, _shared: Option<&ClapTestShared>) {
builder
.register::<PluginAudioPorts>();
}
}
DefaultPluginFactoryトレイトの実装
PluginDescriptor
でプラグイン名や作成者、バージョンやタグを設定できます。
with_features
で AUDIO_EFFECT を入れるとDAWでエフェクト一覧に表示されます。
INSTRUMENT を入れると、音源一覧に表示されます。
impl DefaultPluginFactory for ClapTest {
fn get_descriptor() -> PluginDescriptor {
use clack_plugin::plugin::features::*;
PluginDescriptor::new("com.github.saisana299.clap-test", "Clap Test")
.with_vendor("Saisana299")
.with_features([AUDIO_EFFECT, STEREO])
}
fn new_shared(_host: HostSharedHandle) -> Result<Self::Shared<'_>, PluginError> {
Ok(ClapTestShared {})
}
fn new_main_thread<'a>(
_host: HostMainThreadHandle<'a>,
shared: &'a Self::Shared<'a>,
) -> Result<Self::MainThread<'a>, PluginError> {
Ok(Self::MainThread { shared })
}
}
AudioProcessor構造体とそのトレイト実装
オーディオ処理を行う構造体です。
process
メソッド内で、オーディオバッファを取得し、各サンプルの音量を0.5倍にしています。
pub struct ClapTestAudioProcessor<'a> {
_shared: &'a ClapTestShared,
}
impl<'a> PluginAudioProcessor<'a, ClapTestShared, ClapTestMainThread<'a>>
for ClapTestAudioProcessor<'a>
{
fn activate(
_host: HostAudioProcessorHandle<'a>,
_main_thread: &mut ClapTestMainThread<'a>,
_shared: &'a ClapTestShared,
_audio_config: PluginAudioConfiguration,
) -> Result<Self, PluginError> {
Ok(Self { _shared })
}
fn process(
&mut self,
_process: Process,
mut audio: Audio,
events: Events,
) -> Result<ProcessStatus, PluginError> {
let mut port_pair = audio
.port_pair(0)
.ok_or(PluginError::Message("No input/output ports found"))?;
let mut output_channels = port_pair
.channels()?
.into_f32()
.ok_or(PluginError::Message("Expected f32 input/output"))?;
let mut channel_buffers = [None, None];
for (pair, buf) in output_channels.iter_mut().zip(&mut channel_buffers) {
*buf = match pair {
ChannelPair::InputOnly(_) => None,
ChannelPair::OutputOnly(_) => None,
ChannelPair::InPlace(b) => Some(b),
ChannelPair::InputOutput(i, o) => {
o.copy_from_slice(i);
Some(o)
}
}
}
#[allow(unused_variables)]
for event_batch in events.input.batch() {
// 音量を0.5倍にする
for buf in channel_buffers.iter_mut().flatten() {
for sample in buf.iter_mut() {
*sample *= 0.5;
}
}
}
Ok(ProcessStatus::ContinueIfNotQuiet)
}
}
SharedとMainThreadの定義
メインスレッドの処理では、オーディオポートの数と情報を設定しています。
count
メソッドではオーディオポートの数を指定しています。
is_input
がtrueの時は入力、falseの時は出力のポート数を返すようにします。
今回は入力、出力共にポート数1で設定しています。
pub struct ClapTestShared {}
impl<'a> PluginShared<'a> for ClapTestShared {}
pub struct ClapTestMainThread<'a> {
#[allow(dead_code)]
shared: &'a ClapTestShared,
}
impl<'a> PluginAudioPortsImpl for ClapTestMainThread<'a> {
fn count(&mut self, _is_input: bool) -> u32 {
1
}
fn get(&mut self, index: u32, _is_input: bool, writer: &mut AudioPortInfoWriter) {
if index == 0 {
writer.set(&AudioPortInfo {
id: ClapId::new(0),
name: b"main",
channel_count: 2,
flags: AudioPortFlags::IS_MAIN,
port_type: Some(AudioPortType::STEREO),
in_place_pair: None,
})
}
}
}
impl<'a> PluginMainThread<'a, ClapTestShared> for ClapTestMainThread<'a> {}
エントリーポイント
これを入れる事で、ホストがプラグインを認識して、ロードできるようになります。
clack_export_entry!(SinglePluginEntry<ClapTest>);
ビルドしてDAWで読み込む
以下のコマンドでビルドを行います。
cargo build --release
ビルドが完了すると、target
フォルダ内に、<プロジェクト名>.dll
ファイルが生成されています。
このdllファイルを <プロジェクト名>.clap
にリネームして、CLAPプラグインフォルダに入れます。
今回はBitwig Studioで読み込んでみました。
オーディオトラックにプラグインを挿すと音量が50%減ることが確認できました。
参考
Discussion