はてなブログから完全移行するためのAstroブログ環境の構築

最近になってようやく時間の余裕ができたので、しばらく前から計画していたはてなブログからの脱出作業にようやく着手できるようになった。

はてなブログ、もうfirefoxでまともにCSS適用できなくなってきたし、さすがにそろそろ潮時かな

Atsushi Eno (@atsushieno.bsky.social) 2026-04-30T05:41:07.675Z
bsky.app

新しいURLは https://monogatari.audiopluginlab.com になる。新しいドメインで読んでいる人にとっては自明だし、このエントリーははてなブログの最後の投稿になる予定なので、そっちにとっては引っ越し先の告知ということになる。

ブログ程度ですらFirefoxのStrict設定でまともにCSSが取得できないサイトは、もう「終わっている」と判断するのが適切だろう。問題がずっと解決されずに放置されていたので、はてなブログやはてなブックマークに手を入れられる技術者は(何が壊れるかわからないので)もうはてなには残っていないのだろうと理解している。他に自由な代替ソフトウェアがいくらでもあるのに、もうメンテされていないサイトをいつまでも使い続けるというのは、技術者としてはあまり健全ではない。

今回は、2004年にはてなダイアリーに移行してから、はてなダイアリー管理部分とはてなブログ管理部分を合わせて過去22年分のブログ投稿(!)を、全てAstroの静的ページ構成のブログシステムに置き換えた。このエントリーも新しいシステムの上で書いている。置換作業はほとんどAIへの指示出しのみで、自分はAstroやAstro-Paperの詳細には深入りしないままに終わっている。

一応これを公開できるところまで進めることができたので、同様にはてなから脱出しようと考えている人の参考になればと思って詳しくまとめることにした。

前提ブログ執筆環境

はてなブログから脱出するためには、まずはてなブログのエディタを使わない執筆環境に身を置いている必要がある。

ここ数年の間、自分はmarkdownでしかはてなブログを書いておらず、投稿頻度も月に1〜2回で(ただしはてなと別にzenn アカウントにも投稿しているし、別途英語ブログもgithub.ioで出している)、その編集にははてなのページではなくStackEditのインスタンスを使っている。Google Driveと同期して、(頻繁に機能しないページ内ダイアログを出してくることもあるのは鬱陶しいところもあるものの)ちゃんと必要な「どのPCからでも編集できる」という機能を提供してくれる。

StackEditに限らず、この種のmarkdownエディタの問題として「画像のストレージが無い」という問題があるのだけど、近年はimgurに上げてその画像リンクを使うようにしている。20年前ならflickrなどを使っていたことだろうが、2026年に画像を直リンできるサイトは限られている(GitHubなどもできない。その画像URLは手元では見えていても他者には見えないやつだ)。はてなブログのエディタだと、f.hatena.ne.jpのフォトライフ記法を使い、はてなに骨を埋めることになってしまう(とはいえ画像は後述するように取り出せるので、移行できないことはない)。

もっとも、imgurにも固有の問題があって、たとえば英国からのアクセスは年齢認証できない限り完全にブロックされてしまう。旅行中に画像にアクセスできずに開発作業(ブログ執筆ではなく)が止まったことがあった。とはいえ、日本で暮らしている範囲では無理無く使える。メリット・デメリットを比較してimgurに落ち着いていた。

外部リンクに関しては、URLだけの段落を作っておいて、書き上げた後、最終的にはてなに投稿する際にOGPに置き換えていた。具体的にははてなブログのエディター上でURLだけの行を削除して貼り付け直すだけだ。この時に埋め込み方法を問い合わせるダイアログが出てくるので、通常はOGPの埋め込みを選択していた。

これ以外は特別な作業はしていない。通常は投稿した後にMastodon (現在はFedibird) とBlueskyにコメントを付けて(手作業で)URLを投稿している。

Astro-Paperの選択

どのような環境に移行するかは無限に悩むところだったのだけど、死守する条件は「静的ページとしてデプロイできること」だけにした。もちろんvercelなどにデプロイして動かせるものもあるだろうけど、SSG (static site generator)1 にしておけばgitリポジトリ管理が全てになるというのは大きい。

そういうわけで、今回はAstroを使うことにした。もう少し前ならEleventyなどを使っていたかもしれないが、2026年現在、同様にJavaScript依存関係ゼロで構築できるSSGであれば、サイトのテンプレートがいろいろ揃っているほうが始めやすい。Astroは4月にandroidaudioplugin.orgを刷新した時にも使っているので、ごく大まかには把握している(ただこのサイトもほぼ内容が無くAI slopに近い)。

Astroのサイトにはコミュニティが開発したサイトのテンプレートを探せるページがあるので、その中でFreeかつBlogをターゲットにしたものをしばらく探して回ったのだけど、Freeの多くはPaidの機能ダウングレード版が多く、それらを使うとあまり機能追加が前向きに見込めないのと、このサイトで探しても最新登録テーマが先に出てくる感じでどこまで一般的なものか判断に困ったのとで、どうせ大半がGitHubにあるならそっちで探そう…となった。

今回採用したAstro-Paperは比較的地味だけど、他だと人気のfuwariなどと比べると、一応新しいAstro 6.4系列を使っていて、メンテもされている。Astro 7.0はまだリリースされたばかりなので、移行できていないものが多いだろう。この辺りは後々差し替える可能性も十分ある。最悪、他のリポジトリとサブドメインに移行したうえで、こっちは転送リンクを生成するようにしてしまえば、Permalinkが失われることにはならないだろう。

作業方針とソース(コード・データ)の保持指針

今の自分にとって、AstroやAstro-Paperは、その使い方に詳しくなる技術的意義を特に感じられなかったので、ほぼAI任せになっている。ただ「Astroに移行するからこのエクスポートデータを変換して」だけでは十分に移行できているとは言い難いので、その後にさまざまな加工を施すことになった。

基本方針として、本家の更新になるべく簡単に追従できるように、なるべく既存のファイルには手を加えず、はてなからエクスポートしたファイルにも手を加えずに、新規ファイルの追加とAstroによるファイル生成時点で動的な書き換えを行う方向性で進めている。pnpm run buildpnpm devで、はてなのエクスポートデータをAstroのpostsにコンバートして、それをAstro-Paperのやり方で最終的な出力に変換させている。

はてなから脱出するのは「はてなブログやフォトライフなど関連エコシステムはもう長く保たないだろう」という想定があるためなので、たとえばフォトライフにある画像をそのままURLとして残しておいたら、はてなが消滅した時に共倒れになってしまう。そのため、はてなに上げてあった画像などのリソースは全て引き上げてリポジトリ上に保存してある。ちなみにはてなフォトライフにはなぜかはてなブログと異なりエクスポートが用意されていないので、個別に全てダウンロードする必要がある。この辺は今どきなら専用ダウンロードツールが無くてもAIが勝手にHTTP(S)で取得してくれる。

Astro-Paperから変更している部分

はてなフォトライフ画像の変換

f.hatena.ne.jp 上にアップロードした画像は全てリポジトリのローカルファイルとして保存し、デプロイ先にもアップロードされている。ブログエントリではリンク先を差し替えてある。フォトライフ上の画像を参照していた部分はダウンロードされた画像のエイリアスにマッピングされていて、サイトのビルドの時点でもうフォトライフは消えていても困らない状態になっている。

OGPの生成

前半でも書いた通り、はてなブログでは、ブログエディター上でリンクのURLをペーストすると、それを表示する方法をユーザーに問い合わせて、OGP画像などに変換できる仕組みを提供している。ブログエディター上では[URL:embed:cite]のように書き換えられるが、エクスポートデータはそのまま出力されるわけではなく、MovableType形式(!)になっていて、この部分はiframeに変換される(適宜改行を入れた):

<p>
  <iframe
    src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fatsushieno%2Fuapmd-kmp"
    title="GitHub - atsushieno/uapmd-kmp: Kotlin bindings and CMP porting experiment for uapmd - not working due to plugin UI hosting"
    class="embed-card embed-webcard" scrolling="no" frameborder="0"
    style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"
    loading="lazy">
  </iframe>
  <cite class="hatena-citation">
    <a href="https://github.com/atsushieno/uapmd-kmp">github.com</a>
  </cite>
</p>

自分のはてなブログでは、OGPで表示したい場合はURLだけの行を作って、それをはてなのエディター上で置き換えていた(あるいは直接[URL:embed:cite]を書いていた)ので、こちらでもmarkdown中に単一行で書かれたURLはOGPに変換する処理を追加した(させた)。

OGPになる部分は、CORSの制約上、エンドユーザーの環境で動的に生成することができない。このブログで必要になるOGPの件数は100件強だったので、Codexが「URLに対応する各サイト生成済みの画像URLにマッピングした情報をリポジトリに残し、ページ生成時にここから画像URLを解決する」アプローチで解決した。画像そのものは各サイトが提供していればそれを使い、無ければデフォルトでページタイトル等から適当に生成される。

YouTubeやSpeakerDeckの埋め込みコンテンツ対応

YouTubeやSpeakerDeckは、iframeを使って動画をその場で再生したりスライドをその場でめくって見られるように作られていて、はてなブログはそれらを展開するように作られている。

実装のアプローチをあまりサイトの具体的な列挙で分岐してもらいたくはなかったのだけど、生成されたコードを見てみると現状では限定列挙になっていて、さらにBluesky投稿には個別にフィルター関数が存在していたりと、あまり美しくは整理されなかった。

年月単位のアーカイブ

Astro-Paperのデフォルト設定では、アーカイブページはフラットに作られていて、全てが1つのページに展開されることになる。今回インポートしたブログは過去22年分、600本近いエントリーがあって2、そのままリストになっていても目的のページを探し出せない。少なくともはてなブログと同様に年月単位で一覧できるようになっていてほしい。そういうわけでこれはアーカイブページに手を加えて全投稿の一覧とは別に生成してもらうようにした。投稿ファイルは年月日でフォルダ分けされることになった。

日単位のエントリー

今となっては考えられないが、昔はブログがSNS投稿の代わりになっていたので、1日に何本もエントリーを書くことが多かった(これは自分が異常者だというわけではない。あるいは自分だけが異常者だというわけではない)。

この頃のはてなダイアリー(はてなブログではない)は、データの管理がまだ雑だった。エントリーにはコメント(はてなブックマークコメントではない)を書くことが出来たが、コメントは日単位のページに付けられていた。あるいは日単位のページに付けることができた。コメントが個別のエントリーに付くようになったのはおそらく内部設計がはてなブログになってからだ。

コメントはエクスポートデータに含まれていて、今回インポートしたページにもそのまま全文残されている。これは当然自分の著作物ではないので、本来は除外すべきものではあるが、さすがにほぼ権利侵害の要求を受けることもなく、財産的価値も寡少であろうと考えて、そのまま表示している(当然著作権者からの請求があれば削除等で対応させていただく)。

現在はてなでデプロイされているはてなブログには、はてなダイアリー時代に「日単位で」付けられたコメントを表示できないというバグがある。たとえば2004年9月9日のエントリーにはコメントがあるのだが、これは(いつ頃だったか)はてなダイアリーから強制的に変換されたはてなブログの同日のページには残されていない。エクスポートはされるのに。

どのように変換すべきか悩んだけど、最終的に「従来型の投稿で、複数エントリーが公開された日については、日単位で見られるアーカイブページを作る」というアプローチで対応した。同じ文章が2エントリー分表示されるので鬱陶しいところはあるけど、見た時に「昔のまま」っぽいのはこっちのほうだ。一定の時期からこのページは生成されなくなる。はてなダイアリーからはてなブログに引っ越してからはこの問題はなく、そもそも近年では1日に2件も投稿することはなく、ほぼ無用の懸念だ。

新しいエントリーのエディター

このエントリーは、今回は頻繁にこのブログの実装をいじらないといろいろ調べられないのでVSCodiumで書いているが、通常業務としては引き続きStackEditで原稿を書くことになるだろう。最近は同人誌の類も全部StackEditを使ってpandocで書いているので、手に馴染んでいて、現状特に変える理由が無い。

公開するときには、他のエントリーからslugをコピペして内容を適当に調整してpnpm devで確認すれば終わりだ。この辺は既存のGitHub Pagesを使ったブログと変わらない。

はてなのほうに残っているデータを消すかどうかは、今後のはてなの動き次第だ。Twitterのデータは悪用されないよう全て消したが、はてなでも同じことをする必要があるかもしれない。

デプロイ先の選択

今回GitリポジトリとWebサイト発行はGitHub Pagesに妥協して使っている。理想をいえばTangledにリポジトリを作って公開しても良かったのだけど、Tangledでは非公開リポジトリを扱えないため、自分のようにリポジトリを公開しても問題ない勢にしか参考にならない可能性が高いと思って、今回はGitHub Pagesにした。

Tangledでプライベートリポジトリを使えるようになるためには、まずTangledが前提としているATProtocolがPermissioned Dataをサポートできるようになる必要があり、これはBlueskyのATProto開発チームがまだ現在進行形で作業しているところだ。それが完成しないとTangledは先に進めないだろう。

GitHub PagesのURLがpermalinkにならないように、冒頭のbskyの投稿時期に取得しておいた audiopluginlab.com のサブドメインを使っているので、いずれTangledでデプロイできるようになったら、そっちに切り替えるだろう。

この移行プロジェクトの再利用可能性

このリポジトリ自体はgithubで公開状態にしておくので、これを参考に自分もはてなブログから脱出したいという人々の参考になると良いと思っている。


  1. 今回はstatic site generatorをこう呼ぶことにする(server side generatorやsoftware-controlled sound generatorではなく)
  2. 近年ではそんなに熱心に書いていないが、昔はSNSも無かったのでブログが落書きの場になっていたわけだ

5月の開発記録 (2026)

5月の記録ですが、今日書いている余裕はあんまりないです。簡潔に書きます。

AAP: V4 protocol, and GUI change notifiers

先月「プラグインAPIに破壊的な変更が加えられている」と書いてきましたが、M3での展示のために実装を調整していた先月の段階では、いったん破壊的変更は先送りにして多数のプラグインが動くようにrevertされていました。一連のイベントが一区切りしたので、今月はいよいよBinderのレベルで破壊的変更を加えて、パラメーターやStateのAPIのクライアントが非同期的に呼び出せるようになり、uapmd上で楽曲データの保存などが正常に動作するようになりました。

今月はさらにプラグインからのGUIの動的なサイズ変更などに対応できるようにしました。aap-juce-ysfxはしばらく前にAAPに移植してみたもののまともに使えなかったプラグインでしたが、JSFXをいくつかapkにバンドルして、GUIのサイズ変更に対応出来るようにした結果、instrumentプラグインのほうは一応音が出るようになりました。正しい音が出ているかどうかは正直わかりません(そこまで確認している余裕がない)。

uapmd-kmpとKotlinConf 2026

下旬にKotlinConfに遊びに行くことに決めたものの、ここ1年くらいずっとC++でMIDI 2.0の開発をやっていたり、その後はプラグインホスト(uapmd)の開発に注力し、最近はADC JapanのセッションのためにAAPに注力していたので、Kotlinは全然やっていないんですね…せめて何かKotlinで使えるものを適当に作っておこうと思って、uapmdのKotlin Multiplatformバインディングを作りました。ついでにComposeでGUIを作れるようにしてみました。

github.com

どれくらい使い物になるかというと、デスクトップとAndroidでは一応音楽がそのまま再生できます(再生エンジンはC++実装を叩いているだけなので)。uapmd-appに相当するものを作るつもりだったCMPアプリは、現状とてもuapmd-appのマルチウィンドウのUXに敵わない使用感ですが、一応プラグインUIを出せるようにはなっているようです。この部分はCMPで独自対応しなければならなかったので、だいぶ苦労しました。しかしSDL以外のGUIフレームワークでもプラグインUIのホスティングが可能であることを証明できたのは大きいです。

KMPバインディングは、いったんC APIを(AIで)生成させてから、そのC APIをcinteropで自動生成できるようにして、それをJNAやemscriptingバインディングの入力ソースにしています。cinteropは「その出力ならKotlinバインディングは間違いなく作れるだろう」という意図で挟んだものですが、不要だったかもしれません。プロファイリングはしていませんが、uapmd-kmpだけ妙に重い部分があります。uapmd-kmpでバインドしているのはプラグインホスティングのクライアントAPIが中心なので、C++で高負荷な処理を行っている部分は無いと思って作ったのですが、実際にはどこかにあるのでしょう(確認している暇がない)。

一応ネタが出来たので今年もKotlinConfのKotlin FoundationのブースでLTしてきました。ただ実際には今年はQ&Aコーナーみたいな感じの運営になっていて、LTしたければすれば??みたいな感じだったので、スライドを準備していったものの、半分も使っていません。そんなスライドですが一応置いておきます。

speakerdeck.com

augene2

uapmdはDAWとして作り込まれる予定が無いのですが、楽曲再生エンジンについては機能してもらいたいですし、どこまでの楽曲を再現できるのかは未知数です。そのためには.uapmdの楽曲データを生成できる必要があるでしょう。

現状でSMFのインポートは可能になっているのですが、既存の打ち込みデータで自分が一番作り込んでいたのはMMLから作成されたホルストの火星なので、MMLから.uapmdにコンパイルできる新しいaugene-ngが必要となります。augene-ngはTracktion Engineをターゲットにしていましたが、.uapmdは概ねSMF2 Containerと同じ発想で作られているので、今後SMF2 Containerの正式版が公開されたら簡単に追従できるようにしておきたいところです。

uapmdに拡張機能として組み込むことも想定して、C++に移植してみましたが、uapmd-appに拡張機能をどう組み込むかをまだ設計していないので、まだ実現していません。uapmd-appの拡張機能は、どちらかといえばMIDI2クリップのエディタ機能を念頭に置いているものですが、まだ構想段階です。AndroidでもDAWの拡張機能として実現する方法が必要であり、AAPと同様の構成は考えられますが、SurfaceControlViewHostでIMEがサポートできないという問題もあり、現状まだ棚上げです。

uapmd: project loader changes, generic audio nodes

uapmdでは今月は地味な変更が中心で、主にAAPのAPI変更に伴って、プロジェクトローダーがStateをロードする処理を書き換えたり、uapmd-kmpに合わせた変更などが中心でしたが、ひとつ大きな変更を加えた部分があります。音量調整のためにGainノードをオーディオグラフに追加したのですが、uapmdのプロジェクトファイルフォーマットはDAWprojectより汎用的なフォーマットを志しているので、UAPMD独自の方式でGainのノードを規定して値を設定させるというのは、好ましいとはいえません。何らかの汎用的な仕様が必要だろうと考えました。

そこで、uapmd-appはWebブラウザもターゲットにしている程度に汎用的であるべきだと思い出して、Web Audio APIのノードに使われている仕様と同等のものであれば、「標準でサポートすべきもの」という位置付けにしても良いのではないかと考えました。Web Audio APIにはGain以外にもいくつかノードがありますが、それらを含むノードグラフのカタログであれば、実装してもらう前提でも悪くはないでしょう。この仕様のために、(lib)uapmdに含まれていたAudioGraphのAPIは新たにuapmd-graphというライブラリに切り出されて、Gainノード等の実装とともに独自に管理されることになりました。

データのシリアライゼーションは別途uapmd-dataで行われていますが、もしかしたらこれもuapmd-graphから呼び出すかたちにして一般化するかもしれません。このデータフォーマットはまだまだ未完成ですが、最終的に汎用フォーマットとして納得感のある仕様を目指したいところです。

6月の予定

明日からADC Japan 2026が始まるので、忙しくなります。自分のセッションは3日目の後半なので、気の抜けない日々になりそうです。台風も近いですし、参加される方は気をつけておいで下さい。

4月の開発記録 (2026)

技術書典20、音楽技術勉強会、M3、さらにこれに合わせたUAPMDとAAPのリリースラッシュと、嵐のような1か月が過ぎて、ADC Japanまではひと息つけるようになりました…たぶん(?)

CLAPオーディオプラグイン開発者ガイド 第2版

UAPMDの設計と実装の本をまとめる時間はとてもじゃないけど取れなかったので、clap-wrapperを使ったCLAP first、VST3のMITライセンスへの変更、プリセットのスキャナーAPI、WebCLAPやといった最新の話題を反映して、だいぶいろいろ追記したCLAP開発者ガイドのアップデートを出すことにしました。アップデート版なので技術書典オンラインやboothで購入されていた場合は無料アップデートが降ってきています。

Linux DTMガイドブックもシーケンサーエンジン本も刷新したいところですが、先にAAPとUAPMDを何とかしたいと思っています。AAPは全然先が見えない状態だったのですが、今月だいぶ進展が見られたので1.0リリースもできるかもな…!?というムードになってきました。

音楽技術勉強会2026.04

大成功でした。こんなに楽しんでもらえるならまたやりたいですね(毎回それっぽいことを書いている気もする)。これについては昨日書いたので詳しくはそちらを見て下さい。

atsushieno.hatenablog.com

UAPMD v0.4

UAPMDの開発は、先月までは2週間に1回くらい大きなリリースが続いていたのですが、今月は主にAAP開発に注力していたことに加え、上記イベント群に合わせた作業も入ってきて(といってもUAPMDのデモ展示に大きなリソースを割いていたのですが)、リリースとそれに合わせた開発作業は控えめです。AAP 0.10のリリースとM3出展が終わったタイミングでv0.4をリリースしました。

あとそろそろ英語圏向けのアップデートをまとめておかないと…と思ってひと通り説明したものを書きました。日本語でもまとめたものを書いていなかった気もするのですが、適当に日本語訳して読んでもらえればと思います。

リリースノートには詳しく書いていないのですが、ゆとりがあるので少し詳しく書きます。

タイムラインUIの改善

これまではImTimelineというImGuiのライブラリを使ったコンポーネントのインスタンスをトラックごとに作成していたのですが、これだとスクロール対象がトラック単位になってしまって、ユーザビリティは極めて悪いものでしたし、そもそもスクロールがまともに動作しない状態でした(!)。クリップエディタだし余裕が出来たら直そう…と放置していたものですが、さすがに展示用となると多少まともに動作してほしいところです。

ImTimelineはそもそも複数のトラックを構成できるコンポーネントなので、その機能を使うのが自然な解決策であることは自明だったのですが、DAWのトラックリストには左横にさまざまなトラックコントロールが付いて回るので、それらが正しく動作しないうちは、このモデルには移行できませんでした。そして実際、最初はうまくいかなかったわけです。「ちょっと調べたらちゃんと動くものが作れるはず」のところまで手を出せたのがようやく今月ということになります。

DAGを使ったオーディオグラフとレイテンシー補償の実装

3月にWebCLAPをサポートするようになった時点で、オーディオグラフのAPIは完全にRT-safeな実装をAPIとして要求することになりましたが、それがAPIとしても妥当なものであるかは、実際にDAGを実装してみないと分からないところでした。また、プラグインフォーマットのAPIにはレイテンシー報告のAPIやtail lengthを返すためのAPIが用意されており、レイテンシー報告をもとにレイテンシー補償を実装する場合は、直列的なオーディオグラフAPIの実装だけでは、妥当性が確認できません。

そういうわけで、いずれPoCレベルでは作っておくべきと思っていたDAGベースのオーディオグラフ実装に手を染めることになりました。これじゃTracktion Graphの競合を実装しているのと変わらない…! まあシーケンサーエンジンを独自に作り込まないとプラグインホスティングAPIの妥当性は確認できないので、仕方ない話です。DAGを実装するセオリーは十分に枯れているので、完全にAI任せにしていますが、速度的に妥当かどうかはともかく、最長経路に基づくレイテンシーレポーティングとレイテンシー補償の実装はできていると思います。tail lengthの利用も、クリップの再生時間の計算で使われているはずです(うろ覚え)。

オーディオワープの実装

オーディオワープは本質的にはタイムストレッチのdiscreteなリストであり、オーディオトラックの編集機能としては実装しておくべきものです…というわけで単純に位置とテンポ変更率のリストを保持して再生中にsignalsmith-stretchに渡すだけの実装を作り、オーディオトラック上でそれらを編集可能にするUIをやっつけました。

ただ、オーディオワープ機能は、どちらかというとARA2に対応するプラグインがオーディオ入力を受け取ってタイムストレッチを動的に(リアルタイムに、ではないにしても)適用できるように事前処理する仕組み、を表現できる必要があって、いま実装されている静的なリストはARAの仕組みとは相容れません。時間ドメイン(と書くとFFTの話っぽくてややこしいですが)における加工処理にも、一定の抽象化が求められそうです。この辺はvNext issueです。

Androidサポートの拡大

これはAAP v0.10の項目で詳しく説明しますが、AAPのGUIサポートが現実的に使い物になり始めたので、それに合わせて必要になったプラグインフォーマットAPIの変更を反映しています。また、CodexやClaude CodeがAndroidデバイス上で実行しているUAPMDにadbで接続してデバッグできるようなReceiverの入口を用意して、そこからembedded JS Runtimeにリーチできるようにしました。これでデバッグをAI任せにできるようになりました。

AAP v0.10

先月末にADC JapanでAndroidのほうのセッション採択が決まったので、しばらくAAPに注力することにしたわけですが、今月は早速大きなインパクトのある成果が出ました。3年前から「動かない」として放置していたJUCEプラグインのGUIがAndroidでも「プラグインUIとして」まともに動作するようになりました。現在 https://androidaudioplugin.org (AAPのランディングページ)ではJUCEベースのプラグインUIが並んだ画像が出ています。

androidaudioplugin.org captured

一番左上にあるのは今月新しく移植として追加した aap-juce-wavetable です。今回移植された FigBug/Wavetable は、一見地味な配色で気づかないかもしれませんが、波形やパラメーター制御点のアニメーション表示など実は割とぐりぐり動くモダンなシンセサイザーで、プリセットも豊富な上にAPIからも選択できる、MCP-enabled DAWフレンドリーなプラグインといえます。

JUCEのGUIは、モバイル向けに書かれていることはまず無いので、そのまま使ってもユーザビリティは良くないのですが、JUCEプラグインの残念な現状としてプラグインのプリセットの選択すらプラグインフォーマットのAPIを経由して公開しているものが少ないため、「プリセットを選ぶだけ」でもGUIが必要になってしまう、という問題があり、これは「使いにくくても無いよりはあったほうがずっと利用価値が上がる」ということになります。

JUCE GUIは、AndroidのStandaloneアプリとして動作するものであっても、プラグインUIとして動作させると状況次第でクラッシュするものが大半です。その理由はaddToDesktop()のような「グローバルデスクトップ」にアクセスを試みて落ちるというものです。JUCEプラグインのGUIをAAPのプラグインUIとして動作させるためには、SurfaceControlViewHostを使ってSurfaceViewに描画させることになるわけですが(仮にAAP以外のプラグインフォーマットが存在するとしたら、同じことが求められるでしょう)、このリモートプロセスで処理されるGUIのコードが「デスクトップ」にアクセスするためには、親となるSurfaceViewを使用する必要があります。Androidではモーダルダイアログを使用できないのと同じで、AndroidのプラグインUIではグローバルウィンドウにアクセスしない鉄則を守る必要があります。

いずれにせよ、JUCE UIを表示できるようになったのと、UAPMDがGUI表示を十分にサポートできるアプリケーションとして存在している(そして問題が起きた時にその根本のレベルまでデバッグできる)こともあって、プラグインのGUIを利用する場面・機会が著しく拡大しました。AAPのGUI extensionにもいくつかABIの破壊的変更が加えられています。

プラグインフォーマットAPIをひと通り使用するUAPMDが登場して、利用機会が増えるとAPIの妥当性検証が発生し、ABIの破壊的変更が加えられるようになってきました。これまで十分に吟味されてこなかったparameter APIやstate APIが検証のターゲットになり、現状まともに動作しないので根本的な修正が必要になりそうだ、ということが判明しつつあります。一方で、これらのAPIがひと通り機能するようになれば、プラグインフォーマットのバージョン1としては十分ともいえそうだ、という見通しが見えてきました。

5月の予定

5月はADC Japanのプレゼンテーションの準備(という名目でのAAP開発)もあるのですが、KotlinConfに遊びに行くミッション(?)もあり、開発に余計なゴールを持ち込まなければ余裕があるといえるのですが、まあそれなりの成果を出して発表に盛り込みたいところです。

Music Tech Meetup 2026.04 Postmortem

しばらく前の話ですが、4/15にMusic Tech Meetup・音楽技術勉強会を開催しました。

music-tech.connpass.com

オフライン勉強会を開催するのも6年ぶりで、開催するほうの状況もいろいろ変わり、基本的に自分だけで企画して回すという不安要素だらけのスタートでしたが、いざやってみたら大成功でした。メインセッションも4本のLTも最先端を突っ走る刺激的な内容ばかりで、参加者の皆さんからも、ホストしてくださったCRIミドルウェア社の皆さんからも好評で、また開催してほしいという声を多数いただいたので、次も何とか企画して回せればと思っています。ヘルプしてくださった皆さんも含め、参加していただいた皆さんありがとうございました。

音楽プログラミング言語mimium 理論と実装の詳細

mimiumの開発者である松浦さんに、世の中に実は数多のオーディオプログラミング言語がある中で、mimiumという独自の言語を開発するに至った動機、言語の概要紹介、関数オブジェクトの基盤がVMで実行されるコードの実装解説、言語機能としての多段階計算されるマクロの紹介、コンパイル時型解決による動的変更の実現性、他言語との比較と、盛りだくさんの内容を語っていただきました。APLは低レイヤーにも潜り込んだ話が自然に登場する世界であり、今回は高階関数や多段階計算といった関数型言語方面の概念もいろいろ出てくる内容だったので、みんなついてこれるだろうか…?と思いながら聞いていましたが、質疑応答も賑やかに行われていたので安心しました。

2026年に相応しい最先端プラグインホストの設計~と実装~

atsushienoが開発中のUAPMDについて話したものです。agentic codingの影響でDAWがいろいろ作られるようになってきたので、自分好みのDAWを作るには何が必要か、UAPMDは何をするのか、プラグインホストは何をしなければならないのか、AUv3やWebCLAPのサポートには何が必要なのか、MCPで操作できると何ができるのか、といった話を駆け足でしました。実装の話は時間の都合で断念しました(!)

さらにLTセッションが4本ありました:

  • dropcontrolさんの複数トラックで別々の拍子を設定した楽曲を作る、そのためのツールを作る…という話で、この時点で一般的なDAWやシーケンサーではとても扱えないもので、その時点で十分刺激的な内容なのですが、なぜかツールではなく楽曲のほうでICMC 2026で採択されたという話だったので、ハンブルクに行かれる方はチェックしてみてください。GW明けからベルリンで始まるSuperbooth 2026も近く、行ける人がうらやましい…!
  • klknnさんが開発中のhibikiというVST3用のDAWの紹介で、Windows/Mac用のプラグインをネットワークを介してLinux等でも制御できるというのが大きな特徴でした。オーディオプラグイン製品は2026年の現在でもWindows/Macのみでリリースされるものがまだ多く、yabridge (Wine) でうまく動作しない場合もあり、ネイティブプラットフォームのPCがあればそこから再生するというのは、作業に支障が出るほどの遅延がなければ安定的な解決策といえるでしょう。Closure scriptingでヘッドレス動作も指示できて、現実的な利用を意識した設計が見えるコンセプトでした。あとレトロUIも(!?)
  • 余湖雄一さんのWeb Audioを使った即興ライブコーディング環境というか楽曲制作環境のデモで、「今日はライブコーディングが主題ということで…」みたいなノリでカジュアルに作られてきたようなのですが、音声入力の自然言語でその場でサンプラー音源の作成を指示して動かしてしまうという、ライブコーディング環境としては驚きの高水準機能が実現していて、とにかく驚かされました。ADC JapanでもChromiumとWebプラットフォームをもとにプラグイン開発を実現するアプローチのセッションを担当されるようなので、興味のある方はチェックしてみて下さい。ライブコーディング中に音声入力で指示するというのを見るのは初めての体験でした(!)
  • ob5vrさんのGLSL Musicの話は、そのコンセプトの全体的な紹介で、GPGPU等でオーディオ処理ができるという道筋の導入、これまでのライブパフォーマンス実績、ob5vr/wavenerdを使ったライブの紹介まで、コンパクトにまとめていただきました。GLSLの得意な音のインパクトが強くて「こんな音が作れるんだ…!(でも原理を説明してもらえたので分かるっちゃ分かる)」という感想になりました。connpassページの勉強会資料から辿れるので聴いてみて下さい。この方面の方々のライブパフォーマンスや作品はSESSIONSで見ていたのですが、来月末にもDraw(tokyo); #4というイベントにも出られるようなので、興味のある方はチェックしてみて下さい。

今回はLTも含めてだいぶガチ目の内容が多く、全部理解しきれなかった人も少なくなかっただろうと思いますし、それでいいと思っていますが、ソフトウェア開発のトピックが多かったので、プログラミングの知識が無いと無理だったかもしれません。LTの時間で話せることは限られており、何人もちゃんと時間を取って話せるようにするには半日くらいの時間が必要です。

この勉強会の前後で技術書典20とM3 2026春でオーディオプラグイン研究所としてサークル参加していたのですが、プラグインフォーマットやMIDI 2.0の解説書をM3で頒布していると、プログラミングの知識は無いけどDTMはやっているのでソフトウェアの仕組みが気になる、という人が少なからずやって来て、興味を引かれてプログラマーでもないのにお買い上げに…という場面が何回か出てきます。こういう層の皆さんにももっとリーチしていけるような勉強会にしたい…とは思っていますが、基礎から勉強したい人向けの集まりと、最先端を追い求めたい人向けの集まりがブレンドするかはちょっと不透明で、まあ何でも同じ勉強会で実践するのは無理があるかもしれません。

VJも含めたアート表現にはAMCJ(拠点URLがどこなのかわからない…)やAudioVisual勉強会、ゲームサウンドも含む集まりはサウンドミニハッカソン、ハードウェア寄りのクラスタにはSynth Maker Meetupのようなイベントコミュニティがあるので、皆さんでそれぞれ向いているコミュニティを探してみてください。

3月の開発記録 (2026)

3月になって、これまでほど開発に集中はできなくなってきましたが、原稿の進捗を犠牲にしてコードを作り出し続けています(時候の挨拶)。やべーなどうすんべかな…

Music Tech Meetup (音楽技術勉強会) 2026.04の開催

2020年2月上旬に1回開催して以来やっていなかった音楽技術勉強会を、4月15日(水)に渋谷CRIミドルウェア社のオフィススペースをありがたくお借りして開催します。

https://music-tech.connpass.com/event/388526/music-tech.connpass.com

今回は主に、オーディオプログラミング言語mimiumの開発について、開発者である松浦知也さんに30分ほど(仮)解説していただきます。 mimiumはDSPの設計・試行をインスタントに実行できる環境と合わせて作られていて、LLVMでネイティブコードにコンパイルして実行できる一方で、バージョン3.0では静的コンパイルと合わせて実行時の変更コストを下げてライブコーディングにも利用できるといえるものに進化しているようです(自分はmimiumの歴史を詳しく把握していないのでこんな書き方になってしまいますが)。Webで実行できるデモなどもあるので試してみてください。

mimium.org

オーディオプログラミング言語がどういうものか知りたい人はオーディオプログラミング言語Advent Calendar 2020を読むのが良いでしょう(6年前なのでさすがに最近流行りのStrudelとかは入っていませんが)。ライブコーディング環境の実現というのはWebやモバイルでhot reloadingなどを実現する技術とも近く、プログラミング言語クラスタ(?)向けの内容ともいえるかもしれません(当日そういうお話が出てくることはお約束できません)。

qiita.com

もう1本のセッションでは、atsushienoがモダンなDAWプラグインホストの設計と実装と称してUAPMDの話をちょっとします。10分に削ってしまったのでだいぶ端折った内容になるとは思いますが、ここ1年くらいでAIコーディングエージェントの普及とともに増えてきた自作DAW勢の動向に合わせて、そういう開発をやってみたい人向けの話が出来たらと思っています。

以前からオーディオ開発者クラスタの人たちと顔合わせする度にオフライン勉強会やりたいねえみたいな話になっていたのですが、時間的余裕(あるいは機運)・開催できる場所・セッションをお願いできるひと、の3つが揃わずじまいでした。

あと6年前にはTwitterが存在していて、あそこにはそれなりにfollowersがいたので、そこで宣伝すると人に来てもらえたけど、今はもう亡くなったのでbskyとmastodonだけで拡散するかなあ、と思いましたが、蓋を開けてみるとありがたいことに募集開始から1日で定員の半分が埋まり、翌日さらにごく一部のコミュニティDiscordで宣伝したら2/3くらい埋まったので、もう完全にこれで十分な時代になったことが分かってよかったです。

(Twitterを使っていた当時のtweetsはnotestockにインポートした後は全部消してあるので、atsushienoが何か書いていなかったっけ…と思った時はこちらから検索してみてください。)

notestock.osa-p.net

その他のお知らせ

4月には12日に技術書典20、26日にM3 2026春があるのですが、新刊の目処はまだ立っていません(!) UAPMDの開発体験をもとに原稿が書き溜められつつあるのが実を結べば何とか、という感じです。MIDI 2.0プログラミングの本にはならなそうです。思ったよりwinmidiとlibremidiの相性というか進展がいまいちで、現在でも仮想デバイスの作成が出来ない(あるいはそこに接続できない)状態が続いているようです。何かしら出すとは思うので生暖かく見守っていただければと思います(Music Tech Meetupの準備もしないといけないので割と無理筋かもしれない)

UAPMD 0.3.0 + 0.3.1

残りはUAPMD開発記録です。今月は0.3.0と0.3.1という2つのリリースが公開されることになりました。特に0.3.1の進化が激しく、とても0.0.1しか上がらないリリースに収めるような内容ではないのですが、まあまだ初期ということで…

今月も変更点を列挙するのでいっぱいいっぱいでした。それ以上のことは、今月はzennに記事を3本も上げているので、そちらを見てください(以下で随時リンクしています)。ちなみにmidicciにもLinuxまわりで多少修正がありましたが、多数の変更点は無いので省略します。

UAPMD 0.3.0

  • Demucsを使ってオーディオファイルをSTEM分割してトラックを分けてインポートできるようにしました(リリースビルドにしないと遅くて話にならないので、ソースからビルドして試す時は注意してください)
  • オーディオとMIDI 2.0のマルチトラックのクリップ再生機能を活用するために、オフラインレンダリングの機能を実装して、uapmd-applyというRenderManのようなツールを追加しました(remidy-applyというものが存在していたのですが、これを作り直したものです)
  • Android、iOS、Webブラウザでファイル選択できるようなdocument pickerを作って、uapmd-appからファイルをロード/セーブするときにportable-file-dialogsの代わりに使うことにしました
  • Windows上のVST3プラグインの挙動が、先月はかなり不安定だったのですが、今月はもう動作しない既知のプラグインが存在しない程度に安定しました
  • Android上でAAPがGUIも含めて使えるようになりました(ただし未リリースのAAP 0.9.4に依存)。史上最も安定している実用的な(?)AAPのホストといえます
  • オーディオエンジンの有効・無効を切り替えられるようになりました
  • VST3とCLAPのプラグインでMIDI2クリップを演奏できなかった問題を解決しました

UAPMD 0.3.1

  • ピアノロールエディタをやっつけ実装で追加しました(シーケンサー機能の確認のために作られたもので、実用性はそんなに無いです)
  • MIDI2クリップのイベントリストが編集可能になりました
  • プラグインスキャナーを完全に作り直しました
    • リモートプロセスでスキャンできるようになりました(これについては今月書いた記事1が詳しいです)
    • ブロックリストとタイムアウトが実装されました(タイムアウトはリモートプロセスが無いと実現できず、ブロックリストもリモートスキャンやタイムアウト検出が無いとあまり意味がなかったやつです)
    • プラグインスキャンのAPIが非同期になりました
    • 新たにuapmd-scanというツールを追加しました(remidy-scanというものが存在していたのですが、これを作り直したものです)
  • 以前から存在していたスクリプティングの機能を、ローカルHTTPサーバー経由で操作できるようにしました
  • MCPサーバーを追加しました(これについては今月書いた記事2が詳しいです)
  • シーケンサーエンジンのオーディオグラフがリアルタイム処理前提のAPIになり、Web AudioのAudioWorkletで使えるようになりました
  • シーケンサーがファイルをロードする部分とリアルタイムでオーディオグラフを処理するオーディオエンジンが切り分けられ、シーケンサー側はメッセージポンプでオーディオ処理より長いサイクルで非同期的にデータを準備するようになりました
  • MIDI 2.0仮想デバイスのFunction Blockを登録する機能をuapmdのコア部分から切り離しました
  • Web環境で動作するWebCLAPに対応しました
  • WebCLAP対応のために、状態(State)の操作などを非同期API化しました(これについては今月書いた記事3が詳しいです)
  • WebCLAP GUI対応のために、remidy-guiにWeb用のContainerWindowの実装を追加しました
  • APIドキュメントとWebAssemblyビルドの最新版を https://atsushieno.github.io/uapmd/ にホストするようにしました

0.3.1をリリースしたのはつい数日前ですが、その後は現在進行形でレイテンシーレポート対応の実装が進んでいます。まだDAGのサポートもしていない段階で進めているのですが、すでにだいぶ複雑な機能です。

来月の予定(?)

6月初頭に開催されるADC Japan 2026ですが、一応CFPに応募したAndroidネタが採択されました。なので4月以降はそっち方面の開発が進む公算が高いです。UAPMDでAAPをサポートしたマルチトラックシーケンサーのようなものが実現できているので、何とか意味のあるものにしていきたいですね。

2月の開発記録 (2026)

先月に続き2月も自分の開発に100%集中できていて、生産的な時間を過ごしています。といっても生産性の亡霊みたいに生きたい願望は皆無ですし、生産性を最大化して日々過ごしているわけでもないですが。

あとADC Japan 2026がいつの間にかCFPを出していたので1つだけ投稿しましたが、3/2まで時間があるのでもしかしたらもう2つくらい作るかもしれません。

audio.dev

正直どちらかといえばLinux Audio Conference 2026のほうが自分のあり方に近いので、そっちでしゃべったほうがいいのかもしれません。スケジュールを見る限りADCの採択からLACの締切まで1日の猶予があるので、落ちても再利用できそうですが、ちゃんとスケジュール通りに動いていれば…という前提がありますね。

lac26.mucs.club

UAPMD v0.2.0 and MIDICCI v0.2.0 released

2月中旬にUAPMDとMIDICCIのv0.2.0をリリースしました。実際には、v0.1.4を2月初頭にリリースして、その2週間後にv0.2.0をリリースして、実際にはちょっとやらかしててuapmdはv0.2.1をリリースしているのですが、まあ誤差でしょう。

UAPMD v0.1の主要なアチーブメントが「任意のオーディオプラグインをMIDI 2.0デバイス化する」にあるとしたら、v0.2は「マルチトラックのオーディオ / MIDI (2.0) クリッププレイヤー」を実現できたことをもって決定されたリリースです。SMF2 Container Formatはこういうフォーマットとして作られているはずなので、それを実現したものである、ということもできます。これを実現するために、UAPMD v0.2.0にはいくつか新機能が追加されました:

  • プラグインにUMPイベントを送信できるSMF2クリップをトラックの一部として再生できるようになりました。SMF2クリップの送信対象はプラグインであり、オーディオクリップと並列に走らせることもできるので、エフェクトプラグインオートメーションに相当する処理もSMF2クリップで実現します。
  • UAPMDプロジェクトファイルのロードとセーブ: SMF2 Containerフォーマットはまだ仕様が公開されていないので、独自形式で保存されています。
  • SMF importer: SMFで制作された楽曲の各トラックをSMF2Clipに分解できる(保存はプロジェクト保存と同時に行われる)。これで既存のSMFファイルからSMF2ファイルをカジュアルに作成できるようになりました。
  • マスタートラック: SMFでは明確な仕様ではないのですが、テンポや拍子の管理は1つのトラックに吸収して管理されています。管理といっても現状では全トラックから吸い上げているだけで、正直これを編集可能にするときにどうしたものかはわかりません。MIDI 2.0の場合テンポや拍子の設定はUMPの一部 (Flex Data) なので情報の保持は難しくないのですが。
  • トラックのタイムラインのコンセプトがほぼクリアになりました。具体的には、オーディオI/Oからのオーディオグラフにおける位置付けが、再生時にはいったんトラックの内容から「今回のオーディオ処理に含まれるサンプルとイベント」を切り出して、プラグイングラフに渡す(プラグイングラフの構築と処理は実装次第)、というかたちが明確になりました。
  • SMF2クリップの内容を表示するMIDIダンプリストを表示できるようになりました。

MIDI 2.0楽曲ファイルの作成と再生については、ktmidiにSMFをそのまま延長したような構成のAPIがあったのですが、UAPMDというかたちで音源というかプラグインホストを作成してみると、明らかにこの構成では足りないことが明白になったので、先月ktmidiからumppiとして取り込んだばかりのMidi2Musicクラスは消えて無くなる事になりました。

UAPMD & MIDICCI app design polish

UAPMDは元々GUIアプリケーションにするつもりが無かったこともあって、GUIとしての見てくれはUIテーマをBootstrapっぽいやつにしたりフォントをRobotoにした以上のことはしてこなかったのですが、MIDICCIと併せて今回はもう少し手を加えることにしました:

  • Lightテーマ対応: ImGuiにデスクトップからlight/dark themeを判別する機能は用意されていないようなので手動切替になりますが、一応ホワイト中心の配色を用意しました。ただImTimelineの中は調べきれていなくてそこだけ暗いです(逆効果かもしれん)
  • GUI上の機能が増えすぎて文字ボタンがスペースを取りすぎるのが無視できなくなってきたので、FontAwesome + fontaudioを使って画像で代用することにしました…のつもりが、fontaudioはまだ出番が来ていません(取り込みはしましたが)。カスタムフォントとしてImGuiに統合するのはjuliettef/IconFontCppHelpersと組み合わせてC++ヘッダー化したフォントファイルのリソースを使えば簡単…というわけでAIにC++ blob headerを自動生成させていたのですが、最近公開されたeyalamicmusic/ResEmbedを使うとさらにクリーンなかたちで統合できたので、公開が告知されたその日のうちに対応しています。
  • アプリアイコン: v0.1の時点ではアイコンの類を何も用意しなかったのですが、アプリケーションとしてインストールできるようにしているわけだしもう少し実用品っぽくしておこう…と思って用意することにしました。とはいえ考えるのめんどいしどうしようかなあ…とぼやいていたらmuojpせんせいから天啓が…

最近ClaudeでAndroid用のXMLアイコン描かせてるんですが、妙に味があって気に入ってしまい困る事案にw

muo (@muo.jp) 2026-02-14T06:19:49.867Z
bsky.app

DroidKaigi 2025の関連セッションなどを見ているとわかりみが出てくると思いますが、完全にそれっぽい…!と思って真似してみました。おかげさまでそれっぽいものが生成できています:

VST3/AU host on macOS: every plugin loads

UAPMDの大きな弱点のひとつはremidyのプラグインホスト機能がjuce_audio_plugin_clientなどと比べると安定的にあらゆるプラグインを利用可能になっていなかったことでした。具体的には、Absynth5のAUがインスタンス生成に失敗したり、Kontakt 6/7/8のGUIがAUでのみクラッシュしたり、KORG M1やVienna EnsembleのVST3がインスタンス生成時にクラッシュしたりといった問題が起こっていました。

これらについては毎月のように修正を試みていたのですが、今月はついに自分の手元にあるプラグインについては全て解決しました(そんなに大量に抱えているわけではありませんが):

  • Absynth5 AUとKontakt 6/7/8のUIを妨げていたのはAUのインプロセスモードの明示が原因でした(!) プラグインインスタンスの生成は時間がかかるので非同期のAudioComponentInstantiate()で行うべきですが、その引数にkAudioComponentInstantiation_LoadInProcessを指定すると、インプロセスモードで実装されていないプラグインの機能が全て死にます。
    • Absynth5 AUが死んだ理由はこれがarm64ではなくx64のABIでビルドされた旧時代のプラグインであり、Rosetta2経由で動いていたため、別のプロセスでロードしなければならず失敗していた(クラッシュまではしなかった)ということになります。
    • Kontaktのほうは、おそらくmulti outの機能を実現するうえで1つのGUIプロセスが複数のプラグイン インスタンスとやり取りするかたちになっていて、DSP側のインスタンスだけなら正常に動作できてもGUIを表示しようとした瞬間にAUのフレームワーク側から想定外のブロックを食らって死んでいたのでしょう。
  • VST3のM1とViennaのほうは、単純にremidyのオーディオバス調整まわりでプラグインとの不一致が原因でした。

そんなわけで、VST3とAUのプラグインホスト機能は少なくともmacOS上ではだいぶ安定感が出てきました。とはいえ何もかもが動くようになったというわけではなく、CLAPプラグインではFloeが動かず、またLinuxではyoshimiが自分の知る限りでは動いていません。この辺はもう少し修正が続いていきそうです。とはいえ互換性地獄のプラグインホスト開発でここまで順調にいろいろ使えるようになったことを考えると、だいぶいい戦果を出しているのではないかと思います。

MIDI Message Report: The Latest Parameter Value Saga

先月の作業の最大の成果は、プラグインのパラメーターリストの動的な変更とホスト側への最新パラメーター情報の通知を何とか実装したことだったのですが、これがuapmd-appからmidicci-appへの通知となるとまだまだ完成していませんでした。これは、簡単にいえば「仮想MIDIデバイス生成時にはプラグインパラメーターの値が既に初期値から動いてしまっているため」です。同様の問題はuapmd-app自身がホストとしてプラグインのパラメーターの現在値を通知してもらう流れでも発生していました。

この問題を解決するために、仮想MIDIデバイスにMIDI-CI接続が新たに発生した時に、MIDI-CIの機能の一番目立たないMIDI Message Reportという機能を使って、現在のプラグインのパラメーター…に対応するAssignable ControllerとPer-Note Assignable Controllerのリストをmidicci-appからリクエストできるようにして、またuapmd-appもMIDI Message Reportに応答できるようにしました。

Process Inquiryの機能を実装しているのはktmidiとmidicci(とuapmd)だけなので、juce_midi_ci(と呼ばれていたモジュール)を使っていると実現できません。

あと関連項目として、UAPMDのAllCtrlListプロパティでは、パラメーターの最大値と最小値を指定するminMaxという項目を生成しなくなりました。これが指定されると、パラメーターのnormalized valueの値域が0..1からシンプルに0..UINT32_MAXに対応しなくなってしまい、ユーザーに余計な混乱を与えることになります。MIDIデバイスとしてやり取りするのは常にnormalized value、enumなどのdiscrete valueに変換するときも0.0..1.0にマッピングしていたのと同じことをする、という前提で再構成しています。

midicci diverged: UMP type migration from uint8_t to uint32_t

midicciはそのアイデンティティが「C++プロジェクトに組み込めるktmidi相当のMIDI-CI機能を実現するライブラリ」にあって、これまではずっと「ktmidiに新機能を実装し、それをコーディングAIエージェントに移植させる」というモデルで進めてきました。

このアプローチは現状でも破綻しているわけではないのですが、今月になってmidicciはついにAAPにも組み込まれるようになり…さまざまな問題が噴出しました。現時点でもAAPのMIDI-CI統合は期待通りに動作していません。uapmdとmidicciは2026年の今を生きる人類として開発が進められているライブラリで、libremidiなど2026年のライブラリを想定して設計されています。

ktmidiがUMPサポートを追加したとき、あるいはAAPがMidiUmpDeviceServiceサポートを追加したとき、それらのAPIが念頭に置いていたのは「MIDI 1.0とのシームレスな互換性」でした。そのため、これらのAPIはMIDI 1.0のAPIと同様、1バイト単位(kotlin.Byte, uint8_t)でUMPを受け渡しています。これがlibremidi、ALSA、CoreMIDIなどとデータのやり取りをする場面ではuint32_tなど4バイト単位の受け渡しが必要になります。この変換は単なるポインタのキャストではうまくいきません。なぜならエンディアンネスの問題が発生するためです。UMPのuint8_t*のストリームとしては40 90 2B 00 F0 00 00 00のように渡さないといけないのに、4バイトのlittle endianのメモリには00 2B 90 40 00 00 00 F0のように格納されているためです。こういった複雑な前提の違いは、特にコーディングAIがすぐ間違えます。人類も間違えるでしょう。

そういうわけで、midicciはUMPを扱うAPIの一般的な潮流に合わせて、uint8_tからuint32_tの単位でUMPを表現することにしました。これは結果的にktmidiに追従する開発方針からの転換ということになります。まあ実際これで困ることはもう無いでしょう。midicciの実装は、可読性はともかく、挙動としてはだいぶ信頼できるコードになってきました。

ktmidiもそのうち追従するかもしれませんが、ktmidiのほうをいじっている暇がないので、しばらくは現状の1バイトAPIを維持することになりそうです。

UAPMD: U is for Ubiquitous

UAPMDは特にWindowsがMIDI 2.0をサポートしていない現状ほぼLinuxとmacOSで動かすために作られたものですが、次世代プラグインホストに相応しい立ち位置を実現するために開発しています。これは特にAndroidを含め「デスクトップ限定のホスティングライブラリで終わらない」という側面があります。UAPMDのUはubiquitous(遍在)のUであり、元々は「どんなオーディオプラグインでも」UMPデバイス化する、という意図だったものですが、今月はさまざまなプラットフォームで動かせる可能性が見えてきました。

uapmd-android

まずはAndroid版です。リポジトリはuapmdに統合される可能性がありますが、とりあえずは独立して作られています(雑にAndroid Studio Pandaで作られていますがAGPのバージョンさえ調整すればNarwhalでもいけるはず):

github.com

どのプラットフォームでも一番問題になるのは「ImGuiが動くかどうか」ですが、Android上ではSDL3 on Androidがそこそこ本気で作られているので、この上で動かしています。SDL3 on Androidは特にSDL3のメイン開発者が積極的にコミットしていることもあって、今後が期待できそうです。ただ現時点では「SDL3のメインスレッド」がAndroidのメインスレッドではないという問題があって、挙動が安定しません。Flutterがv3.29で大改修を実現したやつと同根ですが、こちらは単なるリグレッションのようなので、そこまで時間がかからずに直るかもしれません。

Android版のuapmd-appは、ImGuiアプリケーションとしては概ね問題なく動いていたので(最近はやや不安定でしたがこれはuapmd-app本体が不安定という可能性が高いです)、新たにPluginFormatAAPのAPIを作ってmainブランチにも取り込まれていますが、まだプラグインの列挙から先は未確認です。というのも、現状uapmd-appはファイルをロードできないとプロジェクトに取り込むことすらできないわけですが、AndroidでファイルI/Oというわけにはいかないので…

uapmd on Windows

次にWindows版です。Windows MIDI Servicesがついに出る出る詐欺を脱して正式リリースされそうということもあって、だんだんこの界隈が賑やかになってきました。先月libremidiの仮想ポートサポートがまだ出来ていないという話を書きましたが、今月になってこのissueにmicrosoft/MIDIの設計開発者も参戦してきて、実装されたけど動作確認できない〜、みたいな状態で来月に持ち越しそうです。

Windows版はまだまだ全体的に安定しないのですが、プラグインホストまわりでは大きく2つの修正を加えた結果、そこそこプラグインが使えるようになりました:

  • Native Instrumentsのプラグインが全て使えていなかったのですが、これはVST3のバンドルで呼び出すべきInitDll() ExitDll()の関数名が間違っていたのが原因で、これらを修正したら正しく使えるようになりました。
  • 先月までImGuiのバックエンドがGLFWだったのですが、まずSDL3を使うようになり、さらにDirectX11を使うようになり、その後安定性の観点でSDL3に戻ったものの、プラグインUIの描画に問題が出たため、現時点ではDX11に戻っています。SDL3の描画問題はかつてはmacOSでも出ていたので、似たような問題で解決可能かもしれませんが、現状DX11もSDL3も同じくらい不安定なので、今後DX11からさらに動くかはわかりません。

uapmd WebAssembly build

現状まだ本気で取り組むつもりはないのですが、uapmdにはEmscriptenビルドも存在していました。ただ古いブランチのままで、その後のuapmdの成長に全く追従していなかったため、mainブランチに取り込めるようなものではありませんでした。今週ちょっとCodexのトークンが余りすぎていたので一念発起して「ちゃんとmainブランチに取り込んで維持できる」前提のものを作ろうと思い至って、何とかCodexのweekly limitの範囲内で実現できました。

wasm版では意外にもchocのAudioFileReaderが(基本的にファイルI/Oで動いていたのでそのまま動かないのは当然として)iostreamでも期待通りに動かないという問題が出て、miniaudioの組み込みAPIでようやく再生できる(miniaudio自体はWeb Audioバックエンドもサポートしているのでそのまま使える)が、flacは再生できてもoggが再生できない…という事態に陥っています。

また当然ですがwasm版では現状利用可能なプラグインフォーマットが無く、WebCLAPかもしかしたらWAM2が今後サポートできるかどうかわからない状態にある、という感じです。どちらもプラグインを列挙するAPIがまだ無いはず。

AAP midicci integration

AAPには元々ktmidiを利用してMIDI-CI機能を実現するつもりでした。これを実行に移していなかったのは、Kotlinで実装してしまうとMIDI-CI処理をAAPから操作したい場合にややこしい事態になると想定されたためです。この考え方がmidicciの成長を見ていくうちに変わってきました。midicciを使ってAAPのC++実装から調整したほうがuapmdに近いことが出来そうな目論見が立ってきたので、ktmidiの代わりにmidicciを使うルートで実装することにしました。

ただ、midicci-appが存在しているデスクトップ環境とは異なり、midicci-appはまだAndroidに移植されていないので、接続確認にはktmidi-ci-toolを使っています。これがまだAAPのmidicci統合と相互接続できず、まだ試行錯誤している段階です。AAP側にも、インスタンス生成時にパラメーターリストを取得できない問題などがあり、AAPもいろいろ再設計したいという気持ちが高まってきました。ただそれは抜本的な再設計になるので、UAPMDが落ち着いてからがっつり時間を取って着手したいところです。

3月の予定

11月から3ヶ月ほどuapmdとmidicciに集中できていたのですが、4月には技術書典20とM3 2026春があるので、新刊の準備をしないといけなそうです。プラグインホストの設計解説書あたりが良いかと考えていたのですが、Windows MIDI Services正式公開の流れに乗るならMIDI 2.0総合開発本あたりが良いのかもしれません。まあ来月末には告知できる段階にあろうかと思います。

1月の開発記録 (2026)

昨年12月に引き続き、1月もintensive uapmd hack monthでした。今週はだいぶ稼働していないので、成果の大半は最初の3週間半くらいで出てきたものです。

他ではKotlinのライブラリのパッケージメンテナンスをちょっと進めたくらいです。uapmdでMIDI 2.0音源が実用的に使えるようになってきたので、特に(現状Tracktion Engine前提の)augene-ngをMIDI 2.0中心にシフトして進めようとも思ったのですが、やはり単一のUMPデバイスだけではまだまだ楽曲の打ち込みができるとは言い難いので、もう少しuapmdを先に進める必要があります。

uapmd/midicciの進展

uapmdとmidicciは先月末の時点でほぼパッケージングまで完了していましたが、年頭に正式にv0.1をリリースしました。これに合わせてKVR product pageも作成・公開しています。

www.kvraudio.com

uapmdもmidicciも、ここ1ヶ月でいろいろ大きく進展しています。最初にどんな変更が加えられたかを列挙して、後から各論的に細かい部分を詳説しています。時間が無くて書ききれないので、細かい論点は別途切り出して覚書として別投稿するかもしれません。

uapmd improvements

  • UMPのバッファリングの処理が安定し、現在ではuapmdとmidicciあるいはMIDI2.0Workbenchとの間でSysExのやり取りが中断されることはなくなりました。
  • 巨大なJSONのやり取りが必要になっていたAllCtrlListのデフォルト値の出力を大幅に削り落としました。それでも数十チャンクのGetPropertyDataのやり取りが必要にはなりますが、1/4くらいになったのではないでしょうか(未計測)
  • AUv3サポートをAUv3のAPIに沿ってネイティブ実装しました。(後述)
  • uapmd-appのImGuiにPatitotective/ImThemesを利用してBootstrapっぽくしてみたり、Robotoフォントを取り込んでテキストのレンダリングがまともに見えるようにしました。ImGuiのデフォルトの陰鬱な配色も嫌いではないのですが、メリハリが無いのはUXとして改善の余地があると思っています。デフォルトフォントはまあファイルサイズが小さいからとサウンドフォントにTimGM6mbを使うような合理性があるのでしょう。
  • GUIでは表示のカスタムスケーリングにも対応しました。ある程度は正常に動作してくれるはず…現時点でMIDIキーボードが拡大されないことは把握済みなので、そのうち直すと思います。
  • per-note controllersをmidicci-appのUI上に出して調整できるようにしました。ただ音源側がper-note controllerをちゃんと適用できることを確認できたものがなく、UI上は選択できるという以上のものではないです。ちなみにGUI上はGroupやChannelも選択可能になってはいるのですが、Groupは不要そうな気もしますし、ChannelはVST3やAUにそういうコンテキストがあるとはいえるものの、これらを使う音源は何1つ知らないので未確認です。
  • Windowsビルドが動作するようになりました。とはいえ、Windows MIDI Servicesサポートがまだです。これはまずWMS自身のバグでWMSサービスの動作チェックに失敗する問題に引っかかって、それが直ったら今度はlibremidiにwinmidiのopen_virtual_port()実装がない問題に引っかかった、という感じで現状では実装待ちです。
  • JSサポートの位置付けを変更して、uapmdのAPIを外部からFFIでnodeのみサポートする方式ではなく、uapmd-appからQuickJSを使用してJavaScript APIを公開するという方式に切り替えました(後述)
  • シーケンスエディターの構築が始まりました。uapmd 0.1の時点ではオーディオファイルは全てのトラックに流し込まれていましたが、現在ではトラックごとにオーディオクリップを選択して、クリップも複数作成できて、位置も自由に調整できるようにしてあります(その関係でオーディオ入力のスペクトラムアナライザーは現状動いていません)。ここにMIDI2Clipも配置できるようにしたいのですが、現状MIDI2Clipファイルはどこにも存在していないので、これらを作る仕組みを作るところから必要です(道半ば)
  • UMPエンドポイントのFunction Block管理機能を整理しました(後述)
  • シーケンサーエンジンAPIを大幅に再編成しました(後述)
  • パラメーターリストの動的な変更に対応しました。AUv3のMela、VST3/CLAP/AUのBYODなど、ロードされたプリセットに応じてパラメーターリストを切り替えるタイプのプラグインが、最新状態のパラメーターリストを表示できるようになりました。
  • LinuxIRunLoopの実装を全面的に見直し、remidy本体が提供するEventLoop実装が使われていないアプリケーションでWaylandサポートが使えていなかった問題を解決しました(後述)
  • LinuxのFlatpakセットアップが追加されました。ただオーディオ機能はALSAがバイパスされているので、PipeWireのみが動作する、みたいなことにはならないようです。

midicci improvements

今月はmidicciにもさまざまな改善が施されました:

  • midicci-keyboardとmidicci-guiの機能をmidicci-appという1つのアプリに統合しました。MIDI-CIを活用したプロパティの可視化も、MIDI-CIデバッグ作業も、1つのバイナリ実行ファイルで済ませたほうが楽です。
  • 先月UAPMDのバイナリパッケージを作成したのに倣って、midicciもパッケージを作成することにしました。Linuxではdebrpm, macOSではhomebrewとdmgを公開しています。
  • UAPMDに合わせて、こちらもWindows GitHub Actionsビルドのartifactを含めるようになりました。ただ動作は完全に未確認です。Wndows版uapmd-appはまだUMP仮想デバイスを登録できないので…
  • per-note controllersをmidicci-appのUI上に出して調整できるようにしました。ただ音源側がper-note controllerをちゃんと適用できることを確認できたものがなく、UI上は選択できるという以上のものではないです。ちなみにGUI上はChannelも選択可能になってはいるのですが、ChCtrlListのやり取りは行っていないので(してもいいのですが、現状uapmdでChCtrlListを発行しないので)、何も反映されません。
  • uapmdとの間でSysExのやり取りがほぼ安定して動作するようになったので、Send Discoveryボタンはデフォルトで折り畳まれたエリアを作って対比することになりました。デバイス詳細情報もデフォルトで非表示にしています。通常は名前だけで十分なので。
  • uapmdとの間でAllCtrlListやProgramListのやり取りが安定するようになったので、これらのMIDI-CIプロパティはResourceListに含まれていたらRequestボタンが押されるのを待たずに自動的にリクエストするようにしました。
  • uapmdにMIDI2Clipサポートを追加したい関係で、ktmidiのktmidi本体(ktmidi-ciではない部分)をMidiAccess API以外すべて取り込むことにしました。ライブラリ・モジュール・名前空間の構成をどういう扱いにするか悩みましたが、新しいモジュールはUMPサポートが中心になるのでumppiという新しいモジュール・名前空間に切り分けることにしました(midicciに近いネーミング)。これまでcmidi2で行っていた処理もこのumppiで全部書き換えています。

embedded JSスクリプティングのサポート

v0.1.0リリースを公開してからすぐに取り掛かったのがJSサポートのやり直しです。先月少し言及したのですが、先月までのJavaScriptサポートはuapmd APIの公開インターフェース全体にJS shimを自動生成して、JSコードだけでinstrumentationが可能になることを目指していました。これだとuapmdを起動して、GUIコンテキストを用意して、プラグインをスキャンしてリストを構築・管理して…といった作業をJSでやることになり(あるいは「できるようにする」ことになり)、使い勝手が悪く、実用性が無かったといえます。しかもJSバインディングはnodeを前提に作られていて、Webブラウザ上で動かしてWebCLAPみたいなものもサポートしたいとなった場合には使えません。そういうわけでこの方向性は無かったことにしました。

代わりに導入されたのは、一般的なDAWに見られるスクリプティングランタイムの仕組みの導入です。これはchocのQuickJSサポートを組み込んで、chocのJS関数登録の仕組みを活用して実現しています。APIは目下のところ「AIにuapmdのAppModelクラスに含まれていた関数に対応するものを全部作る」というレベルで自動生成させたものだけですが、これで「JS変数にちょっとリストアップしたプラグインインスタンスを生成してプラグインUIを表示する」というスクリプトを自動生成させて、それが現状うまく動作しているので、この段階で満足しています。もう少しやる気が出たら、起動直後に自動実行するスクリプトをuapmd-appにコマンドライン引数として渡すとuapmd-appの初期化が完了したら自動実行する、みたいな機能を作れば、いろいろできることが増えるかもしれません。

組み込みスクリプティングAPIがあればMCPサーバーの追加も難しくはないと思いますが、現状スクリプティングAPIがあれば、それをAIエージェントに食わせるだけで十分いろいろできるはずなので、どちらかといえば外部に接続できてembedded JSランタイムと通信できるWebSocketsなどのproxyサーバーを組み込むほうが有用そうです。最近の話題でいえば、iPlug3が(まだ机上の存在ですが)プラグインMCPサーバーとして動作するものとして設計されているようですが、その動作環境において通信経路を自由に作れるとは限らないプラグインSDKとは異なり、uapmdは普通にライブラリとして利用できるので、一見して使い方が分かるようなAPIが公開されていれば、あとはAIのほうがよろしく解釈してくれるでしょう。

Linux GUIサポートの改善

uapmdの大きな特長のひとつは、uapmd自体はコーディングAIエージェントにプラグインフォーマットのAPIのインターフェースをガンガン実装させて先に進める、というアプローチを全面的に採用していることです。iPlug3が「われわれはAI時代を念頭に置いてフルスクラッチで設計している」みたいなことを宣言していますが、uapmdは(まあまあ抵抗感がありましたが)とっくに採用しています。

ただその弊害で、AI任せであんまし良くない設計のまま放置していた(いる)のがGUIサポート、特にVST3のIRunLoopIWaylandHostIWaylandFrameといった部分でした。ClaudeやCodexはAPIの意図を解釈して実装する分には問題ない仕事をするのですが、uapmdがイベントループのAPIremidy::EventLoop)をどのように使うかは明示的な仕様が無いので、IWaylandHostを実装するよう指示した時にLinuxEventLoopなるクラスを独自に作成してその中でのみIWaylandHostをサポートする、みたいなことをやっており、自分も深く吟味せずに取り込む、みたいな状態になっていました。

EventLoopはremidy「が」実装を規定すべきものではなく、プラグインホストが自身のGUIフレームワークに合わせて提供すべきものとして作られていて、実際uapmd-appはImGuiEventLoopで動作しているので、(その中ではIWaylandHostはサポートされておらず)、Waylandサポートが利用される場面は全く存在しなかったといえます。

Waylandサポートの問題はもう1つあって、プラグインのほとんどはWaylandをサポートしない(IPlugView::isPlatformTypeSupported(kPlatformTypeWaylandSurfaceID)がfalseを返す)ので、ホストのデスクトップ セッションがWaylandでも(XDG_SESSION_TYPE=waylandでも)引き続きX11プラグインUIもサポートしないといけない、ということです。uapmd-appは「現在のデスクトップに合った1つだけのplatform type」のみをチェックしていたので、前述のEventLoopの問題を修正した直後は、(Waylandセッションで動作している場合はWayland UIとして動作するかどうかをチェックし、当然のようにほとんどのプラグインがそのチェックを通らずにUI生成に失敗するため)UI生成が何一つ成功しないという問題に陥っていました。

現在ではX11が通ればX11でUIを生成することにしていますが、これはもしかしたらWaylandオンリーの環境では(特にWaybackなども組み込んでいないため)失敗するかもしれません。とりあえず現時点ではuapmd-appは一般的なLinuxデスクトップのみをターゲットにしていて、あまり現実的な懸念ではないと思って放置しています。Waylandオンリーの環境でも動作するオーディオプラグインが有意に出現してきたら考えます。

AUv3サポートのネイティブ実装

AUv3プラグインはAUv2のホスティングAPIを利用してインスタンス生成が可能です。ただ、そのホストが利用できる機能は、当然ながらAUv2が前提とするものに限られます。今月になってノート別コントローラーの実装を作り込もうとして気付いたのですが、AUv2はAudioUnitGetPropertyというAPIの上ではノート別パラメーターを取得できるようになっているものの、実際にこれでパラメーターを取得できるように作られたプラグインは存在せず、AUv3になってから初めてAUParameterTreeAPIによって使えるようになった機能という位置付けになっているようです。

こうなったらAUv2のAPIではなくAUv3のAPIを使用してAUホストを書き直すしかない…と思ってひと通り実装しました。 これまでは「AUv2もAUv3もAUv2のAPIからアクセスできるからそっちで統一的にやる」というスタンスでやっていましたし、最終的にはAUv2実装が不要になるはずだからAUv3実装に統一するか…と考えていたのですが、今度はper-note controllerなどがAUv3のAUParameterTree経由でないと取得できず、一方でAUv3 APIだけで実装するとAUv2のパラメーター変更が取得できないというややこしい問題に引っかかり(このcommitだけでも確か結局は解決せず)、最終的に2つの実装が併存しています。

Appleがこの辺をきちんとAUv2Bridgeで実装してくれていればAUv3 APIだけで問題なく統一できていたはずなのですが、現実はそうはなってはいなかった、ということで、自分もいろいろ原稿(とは)を書き換えないといけないな…と思っているところです。

Function Block管理機能の再編成

uapmdはここまで、SequenceProcessorがリストを保持するAudioPluginTrackが中でAudioPluginGraphをもち、そのノードであるAudioPluginNodeが保持するプラグインインスタンスのそれぞれについて、UMP Function Blockとしてプラットフォーム仮想MIDIバイスを(libremidiを使って)構築する、という構成になっていました。

UMPメッセージは、uapmd-appのAppModelAPIでは個別のプラグイン インスタンスに対して送られるようになっていた一方で、シーケンサーのトラック管理が進化して1つのトラックに複数のプラグインを直線で繋げるようになった時点で、UMP入力キューはトラックごとに編成されることになりました。プラグインへのUMPイベントの受け渡しは、最初は先頭ノードがUMPを受け取って処理し、次のノードは前のノードの出力を受け取って処理するような設計になっていました。これは失敗でした。MIDIメッセージの何がそのノードで処理され、何が次のノードに「受け継がれる」べきかはわかりません。MIDIイベントストリームの連結はユーザーのホスト操作次第であるべきで、この仕組みでは後続のエフェクトパラメーターすら操作できません。NRPNの番号が衝突するので、最初のUMP入力をそのまま次のプラグインにも渡すというわけにもいきません。

この問題を解決するために、各プラグイン インスタンスには途中からFunction Block ≒ groupアサインされるようになり、uapmd-appはプラグインインスタンスIDを指定してnoteOn()などの命令を呼び出すのに、途中でgroupがアサインされ、それを受け取ったAudioPluginTrackはそのgroupに対応するインスタンスに渡す…といったややこしい実装になっていました。

ややこしいのはともかく、根本的な問題として、AudioPluginTrackにUMP Function Blockを割り当てるという構図がある限り、UAPMDはほぼフル機能のDAWシーケンサーエンジンを実装しなければならないことになってしまいます。それはやりたくないので、Function Blockの管理をシーケンサーエンジンから切り離せるようにしました。この部分は設計が詰められていなかったので、APIから作り直しています。

過去にUapmdMidiDeviceという名前で存在していた仮想MIDIバイスの実体は、その実態に合わせてUapmdFunctionBlockとなり、これらを複数まとめたものが新たにUapmdFunctionDeviceという名前になりました。プラットフォーム上のUMPエンドポイントはUapmdFunctionBlockごとに作成されます。uapmd-engineのAudioPluginTrackは1つのUapmdFunctionDeviceを持つことになるでしょう(プラグインが9件以上刺さって全てが有効となる事態は、現状では想定していません。Kontaktのmulti-outなんかも8つまででしたし)。

シーケンサーを導入するためのモジュールの切り分け

1月になってから、embedded JSランタイムを追加して、またオーディオトラックとMIDI2クリップをカバーするシーケンスエディター機能(トラック編成・編集機能)を構築しようとしたのですが、これらがどんどん複雑なタスクとなっていき、特にuapmd-appAppModelAudioPluginSequencerという2つのクラスが巨大な神クラスと化してきて、機能拡張を進める前にいろいろコードとモジュールの切り分けをしっかりしておくべきだと思うようになりました。特に以下の2点が大きな課題でした。

uapmdのリポジトリには、現状作りかけで開発が進んでいないuapmd-applyという小型ツールがあって、これは基本的にはプラグイン名とオーディオファイルやSMFファイルを渡すと、それを時系列に沿ってプラグインにそれらを入力として適用して、オフライン レンダリングでオーディオファイルを生成する、というものを意図しています。これを実現するにはembedded JSエンジンのようなものをheadlessで動かす必要があるので、たまにuapmdに破壊的変更を加えるとこれがビルドできなくなるので、シーケンサーAPIの切り離しと再編成においてsanity checkの役割を果たしています。

プラグインホスティング、Function Block(仮想デバイス)管理の機能をシーケンサーエンジンから切り離すというのは、uapmdというモジュールをremidyやuapmd-app固有のものではない、再利用可能なライブラリとして位置付け、uapmd-appが最終的にDAWに匹敵する機能を自ら作り込まなくても何とかなるようにする、という目的が込められています。1月末の時点でどのような切り分けが実現しているかをまとめると、以下のようになるでしょう。

  • remidyはプラグインホスティング機能に特化する
  • uapmdはプラグインホストが管理するプラグインインスタンスMIDI 2.0仮想デバイス = Function Blockとしての公開コントローラー機能だけを提供する
    • プラグインホストやプラグインインスタンスの機能はAudioPluginHostingAPIAudioPluginInstanceAPIといったインターフェースのみ規定し、実装はアプリケーションが提供する
    • 仮想MIDIバイスの作成やMIDI I/OのフックはMidiIOFeatureMidiIOFeatureManagerといったインターフェースのみ規定し、実装はアプリケーションが提供する
  • UAPMDのシーケンサー部分はuapmd-engineというモジュールで実装する(SequencerEngineクラスがその大部分を担う)
    • miniaudioとlibremidiを組み込むのはこの部分
    • プラグイン操作APIはこのモジュールに含まれるRemidyAudioPluginHostRemidyAudioPluginInstanceが実装している
  • uapmd-appはuapmd-engineを操作できるGUIを実装する

AudioPluginInstanceAPIの抽象化はまだまだ不完全で、たとえばPluginParameterSupportインスタンスがそのまま返されるような設計になっています。これではまだremidyから切り離されているとは言い難いところです。ただ現状remidy以外の実装たとえばjuce_audio_plugin_clientを組み込む優先度は高くないです。)

切り離しの一番大きな部分は、シーケンサーエンジンが前提とする「トラック」が、Function Blockを編成する主体ではなくなり、またトラックのオーディオグラフのノードがプラグイン インスタンスを保持(管理)する存在でもなくなった、という部分です。具体的には…

  • シーケンサーはオーディオとMIDIイベントの入力を時系列に沿ってオーディオグラフに流し込んで結果を取得するだけの存在になり、そのオーディオグラフがどのような実体になっているかは感知しません。
  • オーディオグラフはプラグインインスタンスのノードを扱いますが、その操作はuapmdモジュールのインターフェースでのみ操作し、実装がどうなっているかは関知しません。
  • プラグイン インスタンスにはFunction Blockを割り当てることができ、複数のFunction Blockが1つの「UMPデバイス」を構築できますが、その編成はシーケンサーのトラック(のオーディオグラフ)と連動する必要はなく、それらのFunction Blockのコンフィグレーション(デバイス名など)をどうするかはuapmdのアプリケーション(uapmd-appなど)に委ねられています。

v0.2と来月の見通し

先月末にはシーケンサー機能を実装してAndroidサポートも追加して…みたいなことを考えていて、実際に年初にはv0.2 milestonesとしても列挙していたのですが、今月はだいぶ方向性の違う作業やシーケンサーのための下回り作業に追われていたこともあって、このままv0.3 milestones扱いにしてv0.2はそろそろ公開してもいいかな…と思っています。まあこの辺のナンバリングは単なるフィーリングですが。

2月も引き続き個人開発にフルコミットできそうな感じなので、シーケンサーを安心して作り込める体制をコード上で構築していくことになると思います。あまりDAWのフルエンジンを作り込むような作業ではなく、あくまでDAWのオーディオエンジンで使えるプラグインホスティング機能を実装するものとして進めていきたいところです。