Skip to content

07 Maneuver announcements

Overview

In this tutorial, we will show you how to generate maneuver announcements from the data given by SDK_GetGuidanceInformation

Base

You can use your results from Tutorial06 as a base for our new project.

Prerequisites

Please copy the following files from the Tutorial07 folder to your project directory:

  • TTSEngine.cpp
  • TTSEngine.h
  • ManeuverStrings.cpp
  • Maneuvertrings.h

Add the new files to the CMakeLists.txt file:

set(Tutorial_SRCS
    ...
    TTSEngine.cpp
    ManeuverStrings.cpp
)

set(Tutorial_HEADER
    ... 
    TTSEngine.h
    ManeuverStrings.h
)
We also have to add the Qt5::TextToSpeech library to the cmake file:

set(Tutorial_LIBS
    ...
    Qt5::TextToSpeech
)

...

# Find the QtWidgets library
...
find_package(Qt5TextToSpeech)

Call cmake ../ in the build directory or build the ALL_BUILD project of the solution to update it.
Also, always trigger a rebuild after copying to ensure that replaced files will be newly built.

Maneuver announcement basics

In the last tutorial, we introduced a control which shows the next maneuver as a sign. In this tutorial, we will create a maneuver text which will be spoken out by the Qt text to speech engine.

Maneuver text generation settings

Maneuver text generating is done internally by the maneuver text generator. It converts the guidance information you get when calling SDK_GetGuidanceInformation to a human readable text with actual maneuver hints. This text is suitable to be spoken by a text to speech engine (TTS).

The generator can also produce output which is intended to prerecorded static wave files, but we will not go into detail of this functionality. For now, we will output readable text for a provided TTS engine. The announcement text can optionally be completed with the current street name and sign post information. Also, the decimal delimiter sign can be set for outputting numbers.

The maneuver generator settings can be adjusted by filling the struct SDK_ManeuverGeneratorSettings.

The struct SDK_ManeuverGeneratorSettings has the following members:

Type Name Description
SDK_HDRINFO Size Size of the structure - for controlling
SDK_HDRINFO version Version of the structure - for controlling. Use SDK_ManeuverGeneratorSettingsVERSION
SDK_WCHAR_T Separator Decimal separator sign used for the TTS output variant
SDK_INT4 OutputType The output type of the maneuver generator. See Maneuver generator output types for details
SDK_BOOL SpeakStreetNames If true, streetnames will be included in the output maneuvertext (only used if output type is SDK_spTTS)
SDK_BOOL SpeakSignPosts If true, signpost entries will be included in the output maneuver text (only used if output type is SDK_spTTS)

The settings will be updated by calling SDK_SetManeuverGeneratorSettings(). You can also get the current settings by calling SDK_GetManeuverGeneratorSettings().

Maneuver text generation

Important

The maneuver text is only generated when a navigation is active. Also, the generator takes care of the timings and the number of announcements. It only delivers a text when it is the right time for an announcement. Every announcement will only be generated once, further calls to the function in the same state will return an empty text. Because of this, calling of SDK_GetManeuverText() outside the navigation loop is not recommended and can lead to unwanted behaviour!

The maneuver text will be generated by the function:

SDK_GetManeuverText(const SDK_NavigationInformation * pNavigationInformation, SDK_WCHAR_T * pManeuverText, SDK_StringCBFuncT pStringRetrieverCallBackFunc)`.  
The method has the following input parameters:

Parameter type Parameter name Description
SDK_NavigationInformation* pNavigationInformation A pointer to a navigation information struct which provides the maneuver data (we get this by calling SDK_GetGuidanceInformation() in our NavigationLoop)
SDK_WCHAR_T* pManeuverText Pointer to a text string which will be filled with the maneuver text
SDK_StringCBFuncT pStringRetrieverCallBackFunc A callback to be implemented for retrieving maneuver strings identified by an id

This pStringRetrieverCallBackFunc function is called by the maneuver generator to get a localized string for the current string id and has to be implemented by the user. This function is used by the manneuver generator to

  • get a localized grammar for a distinct maneuver sentence and
  • get particular sentences for a maneuver

It is of type SDL_StringCBFuncT and its signature is static const SDK_WCHAR_T * stringRetriever(SDK_INT4). We will show later how to use this callback in an example.

The maneuver generator builds up an announcement text by checking which maneuvers will be next on the current route. It tries to get the correct grammar string from the string retriever. Such a grammar looks like "in |DISTANCE |TUNNEL |MANOEUVRE1 |MANOEUVREADDON |STREETNAME |SIGNPOST |, then |IMMEDIATELY |MANOEUVRE2 |MANOEUVREADDON". The fields will be replaced by the correct sentences, which also will be retrieved by the callback function.

The advantage of this approach is that the generator doesn't need any information about localization. All relevant localized strings and even the localized grammar of the sentence are stored outside the generator. The generator itself knows only IDs and gets the correct text by calling the callback function with the corresponding ID.

We created a class named ManeuverStrings to return particular strings for a given localization:

ManeuverStrings.h:

class ManeuverStrings
{
public:
    ManeuverStrings(void);
    ~ManeuverStrings(void);

    static const SDK_WCHAR_T * getString(SDK_INT4 id, SDK_INT4 cc);
};

ManeuverStrings.cpp

 #include "ManeuverStrings.h"

ManeuverStrings::ManeuverStrings(void)
{
}

const SDK_WCHAR_T * ManeuverStrings::getString(SDK_INT4 id, SDK_INT4 cc)
{
    if(cc == 49)
    {
        switch(id)
        {
            case SDK_MG_guidance_continue: return L"weiter";
            case SDK_MG_guidance_turnLeft: return L"links abbiegen";
            case SDK_MG_guidance_turnRight: return L"rechts abbiegen";
            case SDK_MG_guidance_straightLeft: return L"scharf links abbiegen";
            case SDK_MG_guidance_straightRight: return L"scharf rechts abbiegen";
            case SDK_MG_guidance_holdHalfLeft: return L"links halten";
            case SDK_MG_guidance_holdHalfRight: return L"rechts halten";
            case SDK_MG_guidance_holdStraight: return L"geradeaus halten";
            case SDK_MG_guidance_roundaboutExit1: return L"Im Kreisverkehr die erste Ausfahrt nehmen";
            case SDK_MG_guidance_roundaboutExit2: return L"Im Kreisverkehr die zweite Ausfahrt nehmen";
            case SDK_MG_guidance_roundaboutExit3: return L"Im Kreisverkehr die dritte Ausfahrt nehmen";
            case SDK_MG_guidance_roundaboutExit4: return L"Im Kreisverkehr die vierte Ausfahrt nehmen";
            case SDK_MG_guidance_roundaboutExit5: return L"Im Kreisverkehr die fünfte Ausfahrt nehmen";
            case SDK_MG_guidance_roundaboutExit6: return L"Im Kreisverkehr die sechste Ausfahrt nehmen";
            case SDK_MG_guidance_roundaboutExit7: return L"Im Kreisverkehr die siebte Ausfahrt nehmen";
            case SDK_MG_guidance_roundaboutExit8: return L"Im Kreisverkehr die achte Ausfahrt nehmen";
            case SDK_MG_guidance_roundaboutExit9: return L"Im Kreisverkehr die neunte Ausfahrt nehmen";
            case SDK_MG_guidance_roundaboutExit10: return L"Im Kreisverkehr die zehnte Ausfahrt nehmen";
            case SDK_MG_guidance_roundaboutExit11: return L"Im Kreisverkehr die elfte Ausfahrt nehmen";
            case SDK_MG_guidance_roundaboutExit12: return L"Im Kreisverkehr die zwölfte Ausfahrt nehmen";
            case SDK_MG_guidance_uTurn: return L"Falls möglich bitte umkehren";
            case SDK_MG_guidance_turnAround: return L"wenden";
            case SDK_MG_guidance_targetReached: return L"haben Sie ihr Ziel erreicht!";
            case SDK_MG_guidance_motorwayTurnRight: return L"rechts abbiegen danach fahren Sie auf die Autobahn auf";
            case SDK_MG_guidance_motorwayTurnLeft: return L"links abbiegen danach fahren Sie auf die Autobahn auf";
            case SDK_MG_guidance_motorwayTurnHalfRight: return L"rechts halten danach fahren Sie auf die Autobahn auf";
            case SDK_MG_guidance_motorwayTurnHalfLeft: return L"links halten danach fahren Sie auf die Autobahn auf";
            case SDK_MG_guidance_motorwayEnter: return L"fahren Sie auf die Autobahn auf";
            case SDK_MG_guidance_motorwayExit: return L"fahren Sie von der Autobahn ab";
            case SDK_MG_guidance_stopover: return L"Übergangspunkt";
            case SDK_MG_guidance_targetAreaEnter: return L"haben Sie das Zielgebiet erreicht.";
            case SDK_MG_guidance_targetAreaEnterRouteList: return L"Sie haben das Zielgebiet erreicht.";
            case SDK_MG_guidance_startAreaExit: return L"Sie haben das Startgebiet verlassen.";
            case SDK_MG_guidance_border: return L"Grenzübertritt";
            case SDK_MG_guidance_motorroadEnter: return L"fahren Sie auf die Schnellstraße auf";
            case SDK_MG_guidance_motorroadExit: return L"fahren Sie von der Schnellstraße ab";
            case SDK_MG_guidance_ferryEnter: return L"und auf die Fähre auffahren";
            case SDK_MG_guidance_ferryExit: return L"und von der Fähre abfahren";
            case SDK_MG_guidance_distMan1ThenMan2: return L"in |DISTANCE |TUNNEL |MANOEUVRE1 |MANOEUVREADDON |STREETNAME |SIGNPOST |, dann |IMMEDIATELY |MANOEUVRE2 |MANOEUVREADDON";
            case SDK_MG_guidance_distMan1: return L"in |DISTANCE |TUNNEL |MANOEUVRE1 |MANOEUVREADDON |STREETNAME |SIGNPOST";
            case SDK_MG_guidance_followRoad: return L"dem Straßenverlauf folgen";
            case SDK_MG_guidance_tunnelAfter: return L"nach dem Tunnel";
            case SDK_MG_guidance_endOfStreetMan: return L"am Ende der Straße |DISTANCE |TUNNEL |MANOEUVRE1 |MANOEUVREADDON |STREETNAME |SIGNPOST";
            case SDK_MG_guidance_tunnelIn: return L"im Tunnel";
            case SDK_MG_guidance_streetDirection: return L"in Richtung";
            case SDK_MG_guidance_streetDirectionSep: return L"auf";
            case SDK_MG_guidance_nowMan1: return L"jetzt |TUNNEL |MANOEUVRE1 |MANOEUVREADDON |STREETNAME";
            case SDK_MG_guidance_nowMan1ThenMan2: return L"jetzt |TUNNEL |MANOEUVRE1 |MANOEUVREADDON |, dann |IMMEDIATELY |MANOEUVRE2 |MANOEUVREADDON";
            case SDK_MG_guidance_now: return L"jetzt";

            case SDK_MG_guidance_continue_sample: return L"dir16";
            case SDK_MG_guidance_turnLeft_sample: return L"dir03";
            case SDK_MG_guidance_turnRight_sample: return L"dir04";
            case SDK_MG_guidance_straightLeft_sample: return L"dir09 dir03";
            case SDK_MG_guidance_straightRight_sample: return L"dir09 dir04";
            case SDK_MG_guidance_holdHalfLeft_sample: return L"dir05";
            case SDK_MG_guidance_holdHalfRight_sample: return L"dir06";
            case SDK_MG_guidance_holdStraight_sample: return L"dir08";
            case SDK_MG_guidance_roundaboutExit1_sample: return L"circ01";
            case SDK_MG_guidance_roundaboutExit2_sample: return L"circ02";
            case SDK_MG_guidance_roundaboutExit3_sample: return L"circ03";
            case SDK_MG_guidance_roundaboutExit4_sample: return L"circ04";
            case SDK_MG_guidance_roundaboutExit5_sample: return L"circ05";
            case SDK_MG_guidance_roundaboutExit6_sample: return L"circ06";
            case SDK_MG_guidance_roundaboutExit7_sample: return L"circ07";
            case SDK_MG_guidance_roundaboutExit8_sample: return L"circ08";
            case SDK_MG_guidance_roundaboutExit9_sample: return L"circ09";
            case SDK_MG_guidance_roundaboutExit10_sample: return L"circ10";
            case SDK_MG_guidance_roundaboutExit11_sample: return L"circ11";
            case SDK_MG_guidance_roundaboutExit12_sample: return L"circ12";
            case SDK_MG_guidance_uTurn_sample: return L"dir19";
            case SDK_MG_guidance_turnAround_sample: return L"dir21";
            case SDK_MG_guidance_targetReached_sample: return L"dir17";
            case SDK_MG_guidance_motorwayTurnRight_sample: return L"dir04 distx02 dir43";
            case SDK_MG_guidance_motorwayTurnLeft_sample: return L"dir03 distx02 dir43";
            case SDK_MG_guidance_motorwayTurnHalfRight_sample: return L"dir06 distx02 dir43";
            case SDK_MG_guidance_motorwayTurnHalfLeft_sample: return L"dir05 distx02 dir43";
            case SDK_MG_guidance_motorwayEnter_sample: return L"dir43";
            case SDK_MG_guidance_motorwayExit_sample: return L"dir12 dir13";
            case SDK_MG_guidance_targetAreaEnterRouteList_sample: return L"misc09";
            case SDK_MG_guidance_startAreaExit_sample: return L"misc04";
            case SDK_MG_guidance_border_sample: return L"dir44";
            case SDK_MG_guidance_motorroadEnter_sample: return L"dir42";
            case SDK_MG_guidance_motorroadExit_sample: return L"dir41";
            case SDK_MG_guidance_ferryEnter_sample: return L"dir45";
            case SDK_MG_guidance_ferryExit_sample: return L"dir46";
            case SDK_MG_guidance_distMan1ThenMan2_sample: return L"distx01 |DISTANCE |TUNNEL |MANOEUVRE1 |MANOEUVREADDON |distx09 |IMMEDIATELY |MANOEUVRE2 |MANOEUVREADDON";
            case SDK_MG_guidance_distMan1_sample: return L"distx01 |DISTANCE |TUNNEL |MANOEUVRE1 |MANOEUVREADDON";
            case SDK_MG_guidance_followRoad_sample: return L"dir16";
            case SDK_MG_guidance_tunnelAfter_sample: return L"distx08";
            case SDK_MG_guidance_endOfStreetMan_sample: return L"distx06  |DISTANCE |TUNNEL |MANOEUVRE1 |MANOEUVREADDON";
            case SDK_MG_guidance_tunnelIn_sample: return L"distx07";  

            case SDK_MG_guidance_nowMan1_sample: return L"distx04 |TUNNEL |MANOEUVRE1 |MANOEUVREADDON";
            case SDK_MG_guidance_nowMan1ThenMan2_sample: return L"distx04 |TUNNEL |MANOEUVRE1 |MANOEUVREADDON |distx09 |IMMEDIATELY |MANOEUVRE2 |MANOEUVREADDON";
            case SDK_MG_guidance_now_sample: return L"distx04";

            case SDK_MG_guidance_streetDirection_sample: return L"";  
            case SDK_MG_guidance_streetDirectionSep_sample: return L"";
            case SDK_MG_guidance_stopover_sample: return L"";  
            case SDK_MG_guidance_targetAreaEnter_sample: return L"";

            case SDK_MG_guidance_50m_sample: return L"dis01";
            case SDK_MG_guidance_100m_sample: return L"dis02";
            case SDK_MG_guidance_150m_sample: return L"dis03";
            case SDK_MG_guidance_200m_sample: return L"dis04";
            case SDK_MG_guidance_250m_sample: return L"dis05";
            case SDK_MG_guidance_300m_sample: return L"dis06";
            case SDK_MG_guidance_350m_sample: return L"dis07";
            case SDK_MG_guidance_400m_sample: return L"dis08";
            case SDK_MG_guidance_500m_sample: return L"dis09";
            case SDK_MG_guidance_600m_sample: return L"dis10";
            case SDK_MG_guidance_700m_sample: return L"dis11";
            case SDK_MG_guidance_800m_sample: return L"dis12";
            case SDK_MG_guidance_900m_sample: return L"dis13";
            case SDK_MG_guidance_1000m_sample: return L"dis14";
            case SDK_MG_guidance_1200m_sample: return L"dis15";
            case SDK_MG_guidance_1400m_sample: return L"dis16";
            case SDK_MG_guidance_1500m_sample: return L"dis17";
            case SDK_MG_guidance_1600m_sample: return L"dis18";
            case SDK_MG_guidance_1800m_sample: return L"dis19";
            case SDK_MG_guidance_2000m_sample: return L"dis20";
            case SDK_MG_guidance_2500m_sample: return L"dis21";
            case SDK_MG_guidance_3000m_sample: return L"dis22";
            case SDK_MG_guidance_3500m_sample: return L"dis23";
            case SDK_MG_guidance_4000m_sample: return L"dis24";
            case SDK_MG_guidance_4500m_sample: return L"dis25";
            case SDK_MG_guidance_1_0km_sample: return L"dis26";
            case SDK_MG_guidance_1_1km_sample: return L"dis26";
            case SDK_MG_guidance_1_2km_sample: return L"dis15";
            case SDK_MG_guidance_1_3km_sample: return L"dis15";
            case SDK_MG_guidance_1_4km_sample: return L"dis16";
            case SDK_MG_guidance_1_5km_sample: return L"dis17";
            case SDK_MG_guidance_1_6km_sample: return L"dis18";
            case SDK_MG_guidance_1_7km_sample: return L"dis18";
            case SDK_MG_guidance_1_8km_sample: return L"dis19";
            case SDK_MG_guidance_1_9km_sample: return L"dis19";
            case SDK_MG_guidance_2_0km_sample: return L"dis27";
            case SDK_MG_guidance_2_1km_sample: return L"dis27";
            case SDK_MG_guidance_2_2km_sample: return L"dis27";
            case SDK_MG_guidance_2_3km_sample: return L"dis21";
            case SDK_MG_guidance_2_4km_sample: return L"dis21";
            case SDK_MG_guidance_2_5km_sample: return L"dis21";
            case SDK_MG_guidance_2_6km_sample: return L"dis21";
            case SDK_MG_guidance_2_7km_sample: return L"dis21";
            case SDK_MG_guidance_2_8km_sample: return L"dis28";
            case SDK_MG_guidance_2_9km_sample: return L"dis28";
            case SDK_MG_guidance_3_0km_sample: return L"dis28";
            case SDK_MG_guidance_3_5km_sample: return L"dis23";
            case SDK_MG_guidance_4_0km_sample: return L"dis29";
            case SDK_MG_guidance_4_5km_sample: return L"dis25";
            case SDK_MG_guidance_5_0km_sample: return L"dis30";
            case SDK_MG_guidance_6_0km_sample: return L"dis31";
            case SDK_MG_guidance_7_0km_sample: return L"dis32";
            case SDK_MG_guidance_8_0km_sample: return L"dis33";
            case SDK_MG_guidance_9_0km_sample: return L"dis34";

            case SDK_MG_guidance_50yd_sample: return L"dis01_mi";
            case SDK_MG_guidance_100yd_sample: return L"dis02_mi";
            case SDK_MG_guidance_150yd_sample: return L"dis03_mi";
            case SDK_MG_guidance_200yd_sample: return L"dis04_mi";
            case SDK_MG_guidance_250yd_sample: return L"dis05_mi";
            case SDK_MG_guidance_300yd_sample: return L"dis06_mi";
            case SDK_MG_guidance_350yd_sample: return L"dis07_mi";
            case SDK_MG_guidance_400yd_sample: return L"dis08_mi";
            case SDK_MG_guidance_500yd_sample: return L"dis09_mi";
            case SDK_MG_guidance_600yd_sample: return L"dis10_mi";
            case SDK_MG_guidance_700yd_sample: return L"dis11_mi";
            case SDK_MG_guidance_800yd_sample: return L"dis12_mi";
            case SDK_MG_guidance_900yd_sample: return L"dis13_mi";
            case SDK_MG_guidance_1000yd_sample: return L"dis14_mi";
            case SDK_MG_guidance_1200yd_sample: return L"dis15_mi";
            case SDK_MG_guidance_1400yd_sample: return L"dis16_mi";
            case SDK_MG_guidance_1500yd_sample: return L"dis17_mi";
            case SDK_MG_guidance_1600yd_sample: return L"dis18_mi";
            case SDK_MG_guidance_1800yd_sample: return L"dis19_mi";
            case SDK_MG_guidance_2000yd_sample: return L"dis20_mi";
            case SDK_MG_guidance_2500yd_sample: return L"dis21_mi";
            case SDK_MG_guidance_3000yd_sample: return L"dis22_mi";
            case SDK_MG_guidance_3500yd_sample: return L"dis23_mi";
            case SDK_MG_guidance_4000yd_sample: return L"dis24_mi";
            case SDK_MG_guidance_4500yd_sample: return L"dis25_mi";
            case SDK_MG_guidance_1_0mi_sample: return L"dis26_mi";
            case SDK_MG_guidance_1_1mi_sample: return L"dis14_mi";
            case SDK_MG_guidance_1_2mi_sample: return L"dis15_mi";
            case SDK_MG_guidance_1_3mi_sample: return L"dis16_mi";
            case SDK_MG_guidance_1_4mi_sample: return L"dis16_mi";
            case SDK_MG_guidance_1_5mi_sample: return L"dis17_mi";
            case SDK_MG_guidance_1_6mi_sample: return L"dis18_mi";
            case SDK_MG_guidance_1_7mi_sample: return L"dis19_mi";
            case SDK_MG_guidance_1_8mi_sample: return L"dis19_mi";
            case SDK_MG_guidance_2_0mi_sample: return L"dis27_mi";
            case SDK_MG_guidance_3_0mi_sample: return L"dis28_mi";
            case SDK_MG_guidance_4_0mi_sample: return L"dis29_mi";
            case SDK_MG_guidance_5_0mi_sample: return L"dis30_mi";
            case SDK_MG_guidance_6_0mi_sample: return L"dis31_mi";
            case SDK_MG_guidance_7_0mi_sample: return L"dis32_mi";
            case SDK_MG_guidance_8_0mi_sample: return L"dis33_mi";
            case SDK_MG_guidance_9_0mi_sample: return L"dis34_mi";
            default: return L"";
        }
    }
    else if(cc == 33)
    {
        switch(id)
        {
            case SDK_MG_guidance_continue: return L"continue";
            case SDK_MG_guidance_turnLeft: return L"turn left";
            case SDK_MG_guidance_turnRight: return L"turn right";
            case SDK_MG_guidance_straightLeft: return L"take sharp left turn";
            case SDK_MG_guidance_straightRight: return L"take sharp right turn";
            case SDK_MG_guidance_holdHalfLeft: return L"keep left";
            case SDK_MG_guidance_holdHalfRight: return L"keep right";
            case SDK_MG_guidance_holdStraight: return L"keep driving straight ahead";
            case SDK_MG_guidance_roundaboutExit1: return L"leave roundabout at first exit";
            case SDK_MG_guidance_roundaboutExit2: return L"leave roundabout at second exit";
            case SDK_MG_guidance_roundaboutExit3: return L"leave roundabout at third exit";
            case SDK_MG_guidance_roundaboutExit4: return L"leave roundabout at fourth exit";
            case SDK_MG_guidance_roundaboutExit5: return L"leave roundabout at fifth exit";
            case SDK_MG_guidance_roundaboutExit6: return L"leave roundabout at sixth exit";
            case SDK_MG_guidance_roundaboutExit7: return L"leave roundabout at seventh exit";
            case SDK_MG_guidance_roundaboutExit8: return L"leave roundabout at eighth exit";
            case SDK_MG_guidance_roundaboutExit9: return L"leave roundabout at ninth exit";
            case SDK_MG_guidance_roundaboutExit10: return L"leave roundabout at tenth exit";
            case SDK_MG_guidance_roundaboutExit11: return L"leave roundabout at eleventh exit";
            case SDK_MG_guidance_roundaboutExit12: return L"leave roundabout at twelfth exit";
            case SDK_MG_guidance_uTurn: return L"If possible, turn around";
            case SDK_MG_guidance_turnAround: return L"turn around";
            case SDK_MG_guidance_targetReached: return L"you will have reached your destination!";
            case SDK_MG_guidance_motorwayTurnRight: return L"turn right then join the motorway";
            case SDK_MG_guidance_motorwayTurnLeft: return L"turn left, then join the motorway";
            case SDK_MG_guidance_motorwayTurnHalfRight: return L"keep right, then join the motorway";
            case SDK_MG_guidance_motorwayTurnHalfLeft: return L"keep left, then join the motorway";
            case SDK_MG_guidance_motorwayEnter: return L"join the motorway";
            case SDK_MG_guidance_motorwayExit: return L"leave the motorway";
            case SDK_MG_guidance_stopover: return L"Stopover point";
            case SDK_MG_guidance_targetAreaEnter: return L"you have reached the destination area.";
            case SDK_MG_guidance_targetAreaEnterRouteList: return L"You have reached the destination area.";
            case SDK_MG_guidance_startAreaExit: return L"You have left the start area.";
            case SDK_MG_guidance_border: return L"border crossing";
            case SDK_MG_guidance_motorroadEnter: return L"join the highway";
            case SDK_MG_guidance_motorroadExit: return L"leave the highway";
            case SDK_MG_guidance_ferryEnter: return L"and drive onto the ferry";
            case SDK_MG_guidance_ferryExit: return L"and drive off the ferry";
            case SDK_MG_guidance_distMan1ThenMan2: return L"in |DISTANCE |TUNNEL |MANOEUVRE1 |MANOEUVREADDON |STREETNAME |SIGNPOST |, then |IMMEDIATELY |MANOEUVRE2 |MANOEUVREADDON";
            case SDK_MG_guidance_distMan1: return L"in |DISTANCE |TUNNEL |MANOEUVRE1 |MANOEUVREADDON |STREETNAME |SIGNPOST";
            case SDK_MG_guidance_followRoad: return L"follow the direction of the road";
            case SDK_MG_guidance_tunnelAfter: return L"after the tunnel";
            case SDK_MG_guidance_endOfStreetMan: return L"At the end of the road |DISTANCE |TUNNEL |MANOEUVRE1 |MANOEUVREADDON |STREETNAME |SIGNPOST";
            case SDK_MG_guidance_tunnelIn: return L"in the tunnel";
            case SDK_MG_guidance_streetDirection: return L"towards";
            case SDK_MG_guidance_streetDirectionSep: return L"onto";
            case SDK_MG_guidance_nowMan1: return L"now |TUNNEL |MANOEUVRE1 |MANOEUVREADDON |STREETNAME";
            case SDK_MG_guidance_nowMan1ThenMan2: return L"now |TUNNEL |MANOEUVRE1 |MANOEUVREADDON |, then |IMMEDIATELY |MANOEUVRE2 |MANOEUVREADDON";
            case SDK_MG_guidance_now: return L"now";

            case SDK_MG_guidance_continue_sample: return L"dir16";
            case SDK_MG_guidance_turnLeft_sample: return L"dir03";
            case SDK_MG_guidance_turnRight_sample: return L"dir04";
            case SDK_MG_guidance_straightLeft_sample: return L"dir09 dir03";
            case SDK_MG_guidance_straightRight_sample: return L"dir09 dir04";
            case SDK_MG_guidance_holdHalfLeft_sample: return L"dir05";
            case SDK_MG_guidance_holdHalfRight_sample: return L"dir06";
            case SDK_MG_guidance_holdStraight_sample: return L"dir08";
            case SDK_MG_guidance_roundaboutExit1_sample: return L"circ01";
            case SDK_MG_guidance_roundaboutExit2_sample: return L"circ02";
            case SDK_MG_guidance_roundaboutExit3_sample: return L"circ03";
            case SDK_MG_guidance_roundaboutExit4_sample: return L"circ04";
            case SDK_MG_guidance_roundaboutExit5_sample: return L"circ05";
            case SDK_MG_guidance_roundaboutExit6_sample: return L"circ06";
            case SDK_MG_guidance_roundaboutExit7_sample: return L"circ07";
            case SDK_MG_guidance_roundaboutExit8_sample: return L"circ08";
            case SDK_MG_guidance_roundaboutExit9_sample: return L"circ09";
            case SDK_MG_guidance_roundaboutExit10_sample: return L"circ10";
            case SDK_MG_guidance_roundaboutExit11_sample: return L"circ11";
            case SDK_MG_guidance_roundaboutExit12_sample: return L"circ12";
            case SDK_MG_guidance_uTurn_sample: return L"dir19";
            case SDK_MG_guidance_turnAround_sample: return L"dir21";
            case SDK_MG_guidance_targetReached_sample: return L"dir17";
            case SDK_MG_guidance_motorwayTurnRight_sample: return L"dir04 distx02 dir43";
            case SDK_MG_guidance_motorwayTurnLeft_sample: return L"dir03 distx02 dir43";
            case SDK_MG_guidance_motorwayTurnHalfRight_sample: return L"dir06 distx02 dir43";
            case SDK_MG_guidance_motorwayTurnHalfLeft_sample: return L"dir05 distx02 dir43";
            case SDK_MG_guidance_motorwayEnter_sample: return L"dir43";
            case SDK_MG_guidance_motorwayExit_sample: return L"dir12 dir13";
            case SDK_MG_guidance_targetAreaEnterRouteList_sample: return L"misc09";
            case SDK_MG_guidance_startAreaExit_sample: return L"misc04";
            case SDK_MG_guidance_border_sample: return L"dir44";
            case SDK_MG_guidance_motorroadEnter_sample: return L"dir42";
            case SDK_MG_guidance_motorroadExit_sample: return L"dir41";
            case SDK_MG_guidance_ferryEnter_sample: return L"dir45";
            case SDK_MG_guidance_ferryExit_sample: return L"dir46";
            case SDK_MG_guidance_distMan1ThenMan2_sample: return L"distx01 |DISTANCE |TUNNEL |MANOEUVRE1 |MANOEUVREADDON |distx09 |IMMEDIATELY |MANOEUVRE2 |MANOEUVREADDON";
            case SDK_MG_guidance_distMan1_sample: return L"distx01 |DISTANCE |TUNNEL |MANOEUVRE1 |MANOEUVREADDON";
            case SDK_MG_guidance_followRoad_sample: return L"dir16";
            case SDK_MG_guidance_tunnelAfter_sample: return L"distx08";
            case SDK_MG_guidance_endOfStreetMan_sample: return L"distx06  |DISTANCE |TUNNEL |MANOEUVRE1 |MANOEUVREADDON";
            case SDK_MG_guidance_tunnelIn_sample: return L"distx07";  

            case SDK_MG_guidance_nowMan1_sample: return L"distx04 |TUNNEL |MANOEUVRE1 |MANOEUVREADDON";
            case SDK_MG_guidance_nowMan1ThenMan2_sample: return L"distx04 |TUNNEL |MANOEUVRE1 |MANOEUVREADDON |distx09 |IMMEDIATELY |MANOEUVRE2 |MANOEUVREADDON";
            case SDK_MG_guidance_now_sample: return L"distx04";

            case SDK_MG_guidance_streetDirection_sample: return L"";  
            case SDK_MG_guidance_streetDirectionSep_sample: return L"";
            case SDK_MG_guidance_stopover_sample: return L"";  
            case SDK_MG_guidance_targetAreaEnter_sample: return L"";

            case SDK_MG_guidance_50m_sample: return L"dis01";
            case SDK_MG_guidance_100m_sample: return L"dis02";
            case SDK_MG_guidance_150m_sample: return L"dis03";
            case SDK_MG_guidance_200m_sample: return L"dis04";
            case SDK_MG_guidance_250m_sample: return L"dis05";
            case SDK_MG_guidance_300m_sample: return L"dis06";
            case SDK_MG_guidance_350m_sample: return L"dis07";
            case SDK_MG_guidance_400m_sample: return L"dis08";
            case SDK_MG_guidance_500m_sample: return L"dis09";
            case SDK_MG_guidance_600m_sample: return L"dis10";
            case SDK_MG_guidance_700m_sample: return L"dis11";
            case SDK_MG_guidance_800m_sample: return L"dis12";
            case SDK_MG_guidance_900m_sample: return L"dis13";
            case SDK_MG_guidance_1000m_sample: return L"dis14";
            case SDK_MG_guidance_1200m_sample: return L"dis15";
            case SDK_MG_guidance_1400m_sample: return L"dis16";
            case SDK_MG_guidance_1500m_sample: return L"dis17";
            case SDK_MG_guidance_1600m_sample: return L"dis18";
            case SDK_MG_guidance_1800m_sample: return L"dis19";
            case SDK_MG_guidance_2000m_sample: return L"dis20";
            case SDK_MG_guidance_2500m_sample: return L"dis21";
            case SDK_MG_guidance_3000m_sample: return L"dis22";
            case SDK_MG_guidance_3500m_sample: return L"dis23";
            case SDK_MG_guidance_4000m_sample: return L"dis24";
            case SDK_MG_guidance_4500m_sample: return L"dis25";
            case SDK_MG_guidance_1_0km_sample: return L"dis26";
            case SDK_MG_guidance_1_1km_sample: return L"dis26";
            case SDK_MG_guidance_1_2km_sample: return L"dis15";
            case SDK_MG_guidance_1_3km_sample: return L"dis15";
            case SDK_MG_guidance_1_4km_sample: return L"dis16";
            case SDK_MG_guidance_1_5km_sample: return L"dis17";
            case SDK_MG_guidance_1_6km_sample: return L"dis18";
            case SDK_MG_guidance_1_7km_sample: return L"dis18";
            case SDK_MG_guidance_1_8km_sample: return L"dis19";
            case SDK_MG_guidance_1_9km_sample: return L"dis19";
            case SDK_MG_guidance_2_0km_sample: return L"dis27";
            case SDK_MG_guidance_2_1km_sample: return L"dis27";
            case SDK_MG_guidance_2_2km_sample: return L"dis27";
            case SDK_MG_guidance_2_3km_sample: return L"dis21";
            case SDK_MG_guidance_2_4km_sample: return L"dis21";
            case SDK_MG_guidance_2_5km_sample: return L"dis21";
            case SDK_MG_guidance_2_6km_sample: return L"dis21";
            case SDK_MG_guidance_2_7km_sample: return L"dis21";
            case SDK_MG_guidance_2_8km_sample: return L"dis28";
            case SDK_MG_guidance_2_9km_sample: return L"dis28";
            case SDK_MG_guidance_3_0km_sample: return L"dis28";
            case SDK_MG_guidance_3_5km_sample: return L"dis23";
            case SDK_MG_guidance_4_0km_sample: return L"dis29";
            case SDK_MG_guidance_4_5km_sample: return L"dis25";
            case SDK_MG_guidance_5_0km_sample: return L"dis30";
            case SDK_MG_guidance_6_0km_sample: return L"dis31";
            case SDK_MG_guidance_7_0km_sample: return L"dis32";
            case SDK_MG_guidance_8_0km_sample: return L"dis33";
            case SDK_MG_guidance_9_0km_sample: return L"dis34";

            case SDK_MG_guidance_50yd_sample: return L"dis01_mi";
            case SDK_MG_guidance_100yd_sample: return L"dis02_mi";
            case SDK_MG_guidance_150yd_sample: return L"dis03_mi";
            case SDK_MG_guidance_200yd_sample: return L"dis04_mi";
            case SDK_MG_guidance_250yd_sample: return L"dis05_mi";
            case SDK_MG_guidance_300yd_sample: return L"dis06_mi";
            case SDK_MG_guidance_350yd_sample: return L"dis07_mi";
            case SDK_MG_guidance_400yd_sample: return L"dis08_mi";
            case SDK_MG_guidance_500yd_sample: return L"dis09_mi";
            case SDK_MG_guidance_600yd_sample: return L"dis10_mi";
            case SDK_MG_guidance_700yd_sample: return L"dis11_mi";
            case SDK_MG_guidance_800yd_sample: return L"dis12_mi";
            case SDK_MG_guidance_900yd_sample: return L"dis13_mi";
            case SDK_MG_guidance_1000yd_sample: return L"dis14_mi";
            case SDK_MG_guidance_1200yd_sample: return L"dis15_mi";
            case SDK_MG_guidance_1400yd_sample: return L"dis16_mi";
            case SDK_MG_guidance_1500yd_sample: return L"dis17_mi";
            case SDK_MG_guidance_1600yd_sample: return L"dis18_mi";
            case SDK_MG_guidance_1800yd_sample: return L"dis19_mi";
            case SDK_MG_guidance_2000yd_sample: return L"dis20_mi";
            case SDK_MG_guidance_2500yd_sample: return L"dis21_mi";
            case SDK_MG_guidance_3000yd_sample: return L"dis22_mi";
            case SDK_MG_guidance_3500yd_sample: return L"dis23_mi";
            case SDK_MG_guidance_4000yd_sample: return L"dis24_mi";
            case SDK_MG_guidance_4500yd_sample: return L"dis25_mi";
            case SDK_MG_guidance_1_0mi_sample: return L"dis26_mi";
            case SDK_MG_guidance_1_1mi_sample: return L"dis14_mi";
            case SDK_MG_guidance_1_2mi_sample: return L"dis15_mi";
            case SDK_MG_guidance_1_3mi_sample: return L"dis16_mi";
            case SDK_MG_guidance_1_4mi_sample: return L"dis16_mi";
            case SDK_MG_guidance_1_5mi_sample: return L"dis17_mi";
            case SDK_MG_guidance_1_6mi_sample: return L"dis18_mi";
            case SDK_MG_guidance_1_7mi_sample: return L"dis19_mi";
            case SDK_MG_guidance_1_8mi_sample: return L"dis19_mi";
            case SDK_MG_guidance_2_0mi_sample: return L"dis27_mi";
            case SDK_MG_guidance_3_0mi_sample: return L"dis28_mi";
            case SDK_MG_guidance_4_0mi_sample: return L"dis29_mi";
            case SDK_MG_guidance_5_0mi_sample: return L"dis30_mi";
            case SDK_MG_guidance_6_0mi_sample: return L"dis31_mi";
            case SDK_MG_guidance_7_0mi_sample: return L"dis32_mi";
            case SDK_MG_guidance_8_0mi_sample: return L"dis33_mi";
            case SDK_MG_guidance_9_0mi_sample: return L"dis34_mi";
            default: return L"";
        }
    }
    else
        return L"";
}

ManeuverStrings::~ManeuverStrings(void)
{
}

}

As you can see, the class has the central function getString(), which returns a text for a given id and country code.
The id identifies the desired text snippet and grammar. If the generator requests a string with the id "SDK_MG_guidance_holdHalfLeft", we will return "keep left" or "links halten" dependent on the country code. Our example is able to return text for english and german language. We also have strings for many other languages, so there is nothing to do beside filling the ManeuverStrings class for a quick start with localized data (please ask for more languages, if needed).

The above example contains all available IDs, so you can take it as a starting point for your own localization class.

Output format of the generated maneuver text

The generator is able to generate two output formats:

  • A "normal" text for TTS output
  • A text with all prerecorded sample names that have to be played for the current maneuver (we will not cover this functionality in this tutorial)

The first format is self explaining, the output will be a text like "in 250m, turn right onto Sunset Boulevard". You can pipe it directly to a TTS engine. The second format is used for the maneuver announcement via prerecorded samples. The output will be a list of sample names in the correct order.

Output of the announcement text through the Qt text to speech engine

To output text via TTS, we use a little helper class called TTSEngine to initialize and finalize the TTS engine, to get maneuver text and to output text through the TTS engine.

The TTSEngine class looks like this:

TTSEngine.h:

#pragma once

#include "../navigationsdk/SDKInterface.h"
#include <QTextToSpeech>

class TTSEngine : public QObject
{   
    Q_OBJECT
public:
    TTSEngine();
    ~TTSEngine();

    static const SDK_WCHAR_T * SDK_API stringRetriever(SDK_INT4 id);

    void update(const SDK_NavigationInformation& navInfo);
    void speak(const QString& text);

protected:
    QTextToSpeech * mSpeech;

protected slots:
    void stateChanged(QTextToSpeech::State);
};

TTSEngine.cpp:

#include "../navigationsdk/SDKInterface.h"
#include "TTSEngine.h"
#include "ManeuverStrings.h"
#include <QVoice>
#include <QVector>

const SDK_WCHAR_T * SDK_API TTSEngine::stringRetriever(SDK_INT4 id)
{
    return ManeuverStrings::getString(id, 33);
}
The TTSEngine::stringRetriever() function is our callback function for SDK_GetManeuverText(). It will call getString() of our ManeuverStrings class to get the correct string for the given id and country code. In our example, we will retrieve texts and grammar in english, you can change this to german (cc==49) too.

TTSEngine::TTSEngine() 
{
    SDK_ManeuverGeneratorSettings maneuverSettings;
    SDK_InitManeuverGeneratorSettings(&maneuverSettings);

    maneuverSettings.OutputType = SDK_spTTS;
    maneuverSettings.Separator = L'.';
    maneuverSettings.SpeakSignPosts = SDK_FALSE;
    maneuverSettings.SpeakStreetNames = SDK_TRUE;
    SDK_SetManeuverGeneratorSettings(&maneuverSettings);

    mSpeech = new QTextToSpeech(this);

    connect(mSpeech, SIGNAL(stateChanged(QTextToSpeech::State)), this, SLOT(stateChanged(QTextToSpeech::State)));

}
In the constructor of TTSEngine, we initialize the maneuver settings to output the text for TTS by setting the OutputType to SDK_spTTS. Numbers should be outputted with a '.' as decimal separator. We do not want to speak sign post information, so we set this to SDK_FALSE, but we want to speak out street names, so we set this to SDK_TRUE. SDK_SetManeuverGeneratorSettings() will update the maneuver settings.
We create a new QTextToSpeech() object which we later use to speak the text. We also connect the signal stateChanged() to a slot called stateChanged() in our class to get some status informations.

void TTSEngine::speak(const QString& text)
{
    mSpeech->stop();
    mSpeech->say(text.toLatin1());
}

The speak() function is the central function to speak out a given text by the TTS Engine.

void TTSEngine::update(const SDK_NavigationInformation& navInfo)
{
    SDK_WCHAR_T maneuverText[255] = { 0 };
    SDK_INT4 mRC = SDK_GetManeuverText(&navInfo, maneuverText, this->stringRetriever);
    QString text = QString::fromWCharArray(maneuverText);
    if (!text.isEmpty())
        speak(text);
}

The update() function will be called in the NavigationLoop when retrieving new guidance information. It calls SDK_GetManeuverText() to get the current maneuver text and speaks it (if not empty).

To let the NavigationLoop know the TTSEngine, we add a new parameter to its constructor with the pointer to the created TTSEngine. The engine itself will be created in the MainWindow.

Change the following in the NavigationLoop class:

NavigationLoop.h:

...
#include "TTSEngine.h"

class NavigationLoop : public QObject, public QRunnable
{   
    Q_OBJECT
public:
    NavigationLoop(MapWidget * mapWidget, TTSEngine * ttsEngine);
    ...
private:
    ...
    TTSEngine * mTTSEngine;

NavigationLoop.cpp:

NavigationLoop::NavigationLoop(MapWidget * map, TTSEngine * ttsEngine) : QObject(), QRunnable()
{
    ...
    mTTSEngine = ttsEngine; 
}

void NavigationLoop::run()
{
    ...
    if (rc == SDK_ERROR_Ok || rc == SDK_ERROR_SamePosition)
    {   
        ...
        mMap->update();

        mTTSEngine->update(navInfo);
        ...
    }
}

In the NavigationLoop class, we add a new parameter TTSEngine to the constructor. Also we add a call of TTSEngine::update() to the run() method. It is safe to do this in a separate thread like we do.

Add/Change the following in the MainWindow class:

MainWindow.h:

...
class MainWindow : public QMainWindow
{
...
private:
    ...
    TTSEngine * mTTSEngine;
    QTextToSpeech * mSpeech;
}
MainWindow.cpp:
MainWindow::MainWindow(QMainWindow * parent) : QMainWindow(parent)
{
    ...
    mTTSEngine = new TTSEngine();

    mNavigationLoop = new NavigationLoop(ui.map, mTTSEngine);
    ...
}

MainWindow:: ~MainWindow()
{
    ...
    if (mTTSEngine)
        delete mTTSEngine;
}