6#include "VoiceNavigationModel.h" 
   10#include "MarbleDebug.h" 
   11#include "MarbleDirs.h" 
   16class VoiceNavigationModelPrivate
 
   20        bool announcementDone;
 
   21        bool turnInstructionDone;
 
   25            announcementDone = 
false;
 
   26            turnInstructionDone = 
false;
 
   30    VoiceNavigationModel *m_parent = 
nullptr;
 
   34    bool m_speakerEnabled;
 
   36    QMap<Maneuver::Direction, QString> m_turnTypeMap;
 
   38    QMap<Maneuver::Direction, QString> m_announceMap;
 
   42    qreal m_lastDistanceTraversed;
 
   44    GeoDataLineString m_lastRoutePath;
 
   46    Maneuver::Direction m_lastTurnType;
 
   48    GeoDataCoordinates m_lastTurnPoint;
 
   52    QString m_announcementText;
 
   54    bool m_destinationReached;
 
   58    QList<Announcement> m_announcementList;
 
   60    explicit VoiceNavigationModelPrivate(VoiceNavigationModel *parent);
 
   64    QString audioFile(
const QString &name) 
const;
 
   66    QString distanceAudioFile(qreal dest) 
const;
 
   68    QString turnTypeAudioFile(Maneuver::Direction turnType, qreal distance);
 
   70    QString announcementText(Maneuver::Direction turnType, qreal distance);
 
   72    void updateInstruction(
const RouteSegment &segment, qreal distance, Maneuver::Direction turnType);
 
   74    void updateInstruction(
const QString &name);
 
   76    void initializeMaps();
 
   79VoiceNavigationModelPrivate::VoiceNavigationModelPrivate(VoiceNavigationModel *parent)
 
   81    , m_speakerEnabled(true)
 
   83    , m_lastDistanceTraversed(0.0)
 
   84    , m_lastTurnType(Maneuver::
Unknown)
 
   85    , m_destinationReached(false)
 
   91void VoiceNavigationModelPrivate::reset()
 
   94    m_lastDistanceTraversed = 0.0;
 
   97QString VoiceNavigationModelPrivate::audioFile(
const QString &name)
 const 
  102    QStringList const formats = 
QStringList() << QStringLiteral(
"ogg") << QStringLiteral(
"mp3") << QStringLiteral(
"wav");
 
  103    if (m_speakerEnabled) {
 
  104        QString const audioTemplate = QStringLiteral(
"%1/%2.%3");
 
  105        for (
const QString &format : formats) {
 
  106            QString const result = audioTemplate.
arg(m_speaker, name, format);
 
  108            if (audioFile.exists()) {
 
  114    QString const audioTemplate = QStringLiteral(
"audio/%1.%2");
 
  115    for (
const QString &format : formats) {
 
  116        QString const result = MarbleDirs::path(audioTemplate.
arg(name, format));
 
  126QString VoiceNavigationModelPrivate::distanceAudioFile(qreal dest)
 const 
  128    if (dest > 0.0 && dest < 900.0) {
 
  129        qreal minDistance = 0.0;
 
  130        int targetDistance = 0;
 
  132        distances << 50 << 80 << 100 << 200 << 300 << 400 << 500 << 600 << 700 << 800;
 
  133        for (
int distance : std::as_const(distances)) {
 
  135            qreal currentDistance = qAbs(distance - dest);
 
  136            if (!file.
isEmpty() && (minDistance == 0.0 || currentDistance < minDistance)) {
 
  137                minDistance = currentDistance;
 
  142        if (targetDistance > 0) {
 
  150QString VoiceNavigationModelPrivate::turnTypeAudioFile(Maneuver::Direction turnType, qreal distance)
 
  152    bool const announce = 
distance >= 75;
 
  154    if (m_speakerEnabled && 
map.contains(turnType)) {
 
  155        return audioFile(map[turnType]);
 
  158    return audioFile(announce ? QStringLiteral(
"ListEnd") : QStringLiteral(
"AppPositive"));
 
  161QString VoiceNavigationModelPrivate::announcementText(Maneuver::Direction turnType, qreal distance)
 
  164    if (distance >= 75) {
 
  165        announcementText = 
QString(QStringLiteral(
"In ") + distanceAudioFile(distance) + QStringLiteral(
" meters, "));
 
  168    case Maneuver::Continue:
 
  169    case Maneuver::Straight:
 
  170        announcementText += QStringLiteral(
"Continue straight");
 
  172    case Maneuver::SlightRight:
 
  173        announcementText += QStringLiteral(
"Turn slight right");
 
  175    case Maneuver::SlightLeft:
 
  176        announcementText += QStringLiteral(
"Turn slight left");
 
  178    case Maneuver::Right:
 
  179    case Maneuver::SharpRight:
 
  180        announcementText += QStringLiteral(
"Turn right");
 
  183    case Maneuver::SharpLeft:
 
  184        announcementText += QStringLiteral(
"Turn left");
 
  186    case Maneuver::TurnAround:
 
  187        announcementText += QStringLiteral(
"Take a U-turn");
 
  189    case Maneuver::ExitLeft:
 
  190        announcementText += QStringLiteral(
"Exit left");
 
  192    case Maneuver::ExitRight:
 
  193        announcementText += QStringLiteral(
"Exit right");
 
  195    case Maneuver::RoundaboutFirstExit:
 
  196        announcementText += QStringLiteral(
"Take the first exit");
 
  198    case Maneuver::RoundaboutSecondExit:
 
  199        announcementText += QStringLiteral(
"Take the second exit");
 
  201    case Maneuver::RoundaboutThirdExit:
 
  202        announcementText += QStringLiteral(
"Take the third exit");
 
  205        announcementText = QStringLiteral(
"");
 
  208    return announcementText;
 
  211void VoiceNavigationModelPrivate::updateInstruction(
const RouteSegment &segment, qreal distance, Maneuver::Direction turnType)
 
  213    QString turnTypeAudio = turnTypeAudioFile(turnType, distance);
 
  215        mDebug() << 
"Missing audio file for turn type " << turnType << 
" and speaker " << m_speaker;
 
  220    m_queue << turnTypeAudio;
 
  221    m_announcementText = announcementText(turnType, distance);
 
  222    qreal nextSegmentDistance = segment.nextRouteSegment().distance();
 
  223    Maneuver::Direction nextSegmentDirection = segment.nextRouteSegment().nextRouteSegment().maneuver().direction();
 
  224    if (!m_announcementText.isEmpty() && distance < 75 && nextSegmentDistance != 0 && nextSegmentDistance < 75) {
 
  225        QString nextSegmentAnnouncementText = announcementText(nextSegmentDirection, nextSegmentDistance);
 
  226        if (!nextSegmentAnnouncementText.
isEmpty()) {
 
  227            m_announcementText += 
QLatin1StringView(
", then ") + nextSegmentAnnouncementText;
 
  230    Q_EMIT m_parent->instructionChanged();
 
  233void VoiceNavigationModelPrivate::updateInstruction(
const QString &name)
 
  236    m_queue << audioFile(name);
 
  237    m_announcementText = 
name;
 
  238    Q_EMIT m_parent->instructionChanged();
 
  241void VoiceNavigationModelPrivate::initializeMaps()
 
  243    m_turnTypeMap.
clear();
 
  244    m_announceMap.clear();
 
  246    m_announceMap[Maneuver::Continue] = QStringLiteral(
"Straight");
 
  249    m_announceMap[Maneuver::Straight] = QStringLiteral(
"Straight");
 
  250    m_announceMap[Maneuver::SlightRight] = QStringLiteral(
"AhKeepRight");
 
  251    m_announceMap[Maneuver::Right] = QStringLiteral(
"AhRightTurn");
 
  252    m_announceMap[Maneuver::SharpRight] = QStringLiteral(
"AhRightTurn");
 
  253    m_announceMap[Maneuver::TurnAround] = QStringLiteral(
"AhUTurn");
 
  254    m_announceMap[Maneuver::SharpLeft] = QStringLiteral(
"AhLeftTurn");
 
  255    m_announceMap[Maneuver::Left] = QStringLiteral(
"AhLeftTurn");
 
  256    m_announceMap[Maneuver::SlightLeft] = QStringLiteral(
"AhKeepLeft");
 
  257    m_announceMap[Maneuver::RoundaboutFirstExit] = QStringLiteral(
"RbExit1");
 
  258    m_announceMap[Maneuver::RoundaboutSecondExit] = QStringLiteral(
"RbExit2");
 
  259    m_announceMap[Maneuver::RoundaboutThirdExit] = QStringLiteral(
"RbExit3");
 
  260    m_announceMap[Maneuver::ExitLeft] = QStringLiteral(
"AhExitLeft");
 
  261    m_announceMap[Maneuver::ExitRight] = QStringLiteral(
"AhExitRight");
 
  263    m_turnTypeMap[Maneuver::Continue] = QStringLiteral(
"Straight");
 
  266    m_turnTypeMap[Maneuver::Straight] = QStringLiteral(
"Straight");
 
  267    m_turnTypeMap[Maneuver::SlightRight] = QStringLiteral(
"BearRight");
 
  268    m_turnTypeMap[Maneuver::Right] = QStringLiteral(
"TurnRight");
 
  269    m_turnTypeMap[Maneuver::SharpRight] = QStringLiteral(
"SharpRight");
 
  270    m_turnTypeMap[Maneuver::TurnAround] = QStringLiteral(
"UTurn");
 
  271    m_turnTypeMap[Maneuver::SharpLeft] = QStringLiteral(
"SharpLeft");
 
  272    m_turnTypeMap[Maneuver::Left] = QStringLiteral(
"TurnLeft");
 
  273    m_turnTypeMap[Maneuver::SlightLeft] = QStringLiteral(
"BearLeft");
 
  274    m_turnTypeMap[Maneuver::RoundaboutFirstExit] = 
QString();
 
  275    m_turnTypeMap[Maneuver::RoundaboutSecondExit] = 
QString();
 
  276    m_turnTypeMap[Maneuver::RoundaboutThirdExit] = 
QString();
 
  277    m_turnTypeMap[Maneuver::ExitLeft] = QStringLiteral(
"TurnLeft");
 
  278    m_turnTypeMap[Maneuver::ExitRight] = QStringLiteral(
"TurnRight");
 
  281VoiceNavigationModel::VoiceNavigationModel(
QObject *parent)
 
  283    , d(new VoiceNavigationModelPrivate(this))
 
  288VoiceNavigationModel::~VoiceNavigationModel()
 
  293QString VoiceNavigationModel::speaker()
 const 
  298void VoiceNavigationModel::setSpeaker(
const QString &speaker)
 
  300    if (speaker != d->m_speaker) {
 
  302        if (!speakerDir.
exists()) {
 
  303            d->m_speaker = MarbleDirs::path(
QLatin1StringView(
"/audio/speakers/") + speaker);
 
  305            d->m_speaker = speaker;
 
  308        Q_EMIT speakerChanged();
 
  309        Q_EMIT previewChanged();
 
  313bool VoiceNavigationModel::isSpeakerEnabled()
 const 
  315    return d->m_speakerEnabled;
 
  318void VoiceNavigationModel::setSpeakerEnabled(
bool enabled)
 
  320    if (enabled != d->m_speakerEnabled) {
 
  321        d->m_speakerEnabled = enabled;
 
  322        Q_EMIT isSpeakerEnabledChanged();
 
  323        Q_EMIT previewChanged();
 
  327void VoiceNavigationModel::reset()
 
  332void VoiceNavigationModel::update(
const Route &route, qreal distanceManuever, qreal distanceTarget, 
bool deviated)
 
  334    if (d->m_lastRoutePath != route.path()) {
 
  335        d->m_announcementList.clear();
 
  336        d->m_announcementList.resize(route.size());
 
  337        d->m_lastRoutePath = route.path();
 
  340    if (d->m_destinationReached && distanceTarget < 250) {
 
  344    if (!d->m_destinationReached && distanceTarget < 50) {
 
  345        d->m_destinationReached = 
true;
 
  346        d->updateInstruction(d->m_speakerEnabled ? QStringLiteral(
"You have arrived at your destination") : QStringLiteral(
"AppPositive"));
 
  350    if (distanceTarget > 150) {
 
  351        d->m_destinationReached = 
false;
 
  354    if (deviated && !d->m_deviated) {
 
  355        d->updateInstruction(d->m_speakerEnabled ? QStringLiteral(
"Deviated from the route") : QStringLiteral(
"ListEnd"));
 
  357    d->m_deviated = deviated;
 
  362    Maneuver::Direction turnType = route.currentSegment().nextRouteSegment().maneuver().direction();
 
  363    if (!(d->m_lastTurnPoint == route.currentSegment().nextRouteSegment().maneuver().position()) || turnType != d->m_lastTurnType) {
 
  364        d->m_lastTurnPoint = route.currentSegment().nextRouteSegment().maneuver().position();
 
  368    int index = route.indexOf(route.currentSegment());
 
  370    qreal 
const distanceTraversed = route.currentSegment().distance() - distanceManuever;
 
  371    bool const minDistanceTraversed = d->m_lastDistanceTraversed < 40 && distanceTraversed >= 40;
 
  372    bool const announcementAfterTurn = minDistanceTraversed && distanceManuever >= 75;
 
  373    bool const announcement = (d->m_lastDistance > 850 || announcementAfterTurn) && distanceManuever <= 850;
 
  374    bool const turn = (d->m_lastDistance == 0 || d->m_lastDistance > 75) && distanceManuever <= 75;
 
  376    bool const announcementDone = d->m_announcementList[index].announcementDone;
 
  377    bool const turnInstructionDone = d->m_announcementList[index].turnInstructionDone;
 
  379    if ((announcement && !announcementDone) || (turn && !turnInstructionDone)) {
 
  380        d->updateInstruction(route.currentSegment(), distanceManuever, turnType);
 
  381        VoiceNavigationModelPrivate::Announcement &curAnnouncement = d->m_announcementList[index];
 
  383            curAnnouncement.announcementDone = 
true;
 
  386            curAnnouncement.turnInstructionDone = 
true;
 
  390    d->m_lastTurnType = turnType;
 
  391    d->m_lastDistance = distanceManuever;
 
  392    d->m_lastDistanceTraversed = distanceTraversed;
 
  395QString VoiceNavigationModel::preview()
 const 
  397    return d->audioFile(d->m_speakerEnabled ? QStringLiteral(
"The Marble team wishes you a pleasant and safe journey!") : QStringLiteral(
"AppPositive"));
 
  400QString VoiceNavigationModel::instruction()
 const 
  402    return d->m_announcementText;
 
  407#include "moc_VoiceNavigationModel.cpp" 
QString name(StandardAction id)
 
Binds a QML item to a specific geodetic location in screen coordinates.
 
KOSM_EXPORT double distance(const std::vector< const OSM::Node * > &path, Coordinate coord)
 
bool exists(const QString &path)
 
QString arg(Args &&... args) const const
 
bool isEmpty() const const
 
QString number(double n, char format, int precision)
 
QFuture< void > map(Iterator begin, Iterator end, MapFunctor &&function)