Androidアプリで「喋るAIエージェント」を作りたい時、最大の悩みは「音声合成(TTS)」のクオリティと速度のバランスです。
現在開発中の介護支援AIキャラクター「バル君」の実装を通して、**「Sherpa-ONNX(オフライン・エッジ)」と「Gemini API(クラウド・マルチモーダル)」**という2つのアプローチを試しました。それぞれの実装方法と、最終的にクラウド型を選んだ理由を技術的に解説します。
1. Sherpa-ONNXによるオフラインTTSの実装
インターネット不要で、デバイス上のみで完結させたい場合は sherpa-onnx が現状の最適解の一つです。
ハマりポイント:AARの手動配置
implementation 1行で導入できるMaven公開はないため、Releasesから AAR ファイルをダウンロードして libs/ に配置する必要があります。
// app/build.gradle.kts
dependencies {
implementation(files("libs/sherpa-onnx-1.12.29.aar"))
}
実装の肝:AudioTrackによるPCM再生
Sherpa-ONNXが生成するのは生の音声データ(PCM)のため、Androidの MediaPlayer では再生できません。AudioTrack を使ってサンプリングレートを指定して流し込む必要があります。
private fun playAudio(samples: FloatArray, sampleRate: Int) {
val audioTrack = AudioTrack.Builder()
.setAudioFormat(AudioFormat.Builder()
.setEncoding(AudioFormat.ENCODING_PCM_FLOAT)
.setSampleRate(sampleRate)
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO).build())
.setBufferSizeInBytes(samples.size * 4)
.setTransferMode(AudioTrack.MODE_STATIC).build()
audioTrack.write(samples, 0, samples.size, AudioTrack.WRITE_BLOCKING)
audioTrack.play()
// ... release処理
}
2. Gemini 2.5 Flashによる「感情の乗った」音声生成
Sherpa-ONNXは爆速ですが、日本語のイントネーションがどうしても「機械的」になりがちでした。そこで、クオリティを最優先し、バックエンド(Go / Cloud Run)経由で Gemini 2.5 Flash のマルチモーダル出力を採用しました。
Gemini APIの三段活用
- STT (文字起こし): Gemini 2.5 Flash-Lite でフィラーを除去。
- Thinking (思考): キャラクター設定を反映した返答を生成。
- TTS (音声生成): Gemini 2.5 Flash (preview-tts) で直接「音声バイナリ」を出力。
Goバックエンドでの実装例
ResponseModalities に "AUDIO" を指定することで、テキストを介さずGemini自身が喋る音声を生成できます。
ttsConfig := &genai.GenerateContentConfig{
ResponseModalities: []string{"AUDIO"},
SpeechConfig: &genai.SpeechConfig{
VoiceConfig: &genai.VoiceConfig{
PrebuiltVoiceConfig: &genai.PrebuiltVoiceConfig{
VoiceName: "Aoede",
},
},
},
}
// モデルには gemini-2.5-flash-preview-tts を使用
resp, err := client.Models.GenerateContent(ctx, "gemini-2.5-flash-preview-tts", contents, ttsConfig)
補足:StyleBertVITS2への挑戦と断念
実は、オフライン環境でも「もっと自然な日本語」を追求するために、高音質な StyleBertVITS2 モデルのAndroid上での実行も検討しました。
しかし、以下の理由から今回は断念しました:
- ライブラリの壁: 現時点での
sherpa-onnx(v1.12.29) ではOfflineStyleBertVits2ModelConfigが存在せず、そのままでは利用できない。 - リソースの壁: StyleBertVITS2は非常に表現力が高い反面、モデルサイズが大きく計算量も多いため、スマートフォンのリソース(メモリやバッテリー)を激しく消費してしまう。
介護現場での「安定稼働」を第一に考え、最終的には軽量なVITS(Sherpa)か、最高品質のマルチモーダル(Gemini)かの二択に絞ることにしました。
3. なぜ最終的にGemini(クラウド)を選んだのか?
| 比較項目 | Sherpa-ONNX (オフライン) | Gemini API (クラウド) |
|---|---|---|
| 音質・自然さ | やや機械的 | 圧倒的に人間らしい |
| レスポンス | 爆速 (ミリ秒単位) | 数秒(通信依存) |
| コスト | 無料 | API利用料 |
| 実装難易度 | 高い(C++ライブラリ等) | 低い(API呼出) |
介護現場で高齢者の話し相手になる「バル君」に求められたのは、速度よりも**「寄り添うような自然な会話」**でした。Geminiが生成する音声は、驚くほどイントネーションが自然で、方言や曖昧な発話も文脈から汲み取ってくれます。
まとめ:技術で人を癒やすために
オフラインTTS(Sherpa-ONNX)は、ツール系アプリや通信環境が悪い場所では最強の武器になります。一方で、対話そのものを目的とするエンタメ・介護系アプリでは、LLMが直接音声を生成する「マルチモーダル出力」が今後のスタンダードになると確信しました。
今後はレスポンスタイムをさらに削るため、ストリーミング再生の実装に挑戦していく予定です。
「技術的な正解」よりも「ユーザー(おじいちゃん、おばあちゃん)にとっての心地よさ」を追求して、これからも開発を続けていきます!
それでは、また!