> For the complete documentation index, see [llms.txt](https://docs.mapbox.com/android/navigation/llms.txt)

# Voice instructions

The Mapbox Navigation SDK allows you to provide prompt and detailed voice instructions to users of your application. You can use a voice instruction to notify the user of an upcoming turn or announce that a faster route has been detected.

The Navigation SDK uses the [Mapbox Java SDK's](https://docs.mapbox.com/android/java/guides/) `VoiceInstructions` class to hold information that should be announced out loud by the device (for example, the street that the user should stay on for a certain distance).

For most use cases, use [`MapboxAudioGuidance`](https://docs.mapbox.com/android/navigation/api/coreframework/3.30.0-rc.1/voice/com.mapbox.navigation.voice.api/-mapbox-audio-guidance/) — a higher-level service that manages audio guidance end to end. If you need full manual control over individual announcements, use [`MapboxSpeechApi`](https://docs.mapbox.com/android/navigation/api/coreframework/3.30.0-rc.1/voice/com.mapbox.navigation.voice.api/-mapbox-speech-api/) and [`MapboxVoiceInstructionsPlayer`](https://docs.mapbox.com/android/navigation/api/coreframework/3.30.0-rc.1/voice/com.mapbox.navigation.voice.api/-mapbox-voice-instructions-player/) directly.

## Use `MapboxAudioGuidance`

[`MapboxAudioGuidance`](https://docs.mapbox.com/android/navigation/api/coreframework/3.30.0-rc.1/voice/com.mapbox.navigation.voice.api/-mapbox-audio-guidance/) is a service which builds on top of [`MapboxSpeechApi`](https://docs.mapbox.com/android/navigation/api/coreframework/3.30.0-rc.1/voice/com.mapbox.navigation.voice.api/-mapbox-speech-api/) and [`MapboxVoiceInstructionsPlayer`](https://docs.mapbox.com/android/navigation/api/coreframework/3.30.0-rc.1/voice/com.mapbox.navigation.voice.api/-mapbox-voice-instructions-player/). It integrates directly with the [`MapboxNavigationApp`](https://docs.mapbox.com/android/navigation/api/coreframework/3.30.0-rc.1/navigation/com.mapbox.navigation.core.lifecycle/-mapbox-navigation-app/) lifecycle and handles observer registration, voice instruction prefetching, and mute state persistence automatically — so you don't need to manage these concerns yourself.

### Get or create an instance

The recommended way to use `MapboxAudioGuidance` is through the registered singleton, which ties into the `MapboxNavigationApp` lifecycle automatically:

```kotlin
val audioGuidance = MapboxAudioGuidance.getRegisteredInstance()
```

`getRegisteredInstance()` returns the existing registered instance if one is already registered, or creates a new one and registers it.

If you need a standalone instance that you manage yourself (for example to register it conditionally or with specific options), use `create()` instead.

```kotlin
val options = MapboxSpeechApiOptions.Builder()
    .gender(VoiceGender.MALE)
    .build()

val audioGuidance = MapboxAudioGuidance.create(options)
MapboxNavigationApp.registerObserver(audioGuidance)
```

### Observe audio guidance state

`MapboxAudioGuidance` exposes a [`StateFlow`](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-state-flow/) of [`MapboxAudioGuidanceState`](https://docs.mapbox.com/android/navigation/api/coreframework/3.30.0-rc.1/voice/com.mapbox.navigation.voice.api/-mapbox-audio-guidance-state/). Collect this flow to react to guidance state changes without affecting playback.

```kotlin
audioGuidance.stateFlow().collect { state ->
    // do something when audio state has changed
}
```

### Control mute state

Call `mute()`, `unmute()`, or `toggle()` to control whether voice instructions are played aloud. When muted, voice instructions are still received and reflected in `stateFlow()` — only playback is suppressed.

```kotlin
// Mute voice guidance
audioGuidance.mute()

// Unmute voice guidance
audioGuidance.unmute()

// Toggle between muted and unmuted
audioGuidance.toggle()
```

`MapboxAudioGuidance` persists the muted state across app sessions using `DataStore`. When the app is relaunched and `MapboxAudioGuidance` attaches, it will restore saved mute state automatically.

### Customize speech API options

You can configure the voice gender and API base URL via [`MapboxSpeechApiOptions`](https://docs.mapbox.com/android/navigation/api/coreframework/3.30.0-rc.1/voice/com.mapbox.navigation.voice.options/-mapbox-speech-api-options/). Pass options when creating the instance:

```kotlin
val options = MapboxSpeechApiOptions.Builder()
    .gender(VoiceGender.FEMALE)
    .build()

val audioGuidance = MapboxAudioGuidance.create(options)
```

You can also update options at runtime :

```kotlin
val updatedOptions = MapboxSpeechApiOptions.Builder()
    .gender(VoiceGender.MALE)
    .build()

audioGuidance.updateSpeechApiOptions(updatedOptions)
```

### Access the player

Use `getCurrentVoiceInstructionsPlayer()` to access the underlying [`MapboxVoiceInstructionsPlayer`](https://docs.mapbox.com/android/navigation/api/coreframework/3.30.0-rc.1/voice/com.mapbox.navigation.voice.api/-mapbox-voice-instructions-player/)

```kotlin
audioGuidance.getCurrentVoiceInstructionsPlayer()
```

### Manage the lifecycle

Because `MapboxAudioGuidance` implements [`MapboxNavigationObserver`](https://docs.mapbox.com/android/navigation/api/coreframework/3.30.0-rc.1/navigation/com.mapbox.navigation.core.lifecycle/-mapbox-navigation-observer/), it handles its own cleanup when detached from `MapboxNavigationApp`. If you registered the instance yourself, unregister it when it is no longer needed:

```kotlin
override fun onDestroy() {
    super.onDestroy()
    MapboxNavigationApp.unregisterObserver(audioGuidance)
}
```

## Use `MapboxSpeechApi` and `MapboxVoiceInstructionsPlayer`

There are two steps to play voice instructions in your application: generate an announcement and then play an announcement.

### Create an instance of the speech API

Before you can generate an announcement, you need to instantiate the speech API in your `Activity` or `Fragment`:

```kotlin
val speechApi = MapboxSpeechApi(this, Locale.US.toLanguageTag())
```

### Create an instance of the voice instructions player

And before you can play an announcement, you need to instantiate the text-to-speech engine in `onCreate` of your `Activity` or `onActivityCreated` of your `Fragment`. Do not use lazy initialization for this class since it takes some time to initialize the system services required for on-device speech synthesis. With lazy initialization there is a high risk that said services will not be available when the first instruction has to be played.

```kotlin
lateinit var voiceInstructionsPlayer: MapboxVoiceInstructionsPlayer

override fun onCreate() {
  // do your initialization here
  voiceInstructionsPlayer = MapboxVoiceInstructionsPlayer(
    this,
    Locale.US.toLanguageTag()
  )
}
```

### Generate and play an announcement

Start by generating the content of the announcement using [`MapboxSpeechApi`](https://docs.mapbox.com/android/navigation/api/coreframework/3.30.0-rc.1/voice/com.mapbox.navigation.voice.api/-mapbox-speech-api/). `MapboxSpeechApi` accepts a `VoiceInstructions` object and returns a state object that includes the content of the announcement when it is ready or an error and a fallback with the raw announcement.

Then, pass the content of the announcement to [`MapboxVoiceInstructionsPlayer`](https://docs.mapbox.com/android/navigation/api/coreframework/3.30.0-rc.1/voice/com.mapbox.navigation.voice.api/-mapbox-voice-instructions-player/), which will play the announcement aloud. If you provide a synthesized speech MP3 which was successfully generated with the `MapboxSpeechApi`, a media player will be used to play the instruction. If not, an onboard speech-to-text engine, obtained using [built-in Android APIs](https://developer.android.com/reference/android/speech/tts/TextToSpeech), will be used to synthesize the raw announcement's text.

The result of invoking [`MapboxSpeechApi#generate`](https://docs.mapbox.com/android/navigation/api/coreframework/3.30.0-rc.1/voice/com.mapbox.navigation.voice.api/-mapbox-speech-api/generate.html) is returned as a callback containing either a success in the form of [`SpeechValue`](https://docs.mapbox.com/android/navigation/api/coreframework/3.30.0-rc.1/voice/com.mapbox.navigation.voice.model/-speech-value/) or failure in the form of [`SpeechError`](https://docs.mapbox.com/android/navigation/api/coreframework/3.30.0-rc.1/voice/com.mapbox.navigation.voice.model/-speech-error/).

```kotlin
val speechCallback =
    MapboxNavigationConsumer<Expected<SpeechError, SpeechValue>> { expected ->
        expected.fold(
            { error ->
                // In case of error, a fallback announcement is returned that can be played
                // using [MapboxVoiceInstructionsPlayer]
                voiceInstructionsPlayer.play(
                    error.fallback,
                    voiceInstructionsPlayerCallback
                )
            },
            { value ->
                // The announcement data obtained (synthesized speech mp3 file from Mapbox's API Voice) is played
                // using [MapboxVoiceInstructionsPlayer]
                voiceInstructionsPlayer.play(
                    value.announcement,
                    voiceInstructionsPlayerCallback
                )
            }
        )
    }

val voiceInstructionsObserver =
    VoiceInstructionsObserver { voiceInstructions ->
        // The data obtained must be used to generate the speech announcement
        speechAPI.generate(
            voiceInstructions,
            speechCallback
        )
    }
```

The result of invoking [`MapboxVoiceInstructionsPlayer#play`](https://docs.mapbox.com/android/navigation/api/coreframework/3.30.0-rc.1/voice/com.mapbox.navigation.voice.api/-mapbox-voice-instructions-player/play.html) is returned as a callback containing [`SpeechAnnouncement`](https://docs.mapbox.com/android/navigation/api/coreframework/3.30.0-rc.1/voice/com.mapbox.navigation.voice.model/-speech-announcement/). This can be used to cleanup any associated files generated before:

```kotlin
val voiceInstructionsPlayerCallback =
    MapboxNavigationConsumer<SpeechAnnouncement> { value ->
        // Remove already consumed file to free-up space
        speechAPI.clean(value)
    }
```

> **Note: Using only the onboard TTS engine**
> 
> To decrease latency and cost you can consider using only the `MapboxVoiceInstructionsPlayer` with manually created `SpeechAnnouncement`s with only `announcement` text value which will rely only on the usage of the onboard TTS engine.

### Start and stop receiving voice events

Register a [`VoiceInstructionsObserver`](https://docs.mapbox.com/android/navigation/api/coreframework/3.30.0-rc.1/navigation/com.mapbox.navigation.core.trip.session/-voice-instructions-observer/) with [`MapboxNavigation`](https://docs.mapbox.com/android/navigation/api/coreframework/3.30.0-rc.1/navigation/com.mapbox.navigation.core/-mapbox-navigation/):

```kotlin
mapboxNavigation.registerVoiceInstructionsObserver(voiceInstructionsObserver)
```

Don't forget to unregister the observer, cancel any potential in-flight `MapboxSpeechApi` requests and shutdown `MapboxVoiceInstructionsPlayer` in `onStop` or `onDestroy`:

```kotlin
override fun onDestroy() {
    super.onDestroy()
    mapboxNavigation.unregisterVoiceInstructionsObserver(voiceInstructionsObserver)
    speechApi.cancel()
    voiceInstructionsPlayer.shutdown()
}
```

Also, every time a new route is obtained make sure to cancel any potential in-flight `MapboxSpeechApi` requests and clear the `MapboxVoiceInstructionsPlayer` queue:

```kotlin
val routesObserver =
    RoutesObserver { result ->
        // Every time a new route is obtained make sure to cancel the [MapboxSpeechApi] and
        // clear the [MapboxVoiceInstructionsPlayer]
        speechApi.cancel()
        voiceInstructionsPlayer.clear()
    }
```

### Adjust voice instructions volume

To set the volume level of `MapboxVoiceInstructionsPlayer` you need to provide a value between `0.0` and `1.0` (max) through [`SpeechVolume`](https://docs.mapbox.com/android/navigation/api/coreframework/3.30.0-rc.1/voice/com.mapbox.navigation.voice.model/-speech-volume/):

```kotlin
// Sets volume to minimum;
voiceInstructionsPlayer.volume(SpeechVolume(0.0f))
```

> **Note: Volume 0.0f is not the same as muting**
> 
> Setting volume to 0.0f silences playback but does not release audio focus — other audio on the device will still be ducked as if instructions were playing. To suppress voice instructions without ducking, use `MapboxAudioGuidance.mute()` instead. If you are using `MapboxVoiceInstructionsPlayer` directly, there is no built-in mute — you must gate calls to `play()` with your own flag.