6 #include "VoiceNavigationModel.h"
10 #include "MarbleDirs.h"
11 #include "MarbleDebug.h"
16 class VoiceNavigationModelPrivate
22 bool announcementDone;
23 bool turnInstructionDone;
26 announcementDone =
false;
27 turnInstructionDone =
false;
31 VoiceNavigationModel* m_parent;
35 bool m_speakerEnabled;
43 qreal m_lastDistanceTraversed;
45 GeoDataLineString m_lastRoutePath;
47 Maneuver::Direction m_lastTurnType;
49 GeoDataCoordinates m_lastTurnPoint;
55 bool m_destinationReached;
61 explicit VoiceNavigationModelPrivate( VoiceNavigationModel* parent );
67 QString distanceAudioFile( qreal dest )
const;
69 QString turnTypeAudioFile( Maneuver::Direction turnType, qreal distance );
71 QString announcementText( Maneuver::Direction turnType, qreal distance );
73 void updateInstruction(
const RouteSegment &segment, qreal distance, Maneuver::Direction turnType );
75 void updateInstruction(
const QString &name );
77 void initializeMaps();
80 VoiceNavigationModelPrivate::VoiceNavigationModelPrivate( VoiceNavigationModel* parent ) :
82 m_speakerEnabled( true ),
83 m_lastDistance( 0.0 ),
84 m_lastDistanceTraversed( 0.0 ),
85 m_lastTurnType( Maneuver::
Unknown ),
86 m_destinationReached( false ),
92 void VoiceNavigationModelPrivate::reset()
95 m_lastDistanceTraversed = 0.0;
98 QString VoiceNavigationModelPrivate::audioFile(
const QString &name )
const
104 if ( m_speakerEnabled ) {
105 QString const audioTemplate =
"%1/%2.%3";
106 for(
const QString &format: formats ) {
107 QString const result = audioTemplate.
arg( m_speaker, name, format );
109 if ( audioFile.exists() ) {
115 QString const audioTemplate =
"audio/%1.%2";
116 for(
const QString &format: formats ) {
117 QString const result = MarbleDirs::path( audioTemplate.
arg( name, format ) );
127 QString VoiceNavigationModelPrivate::distanceAudioFile( qreal dest )
const
129 if ( dest > 0.0 && dest < 900.0 ) {
130 qreal minDistance = 0.0;
131 int targetDistance = 0;
133 distances << 50 << 80 << 100 << 200 << 300 << 400 << 500 << 600 << 700 << 800;
134 for(
int distance: distances ) {
136 qreal currentDistance = qAbs( distance - dest );
137 if ( !file.
isEmpty() && ( minDistance == 0.0 || currentDistance < minDistance ) ) {
138 minDistance = currentDistance;
143 if ( targetDistance > 0 ) {
151 QString VoiceNavigationModelPrivate::turnTypeAudioFile( Maneuver::Direction turnType, qreal distance )
153 bool const announce =
distance >= 75;
155 if ( m_speakerEnabled &&
map.contains( turnType ) ) {
156 return audioFile( map[turnType] );
159 return audioFile( announce ?
"ListEnd" :
"AppPositive" );
162 QString VoiceNavigationModelPrivate::announcementText( Maneuver::Direction turnType, qreal distance )
165 if (distance >= 75) {
166 announcementText =
QString(
"In "+distanceAudioFile(distance)+
" meters, ");
169 case Maneuver::Continue:
170 case Maneuver::Straight:
171 announcementText +=
QString(
"Continue straight");
173 case Maneuver::SlightRight:
174 announcementText +=
QString(
"Turn slight right");
176 case Maneuver::SlightLeft:
177 announcementText +=
QString(
"Turn slight left");
179 case Maneuver::Right:
180 case Maneuver::SharpRight:
181 announcementText +=
QString(
"Turn right");
184 case Maneuver::SharpLeft:
185 announcementText +=
QString(
"Turn left");
187 case Maneuver::TurnAround:
188 announcementText +=
QString(
"Take a U-turn");
190 case Maneuver::ExitLeft:
191 announcementText +=
QString(
"Exit left");
193 case Maneuver::ExitRight:
194 announcementText +=
QString(
"Exit right");
196 case Maneuver::RoundaboutFirstExit:
197 announcementText +=
QString(
"Take the first exit");
199 case Maneuver::RoundaboutSecondExit:
200 announcementText +=
QString(
"Take the second exit");
202 case Maneuver::RoundaboutThirdExit:
203 announcementText +=
QString(
"Take the third exit");
206 announcementText =
QString(
"");
209 return announcementText;
212 void VoiceNavigationModelPrivate::updateInstruction(
const RouteSegment & segment, qreal distance, Maneuver::Direction turnType )
214 QString turnTypeAudio = turnTypeAudioFile( turnType, distance );
215 if ( turnTypeAudio.
isEmpty() ) {
216 mDebug() <<
"Missing audio file for turn type " << turnType <<
" and speaker " << m_speaker;
221 m_queue << turnTypeAudio;
222 m_announcementText = announcementText(turnType, distance);
223 qreal nextSegmentDistance = segment.nextRouteSegment().distance();
224 Maneuver::Direction nextSegmentDirection = segment.nextRouteSegment().nextRouteSegment().maneuver().direction();
225 if (!m_announcementText.isEmpty() && distance < 75 && nextSegmentDistance != 0 && nextSegmentDistance < 75) {
226 QString nextSegmentAnnouncementText = announcementText(nextSegmentDirection, nextSegmentDistance);
227 if (!nextSegmentAnnouncementText.
isEmpty()) {
228 m_announcementText +=
QLatin1String(
", then ") + nextSegmentAnnouncementText;
231 emit m_parent->instructionChanged();
234 void VoiceNavigationModelPrivate::updateInstruction(
const QString &name )
237 m_queue << audioFile( name );
238 m_announcementText =
name;
239 emit m_parent->instructionChanged();
242 void VoiceNavigationModelPrivate::initializeMaps()
244 m_turnTypeMap.clear();
245 m_announceMap.clear();
247 m_announceMap[Maneuver::Continue] =
"Straight";
250 m_announceMap[Maneuver::Straight] =
"Straight";
251 m_announceMap[Maneuver::SlightRight] =
"AhKeepRight";
252 m_announceMap[Maneuver::Right] =
"AhRightTurn";
253 m_announceMap[Maneuver::SharpRight] =
"AhRightTurn";
254 m_announceMap[Maneuver::TurnAround] =
"AhUTurn";
255 m_announceMap[Maneuver::SharpLeft] =
"AhLeftTurn";
256 m_announceMap[Maneuver::Left] =
"AhLeftTurn";
257 m_announceMap[Maneuver::SlightLeft] =
"AhKeepLeft";
258 m_announceMap[Maneuver::RoundaboutFirstExit] =
"RbExit1";
259 m_announceMap[Maneuver::RoundaboutSecondExit] =
"RbExit2";
260 m_announceMap[Maneuver::RoundaboutThirdExit] =
"RbExit3";
261 m_announceMap[Maneuver::ExitLeft] =
"AhExitLeft";
262 m_announceMap[Maneuver::ExitRight] =
"AhExitRight";
264 m_turnTypeMap[Maneuver::Continue] =
"Straight";
267 m_turnTypeMap[Maneuver::Straight] =
"Straight";
268 m_turnTypeMap[Maneuver::SlightRight] =
"BearRight";
269 m_turnTypeMap[Maneuver::Right] =
"TurnRight";
270 m_turnTypeMap[Maneuver::SharpRight] =
"SharpRight";
271 m_turnTypeMap[Maneuver::TurnAround] =
"UTurn";
272 m_turnTypeMap[Maneuver::SharpLeft] =
"SharpLeft";
273 m_turnTypeMap[Maneuver::Left] =
"TurnLeft";
274 m_turnTypeMap[Maneuver::SlightLeft] =
"BearLeft";
275 m_turnTypeMap[Maneuver::RoundaboutFirstExit] =
"";
276 m_turnTypeMap[Maneuver::RoundaboutSecondExit] =
"";
277 m_turnTypeMap[Maneuver::RoundaboutThirdExit] =
"";
278 m_turnTypeMap[Maneuver::ExitLeft] =
"TurnLeft";
279 m_turnTypeMap[Maneuver::ExitRight] =
"TurnRight";
282 VoiceNavigationModel::VoiceNavigationModel(
QObject *parent ) :
283 QObject( parent ), d( new VoiceNavigationModelPrivate( this ) )
288 VoiceNavigationModel::~VoiceNavigationModel()
293 QString VoiceNavigationModel::speaker()
const
298 void VoiceNavigationModel::setSpeaker(
const QString &speaker)
300 if ( speaker != d->m_speaker ) {
302 if ( !speakerDir.
exists() ) {
303 d->m_speaker = MarbleDirs::path(
QLatin1String(
"/audio/speakers/") + speaker);
305 d->m_speaker = speaker;
308 emit speakerChanged();
309 emit previewChanged();
313 bool VoiceNavigationModel::isSpeakerEnabled()
const
315 return d->m_speakerEnabled;
318 void VoiceNavigationModel::setSpeakerEnabled(
bool enabled )
320 if ( enabled != d->m_speakerEnabled ) {
321 d->m_speakerEnabled = enabled;
322 emit isSpeakerEnabledChanged();
323 emit previewChanged();
327 void VoiceNavigationModel::reset()
332 void 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 ?
"You have arrived at your destination" :
"AppPositive" );
350 if ( distanceTarget > 150 ) {
351 d->m_destinationReached =
false;
354 if ( deviated && !d->m_deviated ) {
355 d->updateInstruction( d->m_speakerEnabled ?
"Deviated from the route" :
"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;
395 QString VoiceNavigationModel::preview()
const
397 return d->audioFile( d->m_speakerEnabled ?
"The Marble team wishes you a pleasant and safe journey!" :
"AppPositive" );
400 QString VoiceNavigationModel::instruction()
const
402 return d->m_announcementText;
407 #include "moc_VoiceNavigationModel.cpp"