前回は、Spotifyを使って自分専用のAIラジオ局を作りたいと思ったきっかけと、「プレイリストではなくラジオ局を作る」という考えにたどり着くまでを書きました。今回は、そのアイデアを実際にMacアプリとして形にしていく中で、どのように「ラジオ番組を作っている感じ」をアプリの中に落とし込んでいったかを書いていきます。

Internal Link ローカルAIラジオを作る #1|Spotifyを自分専用のラジオ局にできないか考え始めた話 Spotify APIとローカルAI、TTSを組み合わせて、自分専用のAIラジオ局をMacで作る構想をまとめました。プレイリストではなく、偶然の出会いがあるラジオ体験を目指した開発ログ第1回です。 Desktop Notes MacでローカルAIラジオを作る構想を表したアイキャッチ画像

ただ曲が流れるだけでは、まだラジオに見えない

最初に考えていたLocal AI Radioは、Spotify APIで曲を再生し、AIが曲間にDJトークを挟むというシンプルなものでした。しかし実際にUIを作り始めると、ただ曲が流れて、ただAIの文章が表示されるだけでは、まだ音楽プレイヤーの延長に見えてしまいます。ラジオっぽさを出すには、曲が流れる前から「今日の番組が作られている」という雰囲気が必要だと思いました。

そこで、番組を生成している待ち時間そのものを、アプリ体験の一部として見せることにしました。普通なら、AIが選曲している時間や台本を作っている時間は、単なるローディング画面になりがちです。しかしローカルAIラジオでは、その待ち時間を「本日の編成会議」や「オープニング台本作成」「DJトーク原稿」「最終チェック完了」といった番組制作のステップとして表示するようにしました。

Local AI Radioで本日の編成会議を進行している番組生成中のステップ表示画面
番組生成中のステップ表示。待ち時間を、放送前の制作過程として見せるようにしました。

この表示にしたことで、ただ待たされている感じがかなり減りました。アプリの中で、裏方スタッフが今日の番組を作っているように見えるからです。AIが曲を選んでいる時間も、DJの台本を考えている時間も、単なる処理ではなく「放送前の準備」に見える。これは、Local AI Radioの世界観にかなり合っていると感じました。

特に気に入っているのは、「今日の番組を生成」というボタンを押したあとに、いきなり全曲が完成するのではなく、番組が少しずつ組み上がっていくように見えるところです。最初に本日のテーマを決め、次にオープニング台本を作り、曲間トークの原稿を生成し、最後に全体をチェックする。実際のラジオ番組制作とはもちろん違いますが、アプリの中に小さな番組制作チームがいるような感覚が出てきました。

待ち時間を見せ方に変える

  • 本日の編成会議として、選曲中の状態を見せる
  • オープニング台本やDJトーク原稿を、制作ステップとして扱う
  • AI処理の失敗や待ち時間も、ラジオ局の裏側として自然に見せる

この「番組制作中」の見せ方は、AIアプリではかなり大事だと思っています。AI処理はどうしても数秒から数十秒の待ち時間が発生します。特に音楽情報を調べたり、曲順を組んだり、台本を書かせたり、TTSで音声化したりする場合、すべてが一瞬で終わるわけではありません。だからこそ、その時間を無理に隠すのではなく、「今、何を作っているのか」が分かるようにした方が、使っていて楽しいアプリになります。

DJトークを画面にも残す

次に考えたのが、再生中のDJトークの見せ方です。Local AI Radioでは、曲と曲の間にAI DJが喋ります。ただ、音声として流れるだけだと、聞き逃したときに内容が分からなくなりますし、仕事中に小さめの音量で流していると、何を言っていたのか分からないこともあります。そこで、DJトークを画面上にも吹き出しのような形で表示することにしました。

再生中の曲情報の下にAI DJのトークと音楽メディアの情報源を表示しているLocal AI Radioの画面
再生中の画面。曲情報の下にDJ TALKを出し、聞き逃した内容も目で追えるようにしています。

この表示はかなり分かりやすくなりました。たとえば、今流れている曲の下に「DJ TALK」として、AI DJが話した内容がそのまま表示されます。音として聞くだけでなく、目でも確認できるので、作業中に少し見ただけでも「今の曲はこういう紹介だったのか」と分かります。

ラジオではDJの言葉は流れて消えていきますが、アプリの場合は画面があるので、少しだけ文字として残しておくことができます。ただし、テキストを前面に出しすぎると音楽アプリではなく読み物アプリのようになってしまうので、あくまで曲情報の補助として、短い吹き出しで表示するくらいがちょうど良いと感じています。

情報ソースを曲紹介の近くに置く

DJトークの下には、情報ソースを表示できるようにもしました。AIがアーティストや曲について何かを紹介するとき、その情報がどこから来たものなのか分かるようにしておきたかったからです。ソースとなる音楽メディアや記事がある場合は、DJトークの下にリンク付きで表示し、クリックするとブラウザでそのページを開けるようにしています。

Local AI Radioの設定画面でEvidence Packの検索プロバイダーや音楽メディアDBを指定している画面
リサーチ用のEvidence Pack設定。検索プロバイダーやローカル音楽メディアDBを分けて扱えるようにしています。

これは、ハルシネーション対策としても重要ですが、それ以上に「気になったアーティストをすぐ調べられる」導線として良いと思いました。ラジオを聴いていて、知らない曲が流れたときに「この人たち誰だろう」と思うことがあります。普通のラジオなら、あとで曲名を調べるしかありませんが、Local AI Radioでは、曲紹介のすぐ下に情報ソースがあれば、そこからアーティストやアルバムの情報を追いかけられます。

この仕組みは、ただAIが勝手に語るのではなく、AIが「調べた情報を元に紹介している」ことを見せる意味もあります。もちろん、すべての曲に十分な情報ソースが見つかるわけではありません。特に新しいアーティストやマイナーな曲では、情報が少ないこともあります。その場合は無理に具体的な経歴を語らせず、曲の雰囲気や番組内での位置づけを話すようにします。

ここで大事になるのが、前回から考えていたEvidence Packの考え方です。Spotifyから取得できる曲名、アーティスト名、アルバム名、リリース日などは基本情報として扱い、アーティストの背景やニュース性のある情報は、Web検索や音楽メディアから取れたものだけを使う。AIには、その情報をもとにDJらしい言葉へ変換させますが、根拠のない出身地や経歴を作らせないようにします。

この仕組みをUIに反映したのが、メディアソース表示です。AIが何を根拠に話したのかを完全に厳密に表示するところまではまだ先の話ですが、少なくとも「この曲について参照できる情報源がある」という状態を見せることで、アプリ全体の信頼感は上がります。さらに、ユーザーにとっては新しい音楽を掘る入口にもなります。

AIが失敗してもラジオを止めない

一方で、AI処理は必ず成功するとは限りません。OpenRouterのクラウドモデルが一時的に使えなかったり、ローカルAIが応答しなかったり、検索結果がうまく取れなかったり、TTSが失敗したりすることもあります。ラジオアプリとしては、こういうときに無音になったり、曲間で止まったりするのは避けたいところです。

そこで、フォールバックの仕組みも入れることにしました。DJトークの生成に失敗した場合でも、あらかじめ用意しておいた短い台詞を使って、曲間に一言挟んでから次の曲へ進むようにします。たとえば、「ここから少し空気を変えていきましょう」「次の曲です」「今日の流れに合いそうな一曲を続けます」といった汎用的な台詞です。

このフォールバックは地味ですが、かなり重要です。AIが失敗した瞬間にアプリ全体が止まるのではなく、ラジオとしての流れを保ったまま次に進める。完璧なDJトークが生成できなくても、曲間に短い一言が入るだけで、ただの連続再生にはなりません。これは、ラジオ体験を守るための保険のようなものです。

AIプロバイダーを役割ごとに分ける

AI設定についても、かなり自由度を持たせました。Local AI Radioでは、ひとつのAIモデルにすべてを任せるのではなく、役割ごとにローカルモデルやクラウドモデルを割り当てられるようにしています。選曲を担当するMusic Director、台本を書くScript Writer、事実確認をするFact Checker、情報整理をするResearch Summaryなど、役割を分けて、それぞれに別のAIプロバイダーを指定できるようにしました。

LM Studio、Mock、OpenRouterなどのAIプロバイダーを追加編集できるLocal AI Radioの設定画面
AIプロバイダーの登録画面。LM Studio、OpenRouter、Mockなどを差し替え前提で登録できます。

これは、以前から作っているMem BuddyのAIプロバイダー設計に近い考え方です。ローカルAIだけで完結させることもできるし、OpenRouter経由でGrokやGeminiを使うこともできる。軽い処理はローカルモデルに任せ、DJ台本のように表現力が欲しい部分だけクラウドモデルを使うこともできます。

この設計にしておくと、かなり運用しやすくなります。たとえば、Macの発熱が気になるときは選曲や事実確認をクラウド側に寄せることができますし、逆に通信を減らしたいときはローカルモデル中心にすることもできます。クラウドAIは価格が変わったり、モデル名が変わったり、突然使えなくなったりする可能性があるので、最初から差し替え前提にしておくのは大事だと感じています。

TTSもステーションの個性になる

TTSについても、同じように差し替えられるようにしました。初期実装ではAivisSpeech Engineを中心に使いますが、将来的にはIrodori-TTS Serverも使えるようにし、さらにmacOS標準のsayコマンドも簡易フォールバックとして使えるようにしています。AivisSpeech Engineはすでに別アプリで使っているので扱いやすく、日本語のDJ音声として安定しています。一方で、Irodori-TTSはDJ専用ボイスを作る方向で面白そうです。

AIロール割り当てとAivisSpeech Engine、Irodori-TTS Server、macOS Speechを設定できるLocal AI Radioの画面
TTS Provider設定。AivisSpeech Engine、Irodori-TTS Server、macOS Speechを用途に合わせて切り替える想定です。

音声生成も、番組体験にかなり影響します。同じ台本でも、声が違うだけでステーションの印象は大きく変わります。Tokyo Hit Radarなら明るい女性DJ、Midnight Jazz Loungeなら落ち着いた深夜番組風の声、Old School HipHop FMなら少し渋めの男性DJ、Ambient系ならほとんど喋らないナレーションのような声。こういう違いが出せると、ステーションが単なるカテゴリではなく、本当に別のラジオ局のように感じられます。

さらに、アプリ内ではAIプロバイダーを追加・編集できるようにもしました。OpenRouterのモデル、LM Studioのローカルモデル、Mockのデバッグ用プロバイダーなどを登録でき、接続テストやコピー、削除もできるようにしています。これにより、開発中にモデルを試し替えたり、後から新しいクラウドモデルを追加したりしやすくなりました。

小さなラジオ局が動いているように見せる

このあたりまで作ってみて、Local AI Radioは単なるSpotify操作アプリではなく、かなり「番組制作アプリ」に近づいてきたと感じました。画面上では曲が流れているだけでなく、その裏側で今日の編成会議があり、オープニング台本が作られ、DJトークが生成され、情報ソースが紐づけられ、TTSで音声化されていく。ユーザーはただ音楽を聴いているだけですが、アプリの中では小さなラジオ局が動いているように見えます。

もちろん、まだ課題はたくさんあります。曲順の精度、DJトークの自然さ、情報ソースの信頼性、TTS音声のキャラクター性、Spotify APIとの同期、スマホで聴く場合の扱いなど、詰めるべき部分は多いです。それでも、初期型としては「ラジオっぽい」と感じられる要素が少しずつ揃ってきました。

特に大きかったのは、待ち時間をただのローディングにしないこと、DJトークを音声だけでなく吹き出しとして見せること、情報ソースへすぐ飛べるようにすること、AIが失敗してもフォールバック台詞で番組を止めないこと、そしてAIやTTSを役割ごとに差し替えられる設計にしたことです。このあたりが入ることで、Local AI Radioは単なる実験アプリではなく、毎日使える音楽アプリに少し近づいたように思います。

第2回では、Local AI Radioの初期型を作る中で、番組制作っぽさをどうUIに出したか、AIプロバイダーやTTSをどう分けたか、DJトークと情報ソースをどう見せるかについて書きました。自分の欲しいと思ったアプリがAIの力で自給自足できるのはとても面白く、貴重な体験でした。選曲に偏りがあったり、うまくラジオ局の特徴が出せなかったりと課題もありますが、そこは追々詰めていこうと思います。

ローカルAIラジオの話はこれで終了としますが、AI関連の話は今後も何かしら書いていこうと思います。

よくある質問

Local AI Radioの第2回では何を書いていますか?

SpotifyとAIを使ったローカルAIラジオを、ただの音楽プレイヤーではなく「番組を作っているアプリ」として見せるためのUI、DJトーク、情報ソース表示、AI設定について書いています。

Evidence Packは何のために使うのですか?

AI DJがアーティストや曲について話すときに、Spotifyの基本情報やWeb検索で得た音楽メディアの情報を根拠として扱い、根拠のない経歴や出身地を作らせないために使います。

AIプロバイダーやTTSを分ける理由は何ですか?

選曲、台本生成、事実確認、リサーチ要約、音声合成は負荷や求める表現力が違うため、ローカルAI、OpenRouter、AivisSpeech Engine、Irodori-TTSなどを役割ごとに差し替えられるようにするためです。

AIローカルAISpotify APIAIラジオMacアプリTTSAivisSpeechOpenRouterEvidence PackUI設計