ブログ
Cover Image - クロスプラットフォームの音と映像のインタラクティブ App:ælbum の構築
プロダクト

クロスプラットフォームの音と映像のインタラクティブ App:ælbum の構築

きっかけ

2024 年 7 月、私は一種の「AI 疲労症候群」に陥りました。AI プロダクトマネージャーでありながら、AI に関する全てに興味が持てなくなりました。おそらく Generative AI の非常に興奮した業界サイクルを経験し、PTSD になったのでしょう。そこで、AI の含有量が 0% のことをすることにしました。

オーディオ

私は、ずっとやりたかったけれど時間がなくてできなかった分野を再び取り上げることにしました:オーディオです。

大学時代、録音エンジニアリングを学ぶ多くの学生たちの影響を受け、一冊の奇書を読みました:『Designing Sound』。初めて読んだときから衝撃を受け、音の合成のシステムモデリングに魅了されました。例えば、昆虫の飛行音をどのように合成するか、翅が空気を乱す音、翅と空気の摩擦音、筋肉と翅の接続部分の音、筋肉の振動音などに分けられます。開くたびに学びたいと思いましたが、Pure Data のプリンターメニューのようなインターフェース(先生:これは無形文化遺産ですね)と、傍らで爆発している AI モデルの勾配を見て、諦めていました。

アルゴリズムの原理が難しいだけでなく、オーディオアルゴリズムの実装も非常に困難です。音声処理のアルゴリズムは一般的に画像より複雑で、FFT なしでは始まりません。音声を合成・処理できる app を作りたいと思い、コミュニティを探し回りましたが、Juce のような商用ソフトウェアしか開発ニーズを満たせず、しかもそれは C++ で、開発体験は言うまでもありません。

起動困難症が発症している時、私は非常に興味深い Rust ライブラリを発見しました:FunDSP です。

FunDSP は Rust のオーディオ DSP ライブラリで、このライブラリは非常に興味深い設計を採用しています。オーディオ信号流の処理を Rust(trait を通じて実装)の演算子オーバーロードと組み合わせ、オーディオ信号に + - * / を適用できます。これらは全て、Rust の非常に興味深いプログラミングパラダイムによって実現されています。

FunDSP operators
FunDSP operators

そこで私は FunDSP のサンプルを基に、シンプルな Synthesizer を作りました。複数の notes が ADSR エンベロープによって制御され、sine wave などの異なる波形を駆動し、フィルター、ハーモナイザー、リバーブを通して最終的な音声を出力します。

このような発声機構を使って、YouTube で広く知られている poly rhythm をプログラムで再現しました。

Poly rhythm
Poly rhythm

レンダリング

オーディオ開発の難しさを解決した後、残る大きな課題はレンダリングでした。

最初、私は Godot をレンダリングエンジンとして使用しようと考えました。初めて触れたとき、非常に興奮しました(おそらく Unity と Unreal に苦労していたからでしょう):こんなに軽量で、独自のスクリプト言語 + IDE の開発体験が素晴らしく、教材を作る人も増えてきており(例えば私の大好きな Unity ブロガー Brackeys)、コミュニティが維持している Rust 拡張も非常に使いやすく、あっという間に Rust の音声アルゴリズムを統合し、全プラットフォームで使用可能になりました。

しかし、完成してから問題が一気に浮上しました。

当時の Godot は macOS と iOS で Metal の直接コンパイルをサポートしておらず、全て Vulkan を経由する必要がありました。これはパフォーマンスの問題だけでなく、機能のサポートも不完全でした。例えば、視覚効果の開発で最もよく使用される compute shader(GPGPU)は、Godot ではほぼ使用できない状態で、Unity(Visual Effect Graph)や Unreal(Niagara)のような GPU パーティクルシステムを内蔵し、エンジニアリングコードと相互運用できるものとは全く異なるレベルでした。

やはり Unity の方が良かったです。Unity エンジン、私が間違っていました。

第二の問題は、ここで「ビジュアルアルバム」のコンセプトが生まれ始めたことです。これらの音と映像のインタラクティブなシーンを、インタラクティブな「アルバム」としてパッケージ化したいと考えました。モバイルでは、音楽プレーヤーのような app にすべきだと考え、Godot で iOS app の UI を実装しようとしました。クールな Web3D サイトのようにできると思いましたが、現実は厳しく、120Hz でリアルタイムレンダリングのソフトウェアメインメニューを実行し、各シーンを Viewport でレンダリングすると、最新の iPhone でも対応できませんでした。

All in Rust!

最終的に、私は Godot を放棄し、非常に低レベルな実装アプローチを選択し、アーキテクチャを再設計してプロジェクトを完全に再構築しました:

ælbum アーキテクチャ図
ælbum アーキテクチャ図
  1. レンダリング層には wgpu を使用し、WGSL で shader を書き、compute shader や indirect draw など、私が望むほぼ全ての描画機能をサポートしました。ハードウェアレイトレーシングのような API だけが含まれていません。

  2. iOS app は SwiftUI でネイティブ開発し、シンプルで、パフォーマンスが良く、サイズが小さいだけでなく、ウィジェット、バックグラウンド音楽再生、触覚フィードバックなど、apple だけができる機能も使用できます。

  3. Windows / macOS 版は Electron + react.js をランチャーとして使用し、Rust で書かれた player プログラムを起動して再生を行います。この方法には後で説明する隠れたメリットもあります。

wgpu

最下層に wgpu を選択した理由は多くあります。

wgpu はクロスプラットフォームのレンダリングライブラリで、開発言語として Rust を、シェーディング言語として WGSL を使用し、最終的に iOS(Metal)、Android(Vulkan)、macOS(Metal)、Windows(Vulkan / DirectX)の描画プログラムにコンパイルして実行できます。そのクロスプラットフォーム特性に魅力を感じ、Rust の優れたツールチェーンと組み合わせることで、wgpu は独立開発に非常に適したものとなりました。

2 年前、私は wgpu を使って学部の卒業研究を完成させました:大規模 3D グラフ可視化アプリケーション GraphPU です。グラフ可視化におけるスプリング電子力分布シミュレーションを加速するために 18 個の GPGPU カーネルを手書きし、macOS でも compute shader 内での再帰を可能にする(並列版の barnus-hut アルゴリズムを実装するため)ために、wgpu のシェーダーコンパイラ naga を改造までしました。以前 IEG でインターンをしていた時も、intern leader から RenderDoc を使って GPU プログラムをデバッグする方法を学び、API はまだ安定していないものの、非常に未来志向のこのレンダリングライブラリを使いこなす自信がさらに深まりました(WebGPU は将来さらに普及する可能性があり、wgpu ベースのゲームエンジン bevy も着実に改善されています)。

GraphPU スクリーンショット
GraphPU スクリーンショット
GraphPU スクリーンショット
GraphPU スクリーンショット
GraphPU パイプラインアーキテクチャ図
GraphPU パイプラインアーキテクチャ図

wgpu が私のニーズを満たせるか(Godot ができないことができるか)をテストするため、以前作成したニューメディア作品「パラメトリックライフ」を wgpu で完全に再現しました。これは数万個の相互作用するパーティクルで構成される複雑なシステムで、このようなパーティクルシステムは最適化せずに計算すると複雑度は O(N2) となり、VFX Graph や Niagara でも実現は困難です。しかし、Claude の助けを借りて、ほぼ 1 日でシミュレーション、レンダリングコード、シェーダーコードの移植を完了し、WGSL の構造体サポートが向上したおかげで、コードはより明確で読みやすくなりました。

パラメトリックライフ app スクリーンショット
パラメトリックライフ app スクリーンショット

将来的に wgpu で通常のパーティクルシステムの開発ニーズを満たすため、Unity(コンパイル済みの Shader と Shader Graph、Visual Effect Graph のライブラリファイルを参照)とオープンソースエンジンを参考に、それらの GPU パーティクルシステムを再現しました。

iOS App

wgpu のレンダリングプログラムができた後、ælbum はレンダリング画面を iOS ネイティブ UI で表示する問題をどのように解決したのでしょうか?ここで Jinlei Li さんの素晴らしい仕事に触れないわけにはいきません。彼は わかりやすいドキュメント とオープンソースのサンプルプログラムを提供し、iOS app に wgpu を組み込む方法を説明しました。彼が作った app「字習」も App Store でダウンロードして体験できます。

何年も前、私は同僚と Unity を iOS に組み込むプロジェクトを完成させたことがありました。それと比べると、Unity は非常に重く、起動時に強制的な splash screen があり、画面の resize に非常に不親切で、レンダリングエンジンとしてだけ使うのは非常に非効率でした。wgpu はこれら全ての問題を完璧に解決し、基本機能の wgpu を iOS にパッケージングしても 10MB 程度のスペースしか増えず、起動も非常に速く、app の起動アニメーションが完了する前にレンダリングを開始できます。また、iOS の CADisplayLink の制御がより直接的になったことで、レンダリングの開始・停止、フレームレート、解像度をより適切に調整でき、HDR やオフスクリーンレンダリングのサポートも非常に優れています。

そこで私は、操作感が Apple Music に似たプレーヤー app を作成しました。アルバムは一枚一枚ネイティブ UI で表示され、下部のプレーヤーは折りたたまれているときは各アルバムのビジュアルコンテンツをガウスぼかしでレンダリングし、展開時はフルスクリーンでブラウジングとインタラクションを体験できます。

ælbum スクリーンショット
ælbum スクリーンショット

外層が iOS native なので、アップルの API を何でも呼び出すことができ、メッセージチャネルを通じてレンダリングプログラムと通信できます。例えば、パラメトリックライフのアルバムでは、パラメータの UI をレンダリングプログラムで描画する必要がなくなり、SwiftUI で一つ一つのノブを描画できます。ノブを押したり、離したり、値を変更したりするたびに、非常に微妙な haptic feedback があり、体験は非常に興味深いものになっています。さらに、マイク、ジャイロスコープ、Apple Pencil に関連するインタラクションも作成できます。

ここまでで、私たちはようやく当初の目標を実現できました:ビジュアルアルバムプレーヤーを作成することです。最後のステップは、アップルの background mode をサポートし、アルバムカバーをコントロールセンターに表示できるようにすることでした。そのために、私たちは特別にアップルにメールを送り、バックグラウンドでリアルタイムに音声生成を行うために苦心している私たちに権限を開放してほしい、バックグラウンドを kill しないでほしいとお願いしました。

デスクトップ App

iOS app の開発と並行して、私たちは Electron + Rsbuild + React.js を使用してデスクトップ版 app もパッケージングしました。

デスクトップ版 app での最大の驚きは、Rust の windows / cocoa API を使用して、Windows と macOS を成功裏に hack し、レンダリングプログラムを両プラットフォームで動的デスクトップ壁紙として設定できるようにしたことです。macOS はデスクトップ壁紙用のウィンドウレベル key を提供していますが、Windows では謎のコード 0x052C を送信する必要がありました(本当に不思議で、ドキュメントも全く見つかりません)。

デスクトップ壁紙として設定すると、コードを書きながら、動的壁紙とオーディオがもたらす不思議な雰囲気を楽しむことができます(絵に描いた餅ですが、まだ実装していません、ハハ)。

リリースと公開、今後の計画

ælbum at Demo Inn Shanghai @thepoint
ælbum at Demo Inn Shanghai @thepoint

この app をリリースするために、私は新しい決済アーキテクチャを攻略しました。Pingpong のような決済ツールを使用せずに、オフショア運営の米国企業と米国銀行の法人口座で決済を受け取る方法です。参考までにアーキテクチャ設定図を添付します!

グローバル展開アーキテクチャ図
グローバル展開アーキテクチャ図

設定が完了し、ælbum も無事に App Store と Steam でリリースされました。ぜひダウンロードしてください!

今後、私たちは不定期に ælbum このプロジェクトを更新していく予定です。より目を引くレンダリング画面を追加し、睡眠補助、ストレス解消、集中力維持、創造力喚起という 4 つのテーマの下でより面白いビジュアルアルバムを設計したいと考えています。ぜひフォローしてください!

最後に、この道のりで私と一緒に全てのプロジェクトを書いてくれたパートナー CPunisher に心から感謝します。彼との協力なしでは、私一人で Rust in iOS、Rust in Godot、Rust + Electron を完成させることはできませんでした(この天才は Rust ベースのフロントエンドコンパイラー SWC の core team member であり、フロントエンドツールチェーンのオープンソース貢献者でもあります)。今後も一緒により多くの面白いプロジェクトを作っていけることを楽しみにしています!

同様に、多くのオーディオクリエイティブについて交流した @byh、@qyy、@jxy に感謝します!

完了!

シェア