🌈

にじボイスとHonoでサーバーレスライブ配信ラジオを作った話

2024/12/13に公開

この記事はHono Advent Calendar 2024 7日目の記事です。

TL;DR

  • にじボイスAPIの先行テスト許可をもらったので、それを使って「ずっとしゃべり続けるラジオ」を作ろうと思った

  • 実際に開発中にAPIが正式リリースされてしまったけれど、結果的に複数ユーザーで同期再生できるサーバーレスな「Niji Radio」を作成

デモ

https://niji-radio.kiyo-e.com/
※コスト面の都合で予告なく停止する可能性があります。ごめんなさい!

はじめに

先日、にじボイスAPIが正式公開されました。
にじボイスはAIを活用した音声生成プラットフォームで、感情豊かな音声を簡単に生成できるのが特徴です。
テストユーザーとして利用許可をもらったとき、「24時間ずーっとしゃべり続けるWebラジオ」を作ってみたいと思い立ちました。

実装経緯

ライブ配信を考えると、OBSやCloudflare Streamなどの配信サービスを使う手があります。しかし、配信用のPCを常に稼働させておくのは手間だし、できればサーバーレスで全部回したい。
そこで「音声を都度生成して、それを同期再生する仕組み」をCloudflare Workers+Durable Objectsを使って実現することにしました。ユーザー同士が同じ音声をほぼ同時に再生できるよう、WebSocketでタイミングを合わせています。

アーキテクチャ概要

フロントエンド

  • 単純なHTML+JavaScriptで構成し、Audio APIで音声再生をコントロール

  • WebSocketでバックエンドから同期情報(今どのトラックが何秒目かなど)を受け取って再生位置を合わせる

  • プレイリスト情報もリアルタイムに反映

バックエンド(MusicSyncObject)

  • Durable Objectによるプレイリストと再生状態の一元管理

  • プレイリストの追加・置換・クリア機能

  • トラック終了時には次トラックへ自動的に切り替え

  • WebSocketで全クライアントへ同期状態を通知

  • Hibernation APIでセッション管理

技術スタック

  • Runtime : Cloudflare Workers

  • State Management : Durable Objects

  • Framework : Hono(軽量なWebフレームワーク)

主要機能の解説

プレイリスト管理

Niji Radioは、以下のエンドポイントを通じてプレイリストを操作します。

  • POST /api/setPlaylist:新しいトラックをプレイリスト末尾に追加

  • POST /api/uploadTrack:音声ファイルのアップロード。R2に保存し、保存したものをsetPlaylistで追加する

これらのリクエストを受けてDurable Objectが内部状態を更新し、必要なら再生を即時反映します。
再生中にsetPlaylistが行われれば、現在の曲が終わったタイミングで次の曲へと自動的に進みます。

同期メカニズム

同期にはWebSocketを使っています。クライアントが接続すると、以下のような情報がサーバーから送られます。

{
  "type": "sync",
  "elapsedTime": <再生開始からの経過ミリ秒>,
  "trackUrl": "<現在再生中のトラックURL>",
  "duration": <トラックの全長ミリ秒>
}

クライアントは受け取ったelapsedTimeをもとに、Audio要素を指定の再生位置からスタートさせ、他のユーザーと再生タイミングを揃えます。曲が終わるとchangeTrackメッセージが全クライアントに送られ、次の曲へと自動的に切り替わります。

残り時間表示と次曲再生

フロントエンド側では、この同期情報をもとに

  • 今の曲があと何ミリ秒で終わるか

  • 次の曲までどれくらいの時間があるか

といった情報をリアルタイムで表示できます。

まとめ

Niji Radioは、Cloudflare WorkersとDurable Objectsを活用することで、複数ユーザー間での同期音声再生をサーバーレスかつスケーラブルに実現した例です。
グローバルに展開できるWorkers、ステートフルなDurable Objectsを組み合わせることで、低レイテンシでリアルタイムな体験を提供できました。

時間があれば、今後は

  • コメントを音声合成で読み上げて返信する機能
  • 本当の24時間連続稼働
    • 現状はニュースソースなどの問題で10曲のループになっている

などに挑戦してみたいと思います。

ソースコード

https://github.com/kiyo-e/niji-radio

参考

https://github.com/napolab/y-durableobjects

Discussion