Skip to content
sho10case
Go back

Android音声合成の新常識!Sherpa-ONNXのオフラインTTSとGeminiのマルチモーダル出力を徹底比較

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の三段活用

  1. STT (文字起こし): Gemini 2.5 Flash-Lite でフィラーを除去。
  2. Thinking (思考): キャラクター設定を反映した返答を生成。
  3. 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が直接音声を生成する「マルチモーダル出力」が今後のスタンダードになると確信しました。

今後はレスポンスタイムをさらに削るため、ストリーミング再生の実装に挑戦していく予定です。

「技術的な正解」よりも「ユーザー(おじいちゃん、おばあちゃん)にとっての心地よさ」を追求して、これからも開発を続けていきます!

それでは、また!



Related Posts