Voice instructions
Navigation SDK for Android is only available upon request. Contact us to get started.
The Navigation module includes Text To Speech (TTS) functionality for generating voice instructions. TTS is deployed as a separate module. This means that you have to add the following dependency to your module’s build.gradle.kts
before you can use it.
implementation("com.tomtom.sdk:tts:1.19.0")
TTS engine
The TTS engine is responsible for providing voice synthesis for messages. The Navigation module provides a default AndroidTextToSpeechEngine
based on Android’s TextToSpeech. However, you can also define a custom engine for text to speech conversion. Any custom engine must conform to the TextToSpeechEngine
interface.
The TextToSpeech
class is a facade for performing operations on TextToSpeechEngine
. It takes care of queuing messages based on priority.
You can create TextToSpeech
in two different ways.
- A
TextToSpeech
that uses the defaultAndroidTextToSpeechEngine
engine underneath:val androidTtsEngine = AndroidTextToSpeechEngine(applicationContext)val tts = TextToSpeech(androidTtsEngine) - A
TextToSpeech
with a customTextToSpeechEngine
for voice synthesis:val customTts = TextToSpeech(customTtsEngine)
OnEngineReadyListener
You can listen for whether the TextToSpeechEngine
is ready to be used. To do so, set OnEngineReadyListener
to TextToSpeech
or to TextToSpeechEngine
itself. If the listener has already been added, IllegalArgumentException
is thrown. OnEngineReadyListener.onReady()
is called when the engine is ready. OnEngineReadyListener.onError(TextToSpeechEngineError)
is triggered if engine initialization ends with an error. TextToSpeechEngineError
provides the reason for the failure.
1val onEngineReadyListener =2 object : OnEngineReadyListener {3 override fun onReady() {4 // YOUR CODE GOES HERE5 }67 override fun onError(error: TextToSpeechEngineError) {8 // YOUR CODE GOES HERE9 }10 }11tts.addOnEngineReadyListener(onEngineReadyListener)
If the listener is no longer needed you can remove it. OnEngineReadyListener
will be automatically removed when the TextToSpeech.close()
method is called.
tts.removeOnEngineReadyListener(onEngineReadyListener)
Playing messages
The TextToSpeech.playAudioMessage(AudioMessage, MessageConfig, MessagePlaybackListener)
and the TextToSpeech.playTaggedMessage(TaggedMessage, MessageConfig, MessagePlaybackListener)
methods synthesize the provided message using the underlying TextToSpeechEngine
. The MessageConfig
parameter is used to configure the priority and time limit of the message. Message queuing depends on this priority. If the message that is currently being synthesized has an equal or higher priority to the new message, the new message will be added to the queue (taking the priorities of queued messages into account). If the message that is currently being synthesized has a lower priority than the new one, it will be interrupted and the new message will be processed right away.
val messageConfig = MessageConfig(priority = 10, timeout = TIMEOUT)
You must also provide a MessagePlaybackListener
listener. It is used to provide the playback state. It consists of 4 methods that are triggered in different states.
MessagePlaybackListener.onStart()
- message playback starts.MessagePlaybackListener.onDone()
- message playback ends.MessagePlaybackListener.onError(TextToSpeechEngineError)
- an error occurred during message playback.MessagePlaybackListener.onStop()
- message playback stopped during processing. This can happen if the client callsTextToSpeech.stopMessagePlayback()
or tries to play a message with a higher priority.
1val messagePlaybackListener =2 object : MessagePlaybackListener {3 override fun onStart() {4 // YOUR CODE GOES HERE5 }67 override fun onDone() {8 // YOUR CODE GOES HERE9 }1011 override fun onError(error: TextToSpeechEngineError) {12 // YOUR CODE GOES HERE13 }1415 override fun onStop() {16 // YOUR CODE GOES HERE17 }18 }
Playing an audio message
To play an audio message, use the TextToSpeech.playAudioMessage(AudioMessage, MessageConfig, MessagePlaybackListener)
method.
1val audioMessage =2 AudioMessage(3 message = "In 300 meters turn left",4 messageType = MessageType.Plain,5 )6tts.playAudioMessage(7 audioMessage = audioMessage,8 config = messageConfig,9 playbackListener = messagePlaybackListener,10)
The AudioMessage
can also be provided in Speech Synthesis Markup Language (SSML) format.
1val ssmlMessage =2 AudioMessage(3 "<speak>Turn left onto <phoneme alphabet='ipa' ph='e¬¬.¬f¬¬¬'>A4</phoneme> towards " +4 "<phoneme alphabet='ipa' ph=''sxep.fart.my.'2ze.^m'>Scheepvaartmuseum</phoneme></speak>",5 MessageType.Ssml,6 )
Playing a tagged message
You can also pass the TaggedMessage
with phonetics to be substituted using the TextToSpeech.playTaggedMessage(TaggedMessage, MessageConfig, MessagePlaybackListener)
method. To create a TaggedMessage
, provide the message along with the tags to be synthesized as in the example. The second parameter is PhoneticTranscription
. To create a PhoneticTranscription
, provide:
- List of phonetic transcriptions of phrases that are tagged in the message.
- List of language codes in IETF format, sorted in the same order as the transcriptions.
- Tag surrounding the phrase within the message.
- Phonetic alphabet of the transcriptions (e.g. "ipa", "lhp").
1val roadNumberPhonetics =2 PhoneticTranscription(3 transcriptions = listOf("e¬¬.¬f¬¬¬"),4 locales = listOf(Locale("nl", "NL")),5 tag = "roadNumber",6 alphabet = "ipa",7 )8val signpostPhonetics =9 PhoneticTranscription(10 transcriptions = listOf("'sxep.fart.my.'2ze.^m"),11 locales = listOf(Locale("nl", "NL")),12 tag = "signpostText",13 alphabet = "ipa",14 )
1val taggedMessage =2 TaggedMessage(3 message =4 "Turn left onto <roadNumber>A4</roadNumber> " +5 "towards<signpostText>Scheepvaartmuseum</signpostText>",6 phonetics = listOf(roadNumberPhonetics, signpostPhonetics),7 language = Locale.US,8 )910tts.playTaggedMessage(11 taggedMessage = taggedMessage,12 config = messageConfig,13 playbackListener = messagePlaybackListener,14)
Language
The language of the underlying engine can be changed. Provide the new language in the form of the Locale
. It can be done via the TextToSpeech
constructor or with the TextToSpeech.changeLanguage(Locale)
method. The language is set to American English by default.
tts.changeLanguage(Locale.forLanguageTag("pl-PL"))
Voice
The voice of the AndroidTextToSpeechEngine
can be changed. The configuration of a new Voice
is possible using the AndroidTextToSpeechEngine.setVoice(Voice)
method. Available voices can be retrieved with the AndroidTextToSpeechEngine.getVoices()
method.
After the language of the
AndroidTextToSpeechEngine
is changed, the voice of theAndroidTextToSpeechEngine
is set to the default for that language.
1val currentLanguage = Locale.forLanguageTag("pl-PL")2val voicesResult = androidTtsEngine.getVoices()3if (voicesResult.isSuccess()) {4 val availableVoices = voicesResult.value()5 val voicesInCurrentLanguage = availableVoices.filter {6 // filter voices by current language7 it.locale.language == currentLanguage.language8 }9 // select preferred voice10 val preferredVoice = voicesInCurrentLanguage.first()11 val setVoiceResult = androidTtsEngine.setVoice(preferredVoice)12 if (setVoiceResult.isFailure()) {13 // YOUR CODE GOES HERE14 val error = setVoiceResult.failure()15 }16} else {17 // YOUR CODE GOES HERE18 val error = voicesResult.failure()19}
Message cancellation
Both the TextToSpeech.playAudioMessage(AudioMessage, MessageConfig, MessagePlaybackListener)
and TextToSpeech.playTaggedMessage(TaggedMessage, MessageConfig, MessagePlaybackListener)
methods return a Cancellable
object. It can be used to cancel the message.
If the message is in the queue it will be removed. If the process has already started, it will be stopped and the
MessagePlaybackListener.onStop()
will be called.
1val cancellable =2 tts.playAudioMessage(3 audioMessage = audioMessage,4 config = messageConfig,5 playbackListener = messagePlaybackListener,6 )7cancellable.cancel()
You can also remove all messages from the queue using the TextToSpeech.clearQueue(Boolean)
method. The provided parameter specifies whether the message currently being played should be stopped as well.
tts.clearQueue(stopCurrent = true)
Disposal
If the TextToSpeechEngine
is no longer needed it should be disposed. You can do this either by calling TextToSpeechEngine.close()
directly on the engine, or dispose the underlying engine with the TextToSpeech.close()
method.
After the engine is disposed, audio messages cannot be synthesized. An
EngineNotReadyError
will be raised in that case.
tts.close()
Errors
The Navigation SDK provides the TextToSpeechEngineError
error and its subclasses to report on errors that occurred in the AndroidTextToSpeechEngine
.
AudioFocusError
- Audio focus request did not finish successfully and voice cannot be generated.EngineNotReadyError
-TextToSpeechEngine
is not initialized and voice cannot be generated.PhoneticAlphabetNotSupportedError
-TextToSpeechEngine
does not support the provided phonetic alphabet.MessageTimedOutError
- Message has timed out.UnknownError
- Unexpected error.
Next steps
Since you have learned how to work with voice instructions, here are recommendations for the next steps: