Aoedeは、EPUB、PDF、テキストを読み上げながら、現在の単語をハイライトするmacOS・iOS向けの読書アプリです。自分が持っている本をサービスへ送らず、サブスクリプション型の製品で見た読書体験を使いたくて作りました。ライブラリと音声モデルは端末内に置いたままです。
AccelerandoをAoedeで読んでいるところ。EPUBの構造、同期ハイライト、再生コントロールが表示されています。
仕組み
各インポーターは、対応する形式を章、段落、文という共通モデルへ変換します。音声はAppleのシステム音声でもKokoroでも、共通のハイライト用タイムラインを作ります。その境界があるので、リーダー側はどの音声合成器を使ったかを意識せずにタイミングを追えます。
最初のKokoro実装では、FluidAudio経由でCore MLを使いました。ただしM5 Macでは、試したすべての計算経路でクラッシュしました。同じコードが古いApple siliconでは動いたため、モデルではなくランタイム側の問題だと判断しました。新しいハードウェアで動き、トークンごとの時刻も取得できるMLXへ移行しました。この時刻情報により、単語ハイライトの精度も上がりました。一方、MLXには実際のMetalデバイスが必要なので、iOS SimulatorではApple音声へ切り替えます。
日本語では、読み上げと生成するふりがなの両方にOpenJTalkを使います。同じ読みの情報を使うことで、画面の読みと音声が食い違うのを防ぎます。EPUBや青空文庫に作者が付けたルビは優先します。本気《マジ》のような意図的な読み方は、解析器から推測できないためです。FuriganaQAというコーパス用ツールで、Aoedeの結果と公開作品のルビを比較し、生成側の不具合と作者にしか分からない読みを分けています。
現在のリーダーは、段落ごとに一つのコレクションビューセルを使い、その中をTextKit 2で描画します。以前は単語ごとにSwiftUIビューを作っていました。効果を付けるには便利でしたが、長い章では負荷が大きくなります。文書全体を一つのTextKitオブジェクトにした実験では、逆に長い本の最初のページが出るまで時間がかかりました。段落単位なら、TextKitからレイアウト情報を直接取れる利点を残しつつ、画面の近くにある内容だけを生成できます。
テスト
インポート、音声タイミング、発音、リーダーの動作をテストしています。ふりがなについては、手で選んだ単語だけではなく、実際の処理系にコーパスを通します。TextKit版を実際の本で測定して比較できるよう、以前のSwiftUI版もLabsの切り替えで残しています。
現在
Mac、iPhone、iPadのアプリは同じコアを共有しています。英語と日本語に対応し、端末内の辞書、学習カード、音声を使わないReading Pacerもあります。今のところ個人プロジェクトとして使っています。次はTextKitリーダーを実機で測定し、少人数のTestFlightから有用なフィードバックが得られそうか判断します。