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;
42 qreal m_lastDistanceTraversed;
44 GeoDataLineString m_lastRoutePath;
46 Maneuver::Direction m_lastTurnType;
48 GeoDataCoordinates m_lastTurnPoint;
54 bool m_destinationReached;
60 explicit VoiceNavigationModelPrivate(VoiceNavigationModel *parent);
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(GameStandardAction 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)