Ibotenic by t-sin


Rust製チップチューンシンセのSoyBoy SPをリリースしました

プログラミング Rust 音楽 デジタル信号処理 サウンドプログラミング VST3

2022-06-10T00:53:20.662485+09:00

開発していたRust製のチップチューン用VST3プラグイン、SoyBoy SPが無事v1.0.0になり頒布開始できました。この記事ではSoyBoyの紹介と開発の苦労話などを記しています。

はじめに

6月になってしまいました。アジサイが咲きはじめ、そろそろ梅雨もやってきますね。そうするとキノコのシーズンが到来します。そんな生命の活力に世が沸かんとしている今日このごろ、ずっとつくっていたVST3規格のシンセプラグインSoyBoy SPをリリースしました。

たぶん半年くらい開発してたものが今回日の目を見るに至ったということで、開発やリリースの振り返りとかしてみようと思います。

SoyBoy SPってなに

SoyBoy SP (リポジトリ: soyboy-sp.vst3日本語ウェブサイト)はゲームボーイふうの音がだせる作曲ソフト (DAW; Digital Audio Workstation) 向けのシンセサイザープラグインで、VST3というプラグイン規格に対応したソフトウェアで使うことができます。チップチューンという、ファミコンやゲームボーイの音を指向したジャンル向けに使えるようにつくりました。過去にRustアドベントカレンダー2021向けに書いた記事『RustでつくるVST3プラグイン』の最後に宣伝しているやつの完成版です。

ゲームボーイ音源としての機能として以下

が主要な機能です。SoyBoy SP独自の機能として

があります。

開発の動機

開発の動機は、GNU/Linux環境で使えるチップチューン向けのプラグインがないことでした。GNU/Linux環境はWindowsやmac OSに比べてDTM環境が貧弱になりがちなことをUbuntuでDTMしていて日々ひしひしと感じます。チップチューンがすきなのでやりたいけど、有名なチップチューンプラグイン音源はたいていWindowsかmac向けです。

いや、ほんとはじつはそんなこともなくて、Wineを利用したりしてWindows向けプラグインを気合いでGNU/Linuxで動かせば、magical 8bit plug (チップチューンバンドYMCKの方がつくったファミコンふうシンセ) やKONTAKT (Native Instruments社のサンプラープラグイン。サードパーティがKONTAKT用のサンプルを公開できるようになっており、リアルな音源がたくさんある) を動かすことも可能であったりします。が、GNU/Linuxがターゲットになっているか・視野に入っているかというのはGNU/Linuxユーザとしてはやはり気になるわけです。

そして、あとはVSTプラグインを自作するのは大学生くらいからの憧れでもありました。自分でつかうシンセを自作すると、間違いなく楽しいですから。そんなふうにして「GNU/Linux向けチップチューンプラグインあんまりない」を「ない」と読み替えることで、念願のVSTプラグイン自作を、ちょうどやりたい音楽であるチップチューンをやるために「しかたなく」挑戦する理由としたわけです。

ちなみに、これまた有名なチップチューンシンセにSANA 8BIT VSTというファミコン音源があり (いいプラグインです)、ソースコードが公開されているのでPRをだすなりフォークするなりしてGNU/Linuxに対応させるのも可能だったでしょう。でも、つくりたかったのでつくりました。

動機としてはそんなところですがつくりはじめる直接的なきっかけもありまして、それはRustのクレート (ライブラリのこと) にvst3-sysというのがあるのを知ったことです。これの何が嬉しいかというと、

というあたりがうれしいところでした。

こんなの (vst3-sys) 見ちゃったらなにかつくりたくなるじゃない、と思ってアイデアを考えたところちょうどチップチューンVSTがほしくて、かつチップチューンシンセは作りが単純そうで難易度的にも丁度よかろうということでSoyBoy SPの開発をスタートしたのでした。

利用したもの

SoyBoy SPをつくるのに主に以下のRustライブラリを利用しました。

vst3-sysはVST3のプラグインインターフェースを提供するライブラリですが、VST3のプラグインインターフェースはあのCOM (Component Object Model)ベースなのでCOMのライブラリをvst3-comとして内包しています。

eguiは「即時モードGUI」を提供するGUIライブラリです。GUIに関してはOrbTk (Redox OS生まれのGUIライブラリ) かeguiかで悩んだんですが、そのときeguiのほうが興味が強かったためそちらにしました。この決断はたまたま正しかったようで、VST3プラグインのGUI用途に限れば、OrbTkは仕組み上まだ利用できなさそうでした。このあたりは後述します。

bincodeは、VST3のプラグイン状態をシリアライズ・デシリアライズするのに利用しました。DAWで曲をつくっているときVSTプラグインの設定が保存されないと地獄ですが、これを実現するため内部状態をVST3規格所定のストリームに読み書きできる必要があったのです。機能から想像がつきますが、裏にはもちろんあのserdeがいます。

開発の過程

ざっくり開発がどんなだったか要点を絞って振り返ってみます。

最初のすがた: ゲームボーイ厳密再現 (頓挫)

これが最初のコミットです。リポジトリをつくったのは2020年10月だそうです。コミット履歴をここから見ていくと、どうもこのころは厳密にゲームボーイの音源とAPU (Audio Processing Unit; サウンドチップ制御用CPU) を再現しようとしてたようで、ゲームボーイエミュレータのサウンドまわり仕様書とすごくにらめっこしてた記憶がたしかにあります。

ただこれ、音がいつまでも出ないのでモチベーションの維持がだんだん難しくなっていきます。さらに途中で「APUを再現したいんじゃなくてGB的な音がでるシンセが本当はほしかったのでは?」と気付いちゃったため、頓挫というかいったんストップしました。

再スタート: 厳密には再現しない方針

そして2021年1月に作業を再開します。ちょっと間が開いてますが別のプログラミングプロジェクトでもやってたんですかね。

さて、APU再現路線を諦めて簡単な構造でゲームボーイ音源にアプローチすることにしたとき、まず最初にやったのはシンセとしての構造設計です。SoyBoy SP (当時はgbi (GameBoy instrumentという名前でしたが) にどんな要素があって、どのように影響するのかをざくっと図にしました。

これで仕様が定まり開発可能になりました。あとはvst3-sysのサンプルコードを真似しながら、ひたすら実装していきます。詳細は書きませんが、音のHello Worldであるところのサイン波鳴らしをし、VST3ホスト (プラグインのホストのことで、たいていはDAW) からのイベントを受けて音のオンオフ等を実装し、DAWからのパラメータ変更をできるようにVST3のことばでやりとりをし、とここまででDAWで操作可能な最小の「シンセサイザー」ができあがります。だいたいそのあたりのスクショ動画があったので貼っておきます。

VST3ホストはパラメータを持つけど自前GUIを持たないプラグインを操作するためのUIを提供しています (してない場合もあるかも)。そのUIを使ってシンセサイザーのパラメータを編集しているのがこの動画です。わりとそれっぽく動いていますね。あとはシンセサイザー部分を作り込んでいけば、とりあえず操作可能なものができあがります。

GUIの実装

背景

そんなわけで自前GUIがないどとりあえず操作可能なプラグインを実装することができました。SoyBoy SPはゲームボーイの音源をなんとなく再現することを目標としていますが、実はVST3ホストの提供するUIではすごく操作しづらいパラメータが存在します。それは波形テーブルの値です。

波形テーブル音源は信号の値をテーブルに持っておき、それを読み取って出力する方式です。詳細な説明は波形メモリ (電子音源の合成方式) - Wikipediaなどを参照していただきたいですが、ゲームボーイの波形テーブル音源は以下のような仕様です:

以下のスクショの左下にあるのが実際のSoyBoy SPのGUIにある波形テーブルのエディタです。

たしかに32個の棒 (テーブルの値) があり、なんか離散値が入ってそうな感じがあります。VST3のしくみで通常用意できる「パラメータ」は0.0 ~ 1.0のf64値です。もしVST3のホスト提供UIでテーブルの値を編集するにはテーブル中の位置とその位置の値とを同時に指定する必要があります。あるいは値の数だけのパラメータを用意する…??

いずれにしてもめんどいですね。それこそ例として出したテーブルエディタがほしくなります。そしてこれこそがGUI実装にちゃんと取り組む正当なほうのモチベーションでした。邪なほうのモチベーションはOrbTkとかeguiとかを使ってウハウハしたかっただけです。

ライブラリ選定

まずはGUIのモックをつくって (モック画像)、それからライブラリの選定に入ります。使ってみたかったOrbTkやeguiがVST3のGUIを実装するのに使えるか、できない場合ほかのGUIライブラリ (icedやconrod) はどうなのか、などなど調べる必要がありました。

VST3において自前GUIを用意する場合、以下の要件を満たすライブラリである必要があります。

  1. ウィンドウのハンドルというかIDはVST3ホストが指定するものを使うこと
  2. ウィンドウのイベントループは自前でスレッドなり立てること
    • IPlugView::attached()はGUI起動後即座に終了せねばならない

2は、RustのGUIウィンドウ抽象化ライブラリwinitで「メインスレッド以外でイベントループを実行」的なオプションを利用できるライブラリならなんとかなります。問題は1でした。winitがそもそもこれに対応してないのです。RustでVSTプラグイン (VST2も含む) をつくろうとしてこの点は必ず突き当たる問題らしく、Windows向けとかでは対応がされているようでした。でもUNIXは未対応です。

そこでUNIX向けに対応を行ってみて、GUI実装実験実施当時やりたい欲が高まっていたeguiを使ってみたところ、うまくうごきました。なのでそれをPRを出し、それがマージされるまで待つのはつらいのでwinitに依存するライブラリをすべて一時的にフォーク (winit, glutin, egui) してつかってます。ちなみにeguiは即時モードGUIというタイプのGUIライブラリですが、SoyBoyのGUIは即時モードでなければならないわけではないです。即時モードGUIを単に体験したかっただけです。

winitに出したPRはまだマージされてませんがいくつか理由がありまして、X11のメンテナがいない問題 (メンテナの方がいってた) と、ウィンドウまわり詳しそうなコントリビュータの方に改善の指摘をもらっててまだ完成してなかったり、とりあえずいったん動いてるのでSoyBoyを進めまくってて放置してたり、といったかんじです。そろそろPRのメンテしないと……。

ちなみにOrbTkは先に挙げた1の条件を満たすことが現状できないため、そもそもVST3のGUIには使えないことが後から発覚したりしました。理由はバックエンドにSDL2を使っているからで、SDL2はウィンドウIDを指定して (つまり既存ウィンドウから) ウィンドウをつくること自体はできますが、そうするとOpenGLの描画コンテキストを既存ウィンドウに結び付ける操作ができないため、SDLによるOpenGL描画が行えないのです。OrbTkは当時確認したときはSDL2をバックエンドにしており、eguiのSDL2バックエンドも同様ですが、これらOpenGLによる描画をするGUIライブラリはバックエンドがSDL2で実現されているとVST3のGUIにはつかえないのです。このツイートがそのあたりを調べてたころのつぶやきです。この調査によってwinitに手を入れることにして、上に述べたようなPRをつくり、今に至ります。

実装時のたいへんだった点

VST3の自前GUIは、GUIプログラミングがはじめてだったためというのもあり、苦難の連続でした。以下にたいへんだったポイントを挙げます。

GUIは別スレッド

VST3のプラグインは通常2つのスレッドによって動いています。音声信号を計算 (IAudioProcessor::process()を実行) するオーディオスレッドと、UIスレッド (VST3ホスト側のUIのことです) です。どちらもVST3が生成し、適切なタイミングでプラグインの各種インターフェースを呼んでプラグインを動かします。どちらのスレッドもVST3が生成するので、プラグイン実装者が止めたりするとホストを全体的に殺すこともあります。なのでGUIのイベントループを動かすときは自分でスレッドを用意する必要がでてきます。

以降のたいへんポイントは、オーディオスレッドともUIスレッドとも別に自前GUIスレッドを用意したことによるつらみ、と言い換えてもいいでしょう。

GUIの表示とパラメータの同期は自前スレッド間通信

あたりまえですが、スレッドが別である以上状態の同期にはスレッド間の通信が必要になります。ただGUIを実装しても、GUI上のパラメータ変更はプラグインの内部状態を変更しません。そして、VST3ではプラグインは信号処理部 (IAudioProcessor) と制御部 (IEditController) に分かれており、この2パート間の同期にはVST3ホストを介して行います。それがなに意味するかというと、GUIから信号処理部 (シンセのコアを持っている) へパラメータ変更を伝達するには、以下の手順を踏む必要があります。

  1. GUIスレッドからUIスレッドを介してIEditControllerに変更を伝達
  2. IEditControllerへの変更はIAudioProcessorのキューへUIスレッドが投げる
  3. IAudioProcessor::process()でパラメータ変更を受け取って、シンセのコアに設定する

これを理解するまでかなりぐぬぬっておりました。しかもf64の0.0 ~ 1.0のパラメータについてだけ2をVST3ホストが自動でやってくれるのですが、波形テーブル編集イベントのように自前メッセージを飛ばす必要がある場合、2も自前です。VST3ホストを介してバイナリデータを投げたり受け取ったりすることになります。わーお。

VST3の信号処理部とパラメータ処理部の通信も実はスレッド間通信

ぼくははじめ、スレッドセーフさが皆無なstd::cell::RefCellでなんでも状態を保持していました。でもパラメータ変更の伝達手順の2もじつはスレッド間通信なので、スレッドセーフなデータ構造を使っていないと動作は保証されません。コードでいうとスレッドセーフでない信号処理部の構造体がこうであり、他スレッドから触るスロットはstd::sync::Mutexなどにしたのが最新の信号処理部構造体です。

無保証なので、保証はないながら正しく動いてたのがまた怖いですね。GUIスレッドのほうはMutexやArc等を用いて書いてましたが、プラグイン開発の最初期のコード (vst3-sysのサンプルを写して書いた) はスレッドを跨がなかったのでRefCellでした。そのまま膨らませていったところスレッドセーフティが必要になった場面を見事見逃したのでした。ああこわい。

オーディオスレッドでメモリ確保とスレッド間通信をしてはいけない

音声信号処理のプログラムはパフォーマンスに非常にシビアです。オーディオスレッドはVST3ホストの設定によりますが1秒の音信号を計算するためにだいたい44100回サンプルを計算しつづけます。たいていバッファされているのでIAudioProcessor::process()内でバッファ長分サンプルを計算し、IAudioProcessor::process()を呼ぶタイミングはホストが決めているわけです。DAWとかでもっと高いサンプリング周波数に設定していたら、もっと計算量が増えることになります。

こんなところでメモリ確保とスレッド間通信 with ロックをキメてはいけません。反省。

ぼくのマシン (Ryzen 7のデスクトップ機でもThinkPad X1 Carbonなラップトップ機でも) ではたまたま問題ありませんでしたが (CPU使用率は多めになってたかもしれない) 、さまざまな環境で動くものなのでこれはリリースの前に解消しておきました。

当たり判定ずれてた問題

なんかGUI、重いなって感じてた時期がありました。マウスのクリックに反応しないときがあり、なんだかマウスをクリックするときに力をこめると反応がよくなる……ような気がしたりしていて、egui自体が重いのかとか考えて調査をしていたのです。

ふと当たり判定を表示してみたところ、ちょうど半分、下に当たり判定がズレていました。人間はUIが上半分だけ反応しないと「GUIが重い」と認識するんですね。これは長いことハマっていた問題だったんですが、解決したときには妙に納得しました。

GUIを実装するときは当たり判定をすぐ表示できるようにしておいたほうがよいです。

そもそもGUIを実装すること自体がたいへん問題

と、ここまで挙げた問題だけでもなかなか大変でしたが、そもそもGUIのパーツ類を実装して組み上げていくのはすごく大変でした。当たり判定の問題しかり、状態の正しい変更および伝達、というかGUIの設定がゲームボーイすぎて既存のパーツがまったく使えないのでぜんぶ実装、というところ。

ほんとうはv0.9.9からv1.0.0にするときに、ゲームボーイの電源投入時のようにTantendoロゴが降りてきてピ↓ローン↑ってなる隠しコマンドを実装しようと思ってすこし実装していたのですが、これを入れることでGUIがめたくそに壊れたら悲しくなるので見送ってしまいました。

ReactとかVueとか、Webのフロントエンド界隈にはGUIをステートレスに記述するフレームワークがありますが、あれらが便利であることを痛感しました。そうか……eguiの上に自作Reactライクなサムシングを……?

余談ですが、@carrotflakesさんにv0.6.xくらいのときに試してもらったらGUI立ち上げるとガクガク激重になると言われ、しかし手元では再現しなかったため perf (Ubuntu) や VerySleepy (Windows) といったプロファイラを使ってボトルネック調査をして見事改善したのはいい経験になりました。OSやアーキテクチャ、あとVST3プラグインの場合はVST3ホストを複数動作確認しておいたほうがよい、という学びを得ました。あとプロファイリング、超だいじ。

開発過程で生まれたRust界への貢献

Rustでのソフトウェア開発はこれで2回目なのですが (1回目はKotoで、どんなものかはKoto - ファイルシステムをシンセサイザーにするに書いてます) 、そのときは依存ライブラリが多くなかった上けっこう安定していたので、Kotoで閉じた開発をしていました。SoyBoy SPのほうではvst3-sysというまだ未発達なライブラリを使っていたためか、いくつかの貢献を生むことができました。以下のような感じです。

  1. MERGED: vst3-sys: 一部型にCloneとCopyをつけた
  2. MERGED: vst3-sys: ベロシティ (音の強さ) の型がVST3 SDKと違ってたので修正
  3. MERGED: vst3-sys: VST3 SDKのアロケーションメソッドの型が違ってたので修正
  4. WIP: GUIウィンドウ関連抽象化ライブラリwinitで、UNIX向けに子ウィンドウを実装

4はGUIのところで述べたPRです。3は使わないときは使わなさそう (GUIとかでカスタムなパラメータが必要になってくる要りそう) なのでわかるのですが、2はキーボードでキーを押す強さ (=速さ) の型が間違っていたことに対する修正でしたが、わりと基本的な機能なので「もしや……vst3-sysってまだだれもつかってない……?」と思った瞬間でもありました。実際にはユーザはいるっぽいですが。

リリース

さて、当初想定した機能をすべて実装して不具合も解消できたら、やることはリリースです。開発当初はGNU/Linuxだけで動けばいいと考えていたので頒布を考えてなかったのですが、Windowsでもほぼそのままでビルドできて動くとわかると使ってもらいたくなりました。今回は後述の事情もあって、BOOTHで無料頒布有料頒布 (500円)を同時に行っています。

リリース、というか頒布と価格設定に当たってはけっこう悩んだので (そしてそれをわすれて頒布開始しグダったので) 、ここに備忘録として記しておきます。次回作以降の頒布の前にここを読み返したまえ to 自分

頒布形態についての悩み

頒布時の形態と価格についてはずっと悩んでいました。そのあたりを特に深く考えるようになったのは、2021年5月20日ごろにみた音源開発者の方の引退記事だったと記憶しています。ちょっとその記事じたいが出てこないのですが (リツイートかfavかをした気がしますが、それらは検索不能) 以下のようにかなりショックを受けていました。

ぼくが大学生くらいのころからアマチュアDTMerが「無料のVSTプラグイン」「無料のサウンドフォント」を収集することは一般的だったように思います。DAWもってなかったけど、無料のプラグインを英語をかいぐぐりながらダウンロードした記憶がぼくにもあります。有料のプラグインを販売している会社がキャンペーンとして無料にしたり、販促用に無料のプラグインをつくって公開していたり、ということもあります。

しかし無料のプラグインがこれだけ溢れているなか、個人でVSTのプラグインであったりサンプル音源であったりを販売することで生計を立てようとしている方々にとって、これらはどう影響するでしょうか。趣味の成果物や習作を無料で公開することによって、つまり相応の対価が発生するような成果物を無料で公開して価格破壊することによって、そのような人たちの食い扶持を奪うことになっていないか。その点が引退の記事を読んで感じたことです。

2022年5月頭のころの考え

以上の流れがあり、v1.0.0への道のりが近くなってきた2022年5月頭ごろから頒布の形態について考え、結果以下のような結論をだしました。

これであとは価格そのものを確定させてしまえばあとは頒布準備が整えばGOするつもりでした。

リリース時のぐだぐだ

そういうことを考えていたことを、v1.0.0のタグを切った瞬間にはすっかり忘れてしまっていました。単に「1000円以上にする」だけが頭に残っており、v1.0.0ができたときにはその高揚感のままに「1500円って高すぎないかな」などと不安がっていました。メモってだいじですね。

結局頒布をした瞬間には、1500円でのWindows版有料頒布ページのみをつくって公開としました。ただ、このあとすごくブレブレします。熟慮を忘却した状態でリリースをしたものだから、なにかの切っ掛けで「1500円は高すぎるのでは」「無料版を用意し、BOOTHのブースト機能を以ってお布施とするべきでは」とか迷いはじめます。で、この時点で購入者が一人もいなかったため有料版を非公開にし、無料版の頒布ページを公開しました。で、知人に無料版を展開したら「無料版でブーストしようとしたらブースト自体なかったよ」とおしえてもらい、「じゃあお布施版を再公開するか」と有料版を再度公開しました。値段はブーストあるから1500円でなくていいかと500円に変更しました。これが深夜1時のリリースから14時間くらいまでのできごとです。ブレブレですね。

そしてその夜にはじめて、ここに書いた過去の頒布についての悩みと決定を思い出すわけです。そのときのがっくり感といったらなかった。あれだけ熟慮をしていたのに、いざ公開となったときには忘れ、ぐだぐだな動きをかまし、信念と真逆なことをしてしまった。

次回作では信念に則った行動をするぞという自戒を込めて、ここにぐだぐだを記しておくこととします。

おわりに

というわけで、リポジトリができてから1年半、再スタートしてからだと1年かけて試行錯誤しながら開発してきたVST3プラグインを無事v1.0.0リリース & 頒布開始することができました。長かった。

反省点を挙げるとすれば次のような感じでしょうか:

とはいえ良かった点のほうが多かったです:

RustでVST3開発する詳しい解説は同人誌にしたためて技術書典にて頒布したいなぁと考えています。ぶ厚くならなければいいな。まずそのためにはサンプルプロジェクトとして簡単なVST3プラグインを設計して実装しておかねばならぬと思っているのですが。出せるかなあ。出せるといいなあ。

開発していたRust製のチップチューン用VST3プラグイン、SoyBoy SPが無事v1.0.0になり頒布開始できました。この記事ではSoyBoyの紹介と開発の苦労話などを記しています。

タグ: プログラミング , Rust , 音楽 , デジタル信号処理 , サウンドプログラミング , VST3

作成日時: 2022-06-10T00:53:20.662485+09:00

更新日時: -