Jetpack Compose Glimmer の音声入力インジケーター

対象となる XR デバイス
このガイダンスは、次のようなタイプの XR デバイス向けのエクスペリエンスを構築する際に役立ちます。
ディスプレイ グラス

VoiceInputIndicator は、ユーザーにサウンド アクティビティを表示する Jetpack Compose Glimmer コンポーネントです。このコンポーネントを使用すると、音声入力などの音声を受け取り、アプリが音声をキャプチャしていることを示す視覚的なフィードバックを提供できます。

このコンポーネントは、純粋に視覚的な要素です。音声の録音やマイクデータの処理は行いません。代わりに、アプリから提供される音声 level に応答し、音声の強度を視覚的に表示します。

図 1. VoiceInputIndicator の例。

予測される権限をリクエストする

インジケーターを駆動する音声データをキャプチャするには、アプリに Manifest.permission.RECORD_AUDIO 権限が必要です。

ディスプレイ グラス用の拡張エクスペリエンスを構築する場合は、デバイスとグラスの両方で権限を正しくリクエストしてください。ProjectedActivityCompat.requestPermissions を使用してユーザーにプロンプトを表示します。

投影環境での権限のリクエストの詳細については、ハードウェア権限をリクエストするをご覧ください。

音声をキャプチャして正規化する

VoiceInputIndicator では、音声の強度を視覚的に反映するために、0.0f~1.0f の Float が必要です。これは純粋に視覚的なコンポーネントとして動作し、音声をキャプチャしないため、アプリはマイクをアクティブにモニタリングし、音量をインジケーターの level パラメータにストリーミングする必要があります。

音声データをキャプチャする方法はいくつかありますが、その 1 つが SpeechRecognizer API を使用する方法です。SpeechRecognizer を使用する場合、RecognitionListener.onRmsChanged コールバックを使用してリアルタイムの音量更新をキャプチャできます。このコールバックは、デシベル(rmsdB)単位で音量レベルを提供します。

この未加工の値は通常 0 ~ 10 の範囲になるため、出力を必要な 0.0f~1.0f の範囲に正規化する必要があります。この正規化されたレベルを状態変数に保持して、インジケーターを継続的に更新できます。

RecognitionListener を使用して状態変数を更新する例を次に示します。

// Example state variable to hold the normalized level (0.0 to 1.0)
// that the VoiceInputIndicator component expects.
val audioLevel = MutableStateFlow(0f)

// Initialize the Android SpeechRecognizer
val speechRecognizer = SpeechRecognizer.createSpeechRecognizer(context)

// Listener to capture speech events and audio level changes
val listener = object : RecognitionListener {
    override fun onRmsChanged(rmsdB: Float) {
        // Normalize raw dB level to a 0.0-1.0 range.
        // Android SpeechRecognizer's rmsdB typically ranges from 0 to ~10.
        audioLevel.value = ((rmsdB - 1f) / 9f).coerceIn(0f, 1f)
    }

    // ... Implement other required RecognitionListener methods ...
    override fun onReadyForSpeech(params: Bundle?) {}
    override fun onBeginningOfSpeech() {}
    override fun onEndOfSpeech() {}
    override fun onError(error: Int) {}
    override fun onResults(results: Bundle?) {}
    override fun onPartialResults(partialResults: Bundle?) {}
    override fun onEvent(eventType: Int, params: Bundle?) {}
    override fun onBufferReceived(buffer: ByteArray?) {}
}

// Attach the listener to the recognizer
speechRecognizer.setRecognitionListener(listener)

// Create an intent to specify the recognition model and behavior
val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH).apply {
    putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)
}
// Begin listening for audio input
speechRecognizer.startListening(intent)

音声入力インジケーターを統合する

音声ストリームが適切に正規化され、状態として公開されたら、VoiceInputIndicator に直接渡すことができます。

@Composable
fun VoiceInputExample(modifier: Modifier) {
    // Collect the flow as Compose State so the UI reacts to changes in real-time.
    val currentLevel by audioLevel.collectAsState()

    VoiceInputIndicator(
        // The VoiceInputIndicator component provides a visual "pulse" or indicator
        // that changes based on the 'level' lambda, which returns a Float between 0.0 and 1.0.
        level = { currentLevel },
        modifier = modifier
    )
}

カスタマイズ パラメータ

VoiceInputIndicator には、動作と外観をカスタマイズするためのパラメータがいくつか用意されています。

  • level: 音声入力のレベルを表す Float を返す関数。0.0(無音)から 1.0(最大音量)までの範囲。
  • indicatorColor: インジケーター バーの色。デフォルトでは、Glimmer テーマのプライマリ カラーが使用されます。GlimmerTheme.colors.primary をご覧ください。
  • modifier: インジケーターに適用される標準の Compose modifier。

UI でインジケーターを表示可能な背景コンテナ内に配置する必要がある場合は、ContainedVoiceInputIndicator を使用します。この方法を使用すると、インジケーター バーが透明になり、背景コンテナが透けて見えるようになります。