月初から日本が天変地異に襲われる中ずっと台北にいたのですが、珍しく雨が少なく(?)、今年は日本より暑いなあ…と思いながら過ごしました。
design and implementation of a next-gen. DAW sequencer engine for desktop, mobile and web @ COSCUP 2026
先月からずっと苦心して準備していた次世代DAW開発の話は、何とか30分に収めて登壇してきました。スライドはこちらで公開しています。
何と動画ももう公開されています。
https://www.youtube.com/watch?v=A3txy6caQ2U
このセッション自体はもちろんUAPMDの開発に関する話なのですが、次世代DAWといっても、当然ながら数多のDAWがひしめく業界でUAPMDが一番優れている、みたいな話になるわけではなく、次世代のDAWと呼ぶに相応しい特性に限定した話が中心です。4月にMusic Tech Meetup Tokyoで話した「最先端プラグインホスト」の話と重複する部分が多いです。
モバイルやWebとのアーキテクチャの違いを吸収する話が一つの軸でしたが、今回は30分あったので、もう少し具体的に、リアルタイム制約の下にあるaudio graph APIの設計と実装、拡張性と柔軟性を維持しつつ機能を拡張していく設計について、具体的に先月書いたtrack freezingの機能を引きながら解説し、さらに汎用性のある楽曲データフォーマットについての考えを開陳してきました。これは実のところCOSCUP 2025でも言及したのですが、この1年でだいぶrefinementがあって、だいぶSMF2 Containerに肯定的な内容になったと思います(まあMIDI 2.0で表現できないことはあるのですが)。
AAP plugin and host ports
今月はJUCEアプリケーションのportsが珍しく3つも生えました。
aap-juce-altar
AltarはいわゆるNAMプラグインのひとつで、本家のiPlug2ベースのプラグインではなくJUCEのプラグインなので移植可能だったものです。Android Audio Devs DiscordのAAPコミュニティで「NAMのプラグインは無い?」という話があったので、試しに…と思ってサクッと移植しました。
https://github.com/atsushieno/aap-juce-altar
同種のプラグインとしてはaap-juce-byodがあるのですが、今でも開発が継続しているプラグインというわけでもなく、またNAMのカスタムモデルを使いたいという需要はありそうなので、有意義なものといえるでしょう。
aap-juce-vmpc
VMPC XL2000はAKAI MPC 2000のクローンエミュレーターですが、最近GitHubである種のプラグインのコードを検索していたときにたまたまAndroidサポートの進捗が出ていることに気がついて、それならAAPで簡単に動かせそうだな?となって移植しました。
https://github.com/atsushieno/aap-juce-vmpc
実のところデスクトップでちゃんと音が出るに至らず、Androidでしか音が出るのを確認できていないのですが、期待通りに動いてはいそうなので、これで良しとしています。
aap-juce-magda
magda-coreは最近流行りのAI-coded DAWっぽいもので、Tracktion Engineに基づいています。Tracktion EngineのDAWエンジンはどれくらいのパフォーマンスが出るんだろう…と思って、試しに移植してみました。めちゃくちゃ機能豊富なのでだいぶ大変でした(といってもAI codingによるportなので、たかが知れてはいますが)。
https://github.com/atsushieno/aap-juce-magda/
…が、現状まだ全然音声がノイズだらけで、もう少しコードを見てみないと分からないな…となっています。しばらく躊躇していて、昨日ようやく公開としたのですが、まだ良い状態ではないです。
UAPMD hacks
今月はuapmd v0.5.5とv0.5.6をリリースしましたが、全体的にAPI構成のリファクタリングが為され、さらにuapmd-dataとuapmd-engineを中心に根本的な機能が追加されています。
https://github.com/atsushieno/uapmd/releases/tag/0.5.6
大きな変更については以降で詳しく書いていきますが、その他もいくつかマイナーな機能追加がありました:
- MIDI録音機能が追加されました
- ミュート、ソロ、クリップ単位の有効・無効設定が可能になりました
uapmd module refactoring
今月は巨大化して少なからず混乱を招きやすくなったライブラリの、根本的な再編成に着手しました(ライブラリレベルで利用しているユーザーがいたら簡単にはできないやつ)。ここでライブラリの詳細に踏み込んで解説したことはそんなに無かったと思うので、雰囲気だけ伝われば良いという感じで書きますが、
- ライブラリの再編成
remidy-toolingはuapmd-plugin-hostingという名前になり、uapmdライブラリに含まれていたプラグインホスティングの抽象化APIはここに含まれることになりました。uapmdライブラリにはMIDI 2.0マッピングの部分しか残されていなかったので、uapmd-midi-serviceという名前になりました。
- namespaceの再編成:
uapmd-graph,uapmd-midi-service,uapmd-file,uapmd-plugin-hostingそしてuapmd-app-modelとuapmd-appではそれぞれ独自のnamespaceを使うことになりました。
といった感じです。今回uapmd-dataとuapmd-engineには、namespace変更のインパクトが大きすぎるのと今後も大幅に手を加えることになりそう(=namespaceの詳細化によるコストが増大する)ということがあって、手を加えていません。あとremidy-guiも同様ですが、これは元々remidy::guiという名前空間を独自に使っていたためです。
uapmd addin framework (1st. take)
ARAサポートを設計している辺りから考えていたことではあったのですが、UAPMDには外部ライブラリを使用して実現したい機能がたくさんあって、それらの多くはuapmdリポジトリから独立して作り込んだほうが良いものも少なくありません。そういった機能は実行時に動的にロードして利用することになります。
UAPMD本体は割と厳密にMIT/ISCライセンス互換のものだけを使用するようにしているのですが、ARAの他にもApache 2.0ライセンスのライブラリで使いたいものが出てきたりして、モジュールを切り離したビルドの設計を実現したい気持ちがありました。また、先月トラックフリーズ機能を実装する過程で行ったように、いま実装されている機能をいろいろ分解して、より簡単に取り外し・交換が可能になるようにしたいという気持ちが高まってきました。
そういうわけで、今月はUAPMDにアドイン機構を追加しました。ちょっと時系列がアレですがイメージとしてはこんな感じです:
https://bsky.app/profile/atsushieno.bsky.social/post/3mtqhzgf5fk22
アドイン機構は、単なるライブラリの有効・無効の切り離しとはまた別に、動的なモジュールのロードや、機能の有効化・無効化なども含まれるものです。アドイン機構は、JVM言語を前提としたIDEやJavaScriptランタイムを前提としたWebブラウザのような、動的ロードに親和的な環境では設計しやすいのですが、C++とはだいぶ相性が悪い上に、プラットフォームによってはそもそも不可能なので、本来はもう少し抽象化が必要です。とはいえ、とりあえず機能の切り離しと無効化が可能な仕組みを構築しておくだけでも、だいぶコードを整理しやすくなる…という目論見もあって、出来ることを絞って導入することにしました。
アドイン機構は、IDEやWebブラウザではアドイン、拡張、プラグインといった名前で実現している事が多いものですが、今回明らかに「プラグイン」という名称を使うのは有り得ず、また「拡張」は場合によっては各プラグインの拡張機能の仕組み(VST3拡張、LV2拡張)と潜在的に被る可能性があるので、「アドイン」と呼ぶことにしています。
C++アプリだとplugifyを使ったりもできるのですが、現状ではuapmd単体では他言語で操作できるようなAPIの整備には至っていないので(uapmd-kmpの一部がその役割を担っているのみです)、今回は採用を見送っています。
アドイン機構を導入した結果、既存の機能の割と大きな部分を切り離せるようになりました:
- ARAサポート
- ピアノロールエディター
- MIDIイベントリストエディター
一方で、アドイン機構のAPIに沿った機能の切り離しまでは実現したものの、アドインとして独立させてはいないものもあります:
- DAGによるオーディオグラフ実装
オーディオグラフ実装は複数存在しても良いと思っていますが、これを「オフ」にした時にデフォルトのsimple linear graphだけでは今ある機能を問題なくカバーしきれないし、そんなことをする実益がない、といった反論をCodexから受け(!)、それは人間としては想定の範囲内だが…と思いつつ、まあ実益がないのは確かなので、あまり追及せずに残してあります。どちらかというとアドイン機構をブラッシュアップして、要件を切り詰めて、何ならアドイン機構とは別の切り離しの仕組みを導入してから実現するほうが良いのでしょう。Codexにはオーディオグラフ実装のpure virtual interface化だけを実現させました。
アドイン機構とはあまり関係ないところですが、オーディオ処理のコア機能の切り崩しがやりやすくなったので、レイテンシー補償の実装を切り離して、トラックフリーズと同様にイベント処理の一部に再構成しました(トラックフリーズもそれに伴って微調整されています)。
アドイン機構が整備されて、ARAサポートをアドイン化出来た時点で、uapmdリポジトリは「MIT互換ライセンスのコードのみ取り込んでビルドする」ポリシーから「MIT互換でないライセンスのモジュールも取り込むが、必ずopt-inにする」ポリシーに変更しました。これまでuapmd_araリポジトリに隔離されていたコードはuapmdリポジトリに取り込まれ、ARA_SDKはopt-inでこのモジュールが有効になった時にのみCPMAddPackageで取り込まれることになりました。
uapmd undo engine and command interface
UAPMDが実用的なDAWに近づくためには、編集機能にundo/redoが含まれていることが必須です。今月はこれを実現しました。これも大規模な内部処理の変更が必要になったものです。JUCEでいうところのUndoManagerを設計して、それに基づいて、uapmd-appで行われる編集操作を全てundoableにする書き換えを行っています。さらに、uapmd-app-modelで行われていた編集操作の多くを、uapmd-engineやuapmd-dataに移行させました。
どれも大きなトピックで、月報レベルの記事でまとめるようなことではないので、改めて別記事でまとめられればと思いますが、どの辺が設計上の課題だったかを簡単に書くとこんな感じです:
- undo機構の根本的なアプローチの選択: DAWのドキュメントモデルは、テキストエディターなどとは異なり、シンプルな文字列構造のみを対象としているわけではないため、immutableなmementoに全てのドキュメントの状態を保持しておくアプローチには無理があるので、JUCEのUndoableActionと同様の仕組みを、そのままcommandパターンに結びつけた仕組みで実現しています。
- undoable instance lifecycles: ↑の問題をもう少し具体的に書くと、たとえばプラグインのインスタンス生成をundoした場合、そのインスタンスは解放することになりますし、さらにredoした場合、そのインスタンスを再度生成することになります。ここで生成されたインスタンスはエンジンとしては以前のものとは別物になるので、たとえばこの一連の操作から他のundoableな操作を継続していた場合、それらの操作では「同じインスタンスID」を参照することはできません。
- clip identity system: インスタンスの生成・破棄と類似する問題が、トラックやクリップの生成・破棄についても発生します。これらを解決する仕組みとして、addressbookというコンセプトが導入され、
trackIndexやclipIdといった、プロジェクト内での永続化識別子は、addressbookを経由して取得するようになりました。自分ではこの辺の一般的な解決策に詳しくなかったので、Claudeが提案してきたものを採用しています。他にもArdourなどで同じ仕組みが採用されているようです(AI又聞き情報)。 - undoable preset loading: プラグインのプリセットをロードすると、パラメーター変更のイベントが発生しますが、それらはユーザー操作ではないので、パラメーター変更にイベントフックを仕掛けてundoable actionとして登録すると、まともにundo/redo操作が行えなくなってしまいます。オートメーションカーブをペイントツールの類で描画していて、1つ1つのドット単位でundoable actionを登録しないのと同様です。
uapmd-mir addin
uapmdがApache2ライセンスのモジュールをopt-inで取り込めるポリシーになったので、他に取り込みたかったモジュールもこれで取り込んで、アドインを追加することにしました。
まず実現したかったのが、オーディオトラックを分析してテンポ、拍子、コード等をマスタートラック上に補完生成する仕組みです。これは実は当初の目論見ほど単純ではなくて実現したとは言い難いのですが、要素技術は次のライブラリに含まれています:
この種のMIRライブラリはPythonで実現しているものが多く、これらはどちらもPythonライブラリであるlibrosaのC++移植(そのもの/を含むもの)です。オーディオアプリケーションはC++で書かれているものが多いので、それらのC++版をつくろうという動きが出てくるのは自然なことでしょう(それがカジュアルに現実化するようになったのはAIコーディングの時代ならでは、ですが)。
ただ、基本的には短いオーディオチャンクからテンポや拍子を検出するものであり、楽曲全体を渡しても単一の値しか返せないので、チャンクに分割して渡さなければならないのですが、その分割に必要な処理は他の実装が必要になるので、現状これらをオーディオ処理のチャンク単位で実装したuapmd拡張には実用性がありません。libsonare, librosa.cppいずれのライブラリでも同様です(これらは「この」目的で作られたライブラリではありませんし)。オーディオファイルの分析対象も、トラックを分割した状態でその他の情報を何も与えずに推測するのは無理があり(リズムトラックを分析すれば足りるところでピアノのメロディトラックを分析してもほぼノイズにしかならず)、またクリップの分割単位なども事前情報として役に立つはずであり、さまざまな解析アプローチがあり得るでしょう。この辺りは今後の課題です。
もう1つ、分割されたオーディオトラックに対して、ピッチ推測のMIRを適用してUMPクリップを生成するアドインを追加しています。具体的にはこれらのライブラリが実装しています:
- sevagh/pitch-detection: audio to mono MIDI transcription
- spotify/basic-pitch: audio to poly MIDI transcription
pitch-detectionは爆速なのですが、あくまで同時発音数1を前提としており、ヴォーカルパートのようなトラックでしか使い道がありません。basic-pitchはaudio-to-MIDIの実装でよく使われているので知っている人も多いかと思います。
また、demucs.cppを使ったオーディオファイルの分割トラックインポートは、これを機にアドイン化しました。それから、別の音源分離モデルを使った実装も追加しています。これらは分割トラックインポーターのダイアログで実装とモデルファイルを選択して実行するように作られています:
BSRoformerはどうもヴォーカル分離に特化しているのか、多数のトラックに分割しないようなので、やはり用途が限られてくるでしょう。
これら3つの機能と6つの実装は、いずれもMIRに関するものだったので、uapmd-mirというモジュールとしてビルドしています。Apache2ライセンスのlibsonareとbasic-pitchはopt-inでビルドされます。
ステップシーケンサー
uapmd-appでまだまだ十分に使える機能を実現できていないものとして、ピアノロールエディタがあります。もう少し多機能な実装にしたいところです。これとは別にMIDIイベントリストエディタも提供しているのですが、これも現状で十分とは言い難いところがあります。他にもいろいろなシーケンス編集機能を実現したいところです。augene2としてKotlinからC++に移植したMML to MIDI 2.0なコンパイラーの統合も実現したいところです。さすがに別ライブラリとして動的にロードしたいところですが。トラッカーのようなエディターがあってもいいでしょう(これはタイムライン編成のレベルで差し替え可能になる必要があるので難しいところではあります)。
そういったwishlistを抱える中、ステップシーケンサーくらいはデフォルトで実装されていても良いのではないかと思ったので、これは追加することにしました。
https://bsky.app/profile/atsushieno.bsky.social/post/3mu7oqy6uhc2m
ただ、uapmdは多様なクリップ種別を規定しないデータフォーマットを前提としています。UAPMDはこの方面では明確にTracktion EngineやDAWprojectの上を目指しており、「ステップシーケンサークリップ」のような、クリップ形式の独自拡張が可能な楽曲データフォーマットでは、SMF2 Containerの類型としては標準を名乗ることはできません。あくまでSMF2 Clip Fileの範疇で、ステップシーケンサーをエディターとしてサポートしない実装でも問題なく再生できるよう、楽曲にシリアライズする際には、ループを展開しつつ、ロードしたらループ範囲を復元できるようなデータ形式を規定しています(これ自体はUMPのFlex Dataを使えば簡単に実現できます)。
現状、uapmd-appではどのクリップもステップシーケンサーで開けるようになっていますが、これで普通のトラックを編集すると、ステップシーケンサーに含まれないデータは欠損することでしょう。今後のバージョンではステップシーケンサークリップであることをFlex Dataから取得して判別する仕組みを導入するかもしれません。
uapmd-kmp v0.5.6 refresh
9/1からDroidKaigiだし、Kotlinっぽいことをしてみるか…と思って、5月にKotlinConfの直前に作っていたuapmd-kmpに手を加えて、uapmd v0.5.6のバインディングに刷新しました。今回はもう少し時間敵余裕があったので、Compose Multiplatformで作っていたuapmd-appのクローン…を目指して作っていたPoCホスト実装を、根本的に書き直して、きちんとuapmd-appのUIと機能セットに寄せることにしました。前回は「一応機能的にはそれっぽいものが動いている」という以上のものではありませんでしたが、今回はかなり近いものができていると思います。
https://bsky.app/profile/atsushieno.bsky.social/post/3mu65gbhzek2u
Wasm版も正式にビルドの一部になったので、UAPMD本家がWeb版のデモとAPIリファレンスを置いているように、uapmd-kmpのWeb版でも同じことをするようにしました:
https://atsushieno.github.io/uapmd-kmp/
UAPMDはこの3ヶ月でだいぶ実装の再編成が行われ、uapmd-appはImGui依存部分が中心になり、その他のモデル部分はuapmd-app-modelというモジュールに切り離したため、かなり整理されたcinteropバインディングになっています。uapmd-appに近いものが作れた大きな理由のひとつがこれです。
ただあくまで0.5.6ベースの移植なので、最新のアドイン機構やMIR、ステップシーケンサーなどは入っていません。アドイン機構の上でGUIサポートをどう実現するかについては、まだ答えを出していない状況です。たとえばピアノロールやステップシーケンサーはアドインとなったわけですが、任意のアドインを作れるようにしたとき、ImGuiベースの実装をuapmd-app側に実装している現状は、正しい状態とは言い難いところです。
9月の予定
実は9月にはMusic Tech Meetupを開催するつもりで、いくつか登壇の相談をしていたのですが、あまりにも日程調整に問題が生じすぎて、LTメインの会に切り替えるには募集期間が短すぎる感じになったので、会場貸しを快諾いただいたのを陳謝して取り消させていただくという事態になっていました。次回開催の予定は未定です。もしかしたら10月にいけるかもしれませんが、10月以降は自分がそこそこ忙しくなりそうなので期待値半々です。
自分は「みんなが楽しめる」イベントを立てるのは好きですが、「みんなが楽しむために自分が要らぬ苦労を背負う」のは全く好きではないので、今後も負担感が無い範囲でやらせてもらいますし、「参加者も登壇者も運営も対等」というスタンスを守れる範囲でのみ、コミュニティを作っていくつもりです。
まあそれもあって今月はuapmd-kmpの作業に着手する余裕が出来ましたし、来月もゆとりをもってM3 2026秋に向けて動こうと思います(フラグ感)。