2020/12/03

GitHub Actions による VST 3 SDK のビルド

かなりはまったのでまとめておきます。

build.yml

以下は Windows のジョブだけを抜き出した `.github/workflows/build.yml` の内容です。

name: CI

on: [push]

jobs:
  build-windows:
    runs-on: windows-latest
    steps:
    - uses: actions/checkout@v2
      with:
        path: VSTPlugins
        submodules: recursive
    - name: Run script
      run: VSTPlugins/ci/ci_windows.ps1
    - name: Upload
      uses: actions/upload-artifact@v1
      with:
        name: vst_windows
        path: vst_windows

actions/checkout で自前のプラグインのリポジトリをクローン、細かいビルド作業を `ci_windows.ps1` で実行、 actions/upload-artifact でビルドしたプラグインをアップロードする、という単純な CI (continuous integration) です。 GitHub Actions では CI の終了後にアップロードするデータのことをアーティファクトと言います。

以下は実際のコードへのリンクです。

`build.yml` の macOS と Ubuntu のジョブは、ほとんどコピペです。スクリプトは `ci_windows.ps1` へのリンクをたどってディレクトリを一つ登ったところに置いています。

ビルドスクリプト

以下は `ci_windows.ps1` からアーティファクトのアップロードの前処理を省いた抜粋です。言語は PowerShell です。

git clone --recursive $VST3SDK_REPOSITORY_URL
mkdir build
mkdir target
$SRC_ROOT = (Get-Item .).FullName

cmake `
  -S vst3sdk `
  -B build `
  -G "Visual Studio 16 2019" `
  -A x64 `
  -DSMTG_MYPLUGINS_SRC_PATH="$SRC_ROOT\VSTPlugins" `
  -DSMTG_PLUGIN_TARGET_PATH="$SRC_ROOT\target" `
  -DSMTG_ADD_VST3_HOSTING_SAMPLES=FALSE `
  -DSMTG_ADD_VST3_PLUGINS_SAMPLES=FALSE `
  -DSMTG_CREATE_PLUGIN_LINK=FALSE
  
cmake --build build -j --config Release
if (!$?) { Exit $LASTEXITCODE }

VST 3 SDK 固有の cmake オプションの意味は以下の通りです。

  • SMTG_MYPLUGINS_SRC_PATH: 自分のプラグインのソースコードへのパス。
  • SMTG_PLUGIN_TARGET_PATH: プラグインのインストールパス。
  • SMTG_ADD_VST3_HOSTING_SAMPLES: サンプルホストのビルド。
  • SMTG_ADD_VST3_PLUGINS_SAMPLES: サンプルプラグインのビルド。
  • SMTG_CREATE_PLUGIN_LINK: ビルドしたプラグインのリンクをインストールパスに作る。

SMTG_ADD_VST3_HOSTING_SAMPLES 、 SMTG_ADD_VST3_PLUGINS_SAMPLES 、 SMTG_CREATE_PLUGIN_LINK は CI では不要なので FALSE に設定しています。

最後の行の `if (!$?) { Exit $LASTEXITCODE }` は cmake がビルドに失敗したときにエラーコードを出してスクリプトを終了する PowerShell 固有の処理です。 bash ではスクリプトの先頭に `set -e` と書けば似たような効果があります。これらの行が無いとビルドに失敗してもスクリプトが止まらず、問題が残ったまま CI が成功してしまいます。このワークアラウンドは以下のリンクのコメントを参考にしました。

Windows PowerShell failed still marks job successful (#3194) · Issues · GitLab.org / gitlab-runner · GitLab

アーティファクトの消去

何回もビルドしているとアーティファクトが増えてストレージの容量が圧迫されることがあります。アーティファクトは手でも消せますが、時間がかかるので GitHub の提供する REST API を使うと楽です。 GitHub の REST API の詳細は以下のマニュアルを参照してください。マニュアルの Guides のページを前から順に試せばだいたい理解できます。

GitHub REST API - GitHub Docs

まずは以下のページの手順に従ってアクセストークンを取得します。アクセストークンがあればアカウントを乗っ取れてしまうので、何があっても誰にも公開しないでください。

Creating a personal access token - GitHub Docs

以降ではアクセストークンを `GITHUB_TOKEN` 、ユーザ名を `GITHUB_USER` とします。

以下のコマンドを実行すれば curl を使ってアーティファクトの一覧を取得します。

curl \
  -H "Accept: application/vnd.github.v3+json"\
  -u "$GITHUB_USER:$GITHUB_TOKEN" \
  "$REPOSITORY_URL/actions/artifacts?per_page=100" \
  > artifacts.json

実行前に `$GITHUB_USER` と `$GITHUB_TOKEN` にそれぞれ適切な値を代入しておいてください。 `$REPOSITORY_URL` は GitHub リポジトリのトップページの URL です。 `https://github.com//` の形式になっています。 `` と `` の部分は自分のリポジトリに合わせて置き換えてください。

`$REPOSITORY_URL` のあとに続く `actions/artifacts?per_page=100` の部分が REST API です。アーティファクトに関する API は以下のリンクに載っています。

Actions - GitHub Docs

取得したアーティファクトの一覧を保存した `artifacts.json` を使って以下の Python3 のスクリプトでまとめてアーティファクトを消します。

import json
import subprocess

github_user = # ユーザ名
github_token = # アクセストークン
repository_name = # リポジトリ名

with open("artifacts.json", "r", encoding="utf-8") as fi:
    data = json.load(fi)

for artifact in data["artifacts"]:
    print(f"████ Deleting {artifact['id']}")
    subprocess.run([
        "curl",
        "-i",
        "-X",
        "DELETE",
        "-u",
        f"{github_user}:{github_token}",
        f"https://api.github.com/repos/{github_user}/{repository_name}/actions/artifacts/{artifact['id']}",
    ])

ユーザ名、アクセストークン、リポジトリ名は文字列で指定してください。

この方法では一度に 100 個のアーティファクトまでしか消せません。アーティファクトが多いときはスクリプト側で curl コマンドを呼び出すことが考えられます。ただし接続回数の制限には注意してください。

また、任意のアーティファクトだけ残すような処理も行っていません。この辺はマニュアルや `artifacts.json` を読んで個別に対応してください。

その他

今は解決しましたが、昨日までは Windows の CI スクリプトの最初の cmake が `C:\Users\RUNNER~1\AppData\Local\Temp` とだけ出力して止まっていました。この問題は GitHub Actions では VST 3 SDK のデフォルトのインストールパスである `C:/Program Files/Common Files/VST3` を作成できないことが原因だったようです。上のスクリプトでは適当なディレクトリ `target` を作って cmake のオプションから `SMTG_PLUGIN_TARGET_PATH` に設定するというワークアラウンドを使っています。

ここからは解決策が見つかるまでの道のりを書いています。まずは "github actions cmake C:\Users\RUNNER~1\AppData\Local\Temp" でグーグル検索したところ以下の issue が出てきました。

%TEMP% is broken on Windows · Issue #712 · actions/virtual-environments · GitHub

この issue に書いてあるワークアラウンドを試しても問題が解決しなかったので、 `vst3sdk/cmake` ディレクトリ内で "TEMP" という文字列を検索したところ SMTG_Platform_Windows.cmake が引っかかりました。 SMTG_Platform_Windows.cmake の中に唯一書いてある関数 "smtg_create_directory_as_admin_win" でもう一度 `vst3sdk/cmake` 内を検索したところ SMTG_AddVST3Options.cmake の以下の行が引っかかりました。

if(SMTG_WIN)
    smtg_create_directory_as_admin_win (${SMTG_PLUGIN_TARGET_PATH})
else()

ここでようやく SMTG_PLUGIN_TARGET_PATH が原因になっていそうなことが分かりました。

2020/11/20

トゥルーピークの計算

 

トゥルーピークの計算を読む (github.io)

ITU-R BS.1770 で触れられているトゥルーピークの計算について調べました。いろいろな分数ディレイフィルタを試したところ、以前に調べた群遅延が平坦になるように凸最適化によって設計した FIR フィルタを使うと良さそうなことがわかりました。

画像は今回設計した FIR の分数ディレイフィルタの特性です。 Sinc 補間と比べたときの誤差は BS.1770 の Annex 2 に掲載されているフィルタと同程度ですが、フィルタのタップ数が 7 なので、より速く計算できます。

EBU TECH 3341 に掲載されているトゥルーピークのテストを通ればいいだけならフィルタのタップ数は 5 まで減らせます。

2020/10/15

ステップ応答が S 字を描くフィルタ

ステップ応答が S 字を描くフィルタを読む (github.io)

リミッタのエンベロープのスムーシングに使うためにステップ応答が S 次を描くフィルタについて調べました。移動平均の重ね掛け、バイリニア変換した Bessel フィルタ、 Thiran ローパスフィルタを比べています。画像は 3 つのフィルタのステップ応答です。

S 字のステップ応答にこだわったのは、リミッタのエンベロープが矩形波に近い波形になったときでもサイン波に近い波形にスムーシングできるような気がしたからです。

2020/08/19

CollidingCombSynth


ダウンロードとマニュアル (github.io)
ソースコード (github.com)

CollidingCombSynth は Karplus-Strong アルゴリズムによる弦 (K-S弦) をぶつけ合うというアイデアに基づいた実験的なシンセサイザです。たまに擦弦楽器のような音が出ます。

以前に作った KSCymbal を元にしています。

弦の衝突の実装はかなり適当です。まず隣り合う 2 つの K-S 弦の出力の差をとって、ユーザが指定した弦と弦の間の距離 (distance パラメータ) を超えているかどうかを判定します。出力の差が距離を超えているときは、出力の差と距離の間でさらに差をとって次の入力に足し合わせています。

今回の実装では、一方の K-S 弦から、もう一方の K-S 弦だけに衝撃が伝わるので、オシレータシンクのような挙動をします。衝撃を伝える側の弦は一定の周期で距離を縮めるのに対して、衝撃を伝えられる側の弦は自身の周期によらず距離が縮まる影響を受けるからです。

衝撃は UI 上の表記で言うと Strings -> Frequency の 0 から 23 に向かって伝わります。

2020/06/04

L4Reverb


ダウンロードとマニュアル (github.io)
ソースコード (github.com)

L4Reverb は格子構造を使ったステレオリバーブです。

LatticeReverb と比べると 1 チャンネルあたりのディレイの数が 16 から 256 に増えています。また LatticeReverb では 16 セクションの格子構造が 1 つだけでしたが、 L4Reverb は 4 セクションの格子構造が 4 重の入れ子になっています (4 * 4 * 4 * 4 = 256) 。

ローパスフィルタを使っていないので、明るく長いリバーブが得意です。ショートリバーブは金属的な質感になることが多いです。ここでの金属的な質感とはショートディレイをたくさんつないだときのような音を指しています。

2020/05/16

LatticeReverb


ダウンロードとマニュアル (github.io)
ソースコード (github.com)

LatticeReverb はディレイを使った高次のオールパスフィルタを格子状につないで入れ子にしたリバーブです。

単なるショートディレイのような音になったときは LFO を使ってごまかすことができます。

2020/05/02

LightPadSynth


ダウンロードとマニュアル (github.io)
ソースコード (github.com)

LightPadSynth は CubicPadSynth の軽量版です。効率を良くするためにピッチ変調を無くしてディレイを追加しました。ディレイ時間を LFO で変調できるので CubicPadSynth とは異なる音をつくることができます。

以降は LightPadSynth で使った FFT ライブラリについてです。

2020/04/06

PolyBLEP Residual


PolyBLEP Residual を読む (github.io)

Nam, Pekonen, Valimaki による Perceptually informed synthesis of bandlimited classical waveforms using integrated polynomial interpolation で紹介されている B-spline PolyBLEP residual の 4, 6, 8 点の式を導出して試しました。

式の導出は PolyBLAMP residual とほとんど同じで積分が 1 回減るだけです。

画像は PolyBLEP residual のプロットです。

2020/04/05

変な 4-pole フィルタ


変な 4-pole フィルタを読む (github.io)

2重ばねの式の係数や項を適当に入れ替えて変な特性のフィルタを作りました。レゾナンスを動かしたときの音の変わり方が普通のローパスやハイパスとは異なります。

画像はフィルタの振幅特性です。

LV2 の CVPort プラグインとして実装しました。
- https://github.com/ryukau/LV2Plugins/tree/master/lv2cvport/CV_DoubleFilter

2020/04/04

3-pole ローパスフィルタ


3-pole ローパスフィルタを読む (github.io)

ばねとダンパの微分方程式がフィルタになりそうだと思ったので、適当に係数などを入れ替えてローパスフィルタを作りました。

記事では適当に立てたフィルタの式から伝達関数を出して、カットオフ周波数とレゾナンスのチューニングを行っています。このフィルタは安定していてレゾナンスも効くので使いやすいです。

LV2 の CVPort プラグインとして実装しました。
- https://github.com/ryukau/LV2Plugins/tree/master/lv2cvport/CV_3PoleLP

2020/03/20

放物線エンベロープ


放物線エンベロープを読む (github.io)

放物線を使ったエンベロープをつくりました。放物線ではなくなりますが、加速度を一定ではなく適当な関数に置き換えると面白そうです。

2020/03/18

PolyExp エンベロープ



PolyExp エンベロープを読む (github.io)

WobblingMetalBoard を作っているときに思いついたエンベロープについてまとめました。

画像は PolyExp エンベロープの出力です。エンベロープの式は E(t) = t^a * exp(-b * t) です。

2020/03/17

指数曲線のエンベロープ


指数曲線のエンベロープを読む (github.io)

シンセサイザでよく使われている指数曲線のエンベロープについてまとめました。

アタック時間が短いときのプチノイズの低減方法は効果はありますが、かなり適当です。記事中の実装と同じエンベロープを使っている IterativeSinCluster の出力に +80 dB くらいの極端なハイシェルフフィルタをかけるとノイズが完全に消えてはいないことが確認できます。

画像は指数曲線の ADSR エンベロープがリリースの途中でトリガされたときのテスト結果です。

直線の ADSR エンベロープ

直線の ADSR エンベロープを読む (github.io)

直線の ADSR エンベロープについてまとめました。

紹介している実装は CubicPadSynth で使っているものを元にしています。場当たり的に実装してひどいことになってしまった SyncSawSynth のエンベロープからかなり改善しました。

2020/03/16

オーディオプラグインの UI から入力された値の補間


オーディオプラグインの UI から入力された値の補間を読む (github.io)

VST 3 や LV2 で UI から入力されたパラメータをオーディオバッファ内で補間する方法についてまとめました。

ユーザから指定された時間に応じて線形補間するだけなのですが、バージョンの古い SevenDelay などでは補間した値がパラメータの範囲を超えることによって音が止まるというバグ(修正済み)の原因になっていたので、この機会に見直してより安全で速い実装に置き換えました。

2020/03/05

CubicPadSynth


ダウンロードとマニュアル (github.io)
ソースコード (github.com)

CubicPadSynth は PADsynth algorithm を使ったウェーブテーブル方式のシンセサイザです。極端に低い音でも滑らかになるようにバイキュービック補間を使っています。 LFO の波形はおおまかに手書きできます。

MIDI ノート番号の範囲と 20 kHz までのピッチベンドをカバーできるように 136 のウェーブテーブルを内部的に保持しているのですが、これが原因でとても重たいです。 1 つのテーブルは長さ 2^18 サンプルで型が float なので、 136 * 2^18 * 4 / 1024^2 = 136 MiB のメモリを使います。また、マニュアルのページからダウンロードできるバイナリは GitHub Actions でビルドしていますが、私の環境ではローカルでビルドしたバイナリよりもなぜか遅かったです。

2020/01/25

EsPhaser


ダウンロードとマニュアル (github.io)
ソースコード (github.com)

EsPhaser は 4096 の 2 次 Thiran オールパスフィルタと 16 の LFO を備えたフェイザです。 CPU を大きく消費するので使用時は注意してください。

これだけたくさんのオールパスをつなぐと、フェイザというよりディレイに近い音が出ます。 1 つのオールパスで 2 サンプルのディレイが生じるので、サンプリング周波数が 48000 Hz のときは 2 * 4096 / 48000 0.17 秒ほどのディレイがフィードバックで生じるはずです。

2020/01/17

EnvelopedSine

ダウンロードとマニュアル (github.io)
ソースコード (github.com)

EnvelopedSine は 1 つのノートごとに 64 のサイン波を計算する加算合成シンセサイザです。 IterativeSinCluster との違いは、サイン波ごとに独立した AD 音量エンベロープと tanh サチュレータがついていることです。

思っていたよりも作れる音の種類が限られていたのでフェイザとユニゾンをつけてごまかしました。サチュレーションはエイリアシングノイズが出たので、オシレータの周波数が高いときはオフになるようにしています。

このシンセで使ったアルゴリズムはドラムのばちの音を作るときに使える感じがします。