こんにちは。今回はComfyUIでAirPlayのサーバー機能を動かし、スマホなどから音声をストリーミング入力できるカスタムノード「ComfyUI-AirPlay-Node」を作成したので紹介します。
(リポジトリへのリンクはこちら)
https://github.com/ma0001/ComfyUI-AirPlay-Node
動機
「この曲のベースラインどうなってるんだろう?ベースだけ抜き出して聞きたいな」って思った時、ちょっと大変ですよね。
今までは、元データをCDなどからリッピングして、Ultimate Vocal Removerに入れるなどの手順を行っていました。
しかし、サブスクでほとんどの音楽を聞くようになっている今どきの環境に合わせて、もう少しカジュアルに聞きたいな、さらに分離モデルもComfyUI上で色々試したいなと思うようになりました。
「それなら、ComfyUIにAirPlayのサーバ機能を乗っければ色々できて良いのでは?」と思って作成したのが、今回の ComfyUI-AirPlay-Node です。
概要
ComfyUI上でAirPlay経由のストリーミングオーディオデータを受信・処理・モニタリングするためのカスタムノードです。
iPhoneやMacなどからAirPlay対応デバイスとして認識され、受信したオーディオ波形データや再生中の楽曲のメタデータ(タイトル、アーティスト等)をComfyUIのワークフロー内で動的に扱うことができます。
主な機能(ノード)
-
AirPlayAudioReceiver
ローカルネットワーク上でAirPlayサーバー(背後でshairport-syncを利用)を起動し、オーディオストリームを受信します。受信した音声を一定期間(duration秒)ごとにテンソルデータとしてComfyUIパイプラインに出力します。 -
AudioMonitor
得られたオーディオデータをバックグラウンドでシームレスにリアルタイム再生・モニタリングするノードです。
インストール方法
ComfyUIの custom_nodes ディレクトリでリポジトリをクローンします。
cd ComfyUI/custom_nodes
git clone https://github.com/ma0001/ComfyUI-AirPlay-Node
AirPlay通信を受信・デコードするためにバックグラウンドで shairport-sync を使用するため、インストールが必要です(macOSの場合はHomebrewなどでインストール)。
brew install shairport-sync
また、Pythonライブラリとして sounddevice 等も必要になります(requirements.txt を参照の上 pip install してください)。
ComfyUI Managerでaudio-separation-nodes-comfyuiを入れていれば入っているはず
使用方法
リポジトリにサンプルとして workflows/karaoke.json を同梱しています。
ComfyUIのテンプレートメニューからkaraokeを検索してロードすることで、AirPlayで受信した音声を音源分離ノード(audio-separation-nodes-comfyui など別途インストール必要)に流し込み、ボーカル以外のパートを抽出するような「カラオケワークフロー」をすぐに試すことができます。
このサンプルワークフローを動作させるには、以下の拡張機能もあわせてインストール(ComfyUI Manager 等から)してください。
連続再生にはワークフローを連続して実行する必要があるため、実行する(即時)を選択して実行してください。
実行するとAirPlayメニューにComfyUI Node(名称は指定可能、名称が違うなら複数のノードの起動も可能)が表示されると思いますので、Musicアプリなどの出力をこれに指定して音楽再生してください
こんな感じになると思います
歌詞解析はまだまだですね😆
あと当たり前ですが、再生してから音声が鳴るまで(Duration+分離処理時間+initial delay)分の時間が必要です。
ソフト作成方法
実は今回、Antigravityを使って作成したのですが、これ凄すぎます!!!
私はComfyUIはチュートリアルを実施した程度の経験しかなく、AirPlayの知識もほぼない状態からスタートしました。
例えば「Titleやアーティスト表示をAirPlayAudioReceiverに入れてほしい」と呟いただけで、AIが自動的に:
-
shairport-syncを--helpで起動してメタデータ出力のオプションを調べる。 - 次にWebで
"shairport-sync metadata pipe format python"を検索して使い方を調べる。 - 実装プランを提示し、コードを書いてくれる。
というそこまでやってくれるの的な動きをしてくれました。
さらに、タイトルが表示されないバグが出た際も、いろんな所にデバッグコードを入れてバグ解析までしてくれました。その間、私はAIの言われるがままComfyUIの起動・終了を繰り返しているだけでした。
もちろん、AIが出したコードをそのまま使うことはあまりなく、適度なリファクタリングや「動作確認→修正依頼」の繰り返しを行っていますが、AIエージェントがこの先どこまで行くのか本当に恐ろしくもあり、楽しみでもあります。
実装でちょっと工夫(苦労)したところ
処理のパフォーマンスや使い勝手を向上させるため、いくつか独自の工夫を入れています。
-
再生ギャップの防止: チャンク(分割データ)毎に処理時間のばらつきがあるので、そのまま再生するとストリームの途切れ(再生ギャップ)が出てしまいます。これを防止するため、AudioMonitor側に
initial_delayを入れて再生開始を遅らせ、バラツキを吸収するようにしました。 -
無限待ちの防止: AirPlay入力がない状態でも永遠に待ち続けるのを止めるため、入力が
duration + 1分(秒)ない場合はタイムアウトして終了するようにしました。 - 再生停止時の即時終了: クライアント側から再生ストップの変化があった際も、待ち続けずに即座に終了するようにしています。
-
チャンクサイズの一定化: 最後のチャンク以外(特に先頭)は、全てぴったり
duration期間になるようにしています。次のチャンクが来るのにduration分の時間がかかるため、サイズがバラバラだと再生ギャップが生じてしまうためです。
実際に動作させて気づいたAirPlayの動作(クロックのズレ)
音声データのサンプリング周波数は内部で 44.1kHz として処理していますが、実際にAirPlayで送信されてくるデータは、通信ロスや送信側の制御などによりデータ抜けが生じています。
実際にAirPlayにおける音声データの受信レートを計測してみたところ、 Nominal FPS: 44096.51 となっており、44.1kHzに対して約0.1%少ないことが分かりました。
そのため、指定したduration分のデータを蓄積するまでに、実時間として微妙に長くかかってしまいます。この差分が長時間蓄積していくことで、AudioMonitorノードが持つ initial_delay 分のバッファ余裕が徐々に削られ、最終的に必ず再生ギャップ(音途切れ)が発生するという罠がありました。(今回はあくまでネタノードなので、受信データを実際の受信時間に補正するなどの対応までは行っていません)
制限事項
元々ストリーム処理を考慮していないComfyUIや各種ノードに、一定期間(duration)でぶった斬ったデータを連続で処理させているため、色々と無理があります。あくまでもネタとして楽しんでください。
- AudioSeparationなどのノードを使用した場合、データの繋がり部分での処理に違和感(ノイズなど)が出ることがあります。
- AirPlayのサンプリング周波数は 44.1kHz 16Bit stereo 固定としており、受信データの抜けなどの高度な補間処理は行っていません。基本的にduration分のデータが貯まるまで待ちます。
動作環境
以下の構成で動作確認しています
PC : mac studio M4
os : tahoe 26.3
ComfyUI : バージョン0.8.6
終わりに
AIの力を借りることで、未知の領域(AirPlayの仕様やComfyUIのカスタムノード作成)であっても、アイデアをあっという間に形にすることができました。
もしComfyUIでスマホの音声をリアルタイムにいじってみたい方がいれば、ぜひ遊んでみてください!
