Add voice guidance
Voice guidance in the Maps SDK for Android allows you to enhance navigation experiences with spoken instructions. This guide covers how to enable built-in Text-to-Speech (TTS), manage voice settings, switch voices and languages, and integrate custom playback using the onNavigationSound callback for maximum flexibility.
The Maps SDK for Android provides two options for instruction playback:
- Built-in solutions - playback using human voice recordings or computer-generated TTS.
- External integration - manual handling of sound playback through the
onNavigationSoundcallback.
The built-in solution also provides automatic audio session management, ducking other playbacks (such as music) while instructions are playing.
Quick start
Enable voice guidance using the built-in SoundPlayingService by implementing proper sound handling in your navigation listener:
- Kotlin
- Java
import com.magiclane.sdk.core.SoundPlayingService
import com.magiclane.sdk.core.SoundPlayingListener
import com.magiclane.sdk.routesandnavigation.NavigationListener
// Create a sound listener for navigation sounds
val soundListener = object : SoundPlayingListener() {
override fun notifyStart(hasProgress: Boolean) {
// Navigation sound started
}
override fun notifyComplete(errorCode: Int, hint: String) {
// Navigation sound completed
}
}
// Add navigation listener with sound handling
val navigationListener = NavigationListener.create(
onNavigationSound = { sound ->
// Play the navigation sound using the service
val preferences = SoundPlayingService.getPlayingPreferences()
SoundPlayingService.play(sound, soundListener, preferences)
}
// Handle other navigation events...
)
// Start navigation with voice guidance
val navigationService = NavigationService()
navigationService.startNavigationWithRoute(
route = route,
navigationListener = navigationListener,
progressListener = ProgressListener.create()
)
import com.magiclane.sdk.core.SoundPlayingService;
import com.magiclane.sdk.core.SoundPlayingListener;
import com.magiclane.sdk.routesandnavigation.NavigationListener;
// Create a sound listener for navigation sounds
SoundPlayingListener soundListener = new SoundPlayingListener() {
@Override
public void notifyStart(boolean hasProgress) {
// Navigation sound started
}
@Override
public void notifyComplete(int errorCode, String hint) {
// Navigation sound completed
}
};
// Add navigation listener with sound handling
NavigationListener navigationListener = NavigationListener.create(
/* onNavigationSound */ (sound) -> {
// Play the navigation sound using the service
SoundPlayingPreferences preferences = SoundPlayingService.getPlayingPreferences();
SoundPlayingService.play(sound, soundListener, preferences);
}
// Handle other navigation events...
);
// Start navigation with voice guidance
NavigationService navigationService = new NavigationService();
navigationService.startNavigationWithRoute(
route,
navigationListener,
ProgressListener.create()
);
For external TTS integration, override the default sound handling:
- Kotlin
- Java
val navigationListener = NavigationListener.create(
onNavigationSound = { sound ->
// Custom TTS handling
val instructionText = sound.getAsString()
if (instructionText.isNotEmpty()) {
// Use your preferred TTS engine
customTtsEngine.speak(instructionText)
}
}
// Handle other events...
)
NavigationListener navigationListener = NavigationListener.create(
/* onNavigationSound */ (sound) -> {
// Custom TTS handling
String instructionText = sound.getAsString();
if (instructionText != null && !instructionText.isEmpty()) {
// Use your preferred TTS engine
customTtsEngine.speak(instructionText);
}
}
// Handle other events...
);
Note: The SDK does not provide automatic sound playback. You must implement sound handling in the
onNavigationSoundcallback to hear voice instructions.
Basic Voice Guidance
To enable voice guidance, you need to handle navigation sounds in your NavigationListener and use the SoundPlayingService to play them:
- Kotlin
- Java
import com.magiclane.sdk.core.SoundPlayingService
import com.magiclane.sdk.core.SoundPlayingListener
import com.magiclane.sdk.core.SoundPlayingPreferences
// Configure sound preferences
val preferences = SoundPlayingService.getPlayingPreferences()
preferences.volume = 8 // Set volume (0-10)
// Create navigation listener with sound handling
val navigationListener = NavigationListener.create(
onNavigationSound = { sound ->
val soundListener = object : SoundPlayingListener() {
override fun notifyComplete(errorCode: Int, hint: String) {
if (errorCode != 0) {
println("Sound playback failed: $errorCode")
}
}
}
// Play the navigation instruction sound
SoundPlayingService.play(sound, soundListener, preferences)
}
)
import com.magiclane.sdk.core.SoundPlayingService;
import com.magiclane.sdk.core.SoundPlayingListener;
import com.magiclane.sdk.core.SoundPlayingPreferences;
// Configure sound preferences
SoundPlayingPreferences preferences = SoundPlayingService.getPlayingPreferences();
preferences.setVolume(8); // Set volume (0-10)
// Create navigation listener with sound handling
NavigationListener navigationListener = NavigationListener.create(
/* onNavigationSound */ (sound) -> {
SoundPlayingListener soundListener = new SoundPlayingListener() {
@Override
public void notifyComplete(int errorCode, String hint) {
if (errorCode != 0) {
System.out.println("Sound playback failed: " + errorCode);
}
}
};
// Play the navigation instruction sound
SoundPlayingService.play(sound, soundListener, preferences);
}
);
Ensure that a valid TTS voice is configured and the voice volume is set to a positive value within the allowed range (0-10) to hear voice instructions.
By default, the current voice is set to the best computer TTS voice matching the default SDK language.
The Sound Playing Service
The SoundPlayingService provides the following key methods for sound management:
| Method | Description |
|---|---|
play(sound, listener, preferences) | Plays an ISound object with monitoring and custom preferences |
playText(text, listener, preferences) | Plays TTS text using the current TTS voice |
playFile(filePath, mimeType, listener, preferences) | Plays an audio file with specified MIME type |
getPlayingPreferences() | Gets the current sound playing preferences (volume, etc.) |
setTTSLanguage(languageCode) | Sets the TTS language for computer voices |
getTTSLanguages() | Gets list of available TTS languages |
cancel(listener) | Cancels playback associated with a specific listener |
playingSoundsCount() | Returns the number of sounds currently playing |
Voice
The Maps SDK for Android provides a list of voices for each supported language. These voices can be downloaded and activated to provide navigation prompts such as turn instructions, warnings, and announcements.
The SDK offers two types of voice guidance, defined by the EVoiceType enum:
EVoiceType.Human: Uses pre-recorded human voices to deliver instructions in a friendly and natural tone. These voices support only basic instruction types and do not include road or settlement names.EVoiceType.Computer: Leverages the device's Text-to-Speech (TTS) engine to provide more detailed and flexible guidance. These voices fully support street and place names in the spoken instructions. The quality and availability is dependent on the device.
Voice Structure
The Voice class provides the following details:
| Property | Type | Description |
|---|---|---|
id | Long | Unique identifier for the voice. |
name | String | Display name of the voice. |
filename | String | File name which can be used to load the voice (available for EVoiceType.Human). |
language | Language | Associated language object. |
type | EVoiceType | Enum: Human or Computer. |
Do not confuse the Voice and Language concepts.
The Language defines what is said - it determines the words, phrasing, and localization.
The Voice defines how it is said - it controls attributes like accent, tone, and gender.
Always ensure that the selected Voice is compatible with the chosen Language, as mismatched combinations may result in unnatural or incorrect pronunciation.
Relevance
Languageis relevant for both the built-in TTS system and custom solutions using theonNavigationSoundcallback. See the internationalization guide for more info about theLanguageclass.Voiceis relevant only for the built-in voice-guidance (using human and computer voices) playback.
The SDK distinguishes between two language settings:
- SDK language (
SdkSettings.language) : Defines the language used for all on-screen text and UI intended strings. - Voice language (
Voice.language) : Defines the language used for spoken output, whether through the built-in engine (computer/human voices) or via theonNavigationSoundcallback.
Both settings use the same Language class. The synchronization between the SDK language and the voice language should be made by the user, depending on the use case.
Get the Current Voice
Use the voice property provided by the SdkSettings class:
- Kotlin
- Java
val currentVoice = SdkSettings.voice
Voice currentVoice = SdkSettings.getVoice();
Get the List of Available Human Voices
The available human voices list is provided by the getLocalContentList method provided by the ContentStore class.
- Kotlin
- Java
val contentStore = ContentStore()
val items = contentStore.getLocalContentList(EContentType.HumanVoice)
items?.forEach { contentStoreItem ->
// The voice name (ex: Ella)
val name = contentStoreItem.name
// The absolute path to the voice file. Used for applying the voice.
val filePath = contentStoreItem.filename
// Unique identifier for the voice
val id = contentStoreItem.id
// Voice language information
val language = contentStoreItem.language
// Voice type (Human or Computer)
val type = contentStoreItem.type
// Country codes associated with this voice
val countryCodes = contentStoreItem.countryCodes
}
ContentStore contentStore = new ContentStore();
List<ContentStoreItem> items = contentStore.getLocalContentList(EContentType.HumanVoice);
if (items != null) {
for (ContentStoreItem contentStoreItem : items) {
// The voice name (ex: Ella)
String name = contentStoreItem.getName();
// The absolute path to the voice file. Used for applying the voice.
String filePath = contentStoreItem.getFilename();
// Unique identifier for the voice
long id = contentStoreItem.getId();
// Voice language information
Language language = contentStoreItem.getLanguage();
// Voice type (Human or Computer)
EVoiceType type = contentStoreItem.getType();
// Country codes associated with this voice
List<String> countryCodes = contentStoreItem.getCountryCodes();
}
}
Check the Manage Content Guide for information about managing voices and performing operations such as downloading, deleting, and more and details about the ContentStore and ContentStoreItem classes.
Apply a Voice by path
A human voice can be applied by providing the absolute path (obtained from the ContentStoreItem.filename getter) to the setVoiceByPath method provided by the SdkSettings class:
- Kotlin
- Java
val filePath = contentStoreItem.filename
SdkSettings.setVoiceByPath(filePath)
String filePath = contentStoreItem.getFilename();
SdkSettings.setVoiceByPath(filePath);
The SdkSettings.setVoiceByPath method can also be used to set computer voices, provided the computer voice path is known.
For setting TTS computer voices, it's recommended to use SoundPlayingService.setTTSLanguage with the appropriate language code instead.
Apply a Voice by language
Computer voices can be applied using the setTTSLanguage method provided by the SoundPlayingService class. The method requires a language code string:
- Kotlin
- Java
// Get available TTS languages
val ttsLanguages = SoundPlayingService.getTTSLanguages()
// Set TTS language by language code
if (ttsLanguages.isNotEmpty()) {
SoundPlayingService.setTTSLanguage(ttsLanguages[0].code)
}
// Or set directly with a known language code
SoundPlayingService.setTTSLanguage("en-US")
// Get available TTS languages
List<TTSLanguage> ttsLanguages = SoundPlayingService.getTTSLanguages();
// Set TTS language by language code
if (!ttsLanguages.isEmpty()) {
SoundPlayingService.setTTSLanguage(ttsLanguages.get(0).getCode());
}
// Or set directly with a known language code
SoundPlayingService.setTTSLanguage("en-US");
The computer voice is implemented using the device included TTS capabilities.
Selecting a computer voice in an unsupported language may cause a mismatch between the spoken voice and the instruction content. The exact behavior depends on the device and its available text-to-speech capabilities.
Get the TTS Instruction Strings
The TTS instructions can be provided by the NavigationService as strings in order to be further processed and played with external tools. For example, Android's built-in TTS engine or third-party TTS libraries can be used for playing sound based on the TTS text instructions provided by the SDK.
The voice instructions strings will come on the onNavigationSound callback set during navigation or simulation.
Add Android's built-in TTS or a third-party TTS library to your project. For Android's built-in TTS, ensure you have the necessary permissions in your AndroidManifest.xml.
We can use the onNavigationSound callback of the navigation listener in the following way:
- Kotlin
- Java
import android.speech.tts.TextToSpeech
import com.magiclane.sdk.core.ISound
import com.magiclane.sdk.routesandnavigation.NavigationInstruction
import com.magiclane.sdk.routesandnavigation.NavigationListener
// instantiate Android TTS
val tts = TextToSpeech(context) { status ->
if (status == TextToSpeech.SUCCESS) {
// TTS engine initialized successfully
}
}
fun simulationInstructionUpdated(instruction: NavigationInstruction) {
// handle instruction
}
fun handleNavigationSound(sound: ISound) {
val instructionText = sound.getAsString()
if (!instructionText.isNullOrEmpty()) {
tts.speak(instructionText, TextToSpeech.QUEUE_FLUSH, null, "NavigationTTS")
}
}
val navigationListener = NavigationListener.create(
onNavigationInstructionUpdated = { instruction ->
simulationInstructionUpdated(instruction)
},
onNavigationSound = { sound ->
handleNavigationSound(sound)
}
)
val navigationService = NavigationService()
navigationService.startSimulationWithRoute(
route = route,
navigationListener = navigationListener,
progressListener = ProgressListener.create(),
speedMultiplier = 2.0f
)
import android.speech.tts.TextToSpeech;
import com.magiclane.sdk.core.ISound;
import com.magiclane.sdk.routesandnavigation.NavigationInstruction;
import com.magiclane.sdk.routesandnavigation.NavigationListener;
// instantiate Android TTS
TextToSpeech tts = new TextToSpeech(context, (status) -> {
if (status == TextToSpeech.SUCCESS) {
// TTS engine initialized successfully
}
});
private void simulationInstructionUpdated(NavigationInstruction instruction) {
// handle instruction
}
private void handleNavigationSound(ISound sound) {
String instructionText = sound.getAsString();
if (instructionText != null && !instructionText.isEmpty()) {
tts.speak(instructionText, TextToSpeech.QUEUE_FLUSH, null, "NavigationTTS");
}
}
NavigationListener navigationListener = NavigationListener.create(
/* onNavigationInstructionUpdated */ (instruction) -> {
simulationInstructionUpdated(instruction);
},
/* onNavigationSound */ (sound) -> {
handleNavigationSound(sound);
}
);
NavigationService navigationService = new NavigationService();
navigationService.startSimulationWithRoute(
route,
navigationListener,
ProgressListener.create(),
2.0f // speedMultiplier
);
For navigation we can set the onNavigationSound callback in a similar way with startNavigation.
See the Android documentation for TextToSpeech for information on how to set the TTS voice, language and other options such as pitch, volume, speech rate, etc.
To change the language of the instructions provided through the onNavigationSound callback, you can:
- Use
SoundPlayingService.setTTSLanguageand specify the preferred language code. - Or use
SdkSettings.setVoiceByPathwith a voice path corresponding to the desired language.
To disable the internal playback engine, simply don't call SoundPlayingService.play() in your onNavigationSound callback.
The instruction will still be delivered via the callback, but no audio will be played.
Monitor Sound Playback Events
The Maps SDK provides sound playback monitoring through the SoundPlayingListener interface. This listener receives callbacks during sound operations and allows you to track playback progress.
Here's how to create a custom sound playing listener:
- Kotlin
- Java
import com.magiclane.sdk.core.SoundPlayingListener
import com.magiclane.sdk.core.SoundPlayingService
import com.magiclane.sdk.core.SoundPlayingPreferences
class CustomSoundPlayingListener : SoundPlayingListener() {
override fun notifyStart(hasProgress: Boolean) {
// Sound playback started
println("Sound playback started. Has progress: $hasProgress")
}
override fun notifyProgress(progress: Int) {
// Progress update (0-100)
println("Playback progress: $progress%")
}
override fun notifyComplete(errorCode: Int, hint: String) {
// Sound playback completed
if (errorCode == 0) {
println("Sound playback completed successfully")
} else {
println("Sound playback failed with error: $errorCode")
}
}
override fun onVolumeChangedByKeys(newVolume: Int) {
// Volume changed via hardware keys
println("Volume changed to: $newVolume")
}
}
// Use the listener when playing sounds
val listener = CustomSoundPlayingListener()
val preferences = SoundPlayingPreferences()
// Play text with monitoring
SoundPlayingService.playText(
text = "Turn left in 100 meters",
listener = listener,
preferences = preferences
)
import com.magiclane.sdk.core.SoundPlayingListener;
import com.magiclane.sdk.core.SoundPlayingService;
import com.magiclane.sdk.core.SoundPlayingPreferences;
public class CustomSoundPlayingListener extends SoundPlayingListener {
@Override
public void notifyStart(boolean hasProgress) {
// Sound playback started
System.out.println("Sound playback started. Has progress: " + hasProgress);
}
@Override
public void notifyProgress(int progress) {
// Progress update (0-100)
System.out.println("Playback progress: " + progress + "%");
}
@Override
public void notifyComplete(int errorCode, String hint) {
// Sound playback completed
if (errorCode == 0) {
System.out.println("Sound playback completed successfully");
} else {
System.out.println("Sound playback failed with error: " + errorCode);
}
}
@Override
public void onVolumeChangedByKeys(int newVolume) {
// Volume changed via hardware keys
System.out.println("Volume changed to: " + newVolume);
}
}
// Use the listener when playing sounds
CustomSoundPlayingListener listener = new CustomSoundPlayingListener();
SoundPlayingPreferences preferences = new SoundPlayingPreferences();
// Play text with monitoring
SoundPlayingService.playText(
"Turn left in 100 meters",
listener,
preferences
);
Monitoring Navigation Sounds
To monitor navigation sounds specifically, you can create a listener and use it with the sound received from onNavigationSound:
- Kotlin
- Java
val soundListener = object : SoundPlayingListener() {
override fun notifyStart(hasProgress: Boolean) {
// Navigation sound started playing
}
override fun notifyComplete(errorCode: Int, hint: String) {
// Navigation sound finished
}
}
val navigationListener = NavigationListener.create(
onNavigationSound = { sound ->
// Play the navigation sound with monitoring
val preferences = SoundPlayingService.getPlayingPreferences()
SoundPlayingService.play(sound, soundListener, preferences)
}
)
SoundPlayingListener soundListener = new SoundPlayingListener() {
@Override
public void notifyStart(boolean hasProgress) {
// Navigation sound started playing
}
@Override
public void notifyComplete(int errorCode, String hint) {
// Navigation sound finished
}
};
NavigationListener navigationListener = NavigationListener.create(
/* onNavigationSound */ (sound) -> {
// Play the navigation sound with monitoring
SoundPlayingPreferences preferences = SoundPlayingService.getPlayingPreferences();
SoundPlayingService.play(sound, soundListener, preferences);
}
);
Getting Playback Information
You can check the current playback status using these methods:
- Kotlin
- Java
// Check how many sounds are currently playing
val playingCount = SoundPlayingService.playingSoundsCount()
// Get current playing preferences
val preferences = SoundPlayingService.getPlayingPreferences()
val currentVolume = preferences.volume // Volume level (0-10)
val maxPlayingTime = preferences.maxPlayingTime // Max duration in seconds
// Cancel a specific playback
SoundPlayingService.cancel(listener)
// Check how many sounds are currently playing
int playingCount = SoundPlayingService.playingSoundsCount();
// Get current playing preferences
SoundPlayingPreferences preferences = SoundPlayingService.getPlayingPreferences();
int currentVolume = preferences.getVolume(); // Volume level (0-10)
int maxPlayingTime = preferences.getMaxPlayingTime(); // Max duration in seconds
// Cancel a specific playback
SoundPlayingService.cancel(listener);
The SoundPlayingListener is used for all sound operations in the SDK, including TTS, file playback, and navigation sounds. Only one sound can be played at a time through the service.
Play custom instructions
Sometimes you may need to play custom instructions, such as road information warnings or social reports.
To do this, use the playText method of the SoundPlayingService to play back a custom string.
This uses the currently selected TTS voice and requires a listener and preferences.
- Kotlin
- Java
import com.magiclane.sdk.core.SoundPlayingService
import com.magiclane.sdk.core.SoundPlayingListener
import com.magiclane.sdk.core.SoundPlayingPreferences
// Create a listener for the custom instruction
val customInstructionListener = object : SoundPlayingListener() {
override fun notifyStart(hasProgress: Boolean) {
println("Custom instruction started")
}
override fun notifyComplete(errorCode: Int, hint: String) {
if (errorCode == 0) {
println("Custom instruction completed")
} else {
println("Custom instruction failed: $errorCode")
}
}
}
// Get default preferences or create custom ones
val preferences = SoundPlayingService.getPlayingPreferences()
// Play a custom instruction
SoundPlayingService.playText(
text = "Speed camera ahead",
listener = customInstructionListener,
preferences = preferences
)
import com.magiclane.sdk.core.SoundPlayingService;
import com.magiclane.sdk.core.SoundPlayingListener;
import com.magiclane.sdk.core.SoundPlayingPreferences;
// Create a listener for the custom instruction
SoundPlayingListener customInstructionListener = new SoundPlayingListener() {
@Override
public void notifyStart(boolean hasProgress) {
System.out.println("Custom instruction started");
}
@Override
public void notifyComplete(int errorCode, String hint) {
if (errorCode == 0) {
System.out.println("Custom instruction completed");
} else {
System.out.println("Custom instruction failed: " + errorCode);
}
}
};
// Get default preferences or create custom ones
SoundPlayingPreferences preferences = SoundPlayingService.getPlayingPreferences();
// Play a custom instruction
SoundPlayingService.playText(
"Speed camera ahead",
customInstructionListener,
preferences
);
Check the speed warnings and landmark & overlay alarms docs to learn how to get notified about speed warnings and reports.